[Beta] Deferred Lua Event Handling

Honestly, I’m not sure where I got the idea from, but I have always thought this was how events worked :flushed:

I have personally never had this happen to me, but knowing that Roblox does this is why I know I won’t always stay on Roblox. On other engines, the only way for an update to break your game is for you to do it yourself. But on Roblox, if you don’t touch your game for years it will end up broken in one way or another just because of even extremely small behavior changes. Not to mention that it will look completely different because of graphics updates (such as the removal of outlines, the recent AO changes, and the new materials that they plan to force onto existing games soon). I like developing on Roblox, but you kind of don’t get as much of a say in your game as you do on other engines.

Also, what’s kind of funny about this update is that, aside from breaking a lot of games, it even breaks some of the Core scripts.

5 Likes

How does this affect Remotes?

local remote = game.ReplicatedStorage:WaitForChild("RemoteEvent")
print("A")
-- Assuming queued events.
remote.OnClientEvent:Connect(function()
	print("B")
end)
print("C")

With Immediate mode, this would print A, B, C. I assume that, with Deferred mode, this will print A, C, B?

Answer: Yes, queued remote events are deferred.


What about the following?

local remote = game.ReplicatedStorage:WaitForChild("RemoteEvent")
print("A")
remote.OnClientEvent:Connect(function()
	print("B")
end)
remote.OnClientEvent:Connect(function()
	print("C")
end)
print("D")

With Deferred, will the remote’s queue be flushed before listener C is connected, or will both listeners receive queued events?

Answer: The queue is flushed by the first connected listener; listener C will not receive events.


Moreover, when do deferred threads run in relation to non-deferred threads? Consider threads A and B. A produces a deferred thread AD, and B produces BD. There are several ways these threads could be ordered:

Per-thread defer:

A
AD
B
BD

Cumulative defer:

A
B
AD
BD

Answer: Deferred threads are accumulated.

13 Likes

Ouch. Care to share more details? Maybe there are options?

1 Like

I’ve proposed a way to maintain backwards compatibility in existing places, while still allowing this new behavior:

This change is about as breaking as enabling StreamingEnabled on a place that wasn’t designed for it, and therefore I think should be re-considered to be a boolean property, rather than an enum with a “Default” option that can suddenly break existing games.

16 Likes

I just tested this change to see how much it affected my game. It’s unplayable now.

As somebody else mentioned, this feels very similar to forcing a game to use StreamingEnabled.

I’m sure this update has many benefits that go over my head, but backwards compatibility would be a nice thing to keep for projects developers don’t necessarily want to rewrite code for.

9 Likes

Honestly, you know what, I think I may just want to say that, understanding the applications, understanding the benefits, understanding the system:

This is a bad update.

There is no reason whatsoever that this change should come about like this. Sure, maybe it gives some minor performance benefits in the long run, sure, maybe it helps clean up some messy part of the engine. But the lack of backwards compatibility and the sheer impact of this change makes it one I think should never, ever, ever be made. I understand you guys want to improve the engine in new and exciting ways, but there is a limit to what you can do. Businesses don’t tear down an office to replace the floors, and that’s what this is. Ripping out basic core functionality and replacing it, just to get some small benefits. This update breaks, and I don’t think I’m overstating this, probably every existing use of an event on Roblox. At the very least, it adds unintended behavior. Heck, this update breaks the core scripts, one of the most universal pieces of Roblox games.

The existing functionality is fine. Better yet, it’s already in use. Everyone understands and accepts how it works. Don’t change that, please.

11 Likes

Edit: I’ve read zeuxcg’s update and feel better about this now, however I am still not super happy.

Here’s a suggestion. Stop forcing these updates on us. Not all code that will be affected by this is “bad code that just needs updating”. In addition, this has the potential to break plenty of games in ways that will be very difficult to debug for less experienced programmers.

These kinds of updates throw us under the bus because Roblox is basically saying “We made a change, now you have to opt out to give yourself a month to fix all of your games before we force this change”

This is not a critical update. It’s not like filteringenabled.

How can you refuse to give us more advanced settings and features because it would be “too difficult for new users and kids” yet you constantly make these changes and expect inexperienced kids to work around problems you create?

Forcing this on is is an unprofessional and naïve move.

24 Likes

As someone said above it broke PlayerAdded. I can’t test as much as my game doesn’t begin setting up players because of this. Probably should be fixed soonish. Also gonna have to say that this should be optional. I don’t understand forcing updates like these where it doesn’t seem like it needs to be mandatory? It looks like more needless work for us to keep up with updates a lot of us don’t need/want.

I think you guys would get better feedback if you explain reasoning a bit more as to why you make these things mandatory? Give developers the freedom to play around with their stuff and not have to keep up with whatever it being pushed all the time.

7 Likes

Great. Imagine telling people that Roblox broke their code and it’s their job to fix it and that they need to add their own modules for fast spawning and to fix player added. How hard is it for Roblox to just let us have a permanent toggle? Oh, boo hoo they have to maintain some extra code. Who cares. We’re the source of income on the platform. Quit making us suffer please and let us decide for ourselves how our code should work.

11 Likes

I just tried this on my experience and well, 1/3 of our game became unplayable. I hope this stays opt-in permanently because it’s a bit tiresome trying to adapt and constantly make a workaround for behaviour changes when this vital development time should be spent on making features.

16 Likes

This change temporarily broke many modules I use that I myself do not maintain. I hope this stays opt-in in the case that old, usable code can “expire”. What other option do I have other than to rewrite all the broken code from scratch?

2 Likes

This update breaks my game and I don’t know how to fix it or which code is broken because of it.

