Deferred Engine Events

Update: Deferred Engine Events opt-in are now live and can be enabled in experiences.

Hey Developers,

On April 12th we will be allowing you to opt-in to using deferred engine events in your experiences.

Overview

For those of you that are not already familiar with deferred engine events you can find out more about them here but to give a brief summary:

  • Rather than resuming event handlers immediately we queue them up and resume them all at once.
  • This improves performance as code that would usually trigger an event can now complete without needing to run arbitrary event handlers which may themselves trigger further events.
  • This improves security as scripts will only run at specific points in the frame. Doing this eliminates an entire set of vulnerabilities that we would otherwise need to patch on a case-by-case basis.
  • This will allow us to make additional optimizations in the future.

Using Deferred Events

You can enable deferred events by changing the SignalBehavior property of workspace in each of your places. There are a few options to choose from:

  • Default - The default behavior, currently equivalent to Immediate but will eventually change to Deferred.
  • Deferred - All events are deferred and their handlers resumed at specific resumptions points each frame.
  • AncestryDeferred - Equivalent to deferred but only for events triggered by changes in ancestry.
  • Immediate - Event handlers are resumed immediately when the event occurs.

Since our last post, we have also introduced AncestryDeferred which will only defer events related to changes in ancestry. If you are interested in trying out deferred events in an existing experience this may be a good alternative to all events being deferred and will still provide some of the benefits described above.

How will this impact existing experiences?

By default, the behavior of your experiences will remain the same. Right now, the default SignalBehavior is equivalent to immediate (signals will fire as they did before). For those who had previously opted-in or out during the beta, we have reset the value of SignalBehaivor. This was done out of an abundance of caution so that no behavior changes are observed when we enable this feature platform wide on April 12th.

We encourage you to enable deferred mode in your experiences. Doing this may require you to adjust some of your code so make sure to test your experiences before publishing them. We have added some helpful tools to help adjust the code to be compatible with deferred events, such as the new task library (as a replacement for “fast signal” pattern and others), and the Once connection method on signals.

If your experience is not compatible with deferred events, you can keep using the default behavior for now or switch to Immediate to make sure the behavior of your experience is not affected when we switch to deferred-by-default (see below for more details).

Long-term plan for Deferred Engine Events

As you may have noticed, the SignalBehavior property is similar to other multiphase rollouts we have done in the past. Eventually, we would like all experiences to run in deferred mode but we know this is not realistic today.

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. However, we will not do this until we are convinced that doing so results in minimal disruption through a combination of us improving the system and developers adapting to change over time. If it is not possible for us to make this change without impacting your experiences then we will extend our timeline accordingly.

As mentioned, eventually we would like all experiences to run in deferred mode. We won’t be announcing the timeline for this until after we have changed the default but you can expect it to be years before we do this.

Even after we change the default, you will still be able to opt-out by setting SignalBehavior to Immediate.


You can learn more about deferred events and related features by reading the following pages:

We will also be updating our documentation on scheduling so keep an eye out for that in the coming weeks.

If you have any comments or concerns, feel free to reach out to me directly over message or on this thread and we can discuss.

Thank you.

147 Likes

This topic was automatically opened after 10 minutes.

Awesome update. Does this also help with security against some exploits that hook into events before the game loads?

14 Likes

It would be really cool if we can create custom events

7 Likes
13 Likes

So is backwards compatibility just… not a thing anymore? It doesn’t matter how long you wait to make a breaking change, it still breaks all of my games that depend upon the behavior unless I go through and meticulously audit each one. It also breaks games that I like to play whose developers aren’t around to maintain them anymore. Something seriously needs to be done about this because the problem is only going to compound in the coming years.

17 Likes

You can try looking up Good Signal


@WallsAreForClimbing so this is fully released to games right?

I have been waiting to test Deferred Event behavior for a really long time

If not then I can give you a list of place ids to enable for

I struggle slightly to understand what exactly would change, if someone can explain in more simple terms I would really appreciate it.

Does this mean something like Basepart.Touched is going to function differently?
Does it affect timing and to what extend?

Should I expect a simple .Touched event to fire a few frames later/earlier than normal?

2 Likes

