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.