Waiting for a remote event yields unexpected results

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?

1 Like

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)
2 Likes

Why not keep track of a boolean which represents whether or not the player has sold for the first time, then when NotifySell is fired, run the code?

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.

2 Likes

You made me realize the problem when you said

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.

1 Like

This is effectively what I did - switch it to a regular BindableEvent, and it works. I didn’t realize RemoteEvents always queued.

1 Like

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.

1 Like

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 :slight_smile: Thanks!