Been trying to implement proper rollback netcode for a 2D fighting game prototype in Godot 4 and I'm at that stage where I've read everything there is to read and written almost nothing that actually works.
The concept is clear enough: capture inputs every frame, send them to the peer with the frame number attached, predict forward if their packet hasn't arrived yet, roll back and resimulate when it does, compare state hashes to detect divergence. That part makes sense. The implementation is where things fall apart.
The core issue is determinism. For rollback to work correctly, two machines running the same input sequence from the same starting state have to produce bit-identical results every frame. Godot 4's physics engine gives you no such guarantee. Floating point behavior varies by platform, physics steps accumulate drift, and if you're using any built-in 2D physics for movement or collision you're probably already desyncing before round one starts.
The options I've identified so far:
- Go fully deterministic: fixed-point math for all game-state values, no physics engine, custom AABB collision. Painful but the only real guarantee.
- Keep Godot's physics, accept desyncs, implement aggressive state reconciliation and hope for the best.
- Move the simulation layer into C# and rely on explicit determinism controls there, using GDScript only for rendering.
For a simple fighting game where I can avoid the physics engine entirely, option 1 seems feasible. Fixed-point integers in GDScript aren't pretty but they work. I've been using a FP class with a scaled integer representation and it's manageable for a small action set. The concern is rollback budget: how many frames can I realistically resimulate per _process call before there's visible hitching? At 60fps you have ~16ms and a full resim of even a simple frame takes a non-trivial chunk of that.
There's at least one Godot rollback addon that takes a hash-verification approach: peers exchange state hashes each frame, detect divergence, and rollback further when it happens. Doesn't solve determinism, just catches when it breaks. Useful, but it's treating the symptom.
Has anyone actually shipped or seriously prototyped rollback in Godot 4? How did you handle the determinism problem? Did you end up writing your own physics layer? And how deep did your rollback window actually need to go in practice?