Files
settled-reach/server/src/simulation/mod.rs
T
jpmschweitzerandClaude Opus 4.6 aa79dd97e7 fix(simulation): Clippy cleanup and CI enforcement (#635)
Fix all Clippy warnings across the server codebase (2411 insertions, 1341
deletions). Raise type-complexity-threshold to 750 and too-many-arguments
to 12 in .clippy.toml for idiomatic Bevy ECS system signatures. The server
now passes `cargo clippy -- --deny warnings` cleanly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:33:15 +01:00

172 lines
8.5 KiB
Rust

// Simulation module - Core simulation plugin and systems
// Implements deterministic tick-based simulation (D-010 principle 4)
use bevy_app::prelude::*;
use bevy_ecs::schedule::IntoScheduleConfigs;
pub mod chunk_streaming;
pub mod contraband;
pub mod conversation;
pub mod dialogue;
pub mod examine;
pub mod follow;
pub mod generator;
pub mod input;
pub mod interaction;
pub mod inventory;
pub mod knowledge_grant;
pub mod line_pool;
pub mod listening;
pub mod modification;
pub mod monologue;
pub mod movement;
pub mod npc_knowledge_transfer;
pub mod path_follow;
pub mod pathfinding;
pub mod poi;
pub mod poi_discovery;
pub mod pressure;
pub mod rng;
pub mod save_io;
pub mod save_state;
pub mod sound;
pub mod spatial;
pub mod stance;
pub mod ticker;
pub mod tier;
pub mod time;
pub mod triangle;
pub mod zone;
/// Core simulation plugin
/// Manages simulation time, RNG, input processing, and tier transitions
pub struct SimulationPlugin;
impl Plugin for SimulationPlugin {
fn build(&self, app: &mut App) {
// Tier marker components (D-026) — must register before behavior systems
app.add_plugins(tier::TierPlugin);
// Initialize core simulation resources
app.init_resource::<time::SimulationTime>()
.insert_resource(rng::SimRng::new(0))
.init_resource::<input::InputQueue>()
.init_resource::<save_io::SaveLoadPending>()
.init_resource::<crate::knowledge::EntityRegistry>()
.init_resource::<sound::SoundEventQueue>()
.init_resource::<spatial::NaiveSpatialIndex>()
.init_resource::<chunk_streaming::ChunkLoadRadius>()
.init_resource::<chunk_streaming::ChunkStreamingCadence>()
.init_resource::<follow::FollowEndEventQueue>()
.init_resource::<monologue::PostConversationQueue>()
.init_resource::<poi_discovery::PoiDiscoveryEventQueue>()
// Triangle escalation resources (#250)
.init_resource::<crate::simulation::triangle::TriangleCrisisEventQueue>()
.init_resource::<crate::simulation::triangle::ResolveTriangleQueue>()
// discover_pois reads VisibilityGeometry (also populated by PerceptionPlugin).
// Init here so SimulationPlugin works standalone in tests without PerceptionPlugin.
.init_resource::<crate::perception::query::VisibilityGeometry>()
// transfer_npc_knowledge reads RelationshipGraph (also init by NpcPlugin) and
// KnowledgeEventQueue (also init by KnowledgePlugin).
// Init here so SimulationPlugin works standalone in tests without those plugins.
.init_resource::<crate::npc::relationships::RelationshipGraph>()
.init_resource::<crate::knowledge::KnowledgeEventQueue>()
.init_resource::<interaction::TerminalInteractedQueue>()
// Zone crossing event queue (#512, D-077): detect when player moves between zones.
.init_resource::<zone::ZoneCrossEventQueue>()
.init_resource::<zone::PreviousPlayerZone>()
.add_systems(
Update,
(
input::process_player_input,
// execute_save_load is an exclusive system (takes &mut World).
// Must run after process_player_input (which queues the command)
// and before compute_observer_snapshot (which consumes the result).
save_io::execute_save_load
.after(input::process_player_input)
.before(crate::perception::observer::compute_observer_snapshot),
pathfinding::compute_paths.after(input::process_player_input),
path_follow::follow_paths.after(pathfinding::compute_paths),
movement::validate_movement.after(path_follow::follow_paths),
path_follow::cleanup_path_blocked.after(movement::validate_movement),
spatial::sync_spatial_index.after(movement::validate_movement),
listening::update_listening_focus.after(movement::validate_movement),
contraband::check_contraband_scan
.after(movement::validate_movement)
.before(crate::perception::observer::compute_observer_snapshot),
conversation::run_npc_conversations
.after(movement::validate_movement)
.before(sound::collect_sound_events),
npc_knowledge_transfer::transfer_npc_knowledge
.after(conversation::run_npc_conversations),
sound::collect_sound_events
.after(movement::validate_movement)
.before(crate::perception::observer::compute_observer_snapshot),
poi_discovery::discover_pois
.after(crate::perception::observer::compute_visibility_geometry)
.before(crate::perception::observer::compute_observer_snapshot),
interaction::process_door_interaction
.after(input::process_player_input)
.before(movement::validate_movement),
interaction::process_terminal_interaction
.after(input::process_player_input)
.before(crate::perception::observer::compute_observer_snapshot),
examine::process_examine_interaction
.after(input::process_player_input)
.before(crate::perception::observer::compute_observer_snapshot),
// Character pressure (#248) — reads NPC awareness + relationship graph
pressure::update_character_pressure
.after(crate::npc::awareness::detect_player_awareness)
.before(crate::perception::observer::compute_observer_snapshot),
// Triangle escalation (#250) — runs on game-minute boundaries (every 10 ticks)
crate::simulation::triangle::tick_triangle_escalation
.after(crate::npc::tolerance::check_tolerance_threshold)
.before(crate::perception::observer::compute_observer_snapshot),
// Triangle resolution (#250, D-089) — apply player resolve commands
crate::simulation::triangle::apply_resolve_triangle
.after(input::process_player_input)
.before(crate::perception::observer::compute_observer_snapshot),
time::advance_tick.after(path_follow::cleanup_path_blocked),
),
)
// Zone crossing detection (#512, D-077) — separate call to stay within
// Bevy's 20-element system tuple limit.
.add_systems(
Update,
zone::detect_zone_crossings.after(movement::validate_movement),
)
// Chunk streaming (#578, D-012) — loads/unloads chunks around the player.
// Runs before input processing so chunks are available for the current tick.
.add_systems(
Update,
chunk_streaming::chunk_streaming.before(input::process_player_input),
)
// News ticker rotation (#591) — deterministic via SimRng, before snapshot.
.add_systems(
Update,
ticker::tick_news_ticker
.before(crate::perception::observer::compute_observer_snapshot),
);
// Voice enrichment (D-138, Phase 3) — rewrite NPC text with voiced
// variants from cache before the observer snapshot is assembled.
// No-op when VoiceCacheResource is absent (voice pipeline disabled).
app.add_systems(
Update,
(
crate::voice::integration::voice_enrich_dialogue_response
.after(crate::simulation::dialogue::process_talk_interaction)
.before(crate::perception::observer::compute_observer_snapshot),
crate::voice::integration::voice_enrich_conversation_events
.after(conversation::run_npc_conversations)
.before(crate::perception::observer::compute_observer_snapshot),
),
);
// Initialize TickerPool with empty default; populated by ContentPlugin at Startup.
app.init_resource::<ticker::TickerPool>();
tracing::debug!("SimulationPlugin initialized");
}
}