I’ve run into a weird bug where the behavior is inconsistent between studio and live games. I made a network module that finds a remote event on the client and parents it to nil, and every time it fires the remote, it is parented it to the game the instant before.
This helps to prevent exploiters from accessing the remote since it only ever exists in the worldmodel for microseconds at a time. Exploiters can still access the reference of course through script injection, but this dissuades at least some of the bad actors out there.
RemoteReference.Parent = game
RemoteReference:FireServer()
RemoteReference.Parent = nil
This works 100% of the time in studio, I’m assuming due to the server and client both being run locally. However, it only works 9/10 times in a live game.
I was testing my game and on occasion, the server wouldn’t receive any signals from the client, and the game would break since the server doesn’t load the game until it receives that initial signal. I put print statements everywhere and it shows that the server sided callback is never being invoked.
I thought I was going crazy at first so I rapidly left and rejoined the game, and I managed to reproduce the bug like 3 times out of 30 joins. This bug has never occurred even once in studio, after at least 2000 test plays.
The implementations for remote events probably have a line like this:
function RemoteEvent:FireServer()
if self.Parent == nil then return end
...
end
However, if that was the case, setting the parent to the game immediately before firing should 100% update the Parent property and allow it to fire. However, I suspect there is some internal “disabled” flag that gets affected when the Parent property changes.
self.AncestryChanged:Connect(function(c, p)
if p == nil then self.disabled = true else self.disabled = false end
end)
Possibly then due to deferred events, the FireServer() call is being invoked while the “disabled” flag is still true, even though the parent has changed, since the AncestryChanged callback doesn’t run until AFTER FireServer().
However, I have events deferred on both studio and live games, so I honestly do not understand why there’s a difference in behavior. I’ve never experienced this a single time in studio, and its so hard to reproduce in a live server since sometimes it just works??? and sometimes it doesn’t??
UPDATE:
After some more digging I think I’ve found the culprit:
task scheduler remote event wait
When the remote events work, they always work. When they don’t, they never do. When the client FireServer(), if the call is made at the end of a frame, it immediately fires, then the parent is set back to Nil. The calling thread seems to be permanently staggered by some offset time to the firing due to the task scheduler cycle, so if the offset causes the actual firing to occur a frame too late, the parent of the remote has already been set back to nil, so the request is dropped. I’m guessing setting the .Parent isn’t synchronous either for some reason??? The ordering seems to determine whether or not the remote firing is dropped