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>
48 lines
2.2 KiB
Rust
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),
|
|
);
|
|
}
|
|
}
|