How i could improve this age chat status indicator system?

Hello everyone!

A few months ago, I partially developed (helped by an AI) a system for age chat communication capabilities, and I even published this post describing it completely.

This is the full post:
[V1.0] Chat Capabilities Checking System

So i have been thinking about it a bit, and I believe it needs some improvements, such as making it more modular, optimizing it or even improving the visual indicator. But the problem is, I don’t know where to begin.

Here is the code from the client-side script:

--!strict

--> Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--> Remotes
local ChatCheck = ReplicatedStorage:FindFirstChild("ChatCheckFunction")

--> Definitions
local LocalPlayer = Players.LocalPlayer :: Player
local LocalUserId = LocalPlayer.UserId :: number

--> Array
local LocalCombinations: {Combination} = {}

--> Types
type Combination = {
	
	Ab: boolean, -- Result
	Ea: number, -- Self player uid (Local)
	Eb: number -- Other player uid
	
}

-- This function returns the passed values casted to the combination type
local function CreateCombination(Attribute: boolean, EntityA: number, EntityB: number) : Combination
	
	return {
		Ab = Attribute,
		Ea = EntityA,
		Eb = EntityB
	}
	
end

-- This function calls the server for verify the chat status of the other player (Eb) towards you (Ea) from the combination; returns a boolean
local function ChatCheckFunction(Ea: number, Eb: number) : boolean

	local Result = ChatCheck:InvokeServer(Ea, Eb)
	return Result
	
end

-- This function shows a text over the other player head from the combination, depending of flag Ab 
local function DisplayIcon()
	
	for _, combination in ipairs(LocalCombinations) do
		
		local TargetPlayer = Players:GetPlayerByUserId(combination.Eb)
		
		if TargetPlayer then
			
			-- This avoids errors during the character load
			local Character = TargetPlayer.Character or TargetPlayer.CharacterAdded:Wait()
			local HRP = Character:FindFirstChild("HumanoidRootPart") or Character:WaitForChild("HumanoidRootPart")

			-- This avoids duplications
			local ExistingBillboard = HRP:FindFirstChild("ChatCheckDisplay")
			
			if ExistingBillboard then
				ExistingBillboard:Destroy()
			end

			local Display = Instance.new("BillboardGui")
			Display.Name = "ChatCheckDisplay"
			Display.Size = UDim2.new(1, 1, 1, 1)
			Display.AlwaysOnTop = false
			Display.MaxDistance = 30
			Display.StudsOffsetWorldSpace = Vector3.new(0, 5, 0)
			Display.Parent = HRP

			local Label = Instance.new("TextLabel")
			Label.Size = UDim2.new(1, 1, 1, 1)
			Label.Name = "Text"
			Label.BackgroundTransparency = 1
			Label.TextColor3 = Color3.new(1, 1, 1)
			
			if combination.Ab == true then
				Label.Text = "✅ This player can chat with you"
			elseif combination.Ab == false then
				Label.Text = "🚫 This player cant chat with you"
			else
				warn("Unexpected value!", tostring(combination.Ab))
			end
			
			Label.Parent = Display
		end
	end
end

--[[ [DEBUG ONLY] Displays all the combinations at the moment
local function PrintCombinations()
	
	if #LocalCombinations == 0 then
		print("No combinations at the moment")
	elseif #LocalCombinations ~= 0 then
		
		for index, Combination in ipairs(LocalCombinations) do
			print("Combination", index, ": (", Combination.Ab, ",", Combination.Ea, ",", Combination.Eb, ")")
		end	
		
	end
	
end
]]--

-- Sums a new combination with the player passed
local function SumCombination(player: Player)

	if player.UserId ~= LocalUserId then -- This avoids combinations with oneself, like (1, 1)

		local result = ChatCheckFunction(LocalUserId, player.UserId)
		local combination = CreateCombination(result, LocalUserId, player.UserId)

		table.insert(LocalCombinations, combination)
		--print("New combination:", LocalUserId, player.UserId)

		--PrintCombinations()
		DisplayIcon()

	end

end

-- Substracts the combination that matches with the player passed
local function SubCombination(player: Player)
	
	-- Reverse read, check: https://devforum.roblox.com/t/how-to-flip-an-array/1549982
	for i = #LocalCombinations, 1, -1 do 
	
		if LocalCombinations[i].Eb == player.UserId then
			
			table.remove(LocalCombinations, i)
			
		end
		
	end

	--print("Player left:", player.UserId)
	
	--PrintCombinations()
	DisplayIcon()
	
end

-- Processing actual players (Runs once!)
for _, player in ipairs(Players:GetPlayers()) do	
	SumCombination(player)
end

--> Events
Players.PlayerAdded:Connect(SumCombination)
Players.PlayerRemoving:Connect(SubCombination)

Also there is the server-side script:

--!strict

--> Services
local TextChatService = game:GetService("TextChatService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

--> Remotes
local ChatCheck = ReplicatedStorage:FindFirstChild("ChatCheckFunction")

ChatCheck.OnServerInvoke = function(player: Player, Ea: number, Eb: number) : boolean

	local success, result = pcall(function(Ea, Eb)
		return TextChatService:CanUsersChatAsync(Ea, Eb)
	end)

	if success then
		return result -- False or True
	else
		warn("Error on CanUsersChatAsync: " .. tostring(result))
		return false
	end

end

I would really like to discuss the code so i can release a new version for the community!

5 Likes
ChatCheck.OnServerInvoke = function(player: Player, Ea: number, Eb: number) : boolean

name your variables correctly

2 Likes

Yes, regarding that, “Ea” and “Eb” are an abbreviation for “EntityA/B”, sounds stupid i know :sob:

Also i noticed that when the server is invoked, Ea is not required because i could use player.UserId instead!

Have you noticed more fixes?

I’d probably make the server invoke try again if the call fails, or have the client try again if they get an unexpected value

1 Like

I hadnt thought of that, that would be useful for controlling errors. Thank you for suggesting it!