Replay Format

The basic structure of a replay file consists of a Typemap, Headers and Snapshots:

Typemap & Headers

The very first section of the replay file contains the Typemap and Header information. These are preceded by a 4 byte integer which represents the combined byte size of the Typemap and Header together.

Typemap

The first section of the file is the Typemap which maps named and versioned data types to an unsigned short identifier. This is important as knowing the type and version allows for backwards compatibility in the parser.

All datatypes are then referenced via the identifier as opposed to its full name and version.

This mapping is required as the unsigned short type identifier is assigned at runtime and there is no guarantee that the given identifier will be the same for a given type between subsequent replays as the identifiers are assigned in order that each plugin is loaded by bepinex.

The initial version string is for the Typemap data structure itself. Currently there is only 1 version (0.0.1) which has the format as described above.

Headers

The header section stores a list of data that remains unchanged throughout the replay. Its often used to store metadata, map geometry and static item locations such as terminals / power generators.

Each data entry starts with a 2 byte unsigned short which represents the type identifier for the data structure. This identifier can then be looked up in the Typemap to get the typename and version to parse the structure.

The list of entries is terminated by an empty data entry marked with the type identifier for the type ReplayRecorder.EndOfHeader.

A list of header data types and how they should be parsed can be found here.

Snapshots

Every tick the recorder takes a snapshot of the current game state. A snapshot typically consists of 2 parts:

  • Events - Events are used to store discrete moments such as damage / gunshot events. These have accurate timing information allowing them to be accurate beyond the tick rate.

  • Dynamics - Dynamics represent continuous structures such as moving entities. These are polled each tick and require interpolation between ticks.

The tick rate of the recorder is determined by the combat state of the game. If you are not in combat (no awake enemies, no on going alarms etc...), then the tick rate is 10 ticks per second. When in combat, the tick rate is 20 ticks per second.

Each snapshot is given an unsigned integer millisecond timestamp. Currently the recorder does not handle overflow as its unlikely a game lasts long enough for this to be an issue.

Events

Each event entry has its Type ID which represents the type identifier for the data structure. This identifier can then be looked up in the Typemap to get the typename and version to parse the structure.

It also has an additional Offset which represents the exact timing for when the event was triggered with respect to the snapshots timestamp. For example, if the snapshot occurred at t = 1000ms and the event has an offset of 10ms, then the event was triggered at t = 990ms.

A list of dynamic data types and how they should be parsed can be found here.

Dynamics

Each dynamic collection has its Type ID which represents the type identifier for the data structure. This identifier can then be looked up in the Typemap to get the typename and version to parse each entry of the collection.

It should be noted that the list of dynamics presented in each snapshot only represent the dynamics which have changed since the last snapshot as opposed to representing all the dynamics in the current game state. It would be more appropriate to describe them as representing a "Delta Snapshot".

Whether the underlying data structure for a given dynamic stores the delta or actual information is up to the type definition.

A list of dynamic data types and how they should be parsed can be found here.

Within a collection, each dynamic instance will only exist given that it was previously spawned. Similarly a dynamic instance is only considered to no longer exist once it has despawned.

Last updated