Replay: deterministic record & playback (#4)

Add deterministic record/playback for a run.

Recording captures `(seed, config hash, ordered tick-tagged commands)` and re-simulates on playback — no state snapshots. `DotaFactory.exe --replay <file>` re-plays a recorded run view-only with manual speed/pause.

Reviewed-on: #4
Co-authored-by: Malte Langkabel <malte.langkabel@gmail.com>
Co-committed-by: Malte Langkabel <malte.langkabel@gmail.com>
This commit was merged in pull request #4.
This commit is contained in:
2026-07-01 19:20:08 +00:00
committed by mlangkabel
parent cf68ac2862
commit d74ba5bfad
40 changed files with 3385 additions and 129 deletions

View File

@@ -0,0 +1,48 @@
#pragma once
#include <cstddef>
#include <optional>
#include <vector>
#include "ReplayReader.h"
#include "Tick.h"
class Simulation;
// Drives playback of a parsed replay against a Simulation: applies each recorded
// command at its recorded tick (through Simulation::apply) and verifies the RNG
// checksums, reporting the first desync. Entries are consumed in file order,
// which reproduces the exact command/tick interleaving of the original run.
//
// Cadence (mirrors how the run was recorded so checksums line up):
// player.start(); // process tick-0 entries before the first tick
// each frame, for each tick to run:
// if (player.isFinished()) break;
// sim.tick();
// player.advanceTo(sim.currentTick());
class ReplayPlayer
{
public:
ReplayPlayer(Simulation& simulation, std::vector<ReplayEntry> entries);
// Process all entries recorded at tick 0 (the initial checksum and any
// commands issued before the first tick). Call once, before the first tick.
void start();
// Process all entries recorded at `tick`. Call once after each sim.tick().
void advanceTo(Tick tick);
// True once all entries are consumed or a desync was detected.
bool isFinished() const;
// The tick at which the recomputed checksum first diverged, if any.
std::optional<Tick> getDesyncTick() const;
private:
void processEntriesAt(Tick tick);
Simulation& m_simulation;
std::vector<ReplayEntry> m_entries;
std::size_t m_cursor = 0;
std::optional<Tick> m_desyncTick;
};