MST
Technical Article Networking · Client Prediction
Networking · Unreal Engine 5.7

Client Prediction in UE5

Prediction is what makes multiplayer input feel instant without handing authority to the client. This refreshed article focuses on the real Unreal Engine extension points: CharacterMovementComponent first, then GAS prediction, Network Prediction, and Mover when the mechanic truly needs them.

Updated For
UE5.7 networking guidance
Grounded around CharacterMovementComponent, network roles, GAS prediction, Network Prediction, Mover, and current testing workflows.
Best For
Character movement and responsive abilities
Especially useful if you are adding sprint, dodge, custom movement flags, or local ability feel to a multiplayer character.
Key APIs
CMC · FSavedMove · Prediction Keys
The article replaces the old oversimplified RPC example with a real saved-move style extension pattern.
01 · Core idea

Prediction in one sentence

Client prediction exists to make a multiplayer game feel immediate without giving up server authority. The client simulates the expected result of local input right away, the server re-simulates or validates that input authoritatively, and the client corrects if it diverged.

What prediction solves

The delay between input and feedback when a round trip to the server would otherwise block the response.

What prediction is not

A way to let clients decide the final truth. The server still owns the authoritative outcome.

Where it shows up

Character movement, local ability feel, cosmetic firing feedback, and rollback-friendly movement systems.

Where it hurts

When predicted state is too different from server state and corrections become visible pops or rubber-banding.

The old article was directionally right but too generic. In Unreal, the key question is not “should I invent prediction?” It is usually which built-in prediction path already exists for this system, and how do I extend it cleanly?

Best starting point
For standard avatars, start with CharacterMovementComponent. Only move to lower-level prediction systems when the built-in path no longer fits the mechanic.
02 · Networking flow

Roles and replication flow

Thinking in roles makes the whole system easier to reason about.

Role What it does What it should not do
Autonomous proxy Consumes local input immediately, predicts the result, and submits moves or inputs to the server. Declare the final authoritative result of contested gameplay.
Authority / server Validates and simulates the real version of the move, then replicates the authoritative state. Wait for the client to tell it what the outcome was.
Simulated proxies Interpolate replicated state for everybody who is merely observing. Run the full local-owner prediction path.

That means the owner’s view of the pawn is usually “ahead” compared to everyone else, because the owner is predicting the move before the server round trip finishes. That is normal. The system only becomes a problem when the owner’s predicted path differs too much from what the server later approves.

A clean mental flow looks like this:

  1. Capture local input.
  2. Predict locally for immediate feel.
  3. Send the input or move to the server.
  4. Server simulates and validates.
  5. Server replicates the result.
  6. Owner reconciles if needed, observers smooth toward the replicated state.
03 · Built-in path

CharacterMovementComponent first

For a regular character, Unreal already gives you the main prediction path through UCharacterMovementComponent. Epic’s docs and quick-start material still point to it as the built-in movement path that handles replication automatically for characters, and it remains the default place to start for walking, jumping, sprinting, crouching, and similar avatar movement.

This is where many custom implementations go wrong: they bypass the built-in flow and start sending raw positions or custom “ServerMove to this location” RPCs for mechanics that should have been expressed as movement state or movement intent.

Use CMC when

Your mechanic is fundamentally character movement: sprint, dodge, mantle intent, strafe rules, jump variants, temporary speed changes, or movement-mode transitions.

Do not fight it with

A parallel RPC path that sends raw positions for the same pawn.

Why CMC is strong

It already owns move buffering, prediction, reconciliation, and smoothing logic for standard character movement.

When to reconsider

When the mechanic is not character movement anymore and needs a more explicit rollback-friendly simulation model.

A common anti-pattern
If you find yourself replicating the owner’s position manually on top of CharacterMovementComponent, that is usually a sign the mechanic should be expressed inside CMC instead.
04 · Real extension point

Extending CMC with custom input

