replay: route player input through a command chokepoint (Phase 1)
Reshapes every UI-driven sim mutation to flow through one path so it can be recorded and replayed later, with behaviour unchanged. - Command model (lib): Command base + derived types (PlaceBuilding, Demolish, RotateInPlace, SetRecipe, SetShipLayout, Set[Site]SplitterFilters, ClearBeltTiles, ApplySchematicChoice, Reset), each with a playerId for the future-multiplayer shape. PlaceBuilding is atomic (carries optional recipe/layout/filters) because deferred commands never return the new BuildingId to the caller. - CommandManager (lib): FIFO queue holding a Simulation&; enqueue + drain. - Simulation::apply(const Command&): the single chokepoint, dispatching by kind to the existing mutators. Mutators stay public (enforced by convention, not compile-time, so the test suite keeps driving the sim directly). - Timing: GameWorldView owns the CommandManager and drains it once per frame in onFrame, before the tick batch (runs at 0x too, so build-while-paused is preserved). A drained Reset triggers the view reset. - UI fan-in: GameWorldView enqueues its own input directly; MainWindow and SelectedBuildingPanel emit CommandRequestedEvent, which GameWorldView subscribes to and enqueues. No UI site mutates the sim directly anymore. - CommandTest: asserts apply(...) yields byte-identical state to the direct mutator path, and that CommandManager drains FIFO through apply. Full suite green (338 cases / 3353 assertions); determinism double-run still passes. Design doc updated with the atomic-PlaceBuilding and convention- enforcement decisions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUsFgd2Ga6pmLz8giS8WUn
This commit is contained in:
23
src/lib/eventsystem/event/CommandRequestedEvent.h
Normal file
23
src/lib/eventsystem/event/CommandRequestedEvent.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
struct Command;
|
||||
|
||||
// UI fan-in for the command path: widgets emit this with a built command, and a
|
||||
// single subscriber (GameWorldView) enqueues it onto the CommandManager. This
|
||||
// keeps widgets decoupled (consistent with the rest of the UI), while the
|
||||
// sim-mutating command itself is routed through the dedicated CommandManager
|
||||
// queue rather than the EventManager bus (see docs/replay_design.md).
|
||||
class CommandRequestedEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit CommandRequestedEvent(std::shared_ptr<const Command> command)
|
||||
: command(std::move(command))
|
||||
{
|
||||
}
|
||||
|
||||
const std::shared_ptr<const Command> command;
|
||||
};
|
||||
Reference in New Issue
Block a user