From 9edbe40de9a18bdee3bbb6d112a0b9ada67a883c Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 10 Apr 2026 23:00:55 +0200 Subject: [PATCH] =?UTF-8?q?fix(simulation):=20resolve=20Bevy=20schedule=20?= =?UTF-8?q?cycle=20=E2=80=94=20economy=20system=20ordering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tick_economy_simulation was ordered .after(advance_tick) which created a cycle: observer_snapshot → send_snapshot → advance_tick → tick_economy → observer_snapshot. Moved to .after(process_player_input) instead — the economy checks time.tick which works regardless of advance order. Also removed the .after(tick_economy_simulation) from handle_debug_commands that was added during Sprint 34 review — same cycle root cause. This is a symptom of #843 (ad-hoc ordering is fragile). Pair session scheduled to replace with system set phases. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/Cargo.lock | 2 +- server/src/bridge/mod.rs | 5 ++++- server/src/simulation/mod.rs | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index 3adb44117..d8913abde 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -1256,7 +1256,7 @@ dependencies = [ [[package]] name = "settled-reach-server" -version = "0.1.33" +version = "0.1.34" dependencies = [ "bevy_app", "bevy_ecs", diff --git a/server/src/bridge/mod.rs b/server/src/bridge/mod.rs index c78182c86..c88912621 100644 --- a/server/src/bridge/mod.rs +++ b/server/src/bridge/mod.rs @@ -227,7 +227,10 @@ impl Plugin for BridgePlugin { receive_bridge_inputs.before(crate::simulation::input::process_player_input), debug::handle_debug_commands .after(crate::simulation::input::process_player_input) - .after(crate::simulation::economy::tick_economy_simulation) + // Note: NOT ordered after tick_economy_simulation — that creates a + // schedule cycle (debug → observer → advance_tick → econ → debug). + // GetEconState reads from EconStateResource which may be one tick + // stale on economy-tick boundaries. Acceptable for debug tooling. .before(crate::perception::observer::compute_observer_snapshot), crate::perception::observer::compute_visibility_geometry .after(crate::simulation::movement::validate_movement), diff --git a/server/src/simulation/mod.rs b/server/src/simulation/mod.rs index 7e0575baf..6f2edd14f 100644 --- a/server/src/simulation/mod.rs +++ b/server/src/simulation/mod.rs @@ -181,7 +181,13 @@ impl Plugin for SimulationPlugin { Update, ( economy::tick_economy_simulation - .after(time::advance_tick) + .after(input::process_player_input) + // Note: runs BEFORE advance_tick, not after. The economy checks + // time.tick which is the CURRENT tick (not yet advanced). This + // avoids a schedule cycle: observer_snapshot → X → advance_tick + // → tick_economy → observer_snapshot. Running before advance_tick + // means the economy triggers on tick 9 instead of 10 — a naming + // difference, not a correctness issue. .before(crate::perception::observer::compute_observer_snapshot), economy::serve_econ_state_query .after(economy::tick_economy_simulation)