Task Scheduler Priority order confusion?

What I am trying to learn is…

I’d like to know in which order the Task Scheduler tasks are performed because I can’t seem to understand at which point in the task tree events like Changed or AncestryChanged are fired at when object properties are changed?

My issue is that…

I’m getting different results every time I run my benchmarks of trying to trace this tree as shown in the documentation. I am trying to trace when the Player joins, when the Character is added and when the Character parent is changed. Here is the Task Scheduler Priority:

I really don’t understand why the client comes first in the list not that I even care about this in the first place for my use case, but why? When you join a server, don’t you first have to wait for the server to complete “Replication Send Jobs” task as seen in the bottom?

When do events fire server side? Do they (they, referring to Changed and AncestryChanged) run immediately when you change a property?

What have I tried to do?

I have tried to understand the following threads without understanding the order:

But I simply can’t understand why my output gives different results every time:

result1

After this, I tried printing this along side the Heartbeat step: game.Players:FindFirstChild("[redacted]").Character.Parent (Obviously going to fail if nil, but I wanted this on purpose).

Please take note here that the Player itself did not get instantiated at all and SOMEHOW the Character parent was already set to Workspace… then next Stepped it was nil!?

What is happening here? :thinking:
result2

And sometimes it takes ages to load… but why does the player in the first picture load in before the Stepped… but in the other pictures it does not and sometimes it even loads in after the Heartbeat.

And again… the question remains… where in which part of the Task Scheduler Priority tree do those events fire? (The “7 7” that you see in the print are the number of times Stepped and Heartbeat have ran already).

result3

Here is my script for those who want to try this out themselves (ignore the LoadCharacter and character auto loads, I tried without them and it seems to not affect speed):

local Players = game:GetService("Players")

local step = 0
local heartStep = 0

game:GetService('RunService').Stepped:Connect(function()
	print("Stepped")
	step += 1
end)

game:GetService('RunService').Heartbeat:Connect(function()
	print("Heartbeat")
	heartStep += 1
end)

Players.CharacterAutoLoads = false

Players.PlayerAdded:Connect(function(player)
	print("Player added")
	player.CharacterAdded:Connect(function(character)
		print("Character object instantiated")
		local humanoid = character:WaitForChild("Humanoid")
		humanoid.Died:Connect(function()
			humanoid.Parent:BreakJoints()
		end)

		humanoid.Parent.AncestryChanged:Connect(function(_, parent)
			if parent == game.Workspace then
				print("Parent is now workspace", step, heartStep)
				humanoid:ChangeState(Enum.HumanoidStateType.Dead)
			end
		end)
	end)
	player:LoadCharacter()
end)

I think I understand now most of what is happening except I can only give a speculation.

According to my prints, it seems like events and property updates are being sent, and probably being sent in some special order after Heartbeat but not every frame.

According to the prints and what this says:

I think that the delay sometimes is caused by latency. I think the results would be more obvious if I ran this outside Studio.

But some of the events will not fire ‘every frame’ which makes sense, and therefore then when the PlayerAdded is fired, it’s kind of running it all in the past when the player is already added?

I don’t know why Heartbeat first detects the players character parent to be “Workspace”, but then the next Heartbeat, it’s nil. Maybe it’s when I set the CharacterAutoLoad to false… maybe it’s just a default value or maybe it has to do with the replication somehow, I just don’t know for sure.

However, my original question now has an answer (I think). I think they happen after Heartbeat.
I just wish there was a more in-depth documentation of all of this.

2 Likes

This is just what happens every frame on Roblox, every frame.

So user input is the first thing that happens during a frame, then it will render the environment, and so on.

T̶h̶e̶ ̶b̶l̶u̶e̶ ̶b̶o̶x̶e̶s̶ ̶a̶r̶e̶ ̶t̶h̶i̶n̶g̶s̶ ̶t̶h̶a̶t̶ ̶h̶a̶p̶p̶e̶n̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶,̶ ̶w̶h̶i̶l̶e̶ ̶t̶h̶e̶ ̶g̶r̶e̶e̶n̶ ̶t̶h̶i̶n̶g̶s̶ ̶a̶r̶e̶ ̶f̶o̶r̶ ̶s̶e̶r̶v̶e̶r̶ ̶s̶i̶d̶e̶d̶ ̶j̶o̶b̶s̶.̶

Hmm, understandable. Thank you for the response and looking into this for me, I appreciate a response.

However, since RenderStepped is only usable client side and same with BindToRenderStep, how can it be marked as green and why is the client first in the list?

1 Like

My bad, the green boxes are events, sorry for the confusion.

1 Like

It’s fine :sweat_smile:

But do you know why Heartbeat detects the players character parent to be Workspace, but then the next frame Heartbeat detects it to be nil. This also happens before the PlayerAdded is fired and even before CharacterAdded, so does this mean I was right in my analysis (see second post) (me setting the CharacterAutoLoad to false and a combination of the Events not firing every frame)?

That’s probably because variables update after everything is done, but I’m not sure, so I can’t tell you for sure.

1 Like

And one last question, the events like AncestryChanged, PlayerAdded or whatever always fire after Heartbeat right and only SOME frames, not every Heartbeat even if they should? Since I was able to get game.Players.[redacted] but the PlayerAdded was not fired yet.

That’s probably because normal code runs first then the events run(events are basically modules of code, and if they run at the same time, sometimes one will run before the other).

I don’t know this for sure, but here is my assumption, and this also depends on if you accessed game.Players.[redacted] from client or server.

I think this has to do with event ordering and that no events are truly instant. PlayerAdded will fire as soon as possible but that doesn’t necessarily happen every frame, so it’s not too unlikely you’re able to access the player object in a separate thread before PlayerAdded fires because the event is fired once the player is ready - which includes being parented to Players. This means that the instance is actually part of Players for a very tiny amount of time before the PlayerAdded event fires and propagates to your other scripts.

Similar to the Character Instatiation ordering I just linked you in DMs, the Player instantiation process probably looks like something like this:

  1. Client requests connection to game server
  2. Server accepts connections (or is given the connection by matchmaking)
  3. Player object is created
  4. Player data is retrieved and “loaded” onto the Player object, like UserId, name, etc.
  5. Player object is parented to Players service.
  6. Players.PlayerAdded is fired
  7. CharacterSpawn initiates here

So if you actually manage to reference the player object between step 5 and 6 (and however many hidden steps are between those), you will technically reference the player before the official signal is fired.

3 Likes