it’s in the old post you can go read all of the details from there


additional for anyone with doubts and reluctance I can say that you should definitely try enabling this and see what’s broken then fix it overtime, you don’t have to understand what’s happening fully right away as long as you are able to fix bugs caused by the new behavior otherwise you’d be missing out a lot.

6 Likes

I’m not gonna lie chief, I think the team needs to reconsider what they should be working on and prioritizing

5 Likes

There’s likely different teams that work on different things rather than just a single team

25 Likes

There’s a problem that needs to be resolved before this feature becomes the default, I’m not able to connect cleanup functions to .Destroyed or .AncestryChanged events of a script or its ancestry because the scripts thread is terminated and all connections are disconnected once its destroyed, before the deferred callback can even run, this makes self-cleaning scripts impossible to make, an example is this simple script that creates a part, keeps track of it and then destroys it once the script or its paren model is destroyed

local part = Instance.new("Part")
part.Name = "TestPart"
part.Parent = workspace

script.Destroying:Connect(function()
	part:Destroy()
	print("destroyed part")
end)

this works in immediate signal behavior but breaks in deferred mode, it is crucial to have a script be able to clean up after itself once its destroyed, currently my only workaround for this is to let the script know ahead of time when it will be destroyed, wait for a bit and then destroy it.

this issue becomes larger if you have custom classes and objects in the script that create many instances, these are all wrapped up in cleaner objects such as maid and janitor but they don’t have a chance to run the cleanup code, it all just gets deleted instantly with the main script thread

26 Likes

Hm, while deferred engine events may have some benefits such as improving performance and security, it’s important to consider the potential downsides as well. One issue that could arise from using deferred events is that it may make debugging and troubleshooting more difficult, since events will not be triggered immediately and may be queued up to be processed later. This could make it harder to identify and resolve issues in the code, especially if the event queue becomes backed up.

I can see that the SignalBehavior of my games–including local files–has already been reset to Default. If I change it back to Deferred and publish that change, will it automatically go live with the push on the 12th, or should I wait until then to publish the changes?

I have 50k lines of code in my game, if I were to use this feature, I would have to dive deep into my code base and look at possible issues that may arise as a result of this.

I also don’t know still which event is Roblox specifically referring to, is it any event? Like even changing properties that trigger the .Changed event? Is it any event? (Except the task scheduler related ones)

I imagine a lot of developers rely on this default behavior already a lot and just changing the Default to be Deferred is not a good change to do. This can create issues as well for developers who will start experiencing weird issues when they expect an event to trigger immediately after them changing or triggering something, but then the event just triggers afterwards.


I am just asking to not make it the default, unless you are willing to change all developers who have it set to default to immediate because that’s what truly is at the moment. You don’t want games to have unexpected issues by changing the defaults now.

Sounds for sure broke some games (specially the games who expected the sounds to be loaded before playing a sound) and this one could break even more games.

1 Like

It’s impossible to maintain full backwards compatibility with Roblox’s update model. There is always only one version of the engine, period.

2 Likes

I have a solution for this: make it a permanent option to be able to switch from deferred to immediate at any time, as long as Roblox lives. Also, only new experiences will have deferred on from now on, and old experiences will have immediate as the default setting, which will never change. This is a logical solution, but do what you want.

4 Likes

I don’t really understand how this works, I know nothing about services/events. I assume it involves external devices to manage actions on scripts better. Could we get a more detailed explanation on how to set this up. I don’t know if it is useful for my game or not. Thanks!

1 Like

It’s definitely not impossible. There are two obvious solutions:

  1. Never force breaking changes onto existing games. Let them remain optional forever.
  2. Version the engine.

Both of these options are challenging and expensive to engineer, but isn’t that why we give Roblox such a large cut of our revenue? To handle all the complex parts of maintaining an online game?

7 Likes

This is the right call for the future of game architecture. A bad practice I’ve commonly seen in the past is a reliance on race conditions and immediate running to achieve synchronization across systems.

It’s a really unhealthy, haphazard way of engineering games, and I would highly encourage other programmers to avoid these practices in favor of safer defensive code that doesn’t rely on strict timing. It’ll make your codebases far less brittle, and far more predictable.

32 Likes