I am trying to create a script that shows the recent chat history to a user from before they joined the experience (like the legacy Lua Chat System does).
DisplaySystemMessage seems to be my only option here. I’ve been trying to use it to show all of the recent messages as soon as a user joins, and have those messages displayed in order.
In this thread, I am going to simplify the problem by just using numbers rather than message logs.
Consider this code:
task.wait(1) local defaultChannel = game:GetService("TextChatService"):WaitForChild("TextChannels"):WaitForChild("RBXGeneral") for i = 1, 10 do defaultChannel:DisplaySystemMessage("Test " .. tostring(i)) print(i) end
As an aside, I don’t know an alternative to putting
task.wait(1) up there. There seems to be no way to yield for the chat box being ready, so without that task.wait, the code runs too fast for the messages to appear in chat.
Here is how the chat looks after running that code:
Even tossing in a
task.wait() doesn’t do the trick to keep the chat in proper order:
for i = 1, 10 do defaultChannel:DisplaySystemMessage("Test " .. tostring(i)) print(i) task.wait() end
I tried an alternative approach to force the messages into appearing in the right order. The idea is that the
TextChannel.MessageReceived events would be fired as soon as the message appears in chat, and so my loop would just yield for the latest message to have appeared before moving to display the next message.
Here is the new code:
local logSignature = game:GetService("HttpService"):GenerateGUID(false) -- attach logSignature to joining messages so they're distinct from regular ones local logIndex = 0 -- logIndex keeps track of the latest sent message local latestIndex = logIndex -- latestIndex keeps track of the latest received message local loggedMessageLoaded = Instance.new("BindableEvent") -- the loop uses this to yield for the latest message to be received defaultChannel.MessageReceived:Connect(function(TextChatMessage) local logData = string.split(TextChatMessage.Metadata, " ") local signature, index = logData, tonumber(logData) if signature == logSignature and index == logIndex then print("MESSAGE RECEIVED:", index) latestIndex = logIndex loggedMessageLoaded:Fire() end end) task.wait(1) for i = 1, 10 do logIndex += 1 local metadata = string.format("%s %s", logSignature, logIndex) defaultChannel:DisplaySystemMessage("Test " .. tostring(i), metadata) print("MESSAGE SENT:", logIndex) if latestIndex ~= logIndex then print("Yielding for message", logIndex, "to be received...") loggedMessageLoaded.Event:Wait() -- don't go to the next message until the last one has loaded end end
This does not work. Sure, the output looks as if everything is in order and nothing even needs to yield:
Everything is still out of order in the chat, though!
As an alternative to
TextChannel.MessageReceived, there is
TextChatService.OnIncomingMessage — this could be used to intercept chat messages and ensure the correct order before
returning to allow those messages to be displayed in the chat. However, this is not ideal:
I wouldn’t want to override any other modules in the experience which might be using that callback function. Regardless, when I tried this it still had the same problem
MessageReceived did — the chat did not match the output.
This behavior suggests a few things:
TextChannel:DisplaySystemMessage()is asynchronous, not yielding for the message to appear in chat before allowing the code to move on
TextChannel.MessageReceiveddoes not fire when messages actually appear in the chat
TextChatMessagePropertiesinstance, it takes some time before it’s displayed it the chat
There doesn’t seem to be any remaining ways to detect when a message actually appears in chat, and there is simply no way to ensure message order. I could use artificial yields by putting a
task.wait(0.1) at the bottom of the loop, but there could be chat messages by other users in-game while our system messages are still yielding. That messes up the intended effect of all the logged messages being side-by-side. Additionally, using artificial waits to ensure order is just bad practice — it creates a race condition, and may not always work, as seen with the
task.wait() example at the top of this thread.
It would make more sense to me if
DisplaySystemMessage yielded until the message appeared in the chat window. More events and also more explicit naming of events would help here as well.
Repro: dump many chats on join.rbxm (2.2 KB)
Place the folder in
game.TextChatService.ChatVersionis set to
When testing any of the scripts, ensure only one is enabled at a time.
game.Players.CharacterAutoLoadsis disabled for this test just in case it slows down the loading time.
If nothing shows in the chat, just increase the yielding time from right before where the loop begins. Try
task.wait(5), for example.