A good example is sprint. Sprint is not “teleport the server to a faster location.” It is a predicted movement intent that changes max speed and must be encoded into the saved move path so both client and server simulate the same thing.

Custom movement componentPredicted sprint state

UCLASS()
class UMyCharacterMovementComponent : public UCharacterMovementComponent
{
    GENERATED_BODY()

public:
    void SetSprinting(const bool bEnabled)
    {
        bWantsToSprint = bEnabled;
    }

    bool IsSprinting() const
    {
        return bWantsToSprint;
    }

    virtual float GetMaxSpeed() const override
    {
        const float BaseSpeed = Super::GetMaxSpeed();
        return bWantsToSprint ? BaseSpeed * SprintMultiplier : BaseSpeed;
    }

    virtual void UpdateFromCompressedFlags(uint8 Flags) override
    {
        Super::UpdateFromCompressedFlags(Flags);
        bWantsToSprint = (Flags & FSavedMove_Character::FLAG_Custom_0) != 0;
    }

    virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;

protected:
    UPROPERTY(EditAnywhere, Category="Movement")
    float SprintMultiplier = 1.5f;

    UPROPERTY(Transient)
    bool bWantsToSprint = false;
};
Saved move integrationEncode sprint into CMC prediction

class FSavedMove_MyCharacter final : public FSavedMove_Character
{
public:
    uint8 bSavedWantsToSprint : 1;

    virtual void Clear() override
    {
        Super::Clear();
        bSavedWantsToSprint = false;
    }

    virtual uint8 GetCompressedFlags() const override
    {
        uint8 Result = Super::GetCompressedFlags();

        if (bSavedWantsToSprint)
        {
            Result |= FLAG_Custom_0;
        }

        return Result;
    }

    virtual bool CanCombineWith(
        const FSavedMovePtr& NewMove,
        ACharacter* Character,
        float MaxDelta) const override
    {
        const FSavedMove_MyCharacter* Other =
            static_cast(NewMove.Get());

        return bSavedWantsToSprint == Other->bSavedWantsToSprint
            && Super::CanCombineWith(NewMove, Character, MaxDelta);
    }

    virtual void SetMoveFor(
        ACharacter* Character,
        float InDeltaTime,
        const FVector& NewAccel,
        FNetworkPredictionData_Client_Character& ClientData) override
    {
        Super::SetMoveFor(Character, InDeltaTime, NewAccel, ClientData);

        const UMyCharacterMovementComponent* MoveComp =
            Cast(Character->GetCharacterMovement());

        bSavedWantsToSprint = MoveComp && MoveComp->IsSprinting();
    }

    virtual void PrepMoveFor(ACharacter* Character) override
    {
        Super::PrepMoveFor(Character);

        if (UMyCharacterMovementComponent* MoveComp =
                Cast(Character->GetCharacterMovement()))
        {
            MoveComp->SetSprinting(bSavedWantsToSprint);
        }
    }
};

class FNetworkPredictionData_Client_MyCharacter final
    : public FNetworkPredictionData_Client_Character
{
public:
    explicit FNetworkPredictionData_Client_MyCharacter(
        const UCharacterMovementComponent& ClientMovement)
        : Super(ClientMovement)
    {
    }

    virtual FSavedMovePtr AllocateNewMove() override
    {
        return FSavedMovePtr(new FSavedMove_MyCharacter());
    }
};

FNetworkPredictionData_Client*
UMyCharacterMovementComponent::GetPredictionData_Client() const
{
    if (ClientPredictionData == nullptr)
    {
        UMyCharacterMovementComponent* MutableThis =
            const_cast(this);

        MutableThis->ClientPredictionData =
            new FNetworkPredictionData_Client_MyCharacter(*this);

        MutableThis->ClientPredictionData->MaxSmoothNetUpdateDist = 92.0f;
        MutableThis->ClientPredictionData->NoSmoothNetUpdateDist = 140.0f;
    }

    return ClientPredictionData;
}

