I’m building a small intro tutorial for a game. One of the tasks that the player must complete during the tutorial is that they must go collect some resources and then sell it to an NPC.
I have it set up so that the server fires a remote event to the client whenever they complete certain tasks, like collect the resources and sell it. For example, when the server detects that the player has gone near the selling NPC, it fires NotifySell to the client to notify the client that it has just sold resources.
In my tutorial code, I am doing this:
DisplayDialog("Now go sell your items!")
NotifySell.OnClientEvent:Wait()
DisplayDialog("Very good! On to the next step...")
The idea is that the script will halt until the player sells, and then continue moving through different parts of the tutorial.
The problem, however, is that if the player goes to the NPC and sells some goods BEFORE that point in the tutorial, (i.e the event is fired ever at any time in the past), when it comes to that part in the tutorial, it acts as if it was just fired immediately.
I imagine this is because the event gets queued or something and only gets dequeued when I call :Wait(). How can I avoid this behavior?
I think the best solution in this instance is to actually bind some kind of function to the remote event, opposed to trying to use :Wait(). I think in theory :Wait() should work as you are trying to use it, but remote events weren’t really designed for such a use case.
Pseudocode alternative:
local dialogueTable = {"Now go sell your items!", "Very good! On to the next step...")
local currentDialogue = 0 -- sentinel value
local function ProgressDialogue()
currentDialogue = currentDialogue + 1
DisplayDialog(dialogueTable[currentDialogue])
end
ProgressDialogue() -- set up initial dialogue
NotifySell.OnClientEvent:Connect(function()
ProgressDialogue()
end)
You could create a bindable, and wait on that event.
local sellNow = false
local sellSignal = NotifySell.OnClientEvent:Connect(function()
if sellNow then
soldBindable:Fire()
end
end
DisplayDialog("Now go sell your items!")
sellNow = true
soldBindable.Event:Wait()
soldBindable:Destroy()
sellSignal:Disconnect()
DisplayDialog("Very good! On to the next step...")
Alternatively, you could just yield the thread.
local thread = coroutine.running()
local sellNow = false
local sellSignal = NotifySell.OnClientEvent:Connect(function()
if sellNow then
coroutine.resume(thread)
end
end
DisplayDialog("Now go sell your items!")
sellNow = true
coroutine.yield()
sellSignal:Disconnect()
DisplayDialog("Very good! On to the next step...")
You could also just run the code in the NotifySell connection, and just use some flag to check. There are a lot of ways to approach this, directly in the connection is the simplest. Of course you do lose the natural flow of the script, which the first methods can somewhat preserve.
remote events weren’t really designed for such a use case.
I switched it off from a remote event and instead made a regular BindableEvent fire from the client when it attempts to sell. It completely fixed the problem.
I suppose that remote events HAVE to queue like they do because you never know when/if the message will get through across the internet.
Yeah, bypassing the server completely is almost always the best option. It’s usually clean and the most responsive choice. Of course, if you need verification you would have to use a remote.
Luckily for this use case, I don’t care about verification - if someone wants to cheat their way through the tutorial, they won’t gain anything other than a lack of understanding about the way the game works Thanks!