[Urgent] Server lags/haults while it waits for everyone to load player animations

My Dodgeball game has A LOT of player animations. It seems after extensive testing, the server will pretty much pause what it’s doing until everyone is caught up and has downloaded each animation asset that is being played.

For example, a large 30 player server has players all throwing dodgeballs, each are playing equip/throw animations on their client. Anytime a client plays an animation, their client tells the server to tell each other client to also play the animation. Each client will download the animation data themselves, and play it on their client. The server LAGS while it waits for the client to finish before processing anything else.

Let’s say another player joins the game, as they’re joining the game the entire server will freeze/lag and stop processing for example RemoteFunction inputs until that new player is finished loading the player animations in the game.

This is very annoying when there are constant players leaving and joining the game, some with terrible internet connection that can end up taking a full minute before the game will allow me to throw another dodgeball. This hurts the experience in my game.

This system of how player animations are played was never always like this. In the past, the client would send packets to the server of each keyframe for the animation, and server would replicate the keyframes to each client. This new system prevents exploiters from using Fiddler to change the animation, however, the server should not freeze and lag while it waits for everyone to catch up.

TL;DR: Client plays animation → Server tells everyone else to play said animation → Each client downloads the animation data → Server haults all RemoteFunction requests and waits for each client to play animation → New player joins game and server haults again waiting for new player to load animations → repeat.

It’s very apparent and noticeable if you play the game yourself. You’ll notice mostly when a round starts, the game freezes for up to 5 seconds. It’s also apparent anytime a new player joins.

I have tested this myself by forcing all clients in my game with a special command to play an animation they haven’t loaded before, and again the entire server will lag and freeze until everyone has started to play the animation.

It has to run a check to see if you own it on each and every player so it can fully run the script for everyone and not just a handful of people, I had the same problem it just has to run it’s course of finding out if you own it.

I think it’s connected to this problem aswell.

How do you work with the animations? (a script snippet would be welcome)
Do you use GetKeyframeSequence or GetKeyframeSequenceById functions?

[quote] How do you work with the animations? (a script snippet would be welcome)
Do you use GetKeyframeSequence or GetKeyframeSequenceById functions? [/quote]

Sorry, I should have gone into more detail about that.

I am not using those APIs. In a tool, I have a localscript that fetches what animations they should be using. My game sells different types of animations in a shop, so this is why.

I contain all of the Animation objects inside of ReplicatedStorage

Below is a simplified version of the Tool, showing how it loads animations and plays them accordingly. The Constants variable is a ModuleScript which returns information for all the animations. For example if the client has a “Disco pack” animation enabled, it will grab the animation IDs from the Constants variable. It then finds the Animation objects for said ID in ReplicatedStorage.


local Tool = script.Parent;
local Ball = Tool:WaitForChild("Handle");
local Player = game.Players.LocalPlayer;
local PlayerData = game.ReplicatedStorage.Config.Players:WaitForChild(Player.Name);
local ServerConfig = game.ReplicatedStorage:WaitForChild("Config");

local Reference = Ball:WaitForChild("Ref").Value;

local Constants = require(game.ReplicatedStorage:WaitForChild("Constants"));
local Connection = game.ReplicatedStorage:WaitForChild("Connection");

local IsEquipped = false;
local Character = nil;
local Humanoid = nil;
local AnimationsLoaded = false;
local Thrown = false;

local EquipAnim;
local IdleEquipAnim;
local StoppedAnim;
local ThrowAnims = {}; 

local AnimationData = Constants.SHOP.ASSETS[PlayerData["ActiveAnimPack"].Value];

Tool.Equipped:connect(function()
	-- Tool has been equipped
	IsEquipped = true;
	Character = Tool.Parent;
	Humanoid = Character:findFirstChild("Humanoid");
	
	if (AnimationsLoaded == false and Character ~= nil and Character.Parent ~= nil and Humanoid ~= nil) then
		-- Load player animations.
		EquipAnim = Humanoid:LoadAnimation(ServerConfig.Animations[AnimationData.Details.Data.Equip]);
		IdleEquipAnim = Humanoid:LoadAnimation(ServerConfig.Animations[AnimationData.Details.Data.Idle]);

		-- Is there more than one throw animation?
		-- Sometimes animations can have more than 1 throw animation, and will play them at random each throw.
		if (type(AnimationData.Details.Data.Throw) == 'table') then
			for _,P in pairs(AnimationData.Details.Data.Throw) do
				table.insert(ThrowAnims, Humanoid:LoadAnimation(ServerConfig.Animations[P]));
			end
		else
			table.insert(ThrowAnims, Humanoid:LoadAnimation(ServerConfig.Animations[AnimationData.Details.Data.Throw]));	
		end

		AnimationsLoaded = true;
	end	
	
	if (EquipAnim) then 
		EquipAnim:Play();
	end	
end);

function throwDodgeball()
	if (Character ~= nil and Humanoid ~= nil and Humanoid.Health > 0 and Thrown == false and ServerConfig.Game.CanThrow.Value) then
		
		local Direction;
		local BallLocation;

		Thrown = true;
		
		-- [snip] does calculations of where dodgeball should fly
		
		ThrowAnims[math.random(1, #ThrowAnims)]:Play(0.1 ,1, 1);
		
		wait(.32); -- Give reasonable amount of time for animation to play.
		
		-- Ask server to throw Dodgeball, sending details about the Dodgeball and where it should fly.
		local Request = Connection:InvokeServer(
			Constants.THROW_DODGEBALL,
			Reference,
			Direction,
			BallLocation		
		);

		if (Request) then			
			if EquipAnim then
				EquipAnim:Stop();
			end
			if IdleEquipAnim then
				IdleEquipAnim:Stop();
			end
			if StoppedAnim then
				StoppedAnim:Stop();
			end
			
			OnUnequip();			
		end
		
	end
end

Ok. All of this should work fine; also I can’t easily see a problem in your game.

You mentioned that you can modify the game in a way to force everybody to play a certain animation; can you attach a test with this modification in a confidential section?

I quit .-.