Skip to content

Replay your games

Alexandre 'kidev' Poumaroux edited this page Oct 21, 2026 · 3 revisions

Replay your games

ProLeak can save every game you play, official or local, and play it back later in the game: as a spectator, or from your own seat with your HUD. A recording is a single file you can keep and share.

Contents

Recording your games

Turn on Mods > Recordings > Start recording games, or set record: true in ProLeak/config/proleak.yml. The setting applies from the next game, so a recording always starts at the start of its game. If the connection drops and you rejoin the same game, the recording goes on in the same file.

Each game becomes ProLeak/recordings/<date>-<time>-<game id>.ltd2demo. A toast ("Game saved") says when the file is complete, or why it could not be written.

Watching a recording

Mods > Recordings lists your newest recordings like the game's match history: the result, the queue, the last wave and the date, then your value, workers, income, leaks, build and legion. Each game has three buttons:

  • Watch shows the game as a spectator, starting on the recorded player's lane.
  • Watch as player shows it from the recorded player's seat, with their HUD.
  • Delete removes the file, after asking.

The game's own match history (Profile > Match History) also gets a Watch button at the right end of each game that has a recording.

During playback a bar beside the top bar has Pause, Play, Slower and Faster (0.5x to 8x). Chat commands do the same: -speed 2, -pause, -resume and -leave. Once the recording has run out, Quit appears. Seeking back is not supported yet: leave and watch again to start over.

ProLeak plays a recording through its own local server, so watching one reports nothing to the official servers.

What playback cannot show

A recording holds what the server sent you during the game. Two things are missing from it:

  • The other team's building. The server never sent it during the game. Playback fills it in from the end screen, so the other team's fighters appear at the start of each wave, as they stood for that wave. A recording cut short before the end screen leaves them out.
  • In spectator view, what was sent to you alone, such as your Mastermind roll windows.

A recording from another game version

A recording plays as it is on the game version that recorded it. On another version, Watch first checks the recording:

  • When ProLeak can read or convert every message, it offers Watch anyway. The game itself may behave differently on the new version, so the replay may not match the original game.
  • Otherwise it says why the recording cannot be replayed.

What a recording holds

  • Every message the game server sent during the game and every message your client sent, chat included.
  • The game version, the map, the queue, the players' names and account ids, and their legions.
  • How the game ended, and your line of the end screen.

Your login and session ticket are never written.

Warning

Sharing a recording

Recordings hold players' names and account ids: share a file only with people you would show the game to.

A file cut short (the game crashed or closed while recording) can still be played up to its last ten complete seconds.

For tool authors

Reading a recording

tools/DemoInfo describes a file: its game, how it ended, whether its checksum holds, and its bytes by message.

dotnet run --project tools/DemoInfo -- recordings/20261014-213005-abc123.ltd2demo

It exits with 0 for a whole file, 1 for a cut or damaged one and 2 for a file it cannot read. With --against 26.10a it also says whether the recording plays on that game version, from the message formats ProLeak recorded for it.

The file format

All integers are little endian; a varint is an unsigned LEB128 number and a signed varint is its zigzag form.

file    := magic header chunk* index footer trailer
magic   := "LTD2DEMO" u16(version, 1)
header  := u32(length) JSON
chunk   := 'C' u32(compressed length) u32(length) i64(first tick) deflate(records)
record  := varint(kind) signed-varint(tick - previous tick) body
index   := 'I' varint(count) { i64(first tick) i64(offset of the chunk's 'C') varint(records) }*
footer  := 'F' u32(length) JSON
trailer := i64(offset of 'I') u32(CRC-32 of every byte before the trailer) "LTD2DEND"

A record's tick is the client's estimate of the server clock in milliseconds; the first record of a chunk counts from the chunk's first tick. A chunk ends after ten seconds of game or 256 KiB of records.

Kind Body
0, a server message u8(tag) varint(subject) varint(length) bytes: the message's payload as received
1, a client message the same, with the payload as the game encodes it
2, a dev line varint(length) UTF-8 JSON: the decoded message, written in dev mode only
3 to 15 varint(length) bytes, reserved: a reader skips them

The header's keys are format, game_version, proleak_version, map, queue, slot (the local player's index in slot_ids), slot_ids, local, dev, builder_types and seed (null for an official game), game_files (each game assembly's SHA-256), and mode and mode_version (the game mode a local game played, as plugin:name, and that plugin's version; empty otherwise). The footer's are result, waves, records, dropped, messages (each message type the recording holds, by full name, with its format: tag, subject, fingerprint and the fields its reader reads in order), broken (message types the game read short while recording) and card (the recorded player's end-screen line: value, workers, income, leak_first, leak_second, wave_unit, legion_icon, spell_icon, and opening and build: the units of the first and the last wave, each a list of unit types with their count). A reader ignores keys it does not know.


Previous: Troubleshooting | Next: Stream events

Clone this wiki locally