Humanoid:LoadAnimation race conditions prevent animations from loading

I found a bug in my game that breaks certain animations loaded immediately when the character is added. I think it’s due to replication-related race conditions, and has the potential to affect games silently and inconsistently.

Repro:

  1. Download this file
    Animation bug.rbxl (15.0 KB)

  2. Replace the AnimationId in game.ReplicatedStorage.Animation with an animation that you own

  3. Go into StarterPlayer.StarterPlayerScripts and disable one of the scripts/enable the other in order to test the two cases

  4. Start a Play Solo test, and reset your character once. If the animation does not play after 2 seconds, then you have reproduced the bug for that case.

The first example has the following code:

local animation = game.ReplicatedStorage.Animation

-- These animations will break
game.Players.LocalPlayer.CharacterAdded:Connect(function(char)
	local hum repeat
		hum = char:FindFirstChildOfClass("Humanoid")
		if not hum then char.ChildAdded:Wait() end
	until hum

	print("Loading animation after ChildAdded:Wait()")
	hum:LoadAnimation(animation):Play()
end)

This code immediately loads the animation as soon as the character added, and plays it. Note that this is not an issue with playing the animation, but with loading the animation (if you waited a second before the animation loaded to play it, you would get the same result).

In this case, the animation is loaded and returns an AnimationTrack, but whenever the animation is played, it will not actually be rendered.

The second example waits a second after the humanoid is loaded to the character before loading the animation:

local animation = game.ReplicatedStorage.Animation

-- These animations should play fine
game.Players.LocalPlayer.CharacterAdded:Connect(function(char)
	local hum repeat
		hum = char:FindFirstChildOfClass("Humanoid")
		if not hum then char.ChildAdded:Wait() end
	until hum
	wait(1)

	print("Loading animation after wait(1)")
	hum:LoadAnimation(animation):Play()
end)

This script will work every time.

The third example uses char:WaitForChild() instead of char.ChildAdded:Wait() to get the Humanoid:

local animation = game.ReplicatedStorage.Animation

-- These animations will probably play fine, but have the potential to break
-- due to race conditions
game.Players.LocalPlayer.CharacterAdded:Connect(function(char)
	local hum = char:WaitForChild("Humanoid")

	print("Loading animation after WaitForChild")
	hum:LoadAnimation(animation):Play()
end)

This case is interesting, because you’d think it would function identically the char.ChildAdded:Wait() loop. However, it seems that WaitForChild sequences itself a bit differently, probably after the replication or wait() resume parts of each frame.

assumptions are based off of this

I am unsure on the internals of WaitForChild, but my theory is that the Character/Humanoid is being loaded in locally, before the server has verified its replication in some way, and thus any call to LoadAnimation is being called on an object that the server hasn’t verified.

If this theory is accurate, WaitForChild could potentially be inconsistent for attempting to load animations on a newly created Humanoid, depending on whether the thread is resumed based on replication, or simply based on wait(). If this were one frame off due to a laggy connection, then there may be race conditions preventing animations from loading in this case.

Sorry if this is all a bit convoluted. I have just been experiencing problems in my game that requires animations to be immediately loaded, and I would hate to use wait(magic number) to solve my problems here.

edit: Looks like this thread is related ChildAdded is called BEFORE fully parented?

3 Likes

Animations not Replicating Bug I dont know if this is something similar, but I am having issues too.