move FactoryState ownership out of BuildingSystem to Simulation

Simulation (and ArenaSimulation in the balancing tool) now owns the factory's
world data; BuildingSystem holds a reference to it. This is what lets the systems
that operate on the data be handed the same state — phase 3's construction and
deconstruction systems, and later the ecs/system/ classes that today take a
BuildingSystem& only to query it.

reset() clears the state alongside m_admin and m_beltSystem, matching how the
subsystems were already rebuilt from scratch.

Falls short of the tick-argument form I sketched: BuildingSystem still reaches
the data through a member reference rather than a parameter. Making it truly
stateless means the const query surface has to find the data some other way, and
that surface is large — findBuilding alone has 64 call sites, with findSite,
getAllBuildings, getAllSites, isTileOccupied and the rest behind it. Doing that
needs a queries facade behind Simulation::getBuildings() so the callers do not
all move, which is its own decision rather than a side effect of this one.

The constructor gains a parameter, so the four owners and the three test fixtures
that build a BuildingSystem directly are updated; the 33 files that only use one
are untouched.

Verified with a golden-checksum capture before and after — all four sample ticks
identical.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
2026-08-04 20:55:32 +02:00
parent e39b81eb22
commit 2522a8c974
10 changed files with 97 additions and 43 deletions

View File

@@ -12,6 +12,7 @@
#include "BeltSystem.h"
#include "Building.h"
#include "BuildingSystem.h"
#include "FactoryState.h"
#include "BuildingType.h"
#include "ConfigLoader.h"
#include "Item.h"
@@ -83,6 +84,7 @@ static std::vector<Item> outputSideItems(const Building& b)
struct PlacementFixture
{
GameConfig cfg = loadTestConfig();
FactoryState state;
BeltSystem belts{cfg.world.beltSpeed_tps};
int stock = 0;
std::mt19937 rng{0};
@@ -90,7 +92,7 @@ struct PlacementFixture
BuildingSystem bs;
PlacementFixture()
: bs(cfg, belts,
: bs(cfg, state, belts,
[this]() { return nextBuildingId++; },
[this](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -111,7 +113,8 @@ TEST_CASE("BuildingSystem: place miner occupies expected body tiles", "[building
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -219,7 +222,8 @@ TEST_CASE("BuildingSystem: placing a belt registers it with BeltSystem after con
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -248,7 +252,8 @@ TEST_CASE("BuildingSystem: placed building enters construction queue", "[buildin
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -290,7 +295,8 @@ TEST_CASE("BuildingSystem: first queued building starts construction immediately
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -308,7 +314,8 @@ TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[b
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -330,7 +337,8 @@ TEST_CASE("BuildingSystem: construction completes after configured duration", "[
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -496,7 +504,8 @@ TEST_CASE("BuildingSystem: second building starts after first completes", "[buil
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -526,7 +535,8 @@ TEST_CASE("BuildingSystem: miner produces iron_ore after recipe duration", "[bui
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -559,7 +569,8 @@ TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]")
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -599,7 +610,8 @@ TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites",
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -639,7 +651,8 @@ TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -681,7 +694,8 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -723,7 +737,8 @@ TEST_CASE("BuildingSystem: accepted input travels inward before entering the buf
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -764,7 +779,8 @@ TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at th
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -806,7 +822,8 @@ TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection",
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -852,7 +869,8 @@ TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -906,7 +924,8 @@ TEST_CASE("BuildingSystem: miner output buffer drains onto adjacent belt", "[bui
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -945,7 +964,8 @@ TEST_CASE("BuildingSystem: output port couples directly into an adjacent input p
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -985,7 +1005,8 @@ TEST_CASE("BuildingSystem: direct coupling to a non-consumer leaves the item stu
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1024,7 +1045,8 @@ TEST_CASE("BuildingSystem: setRecipe clears output buffer and active production"
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1067,7 +1089,8 @@ TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1099,7 +1122,8 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
// Seed chosen so first roll produces 2-item output (iron_ingot), filling buffer.
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1157,7 +1181,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when tile is
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1176,7 +1201,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the site id for a que
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1199,7 +1225,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the building id for a
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1226,7 +1253,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when building
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1248,7 +1276,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget never rotates a tunnel in pla
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1274,7 +1303,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns nullopt when footprin
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1298,7 +1328,8 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget works for a symmetric multi-t
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1327,7 +1358,8 @@ TEST_CASE("BuildingSystem: rotateInPlace updates the rotation field of a constru
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1350,7 +1382,8 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1374,7 +1407,8 @@ TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direct
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
@@ -1405,7 +1439,8 @@ TEST_CASE("BuildingSystem: rotateInPlace re-registers a belt tile with BeltSyste
int stock = 0;
std::mt19937 rng(0);
BuildingId nextBuildingId = 1;
BuildingSystem bs(cfg, belts,
FactoryState state_bs;
BuildingSystem bs(cfg, state_bs, belts,
[&nextBuildingId]() { return nextBuildingId++; },
[&stock](int n) { stock += n; },
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},