9 Likes

I don’t really understand this update in full because I’m not a largely technically-oriented person so already the confusing explanation of what this is and what’ll affect in tandem with the concerned replies makes me fearful for years worth of code I’ve written and code I’ve yet to write.

Roblox has two very bad habits when it comes to releasing updates:

  1. Being completely radio silent. A staff member was responding very selectively to some of the posts here but, whether intentionally or because they didn’t have enough information, ignoring some other questions even within the same post. There’s still a lot of questions missing answers that could help us make sense of the gravity of this update and how impactful it might be to our coding practices in the future.

  2. Zero community input. Where are the surveys and questionnaires asking if we would be affected by this change? These changes affect us directly so we should also be able to have a voice in how these changes are rolled out and their permanency. I’m glad that there’ll be a window to make changes appropriately but that window needs to be very large if this many games are breaking from this. I’m not so concerned about old unmaintained games but it appears from the replies that new and current actively maintained games are being affected by this.

It’s stupidly frustrating to develop on this platform.

39 Likes

This will definitely be a problem for my game because now I will have to locate where in my code this will be a problem and I know for a fact the First Person stuff in my game will be unplayable with this.

5 Likes

One of the uses for BindableEvents was to do an instant resume which worked with the coroutines of callbacks and module scripts.

BindableEvent Example
local b = Instance.new"BindableFunction"
local function yields()
	wait(1)
	return 1
end
function b.OnInvoke(f) -- f always yields long enough so that the call to Wait happens
	local e = Instance.new"BindableEvent"
	coroutine.wrap(function()
		e:Fire(f())
	end)()
	-- ...
	return e.Event:Wait()
end
print(b:Invoke(yields)) --> 1

Using the coroutine library for this doesn’t work.

Coroutine example
local b = Instance.new"BindableFunction"
local function yields()
	wait(1)
	return 1
end
function b.OnInvoke(f) -- f always yields long enough so that the call to Wait happens
	local t = coroutine.running()
	coroutine.wrap(function()
		coroutine.resume(t,f())
	end)()
	-- ...
	return coroutine.yield()
end
print(b:Invoke(yields)) -- nothing

With this change, how can the coroutine of a callback or module script be resumed instantly?

2 Likes

There are several things in my game broken both loudly and silently by this change, most of the loud ones are third party libraries. I haven’t looked into how difficult they will be to fix, but I don’t imagine it will be too bad in my case, probably ~5 weekends assuming the problems don’t get bigger and more numerous the more I look…

I would still definitely appreciate the performance improvements associated with collapsing duplicate events though. (It sounds like this would also solve the issue with Selection events in Studio firing once per instance deleted when you are deleting many instances at once).

I think this is a significant miscalculation.

18 Likes

Perhaps you’d like to share what some of these optimizations might be instead of staying silent? Might make it easier for us developers, who will have to do the work of rewriting code in preparation for change, more likely to accept the change. Might also help us figure out if we even want this change to begin with, depending on how great the optimizations that come from it will be.

Going “we’re going to force you to rewrite your code but trust me it’ll be really good in the long run because we’ll do some good optimization but no I won’t tell you what they are” does not really give me confidence that these optimizations will, in fact, be worth it.

12 Likes

I checked an old game of mine to see this change for myself. Pretty much on the same boat as you. I’m more scared about the silent breaks more than anything. Behavior that I didn’t intend to happen. I probably won’t try to fix any of the issues though. Wasted time imo.

I have an actual response to this but, it was flagged as off-topic for the right reasons. I’ll move it to a discussion post once I get confirmation that it’s fine to post it there.

15 Likes

I activated Deferred in my game and I didn’t pull the short straw, but I think I noticed a difference :rofl:

I’ve looked deeper into the camera problem mentioned by @DataBrain:

Using this code I’ve determined the timing of InputChanged within the game step pipeline:

local STEP_INDEX

-- 0 to 2000 is from Enum.RenderPriority.First to Enum.RenderPriority.Last
for i = 0, 2000 do
	RunService:BindToRenderStep("RenderIndex" .. i, i, function()
		STEP_INDEX = i -- During BindToRenderStep
	end)
end

RunService.RenderStepped:Connect(function()
	STEP_INDEX = 2001 -- After RenderStep and BindToRenderStep
end)

RunService.Heartbeat:Connect(function()
	STEP_INDEX = -1 -- Before RenderStep and BindToRenderStep
end)

UserInputService.InputChanged:Connect(function(input, processed)
	print(STEP_INDEX)
end)

In Immediate mode, InputChanged fires at index -1 which is before BindToRenderStep - Naturally, users would be anticipating input information to be available during BindToRenderStep to progress user-controlled game states.

In Deferred mode, InputChanged fires at index 2000 which seems to be after BindToRenderStep and before RenderStepped - This completely leaps over a coding design where user input is processed first before the rest of the game code runs during a game step. If InputChanged will fire consistently before RenderStepped, then the developer could just make a custom BindToRenderStep :rofl:

In Layman’s terms:
image
(But sort of not, because it’s after BindToRenderStep and before RenderStepped which feels awkward)

User input getting yeet’ed forward will break a good bunch of user controls code including roblox’s own.

Another relevant problem will meet developers not forking roblox defaulf controls but overriding Humanoid.Jump set by the control scripts… I think this hack could be improved by ditching Jump listeners and checking the value directly at different BindToRenderStep priorities.

31 Likes

Seems like a really bad update that should never be forced onto developers.

Completely breaks PlayerAdded and other critical events in Studio, and developers should not have to write Studio-specific code to counteract PlayerAdded and other events no longer firing as expected within Studio.

11 Likes