#pragma once #include #include #include #include "Tick.h" struct Command; // 64-bit hash (16-char hex) over the *.toml files in configDir. Stored in the // replay header and recomputed on playback to detect a config mismatch. std::string computeReplayConfigHash(const std::string& configDir); // Writes a replay file as the game runs (see docs/replay_design.md). The format // is line-oriented append-friendly text: a small keyed header, then one line per // command (tick-tagged) interleaved with RNG-state checksum lines for desync // detection. Each line is flushed so a crash mid-run still leaves a valid partial // file. // // One file = one run between Reset boundaries; startNewRun() closes the current // file and opens a fresh one. class ReplayRecorder { public: // configDir: hashed (its *.toml files) into the header for config-mismatch // detection on playback. outputDir: where .replay files are written. ReplayRecorder(std::string configDir, std::string outputDir); ~ReplayRecorder(); ReplayRecorder(const ReplayRecorder&) = delete; ReplayRecorder& operator=(const ReplayRecorder&) = delete; // Close any current file, then open a fresh one (named _), // write the header, and record an initial tick-0 checksum. A run boundary. void startNewRun(unsigned int seed, std::uint64_t initialRngFingerprint); // Append one command line tagged with the tick it was applied at, followed by // the post-apply RNG checksum. void recordCommand(Tick tick, const Command& command, std::uint64_t rngFingerprint); // Append a periodic RNG checksum line. void recordChecksum(Tick tick, std::uint64_t rngFingerprint); void close(); bool isOpen() const; const std::string& getCurrentFilePath() const; private: std::string m_configDir; std::string m_outputDir; std::string m_filePath; std::ofstream m_stream; };