Animations playing on the Server instead of Client

Lately, I’ve been working on a Emote Gamepass system for my game. But for some reason when the animation plays, it also plays the animation on the Server. Furthermore, the player has to be moving on the Server instead of the Client to stop the current animation using Connection. I’ve tried researching this issue but to no avail.

Client:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local EmoteData = require(ReplicatedStorage.Modules:WaitForChild("EmotesModule"))
local animEvent = ReplicatedStorage.Events.Remotes:WaitForChild("PlayAnimationEvent")

local player = Players.LocalPlayer
local activeAnimation

local function playAnimation(emote)
	if activeAnimation then
		activeAnimation:Stop()
	end

	local character = player.Character
	if character then
		local humanoid = character:FindFirstChildOfClass("Humanoid")
		local rootPart = character:FindFirstChild("HumanoidRootPart")

		if humanoid and rootPart then
			local animation = Instance.new("Animation")
			animation.AnimationId = EmoteData.Animations[emote]
			local animationTrack = humanoid:LoadAnimation(animation)
			animationTrack:Play()
			activeAnimation = animationTrack
			
			local currentPos = rootPart.Position
			local connection
			
			connection = rootPart:GetPropertyChangedSignal("Position"):Connect(function()
				if (rootPart.Position - currentPos).Magnitude > 0.1 then
					animationTrack:Stop()
					connection:Disconnect()
				end
			end)
			
			animationTrack.Stopped:Connect(function()
				animation:Destroy()
				activeAnimation = nil
			end)
		end
	end
end

local function playSound(emote)
	if EmoteData.Sounds[emote] then
		local character = player.Character
		if character then
			local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
			if humanoidRootPart then
				local sound = humanoidRootPart:FindFirstChild("EmoteSFX")
				if not sound then
					sound = Instance.new("Sound", humanoidRootPart)
					sound.Name = "EmoteSFX"
					sound.SoundId = EmoteData.Sounds[emote]
				end

				sound:Play()
			end
		end
	end
end

animEvent.OnClientEvent:Connect(function(requestingPlayer, emote)
	if not emote or requestingPlayer ~= player then return end
	playAnimation(emote)
	playSound(emote)
end)

Server:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local MarketplaceService = game:GetService("MarketplaceService")

local EmoteData = require(ReplicatedStorage.Modules:WaitForChild("EmotesModule"))
local animEvent = ReplicatedStorage.Events.Remotes:WaitForChild("PlayAnimationEvent")

local gamepassId = 242917287

local function onEmoteRequested(player, emote)
	local success, hasPass = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId)
	end)

	if success and hasPass and EmoteData.Animations[emote] then
		animEvent:FireAllClients(player, emote)
	end
end

Players.PlayerAdded:Connect(function(player)
	player.Chatted:Connect(function(msg)
		if string.lower(msg) then
			msg = string.gsub(msg, "/e ", "")
			onEmoteRequested(player, msg)
		end
	end)
end)
1 Like

When you say the animations are playing on the server, what do you mean specifically? Like other players are also able to see the emote play?

I only ask because your code shows the animation is infact playing on the client, which is good for keeping lag down. Since the client has NetworkOwnership of its character, any animation that is ran on an Animator that was created on the server (i.e. the animator that is in your your character by default) will replicate to other players.

If you wanted to play an animation for just one specific client, that other players cant see, then you can create an Animator using a local script that is only visible to your client, and then use that specific Animator to call :LoadAnimation() – This usually means you have to set it up each time you respawn.

this is how i did it in my game

local function onTouched(hit)
	if debounce then return end -- Check if debounce is active
	debounce = true -- Activate debounce

	-- Check if the player has less tiers than max. If they have 12 or over, do nothing.
	if Player.upgradestats.Tier.Value >= 13 then
		
		warn("Player has max tiers!")
		return
	end
	-- Verify that the hit part belongs to the LocalPlayer's character
	local character = Player.Character or Player.CharacterAdded:Wait()
	if hit:IsDescendantOf(character) then

IIRC, animations always replicate to the server, meaning that animations played on the client will also play on the server. I don’t think there’s any way to stop this unfortunately.

I see, maybe I’ll try to test this out with different animation scripts. If this is true, then the only problem would be the connection running on the server instead of the client.

1 Like