Quick answer: Project Settings → Physics → Common → physics_ticks_per_second = 60 on both ends. Disable physics_interpolation in the deterministic loop. Snapshot state per tick.

Client predicts player movement, server reconciles. After 5 seconds you’re visibly off the server’s answer. Different tick rate or different physics integrator state.

The Symptom

Snap-back corrections. Player teleports as the server overrides predicted position. Worse at long ping.

The Fix

# project.godot (both client & server)
[physics]
common/physics_ticks_per_second = 60
common/physics_interpolation = false

# GDScript: fixed-step sim in _physics_process
func _physics_process(delta):
    sim_tick += 1
    var input = collect_input(sim_tick)
    apply_input(input)
    var snap = snapshot_state(sim_tick)
    history[sim_tick] = snap

Both ends advance at the same rate. Inputs are tagged with sim_tick. Snapshots in history allow rollback when the server says “at tick N you should have been here.”

Reconciliation

func on_server_correction(server_tick: int, server_state: Dictionary):
    if not history.has(server_tick): return
    restore_state(server_state)
    for t in range(server_tick + 1, sim_tick + 1):
        apply_input(input_history[t])
    # now matches server's authoritative state, plus client inputs

RandomNumberGenerator state belongs in the snapshot too — otherwise re-simulation diverges.

Verifying

Run client + server with intentional 200ms latency. Predicted position matches server reconciliation within 1 unit; corrections are imperceptible.

“Same tick rate. Snapshot per step. Rollback on correction.”

Related Issues

For input action just_pressed, see just_pressed. For tween finished signal, see tween signal.

Tick rate locked. Snapshots ready. Sim agrees.