Events can be disconnected in multiple ways. The most obvious way is by calling the disconnect method on a connection object. They can also be disconnected indirectly such as by destroying an instance. The desired behavior depends on how the event is being disconnected. We distinguish these two behaviors as follows:
Hard - Disconnect from the event immediately and drop all pending events associated with the connection.
Soft - Disconnect from the event immediately but run the associated event handler for any events that are still pending.
In the case where you explicitly call disconnect you most probably want a hard disconnect and in the case of destroy, a soft disconnect. While we considered adding two separate methods to reflect these we decided that making all disconnects a soft-disconnect would be better and if a hard disconnect is still desired it can be achieved by reading the Connected field.
That all said, we are very open to this feedback so I will re-open this discussion internally and update the thread accordingly with the outcome.
Hello, I’m having some issues converting over to deferred. on immediate sending inventory weight data back to client through remote event work fine but it just doesn’t update properly on deferred. It even shows the right weight from the server which is updated right before the event fires to the client with the newly updated weight. But the client still thinks the weight is of the current item being removed. I would like to adapt but currently I cannot.
Hey @WallsAreForClimbing I tried switching over one of my experiences to deferred today to see how it would do, and I’m seeing that among other things a couple of things in regards to tools are breaking and was hoping you could help me understand why.
For example, a lot of my tools start with this line (script directly inside the tool):
repeat wait() until script.Parent.Parent:FindFirstChild(“Humanoid”)
With deferred on this line errors saying attempt to index nil with parent. The tool is simply cloned out of a folder and placed directly into the player’s backpack like so:
Tool:Clone().Parent = Player.Backpack
I don’t understand why deferred would cause the code to execute when the tool has no parent, as it is placed into the backpack instantly.
There’s also this line of code elsewhere used to verify the tools the player has equipped:
Backpack.ChildAdded:Connect(function(Tool)
if PlayerHasTool(Player,Tool.ItemID.Value) == false then …
This line now errors with deferred saying that ItemID is not a valid member of tool, but it is always there from the get go before the item is cloned or placed in the backpack. These are all server-side scripts so It is not a replication thing, and I would expect that any direct descendants of the tool would be immediately reachable when it is added to the player’s backpack.
Fixing these things is not too difficult by adding WaitForChild etc., but I’m not quite understanding why this change would affect such things and was hoping you could briefly explain it to me to make it easier to find other issues this may cause in the rest of my or other’s games.
Not OP but I think that there are a lot of cases where turning on deferred mode reveals some really shaky coding practices such as what you have there in the first issue. Even without deferred mode, yielding at the start of a script is often representative of a code smell. For your case it may take an ample amount of rearchitecturing rather than things working out of the box.
I would instead recommend defining the character when the tool is equipped. You don’t have to redefine it multiple times, only once is enough. Similarly, if you need the character to be defined in the topmost scope, you can create an initialiser function to execute on the first equip.
-- Forward declare the character variable.
local character
-- Set it when the tool is equipped; if it doesn't need to be dropped, you can
-- make sure its assigned only once with Once.
Tool.Equipped:Once(function (mouse)
character = Tool.Parent
-- This might be how you choose to initialise.
Tool.Equipped:Connect(onEquipped)
onEquipped(mouse)
end)
I can’t say anything about the second one though. I don’t want to backseat engineer or anything, so just wanted to point out a potential solution you could use for the first issue. Far be it from me to give any pretentious or misinformative answers, so I don’t have any tips to offer for the rest and would also be interested in understanding how best to fix these cases or how deferred affects the code.
By disabling some events I was able to narrow down the event that fired the wrong data. I had to wrap the code in a task.defer() function. This ensured the event fired at the end of the frame, AFTER the inventory weight is updated in a function way above the in the stack! This allowed the new data to be sent and updated on the client’s UI properly. Whereas in immediate everything is happening at once.
It randomly adds : or / before some messages still.
Last week it was still having issues scrolling automatically when people send multi-line messages (though that might be fixed now?
I notice it significantly changes the behavior of game.Players.PlayerRemoving event. With deferred signaling, it is handled AFTER player parent is set to nil, not before. The same in case of instance.Destroying event.
Plugins do not have settings at present that would make it easy to configure which signalling behavior they use. We are aware of this issue though and we are thinking about how best to solve it.
This update is, believe it or not, awful. I’ve been getting so many unreproducible bugs because of this new behavior. I’m sure it’s because of this, as none of them happen for hours of testing when the behavior is set back to Immediate. Why does the behavior have to change?? For security? Performance? Consistency? Maybe for the engine, but not for my game. Immediate events make so much more sense.
I would hate for this to be the default. This should at least come with a way to debug or a way to easily visualize what is going on here.
What does that mean? Event A, B, and C? Is that a connection, or a script signal? A whole bunch of connections? How and when is the order of execution affected? What does the left image mean by ‘yields’?
The left clearly looks less preformant, but it makes much more sense. Deferred events may make sense when replicating between the client-server boundary. I may feel better if this is better explained. When I think of ‘Deferred’, I think the event is going to be fired on the next frame, maybe that’s why I’m confused.
For now, I’m switching back to Immediate until I can get an understanding of what is going on with Deferred events. I’ve been used to Immediate for 4 years.
If you want another way to think of it. It’s a queue, like a checkout queue at the store:
Whenever an event is fired it has to wait at the back of the queue, behind all the events that got fired before it.
That customer may call someone else and tell them to get something while they’re in the queue. That other person will have to wait at the back of the queue too (event fires another event while being handled).
Customers sometimes enter the queue while the teller is busy with something else like cleanup on isle 5 (C++ code is running). The customers will eventually get served but only once the the teller is available again (C++ code reaches an invocation point and starts running handlers from the queue).
This leads to performance wins because it’s easier for the teller to get their other work done all at once rather than being interrupted by rude customers who demand the teller stops what they’re doing and serves them right this instant all the time (what happens right now).
It also leads to security wins because the teller might lose track of what they were doing when they drop everything to serve the customer immediately.
I guess it does help, but there’s a problem. With all my old code, I don’t really know where these connections are being made anymore. It’s like forcing a rewrite. Do you have an idea of when this update will roll out for everybody?
The saving grace is that you likely don’t actually have to change much code: While it may be tricky to find the lines of code that need to change at first for a large existing codebase, if you execute the changeover correctly it’s very unlikely that you actually have to change many lines of code total.
The timeline is long. As the post says:
Our goal for 2023 is to change the default (which is to say, the behavior of places with SignalBehavior set to Default) from immediate mode to deferred mode.
That’s just change the default in 2023, not remove the setting.
Just curious, is the performance/security boost live for games that have this enabled, and how much of a performance boost is this (in % or something similar)?
Unfortunately we actually have to move to a world where there is no setting and events are always deferred before we can reap most of the benefits.
That’s because even though some people have switched to deferred mode, the C++ code still has to be written in the slower way which is capable of handling interruption by immediate mode events while the setting exists. (Technically we could write multiple different variants of various pieces of code now depending on the setting value but that’s a great way to have twice as many bugs too, so not really worth it)