That is the important pattern: predict the intent locally, encode that intent into the saved move path, and let both client and server simulate from the same rule set.

What makes this better than a hand-rolled RPC? The owner gets immediate responsiveness, the server still owns truth, and correction stays aligned with the movement system Unreal already knows how to smooth.

05 · Staying in sync

Reconciliation and corrections

Prediction without reconciliation is just guessing. The server will eventually disagree sometimes, and the client needs a way to converge back to authority cleanly.

In Unreal’s built-in movement path, reconciliation already exists: the owner receives corrections when the authoritative result diverges, while simulated proxies smooth toward replicated state. Your job is to reduce avoidable divergence.

Keep rules deterministic

The closer the client and server run the same logic, the fewer corrections you will see.

Avoid hidden state

Client-only branches, time-dependent randomness, or inconsistent collision assumptions amplify divergence.

Predict inputs, not outcomes

It is usually safer to predict intent and let the same movement rules produce the result.

Keep irreversible gameplay authoritative

Damage, inventory, confirmed hits, and match state should still resolve on the server.

A useful question is: what exactly is being predicted? The best answer is usually one of these:

  • input intent,
  • a deterministic movement transition,
  • or a local cosmetic response that can be corrected harmlessly later.

The worst answer is “the client predicts a permanent contested game result and hopes the server agrees.”

Good predicted feedback
Muzzle flash, camera shake, input feel, sprint start, animation anticipation, or local UI acknowledgement.
Usually server-authoritative
Damage application, inventory mutation, score changes, authoritative target selection, or any irreversible competitive outcome.
06 · Modern UE5 options

Beyond CMC: GAS, Network Prediction, Mover

Not every mechanic belongs in CMC. The rule is not “always use CharacterMovementComponent.” The rule is “use the highest-level built-in prediction path that still matches the problem.”

System Good fit Why it matters
Gameplay Ability System Predicted abilities and local responsiveness around ability activation. GAS has explicit prediction support and prediction keys for synchronizing side effects.
Network Prediction plugin Bespoke, simulation-driven prediction models. Useful when you need a more explicit rollback/resimulation model than stock CMC.
Mover Modular movement systems with rollback networking. Epic’s current Mover docs describe it as a movement plugin built on rollback-friendly networking concepts.

This is the clean way to think about the stack:

  • CMC for traditional character movement.
  • GAS prediction when the mechanic is primarily an ability problem.
  • Network Prediction / Mover when you need explicit rollback-oriented simulation for a custom movement model.

That also means you should not jump to Mover or a custom prediction framework just because you need sprint, dodge, or crouch variants. Reach for them when the movement model itself becomes the new problem.

07 · Verification

Testing under bad network conditions

You do not really know whether prediction feels solid until you test under latency, jitter, and packet loss. Unreal’s current docs still emphasize network emulation for exactly this reason.

At minimum, test these scenarios:

  • low latency, no loss,
  • moderate latency with jitter,
  • high latency with occasional packet loss,
  • and listen server vs dedicated server behavior.
Manual testing checklistPIE + emulation

1. Run with a dedicated server and at least one autonomous proxy client.
2. Enable network emulation in PIE or your test configuration.
3. Verify:
   - local input still feels immediate,
   - corrections are rare and subtle,
   - simulated proxies remain smooth,
   - and no gameplay outcome depends on the client winning a race.
4. Repeat with packet loss and jitter, not just fixed latency.

Watch for these symptoms:

  • Frequent rubber-banding: prediction is diverging too much from authority.
  • Owner-only logic bugs: autonomous proxy and server are not running equivalent rules.
  • Observer jitter: smoothing or replicated state cadence is too weak for simulated proxies.
Practical advice
Do not validate a prediction system only on localhost. A clean localhost demo can still fail badly once latency and packet loss enter the picture.
08 · Sources

References and further reading

These references were used to modernize the article around current UE5 guidance.