#pragma once #include #include 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 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 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 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> m_queue; std::unique_ptr m_recorder; bool m_replayMode = false; };