Server Rewind (Lag Compensation)

Learn how to utilize server rewinding for lag compensation

The Problem

When using entity interpolation for remote characters, you are viewing and smoothly interpolating their past positions. Meanwhile, assuming your locally controlled character is predicted, you are processing your inputs in advance of the server and predicting where your character will be when the server eventually does the same. As a result, when you perform an action locally you can be sure that, by the time that action reaches the server, the remote players will have moved on and be in a different state than you originally saw them. This can present a challenge when you want the server to take action based on what the player saw at the time rather than where everything is at present. A common example is a first-person shooter where you want to determine who a player had lined up in their crosshairs when they fired their weapon rather than who they might have in their crosshairs by the time the player’s input reaches the server.

The Solution

SnapNet provides built-in functionality to perform server rewinding, sometimes called backwards reconciliation. The idea is that the entire simulation is rewound to exactly where it was when the client performed that input on their machine.

To rewind the server, simply use FSnapNetScopedRewind:

void AExamplePawnEntity::Fire()
{
    // Access any networked variable here to check the current state of things

    FHitResult HitResult( ForceInit );
    {
        FSnapNetScopedRewind Rewind( EntityComponent );

        // Access any networked variable here to check the state of things exactly as the owner of EntityComponent saw it

        FCollisionQueryParams QueryParams( SCENE_QUERY_STAT( WeaponSweep ), true, this );
        const FVector WeaponSweepStart = GetPawnViewLocation();
        const FVector WeaponSweepEnd = WeaponSweepStart + GetViewRotation().Vector() * 100000.0f;
        GetWorld()->LineTraceSingleByChannel( HitResult, WeaponSweepStart, WeaponSweepEnd, ECC_Weapon, QueryParams );
    }

    // Everything is restored back to the current state

    if ( HitResult.bBlockingHit )
    {
        UE_LOG( LogTemp, Log, TEXT( "Hit something!" ) );
    }
}

Note that SnapNet doesn’t just rewind certain properties or hitboxes but entire entities. Consider a first-person shooter with a defensive ability that, once activated, makes the entity immune to damage for a short time. Because the whole entity is rewound, you can check whether that ability was active when the attacker originally fired on their screen and whether the ability is currently active now. This empowers designers to decide whether that shot should count based on game design rather than networking limitations.