We ran into a timing issue with Gameplay Cues when predicting Gameplay Effects on a target Ability System Component (ASC) while using mixed replication.
Here is a breakdown of the flow and where we see the problem.
1. Ability Flow
Client Side prediction
- The client starts an ability.
- Predictively applies a Gameplay Effect with a duration to a target ASC.
- The Gameplay Effect triggers a Gameplay Cue, spawning a Niagara VFX.
- We cache the pointer to this effect in the Gameplay Cue for removal in OnCeaseRelevant.
Server execution
- The server executes the same ability.
- Applies the same Gameplay Effect.
- This causes the Gameplay Cue to replicate to the client.
We have observed two possible outcomes now:
Case 1: When it works properly
- OnCeaseRelevant runs first, cleaning up the predicted effect and the Niagara system.
- OnBecomeRelevant (OnActive) runs afterwards, spawning a new Niagara System/Effect.
- Everything works as intended because the old effect was cleared by the time we spawn the new one.
Case 2: Timing/order issue occurs
- OnBecomeRelevant from the replicated cue arrives before the client’s OnCeaseRelevant is called.
- A new Niagara Effect is spawned on top of the old one (which hasn’t been cleared yet).
- When the first OnCeaseRelevant finally runs, it removes the new effect, leaving the old one persisting.
- If we try to remove the effect preemptively on OnBecomeRelevant, we risk deleting the only valid effect when OnCeaseRelevant runs.
We have tried a couple of workarounds that seem to work, confirming the issue explained above:
Workaround 1: Removing the client-side predictive effect
- Avoids overlapping cues entirely.
- Works but reduces responsiveness from client prediction.
Workaround 2: Counting OnActive and OnRemove calls:
- Only remove the effect when the counts match.
- On OnActive, remove the effect if it already exists.
- On OnCeaseRelevant, remove the effect only when the counts are the same.
- Works reliably but is essentially an ugly hack to work around the race condition.
Is it possible to know if the calls to OnBecomeRelevant and OnCeaseRelevant are coming from replicating or prediction? Is there a better approach to do this? Can we somehow enforce the order? Should OnActive and OnRemove even be called when the server corrects the Gameplay Effect?
I have added a graph that hopefully illustrates the issue a bit better.
Thank you so much.