There should be an event for when the chat window has loaded (TextChatService)

If you try to display a chat message to the user as soon as they join the experience, the message does not display because the chat window has not appeared soon enough.

Consider this clientside code, which is supposed to display “Testing 123” to the chat window:

local textChatService = game:GetService("TextChatService")
local textChannels = textChatService:WaitForChild("TextChannels")
local defaultChannel = textChannels:WaitForChild("RBXGeneral")

defaultChannel:DisplaySystemMessage("Testing 123")

The chat works, but the above code had no effect because the window wasn’t loaded in time:

Doing this trick doesn’t fix it either:

image

There doesn’t seem to be a “window loaded” event on any of the relevant Creator pages:

Using WaitForChild on the ChatWindowConfiguration instance doesn’t work, and neither does disabling it in Studio followed by re-enabling it through the script like so:

local Config = textChatService:WaitForChild("ChatWindowConfiguration")
Config.Enabled = true

defaultChannel:DisplaySystemMessage("Testing 123")

It was worth a shot :upside_down_face:

The only option I know of that remains is artificial yielding:

task.wait(1)
defaultChannel:DisplaySystemMessage("Testing 123")

Finally, it works!

But task.wait is icky. Without a clear signal that lets the program know when the chat window has loaded, developers are stuck with a race condition where the safest thing to do is wait a few seconds before running any chat window-related code.

It would be nice if a “Chat Window Loaded” event existed to yield for, since there are many cases where the developer may want to display system messages in the chat as soon as a user joins the experience.

Some method to check the loaded condition, like a “Chat Window Has Loaded()” would be a good precondition as well.

This condition/yield duality has its place in the aforementioned “game:IsLoaded()? no? then game.Loaded:Wait()” check as well, which I’ve seen used commonly.


Edit 2024-02-16T22:39:00Z:

The Legacy Chat System does not have this same problem.

With TextChatService.ChatVersion set to LegacyChatService, the following clientside code displays a system chat message without needing a task.wait at the top:

local msgText = "This is a system message"
game:GetService("StarterGui"):SetCore("ChatMakeSystemMessage", {Text = msgText})

Related:


Repro: send chat on join.rbxm (1.6 KB)

  • Place the folder in game.ReplicatedStorage.

  • Ensure game.TextChatService.ChatVersion is set to TextChatService.

  • When testing any of the scripts, ensure only one is enabled at a time.

  • Ensure game.Players.CharacterAutoLoads is disabled for this test just in case it slows down the loading time.

  • If the Artificial Yield script doesn’t work, just increase the yielding time. Try task.wait(5), for example.

21 Likes

I feel like my method is a bit sloppy, but it’s worth a shot :upside_down_face: :

function ChatLoaded(): RBXScriptSignal
	local TextChatService = game:GetService("TextChatService")
	local event = Instance.new("BindableEvent")
	local function ChannelAdded(channel: Instance)
		if not channel:IsA("TextChannel") then return end
		channel.MessageReceived:Connect(function(message)
			event:Fire() return
		end)
	end
	if TextChatService.ChatVersion.Name == "LegacyChatService" then
		-- do nothing lol (it works fine)
		game["Run Service"].RenderStepped:Connect(function()
			event:Fire() return
		end)
	else
		local Channels = TextChatService:WaitForChild("TextChannels")
		for _, channel in pairs(Channels:GetChildren()) do
			ChannelAdded(channel)
		end
		Channels.ChildAdded:Connect(ChannelAdded)
	end
	return event.Event
end

To use it, just use ChatLoaded():Once, as shown by the below code:

ChatLoaded():Once(function()
	-- do stuff here ig
end)

I literally just created my own event just for this :skull:
But at least it works without having to artificially yield :smile:

If you’re wondering how it works, it gets triggered when a system message (that you don’t see) gets received by the client, which is an indicator that the chat is loaded…(because how can you receive a message if the chat isn’t loaded?) (how can i be so delusional :skull: )

Then the debounce makes it so that it only works one time (it won’t trigger a second time) Actually that wasn’t necessary :skull:

As you can see, it does it instantaneously, without having to worry about any artificial delays:

Still to this day…legends have it that I’m still working to find a better method without having to rely on that one string…

Edit: I have cracked the code for it lol (nvm the hunt is still ongoing lol)
Edit #2: It’s probably now even sloppier than before (try at your own risk :skull: )
Edit #3: If you have made it so far if you’re wondering why this works…trust me bro :skull:
Edit #4: If you’re wondering why this has so many edits, idk…probably because this is PLAGUED with mistakes lol
Edit #5: Realized I didn’t need to use a debounce at all :skull:
Edit #6: You must have ChatTranslation enabled for this to work :skull:

4 Likes