Files
dota_factory/src/lib/sim/CommandManager.h
Malte Langkabel d74ba5bfad 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>
2026-07-01 19:20:08 +00:00

56 lines
2.2 KiB
C++

#pragma once
#include <memory>
#include <vector>
struct Command;
class ReplayRecorder;
class Simulation;
// Ordered queue that funnels every player command into the single
// Simulation::apply chokepoint (see docs/replay_design.md). This is deliberately
// NOT the EventManager pub/sub bus: sim mutations must apply in a strict,
// tick-pinned, recordable order to a single recipient.
//
// Live play pushes commands via enqueue(); they are applied at the next drain(),
// which runs once per frame before the tick batch. Draining before the tick
// (even at 0x game speed) lets a paused player see placed construction sites
// immediately while staying deterministic — replay applies each command at its
// recorded tick regardless of frame cadence.
class CommandManager
{
public:
explicit CommandManager(Simulation& simulation);
// Defined out-of-line so the unique_ptr<ReplayRecorder> member can be
// destroyed where ReplayRecorder is a complete type.
~CommandManager();
// Append a command for application at the next drain (FIFO order).
void enqueue(std::shared_ptr<const Command> command);
// Apply all queued commands in FIFO order through Simulation::apply, then
// clear the queue. If a recorder is attached, each applied command is recorded
// (a Reset rolls the recorder to a new file).
void drain();
bool hasPending() const;
// Attach a recorder and start recording the current run. Ownership is taken.
// Passing nullptr detaches/stops recording.
void setRecorder(std::unique_ptr<ReplayRecorder> recorder);
// In replay mode, enqueue() is a no-op: the queue is driven by the recorded
// stream (ReplayPlayer), so stray live input produces nothing.
void setReplayMode(bool replayMode);
// Record a periodic RNG checksum if the current tick is on the checksum
// cadence. Call once per simulated tick (from the tick loop).
void recordTickCheckpoint();
private:
Simulation& m_simulation;
std::vector<std::shared_ptr<const Command>> m_queue;
std::unique_ptr<ReplayRecorder> m_recorder;
bool m_replayMode = false;
};