Cannot disconnect an event

I’ve been trying to disconnect an event as soon as it is run. However, the connection is always nil when I try to index it from the inside of the function.

local data_stream

data_stream = Core.Remotes.data_stream.OnClientEvent:Connect(function(data)
	print(data_stream, "inside")
	print("Player data received from server.")
	Core.Data = data
	Core.Loaded:Fire()
	data_stream:Disconnect() -- errors here, attempt to index nil with disconnect.
end)

print(data_stream, "outside")

The output is:

nil Inside
Players.mekeldev.PlayerScripts.ClientScript:12: attempt to index nil with ‘Disconnect’
Connection outside

2 Likes

I ran virtually the same code and didn’t get that error. Any other context you can provide?

image

Depending on your case you could also use Wait instead of connecting an event. I’m not entirely sure, without deeper context, if you need a connection here or not. Only thing about Wait is a potential memory leak where the event is never fired, but if that’s covered predictably then no problem.

local data = Core.Remotes.data_stream.OnClientEvent:Wait()
Core.Data = data
Core.Loaded:Fire()
3 Likes

data_stream is a remote event that sends the player their data as soon as it is loaded from the data store so the client can have their own local copy. Core is a module script that holds the player’s data, references to remotes/bindables, and other important game items.

If I put a task.wait() (which I don’t want to do) before I disconnect the event, it seems to work. Otherwise, data_stream is nil when indexed inside.

Using OnClientEvent:Wait() might work, but what happens if the server sends the data before the client runs the script?

Could you try deferring the disconnection for me? Since I can’t repro your problem and you said that the disconnection works if you use task.wait, I figure that data_stream might be nil as in when the event handler is running. I don’t know how immediately you run Fire(All)Client(s) but that’s frankly strange since this code should work as is, unless I’m reading it wrong.

Core.Loaded:Fire()
-- If data_stream is nil, just remove the if statement and defer it only.
-- The if statement is a safeguard so you don't call on something that's nil.
if typeof(data_stream) == "RBXScriptConnection" and data_stream.Connected then
    task.defer(data_stream.Disconnect, data_stream)
end

You’re right though. If the server sends data before the client script starts yielding then the client script will permanently hang and leak memory. Not a good suggestion on my part, sorry.

2 Likes

For @mekeldev as well, yep don’t worry about this Remote events have a queue behavior it will return the first thing on the queue. You can test it out like this:

:Wait() Queue method
--Server
local remote = script.RemoteEvent

remote:FireAllClients("HELLOFROMSERVER")
--Client
local remote = script.Parent:WaitForChild("RemoteEvent")
print("Waiting on client")
wait(10)
print(remote.OnClientEvent:Wait()) --HelloFromServer
Normal event connection queue behavior
--Server
local remote = script.RemoteEvent

remote:FireAllClients("HELLOFROMSERVER")

local test = {1,2,3,4,5,125,15,12,312,312,3,12}

for i,v in pairs(test) do
	remote:FireAllClients(v)
end
--Client
local remote = script.Parent:WaitForChild("RemoteEvent")
print("Waiting on client")
wait(5)
remote.OnClientEvent:Connect(function(...)
print(...) --prints everything
end)

Also looking further this seems to be a bug, it’s nil because it fires before it returns anything.

3 Likes

This is definitely an issue that would be hard to debug, but I don’t think that should dissuade you from using it. If you just keep it in mind and don’t program the server to send the event before it’s appropriate then it’s not a problem.

Using :Connect instead of :Wait doesn’t actually solve the problem either, it just allows statements after it to run even if this part is broken, which is worse than having it completely break the entire game in a very obvious way because failing hard is easier to debug and better than hiding the bugs and hoping they don’t break the game at some point in the future. It still leaks the memory and prevents Core.Data from being set and Core.Loaded from being Fired.

EDIT: nvm @dthecoolest has the answer

2 Likes

I wasn’t too sure about the remote queueing behaviour - I know it’s been talked about, but never really wanted to rely on it myself - but thanks for confirming that for me! That helps me out too since I try to work around potential cases where my threads might permanently hang or race conditions.

Also, RoBama, that is a fair point, since I don’t assume OP’s code would work very well without having the data actually needed to execute game functions.

2 Likes

Using task.defer() works.

That seems to answer my question for now.

What about using a RemoteFunction from the client? Is the server guaranteed to run code first before the client? The only downside I see is when the datastore is too slow to fetch data/fails.

Not always, you could always add a wait() or something causes it to yield which changes the order.

But usually server starts then players join.

Try it out, I believe it’ll have a similar issue as it also has a queue system.