Files
settled-reach/server/src/simulation/economy_plugin.rs
T
jpmschweitzerandClaude Fable 5 6bda9f1697 feat(simulation): auto-pause sim on implant-fullscreen — inspection substrate (T-970)
D-226 layer 1. World-advancing phases gated on pause: Movement/Storyteller/Knowledge/TickAdvance set-gated via sim_not_paused; Simulation + Economy gated per-system at their registration sites — collect_sound_events and serve_econ_state_query stay unconditioned (transient-buffer clear + paused-allowed query; set-gating Simulation leaked a stale tick-7 footstep into frozen snapshots — caught by golden_suite, fixed without touching the fixture; regression test encodes the bug shape). New PlayerAction::AutoPause/AutoResume + AutoPauseState resource implement Option A reconciliation: auto-resume only fires if auto-pause caused the pause; manual pause and Half rate survive implant open/close. PauseParams SystemParam bundle keeps process_player_input under the 16-param ceiling (BookmarkInputParams precedent). Client: HudGroups.gameplay_occluded now sends AutoPause/AutoResume via send_named_action; 5 gdUnit tests + 7 Rust tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 15:44:56 +02:00

48 lines
2.2 KiB
Rust

//! Economy phase plugin — tâtonnement simulation tick and IPC query serving.
//!
//! All systems run in [`TickPhase::Economy`]. Intra-phase ordering:
//! - tick_economy_simulation → serve_econ_state_query (query reads fresh signals)
//!
//! T-970 (D-226 layer 1): `tick_economy_simulation` is individually gated with
//! `sim_not_paused` so it freezes while the sim is paused — but
//! `serve_econ_state_query` stays unconditioned so the paused-allowed
//! `EconStateQuery` (input.rs) keeps being served. `TickPhase::Economy` itself
//! is deliberately NOT set-gated in `tick_phases.rs` for exactly this reason.
//! `.after()` ordering still holds when the upstream system is skipped by its
//! own run condition — a run condition only prevents a system from running,
//! it doesn't relax the ordering edge for the passes where both do run.
use bevy_app::prelude::*;
use bevy_ecs::schedule::IntoScheduleConfigs;
use crate::simulation::time::sim_not_paused;
use crate::tick_phases::TickPhase;
pub struct EconomyPlugin {
/// World seed from `StartupMessage` (#826). Passed to `try_load_economy` so the
/// tâtonnement simulation is seeded deterministically from the client's world seed.
pub seed: u64,
}
impl Plugin for EconomyPlugin {
fn build(&self, app: &mut App) {
// Economy simulation (#821, D-031) — loaded once at startup, no-op when DB absent.
// Seed comes from StartupMessage.world_seed, threaded via SimulationPlugin (#826).
if let Some((econ_sim, econ_state)) = super::economy::try_load_economy(self.seed) {
app.insert_resource(econ_sim).insert_resource(econ_state);
}
// EconQueryBuffer: always registered so EconStateQuery PlayerActions are accepted
// even when the economy DB is absent (queries just produce no response).
app.init_resource::<super::economy::EconQueryBuffer>()
.add_systems(
Update,
(
super::economy::tick_economy_simulation.run_if(sim_not_paused),
super::economy::serve_econ_state_query
.after(super::economy::tick_economy_simulation),
)
.in_set(TickPhase::Economy),
);
}
}