Help with animation markers

So, I’m having trouble with an animation event not being registered on the server from a client animation. It works most of the time but sometimes the Server just doesn’t get the animation/ marker. Here’s what I’m doing on the server rn:

local animator = hum:WaitForChild("Animator")
local animations = animator:GetPlayingAnimationTracks()
local id = Slow(hum, inst:GetAttribute("WalkSpeed"))

self.didhit = false
self.canattack = false

self:ChangeCombo()
combatchar:Transition("Attacking")

for index = 1, #animations do
	local track = animations[index]
	local c = track:GetMarkerReachedSignal("end"):Once(function()
		SpawnWithReuse(function()
			if combo == 4 then
				task.wait(1)
			end
			self.canattack = true
			task.wait(0.2)
			Unslow(hum, id)
		end)
	end)
	track:GetMarkerReachedSignal("Hit"):Once(function()
		combatchar:RevertState()
		SpawnWithReuse(function()
			task.wait(0.1)
			if not self.didhit and combo == 4 then
				self.canattack = true
				Stun(hum, 1)
				c:Disconnect()
			end
		end)
	end)
end
1 Like

Possibly a throttling cuz in server it’s usually skipping in some frame and that event fires after the frame reached along with animation, GetMarkerReachedSignal i believe is only best used on client rather than server side as said earlier.

2 Likes

well that sucks, do you know a good alternative for handling important server-sided states on an animation track?

1 Like

The problem seems to be that due to a combination of the client-server communication and the fact that animation markers are client-side events that might not always reach the server, and roblox animations are inherently client driven, which means that replying on the client to trigger events on the server for example GetMarkerReachedSignal can sometimes lead to missed signals, so basically instead of replying on the GetMarkerReachedSignal directly on the server, you can trigger a RemoteEvent or RemoteFunction from the client when the marker is reached and let the server handle the logic after receiving the signal, in this way the server is specifically notified when the event happens. You can do so by making it so when the marker is reached in the animation, fire a RemoteEvent to notify the server

-- Inside the client script where the animation is playing
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")

-- Get the animation track
local track = animator:LoadAnimation(animation)

-- Listen for the animation marker
track:GetMarkerReachedSignal("Hit"):Connect(function()
    -- Fire the event to the server
    game.ReplicatedStorage.Remotes.HitMarker:FireServer("Hit")
end)

track:GetMarkerReachedSignal("end"):Connect(function()
    -- Fire the event to the server
    game.ReplicatedStorage.Remotes.HitMarker:FireServer("end")
end)

track:Play()

Now on the server side make ti listen for the RemoteEvent and handle the appropriate logic based on it

-- On the server
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("Remotes")

-- Create a RemoteEvent called "HitMarker" in ReplicatedStorage (if not already created)
remotes.HitMarker.OnServerEvent:Connect(function(player, marker)
    -- Check which marker was sent from the client and handle the logic
    if marker == "Hit" then
        -- Handle the "Hit" logic
        combatchar:RevertState()
        SpawnWithReuse(function()
            task.wait(0.1)
            if not self.didhit and combo == 4 then
                self.canattack = true
                Stun(hum, 1)
            end
        end)

    elseif marker == "end" then
        -- Handle the "end" logic
        SpawnWithReuse(function()
            if combo == 4 then
                task.wait(1)
            end
            self.canattack = true
            task.wait(0.2)
            Unslow(hum, id)
        end)
    end
end)

Now in the possible event that the RemoteEvent still doesn’t get fired you can consider adding a failsafe timer that automatically handles the logic, that’s only if the marker event doesn’t fire within a reasonable time, this should help with rare cases of where the event is fully lost

-- Add a fallback timer in case the marker event is missed
task.delay(1, function()  -- Wait 1 second, adjust as necessary
    if not self.canattack then
        -- Perform the logic if the event was missed
        self.canattack = true
        Unslow(hum, id)
    end
end)

This will ensure that if the animation event was to get missed or doesn’t get processed for any reason given, the fallback logic will still occur after the delay. Another issue or reason may be because of the usage of iterating through all animation tracks using the animator:GetPlayingAnimationTracks() each time and to ensure you are only tracking the specific animation you are working with you could load the animation once and keep track of the specific animation instance for where the logic is applied only to the relevant track

-- Instead of getting all animations:
local animations = animator:GetPlayingAnimationTracks()

-- Keep a direct reference to the specific track:
local track = animator:LoadAnimation(animation)
track:GetMarkerReachedSignal("Hit"):Once(function() -- handle event
end)

In short this should use the RemoteEvent or the RemoteFunction to explicit communication from the client to the server, and that means it’ll ensure the server is notified when the maker of the animation marker is reached, now you’ll have a more reliable client server communication

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.