Prediction¶
Client-side prediction in GAS lets the owning client execute gameplay actions immediately without waiting for server confirmation, then reconciles when the server responds. This is what makes abilities feel responsive in networked games -- press a button, see the result instantly, even with 100ms of latency.
The prediction system is built around one core concept: the Prediction Key.
FPredictionKey¶
An FPredictionKey is a unique ID generated on the client and sent to the server. It ties together a predicted action and all of its side effects so they can be confirmed or rolled back as a unit.
Key properties:
- Generated by the client when starting a predicted action (typically ability activation)
- Sent to the server with the activation RPC
- The server associates its authoritative side effects with the same key
- Replication is asymmetric: keys replicate client → server always, but server → client only to the originating client. All other clients receive an invalid (0) key.
This asymmetry is intentional -- only the predicting client needs to reconcile.
What Gets Predicted¶
| System | Predicted? | Notes |
|---|---|---|
| Ability activation | Yes | The core predicted action |
| Chained ability activation | Partial | With caveats (dependent keys) |
| Triggered events | Yes | |
| GE application (attribute mods) | Yes | Modifier-based only, not Executions |
| GE application (tag grants) | Yes | |
| Gameplay Cues | Yes | Both from GEs and standalone |
| Montages | Yes | Via ability system's montage replication |
| Character Movement | Yes | Built into UCharacterMovement, not GAS |
| GE removal | No | |
| GE periodic ticks | No | |
| Execution Calculations | No | Executions run server-only |
| Spawned actors (from abilities) | Partial | Can be predicted with extra work |
Execution Calculations aren't predicted
This is a common surprise. If your damage formula uses an ExecCalc, the damage calculation only runs on the server. The client can predict the ability activation and the GE application, but not the calculated damage value. The health change arrives via replication. For instant-feel damage, some projects use modifiers instead of ExecCalcs for the predicted portion.
The Prediction Window¶
A prediction window is the scope during which predicted side effects are valid. Here's how it works:
- Client calls
TryActivateAbility→ a newFPredictionKeyis generated - Client calls
ServerTryActivateAbility→ the key is sent to the server - Client calls
ActivateAbilitylocally (predictively) → this is the prediction window - Everything that happens in the
ActivateAbilitycallstack gets associated with the prediction key ActivateAbilityreturns → the prediction window closes
The prediction window is one callstack
Anything that happens after ActivateAbility returns is outside the prediction window. This means: timers, delays, latent Blueprint nodes, and anything on the next frame are not predicted. If your ability does ActivateAbility → Delay 0.5s → ApplyEffect, the effect application is not predicted.
FScopedPredictionWindow¶
The engine uses FScopedPredictionWindow to manage prediction windows. It's an RAII object that sets up and tears down the prediction context:
// Inside the ASC's ability activation flow:
FScopedPredictionWindow ScopedPrediction(AbilitySystemComponent, PredictionKey);
// Everything in this scope is predicted with this key
Ability->ActivateAbility(...);
// Scope ends, prediction window closes
You can also create scoped windows manually in ability code for dependent predictions:
{
FScopedPredictionWindow ScopedPrediction(
AbilitySystemComponent, true /* create new scoped key */);
// This predicted action uses a dependent key
ASC->ApplyGameplayEffectToSelf(Effect, Level, Context);
}
The Prediction Flow¶
Here's the full flow for a predicted ability activation:
CLIENT SERVER
────── ──────
TryActivateAbility()
│ Generate PredictionKey = 42
│ Call ServerTryActivateAbility(42) ──→ ServerTryActivateAbility(42)
│ │ Can activate? Check costs, tags, etc.
│ (Don't wait for server) │
▼ │
ActivateAbility() ←─ prediction window │
│ Apply GE (predicted, key=42) │
│ Play cue (predicted, key=42) │
│ Play montage (predicted) │
▼ │
(ability continues locally) ▼
ActivateAbility() on server
│ Apply GE (authoritative, key=42)
│ Play cue (key=42)
│ Play montage
▼
ClientActivateAbilitySucceed(42) ──→ Client
│
▼ (property replication catches up)
ReplicatedPredictionKey matches 42
│ Side effects with key 42 are "caught up"
│ Predicted GE is removed (server's replicated version takes over)
│ Predicted cue is suppressed (already playing)
▼
Reconciled -- client and server agree
Rejection Flow¶
If the server rejects the activation:
SERVER: ClientActivateAbilityFailed(42) ──→ Client
│
▼
Client receives rejection
│ Kill the ability immediately
│ Roll back all side effects with key 42:
│ - Remove predicted GE
│ - Remove predicted cue
│ - Stop predicted montage
▼
State reverts to pre-prediction
Predicted GameplayEffect Handling¶
When a GE is applied predictively:
- Client applies the GE with
PredictionKey = 42→ creates a localFActiveGameplayEffect - Server applies the same GE with
PredictionKey = 42→ replicates down to client - Client receives the replicated GE, sees it has
PredictionKey = 42, checks for a local match - Match found → skip "on applied" events (no duplicate cue, no duplicate tag grant)
- Briefly, two copies of the effect exist (predicted + replicated)
- When
ReplicatedPredictionKeycatches up to 42, the predicted copy is removed - The replicated copy remains as the authoritative state
Attribute Prediction¶
Attributes are replicated as standard UPROPERTYs, so predicting them requires special handling. GAS uses delta prediction: instead of predicting "I have 90 mana," the client predicts "-10 mana from whatever the server says." Instant GE modifications are treated as infinite-duration modifiers during the prediction window.
When the server confirms and the replicated attribute arrives, the predicted delta is removed, and the replicated value takes over seamlessly.
Predicted Actor Spawning¶
Abilities can predict spawning actors (projectiles, etc.) but it requires coordination:
- The ability spawns the actor locally during the prediction window
- The server also spawns the actor
- The server's replicated actor arrives on the client
- The client needs to match and merge the predicted and replicated versions
This is handled through UAbilityTask_SpawnActor and the prediction key system, but it's one of the trickier aspects of GAS prediction.
Common Pitfalls¶
Pitfall 1: Predicting Outside the Window¶
void UMyAbility::ActivateAbility(...)
{
// Inside prediction window
ApplyEffect(CostEffect); // Predicted
GetWorld()->GetTimerManager().SetTimer(Handle, [this]()
{
// OUTSIDE prediction window -- NOT predicted!
ApplyEffect(DamageEffect); // Server-only
}, 0.5f, false);
}
Fix: Apply all predicted effects in the initial callstack, or use FScopedPredictionWindow with dependent keys.
Pitfall 2: ExecCalc Mismatch¶
Client predicts ability activation and GE application, but the ExecCalc only runs on the server. The client's predicted health doesn't change, then suddenly jumps when the server's calculated damage arrives.
Fix: For instant damage feedback, use modifier-based GEs for the predicted portion. Or accept the slight delay and design your cue feedback to mask it.
Pitfall 3: Prediction Key Already Used¶
If you try to reuse a prediction key (e.g., activating another ability in the same frame), you'll get unexpected behavior.
Fix: Each predicted action should have its own unique key. The system handles this automatically for standard ability activation.
Pitfall 4: Client Applies Effect, Server Doesn't¶
If the client's tag state differs from the server's (due to latency), the client might predict an effect application that the server rejects because of tag requirements.
Fix: Design your tag requirements to be lenient during prediction, or accept the rollback. Keep tag-based blocks simple and predictable.
Pitfall 5: Forgetting That GE Removal Isn't Predicted¶
If your ability removes a buff predictively, the client removes it immediately, but the server might not process the removal for a round trip. During that window, the client sees the buff gone while the server still has it.
Fix: GE removal not being predicted is a known limitation. For critical removal timing, use server-authoritative flow.
Key Source File¶
The GameplayPrediction.h header contains an extensive comment block (100+ lines) that documents the entire prediction architecture. It's the definitive reference -- read it if you need to understand the system deeply.
Related Pages¶
- Net Execution Policies -- how policies interact with prediction
- Replication Modes -- what gets replicated
- Batching -- reducing prediction-related RPC overhead
- Cue Parameters -- how prediction keys flow to cues