Lemmings Forums

Lix => Lix Main => Topic started by: Simon on August 22, 2026, 10:00:11 AM

Title: Rewinding, Netplay: Leapfrogging Savestates for Performant Recomputation
Post by: Simon on August 22, 2026, 10:00:11 AM
Hi,

here's how Lix implements performant rewinding and recomputation. This is the backbone of singleplayer rewinding. And during multiplayer, when an incoming packet had some lag, we can integrate it into the replay and recompute.

Automatic savestates:


How it stores:


I call the pair of #2 and #3 a "leapfrogging pair of savestates". Reason: The even-odd rule above means they'll leapfrog each other. Sometimes #2 is the newest and #3 is one day behind, sometimes #3 is the newest and #2 is one day behind. So far, it alternates nicely. When the user rewinds and replays, it's possible that we'll write to #2 several times in succession before we ever write to #3 again, because of how it's used for rewinding:

How it rewinds:


What are good intervals for savestating? Days/months/years are only good for the example. The unit of time in Lix is the physics update, not days/months. Normal gameplay speed is 15 physics updates per second. This leads to the following leapfrogging resolution:


n is the age of the world in physics updates.
% is modulo (computes remainder of division).

Reasoning for the value of 10 for the fastest pair:


See My implementation on github (https://github.com/SimonN/LixD/blob/master/src/physics/world/cache.d). I've abstracted each pair into its own struct LeapfrogPair. The physics cache has 5 leapfrogging pairs, and each pair maintains two savestates ("frogs").

To save VRAM on huge maps, I keep only 3 or 4 pairs, not 5 pairs. But this decision is largely out of fear. I don't know how much this helps with crashing for being out of VRAM. It's unrelated to the discussion of the algorithm. Nonetheless, it's in the code like this. Don't copy this decision; use many savestates.

Optimizations:


-- Simon