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,56 @@
#include "ReplayPlayer.h"
#include <utility>
#include "Command.h"
#include "Simulation.h"
ReplayPlayer::ReplayPlayer(Simulation& simulation, std::vector<ReplayEntry> entries)
: m_simulation(simulation)
, m_entries(std::move(entries))
{
}
void ReplayPlayer::start()
{
processEntriesAt(0);
}
void ReplayPlayer::advanceTo(Tick tick)
{
processEntriesAt(tick);
}
bool ReplayPlayer::isFinished() const
{
return m_desyncTick.has_value() || m_cursor >= m_entries.size();
}
std::optional<Tick> ReplayPlayer::getDesyncTick() const
{
return m_desyncTick;
}
void ReplayPlayer::processEntriesAt(Tick tick)
{
// Consume entries for this tick in file order. The recording writes, at a
// given tick: the periodic checksum (if any) first, then command lines each
// followed by their post-apply checksum — so applying/verifying in file order
// reproduces the original sequence exactly.
while (!m_desyncTick.has_value()
&& m_cursor < m_entries.size()
&& m_entries[m_cursor].tick == tick)
{
const ReplayEntry& entry = m_entries[m_cursor];
++m_cursor;
if (entry.isCommand)
{
m_simulation.apply(*entry.command);
}
else if (m_simulation.rngFingerprint() != entry.fingerprint)
{
m_desyncTick = tick;
}
}
}