Record and Replay System

(Joël Lupien) #1

A system to record entity states and replay them.

Entities with a Recorded component would have their information registered each specific amount of time.
(ie: each frame, 30 times per second, each 5 frames).

The components that would get saved (using saveload) would be specified using a generic on the RecordSystem. Not all components on the entity should be saved.

The ReplaySystem would use the recorded data to set the components to what they were at the specified time.
If the time specified falls between two saved frames, the user can choose to either interpolate the data (if the component supports it) or to use the nearest data.

The ReplaySystem would use a resource called ReplayTracker. The tracker would contain the time at which we are currently reading.

That tracker can either be updated from user systems (clicking on a progress bar to jump through time), or by a system that covers most use cases, called ReplayProgressSystem.

The ReplayProgressSystem would simply read from a resource ReplaySpeed and would move the ReplayTracker time by the time.delta_seconds() * speed.

1 Like

(OvermindDL1) #2

That sounds extremely costly… o.O

Why not just record all external inputs to the system, that’s what I always did and as long as everything is coded well (doesn’t rely on iteration ordering or randomness or so, pseudorandomness is fine, the ‘seed’ is just an external input after all) then it works wonderfully.


(Azriel Hoh) #3

I think both implementation options have use cases:

  • Recording components (perhaps a subset) for immediate replay is useful for implementing visual “running trail” effects (allow processing of the replay entities).
  • Recording input is useful for replaying the game, e.g. upload your “replay” which is just the selected characters, the random seed, and a bunch of inputs, and the game can reproduce what was played before.
1 Like

(Kae) #4

The main problem with only recording inputs is that logic which integrates over time will diverge if you use render-driven delta time. There are two options to make this approach deterministic:

  1. Make all simulation logic delta time agnostic (pure function of time)
  2. Record delta times & frames in replay and use these to drive simulation, then interpolate for rendering.

(OvermindDL1) #5

If it’s a floating point then often yes, if however you use a millisecond or smaller resolution integer and have the time delta each frame ‘be’ one of those external inputs to the system (which it is), then even those still work (that’s the pattern I did), by being replay friendly it also makes networking easier to pull off reliably without desyncs. :slight_smile:


(Joël Lupien) #6

Here I proposed the easiest and most reusable solution. I’m well aware that by using only the inputs + deterministic systems + a couple of data sync points you can achieve the same result with much less saved data. However, doing this implies:

  1. Having deterministic systems
  2. Having a really good precision over the inputs timing

Considering this cannot be enforced at an engine level, but rather at a game level, this should probably be created in an external library.

The solution I proposed, although clearly not optimized, can be added directly into the engine.



(Kae) #7

You can do this, but then you end up with delta times that do not correspond to the render delta times. This will result in stuttery movements and similar issues unless you interpolate.


(Ian Sturdy) #8

I can imagine a lot of games with costly unrendered logic (e.g. AI, hitscan calculations) but few rendered entities–I would expect those to be fairly cheap to record in this fashion. And I am not convinced that any “well-coded” single-player game will be deterministic–if you have one system applying multiple random effects in a single frame, making their outcomes independent of iteration order is non-trivial.


(Joël Lupien) #9

random being the keyword.
If you want to avoid random ordering, you can force the systems to depends on each other. You’ll lose performance, however. It heavily depends on the actual game and its implementation, which is why here I’m proposing to save the state and not the input/events.


(OvermindDL1) #10

I’ve never linked system times and render times together, the renderer always renders whatever the current state it with occasional interpolation data if an entity has such interpolation information and the update rate is very slow (depending on the game style I tend to do either a systems update rate of 60 updates per second, 5 to 20 updates per second, or on-demand based on input changes for things that don’t need to constantly be updating). Never had anything stuttery (as that would drive me utter crazy because I have motion visual annoyances).

You don’t even need to have them depend on each other, you can have them batch up ‘actions’ and perform them later with conflict resolution, this was the pattern I did for many system types and it worked exceedingly well.