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