diff --git a/server/src/bridge/tcp.rs b/server/src/bridge/tcp.rs index 2a1bb56dd..d4ddf2a88 100644 --- a/server/src/bridge/tcp.rs +++ b/server/src/bridge/tcp.rs @@ -46,9 +46,9 @@ impl TcpBridge { // Set non-blocking so receive_inputs doesn't stall the game loop. // read_framed handles WouldBlock by returning Ok(None). - stream.set_nonblocking(true).map_err(|e| { - BridgeError::Transport(format!("failed to set non-blocking: {}", e)) - })?; + stream + .set_nonblocking(true) + .map_err(|e| BridgeError::Transport(format!("failed to set non-blocking: {}", e)))?; // Clone stream for reader and writer let reader_stream = stream.try_clone().map_err(|e| { @@ -81,9 +81,9 @@ impl TcpBridge { local_addr ); - stream.set_nonblocking(true).map_err(|e| { - BridgeError::Transport(format!("failed to set non-blocking: {}", e)) - })?; + stream + .set_nonblocking(true) + .map_err(|e| BridgeError::Transport(format!("failed to set non-blocking: {}", e)))?; let reader_stream = stream.try_clone().map_err(|e| { BridgeError::Transport(format!("failed to clone stream for reader: {}", e)) diff --git a/server/src/content/hot_reload.rs b/server/src/content/hot_reload.rs index 257924499..bff2d2ce9 100644 --- a/server/src/content/hot_reload.rs +++ b/server/src/content/hot_reload.rs @@ -87,7 +87,11 @@ const MAX_WALK_DEPTH: usize = 100; /// Stops recursing at MAX_WALK_DEPTH to guard against symlink loops. fn walk_yaml(dir: &Path, timestamps: &mut BTreeMap, depth: usize) { if depth >= MAX_WALK_DEPTH { - tracing::warn!("walk_yaml: max depth {} reached at {:?}, stopping", MAX_WALK_DEPTH, dir); + tracing::warn!( + "walk_yaml: max depth {} reached at {:?}, stopping", + MAX_WALK_DEPTH, + dir + ); return; } let Ok(entries) = std::fs::read_dir(dir) else { diff --git a/server/src/content/spawn.rs b/server/src/content/spawn.rs index 072151692..47be62c0e 100644 --- a/server/src/content/spawn.rs +++ b/server/src/content/spawn.rs @@ -197,9 +197,10 @@ fn spawn_npc(world: &mut World, profile: &types::NpcProfile, result: &mut SpawnR // Register in EntityRegistry for StableId mapping let stable_id = world.resource_mut::().register(entity); - world - .entity_mut(entity) - .insert((StableEntityId(stable_id), ContentSlug(profile.canonical_id.clone()))); + world.entity_mut(entity).insert(( + StableEntityId(stable_id), + ContentSlug(profile.canonical_id.clone()), + )); result .npc_ids diff --git a/server/src/knowledge/registry.rs b/server/src/knowledge/registry.rs index d5bc0c277..32628f693 100644 --- a/server/src/knowledge/registry.rs +++ b/server/src/knowledge/registry.rs @@ -214,11 +214,7 @@ mod tests { id_first, id_second, "Re-registration must assign a new StableId" ); - assert_eq!( - id_second, - StableId(1), - "Counter advances monotonically" - ); + assert_eq!(id_second, StableId(1), "Counter advances monotonically"); assert_eq!(registry.len(), 1); // New mapping is bidirectionally correct diff --git a/server/src/main.rs b/server/src/main.rs index 3a7f18150..3433036e1 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -162,7 +162,9 @@ fn setup_proof_room(app: &mut App) { use settled_reach_server::simulation::monologue::{ MonologueBuffer, MonologueState, SprintAnomalyQueue, }; - use settled_reach_server::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap}; + use settled_reach_server::simulation::movement::{ + PlayerCharacter, TilePosition, WalkabilityMap, + }; use settled_reach_server::simulation::path_follow::MovementSpeed; use settled_reach_server::simulation::stance::{MovementProfile, PlayerMoveCooldown}; use settled_reach_server::simulation::time::DayPhase; diff --git a/server/src/perception/mod.rs b/server/src/perception/mod.rs index ee9003379..722c88465 100644 --- a/server/src/perception/mod.rs +++ b/server/src/perception/mod.rs @@ -26,10 +26,8 @@ impl Plugin for PerceptionPlugin { .add_systems( Update, ( - anomaly::clear_anomaly_markers - .before(anomaly::detect_anomalies), - anomaly::detect_anomalies - .before(observation::emit_observation_events), + anomaly::clear_anomaly_markers.before(anomaly::detect_anomalies), + anomaly::detect_anomalies.before(observation::emit_observation_events), cognitive_delay::process_cognitive_delay .after(observation::emit_observation_events) .before(crate::knowledge::events::process_knowledge_events), diff --git a/server/src/perception/observer/mod.rs b/server/src/perception/observer/mod.rs index 02a9d0d66..8e7d91571 100644 --- a/server/src/perception/observer/mod.rs +++ b/server/src/perception/observer/mod.rs @@ -15,9 +15,9 @@ use crate::knowledge::{EntityRegistry, KnowledgeGraph, StableId}; use crate::perception::cognitive_delay::CognitiveDelay; use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry}; use crate::perception::vision_cone::Facing; +use crate::simulation::dialogue::DialogueResponseBuffer; use crate::simulation::interaction::NearbyInteractionBuffer; use crate::simulation::inventory::{CarriedBy, InventorySlot, ItemName}; -use crate::simulation::dialogue::DialogueResponseBuffer; use crate::simulation::monologue::{MonologueBuffer, SprintAnomalyQueue}; use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap}; use crate::simulation::stance::Stance; diff --git a/server/src/perception/observer/tests.rs b/server/src/perception/observer/tests.rs index 3cb1eda33..d2e579d01 100644 --- a/server/src/perception/observer/tests.rs +++ b/server/src/perception/observer/tests.rs @@ -1948,7 +1948,11 @@ fn equidistant_npcs_produce_stable_snapshot_ordering() { .map(|e| e.entity_id) .collect(); - assert_eq!(npc_ids.len(), 3, "all three equidistant NPCs should be visible"); + assert_eq!( + npc_ids.len(), + 3, + "all three equidistant NPCs should be visible" + ); // Entity IDs must be in strictly ascending order (Fix B sort guarantee) for i in 1..npc_ids.len() { diff --git a/server/src/simulation/dialogue.rs b/server/src/simulation/dialogue.rs index 7cf965195..1fda1b31e 100644 --- a/server/src/simulation/dialogue.rs +++ b/server/src/simulation/dialogue.rs @@ -19,7 +19,9 @@ use bevy_ecs::prelude::*; use rand::Rng; use crate::bridge::types::{DialogueResponseEvent, RelationshipState}; -use crate::content::line_pool::{AccessTier, IndexedDialogueLine, Mood, Situation, Topic, TrustTier}; +use crate::content::line_pool::{ + AccessTier, IndexedDialogueLine, Mood, Situation, Topic, TrustTier, +}; use crate::content::LinePoolIndexResource; use crate::knowledge::{EntityRegistry, KnowledgeGraph}; use crate::simulation::movement::PlayerCharacter; @@ -219,7 +221,11 @@ pub fn derive_situations( /// - Topic match: +2 per matching topic /// /// Returns 0 only for lines on cooldown (caller handles). -pub fn score_line(line: &IndexedDialogueLine, npc_mood: Option, active_topics: &[Topic]) -> u32 { +pub fn score_line( + line: &IndexedDialogueLine, + npc_mood: Option, + active_topics: &[Topic], +) -> u32 { let mut score: u32 = 1; // Base score — no line is excluded by Layer 4 // Mood match @@ -318,8 +324,14 @@ pub fn process_talk_interaction( return; }; - let Ok((player_entity, observer_kg, talk_request, mut response_buffer, mut cooldown, active_dialogue_opt)) = - player_query.single_mut() + let Ok(( + player_entity, + observer_kg, + talk_request, + mut response_buffer, + mut cooldown, + active_dialogue_opt, + )) = player_query.single_mut() else { return; }; @@ -784,9 +796,14 @@ mod tests { let mut match_count = 0; for seed in 0..100 { let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(seed); - if let Some(line) = - select_dialogue_line(&candidates, Some(Mood::Worried), &[], &cooldown, 0, &mut rng) - { + if let Some(line) = select_dialogue_line( + &candidates, + Some(Mood::Worried), + &[], + &cooldown, + 0, + &mut rng, + ) { if line.id == "matched" { match_count += 1; } @@ -869,9 +886,10 @@ mod tests { role: "dock-worker".to_string(), lines, }; - index - .dialogue - .insert(("the-terminal".to_string(), "dock-worker".to_string()), pool); + index.dialogue.insert( + ("the-terminal".to_string(), "dock-worker".to_string()), + pool, + ); index } @@ -1009,10 +1027,11 @@ mod tests { let mut seen_ids: Vec = Vec::new(); for seed in 0..20 { // Reset for each iteration - world.get_mut::(player).unwrap().response = None; world - .entity_mut(player) - .insert(TalkRequest { target: npc }); + .get_mut::(player) + .unwrap() + .response = None; + world.entity_mut(player).insert(TalkRequest { target: npc }); world.insert_resource(SimRng::new(seed)); let mut schedule = bevy_ecs::schedule::Schedule::default(); @@ -1020,7 +1039,11 @@ mod tests { schedule.run(&mut world); world.flush(); - if let Some(resp) = &world.get::(player).unwrap().response { + if let Some(resp) = &world + .get::(player) + .unwrap() + .response + { if !seen_ids.contains(&resp.line_id) { seen_ids.push(resp.line_id.clone()); } @@ -1046,9 +1069,7 @@ mod tests { world.insert_resource(LinePoolIndexResource(index)); // NPC without DialogueProfile - let npc = world - .spawn((Npc, TilePosition::new(5, 5, 0))) - .id(); + let npc = world.spawn((Npc, TilePosition::new(5, 5, 0))).id(); world.resource_mut::().register(npc); let player = world @@ -1142,10 +1163,11 @@ mod tests { ); // Second talk — same tick, line on cooldown - world.get_mut::(player).unwrap().response = None; world - .entity_mut(player) - .insert(TalkRequest { target: npc }); + .get_mut::(player) + .unwrap() + .response = None; + world.entity_mut(player).insert(TalkRequest { target: npc }); let mut schedule2 = bevy_ecs::schedule::Schedule::default(); schedule2.add_systems(process_talk_interaction); diff --git a/server/src/simulation/input.rs b/server/src/simulation/input.rs index 0954c1ce0..2e4f6a367 100644 --- a/server/src/simulation/input.rs +++ b/server/src/simulation/input.rs @@ -102,12 +102,7 @@ pub fn process_player_input( for input in inputs { // Discard all gameplay actions while paused (D-052, R2-OQ-01). // Only Pause/Unpause are processed — everything else is discarded. - if paused - && !matches!( - input.action, - PlayerAction::Pause | PlayerAction::Unpause - ) - { + if paused && !matches!(input.action, PlayerAction::Pause | PlayerAction::Unpause) { continue; } match input.action { @@ -190,14 +185,20 @@ pub fn process_player_input( handle_place(&mut commands, ®istry, &player_query, target_entity_id); } Some("Talk") => { - handle_talk(&mut commands, ®istry, &player_query, &all_positions, target_entity_id); + handle_talk( + &mut commands, + ®istry, + &player_query, + &all_positions, + target_entity_id, + ); } _ => { tracing::info!( - "Interact: target={:?}, verb={:?} — logged only", - target_entity_id, - verb, - ); + "Interact: target={:?}, verb={:?} — logged only", + target_entity_id, + verb, + ); } }, PlayerAction::WalkAway => { @@ -363,7 +364,9 @@ fn handle_talk( // Server-side range check: reject Talk if target is beyond close range if let Ok(target_pos) = all_positions.get(target_entity) { - let distance = player_pos.manhattan_distance(target_pos).unwrap_or(u32::MAX); + let distance = player_pos + .manhattan_distance(target_pos) + .unwrap_or(u32::MAX); if distance > crate::simulation::interaction::CLOSE_RANGE { tracing::info!( target_id, @@ -1312,11 +1315,7 @@ mod tests { ); let mut query = world.query::<&Stance>(); let stance = query.single(&world).unwrap(); - assert_eq!( - stance.0, - MovementStance::Walk, - "Stance unchanged in batch" - ); + assert_eq!(stance.0, MovementStance::Walk, "Stance unchanged in batch"); assert_eq!( world.resource::().tick_rate, TickRate::Paused, diff --git a/server/src/simulation/monologue.rs b/server/src/simulation/monologue.rs index cc4aaf8bd..2d5b02878 100644 --- a/server/src/simulation/monologue.rs +++ b/server/src/simulation/monologue.rs @@ -39,18 +39,12 @@ pub(crate) const ANOMALY_DELAY_TICKS: u64 = 90; /// Fire DURING cognitive delay (when grey blob appears). Future: move to /// content pools with trigger="observe_anomaly" + character match. const RECOGNITION_LINES: &[(&str, &str)] = &[ - ( - "recognition_01", - "Wait \u{2014} I know that walk.", - ), + ("recognition_01", "Wait \u{2014} I know that walk."), ( "recognition_02", "Those footsteps... I've heard that pattern before.", ), - ( - "recognition_03", - "Something about that silhouette...", - ), + ("recognition_03", "Something about that silhouette..."), ]; /// Hardcoded v0.1 sprint anomaly "double-take" lines. @@ -258,9 +252,9 @@ pub fn trigger_recognition_monologue( let pending = cognitive_delay.pending_mut(); let target_idx = { // First pass: anomalous + unfired - let anomaly_idx = pending.iter().position(|p| { - !p.monologue_fired && anomaly_markers.get(p.target).is_ok() - }); + let anomaly_idx = pending + .iter() + .position(|p| !p.monologue_fired && anomaly_markers.get(p.target).is_ok()); if let Some(idx) = anomaly_idx { Some(idx) } else { @@ -872,10 +866,10 @@ mod tests { // trigger_recognition_monologue tests (#451, D-060) // ----------------------------------------------------------------------- + use crate::knowledge::types::StableId; use crate::perception::cognitive_delay::{ CognitiveDelay, PendingRecognition, RecognitionTrigger, NORMAL_DELAY_TICKS, }; - use crate::knowledge::types::StableId; fn setup_recognition_world() -> World { let mut world = World::new(); @@ -957,7 +951,12 @@ mod tests { // First tick: fires schedule.run(&mut world); assert!( - world.query::<&MonologueBuffer>().single(&world).unwrap().event.is_some(), + world + .query::<&MonologueBuffer>() + .single(&world) + .unwrap() + .event + .is_some(), "first tick should fire" ); @@ -967,7 +966,12 @@ mod tests { // Second tick: should NOT fire (monologue_fired = true) schedule.run(&mut world); assert!( - world.query::<&MonologueBuffer>().single(&world).unwrap().event.is_none(), + world + .query::<&MonologueBuffer>() + .single(&world) + .unwrap() + .event + .is_none(), "second tick should not fire (already fired for this recognition)" ); } @@ -1053,9 +1057,7 @@ mod tests { let mut world = setup_recognition_world(); let normal_target = world.spawn_empty().id(); - let anomalous_target = world - .spawn(crate::perception::anomaly::AnomalyMarker) - .id(); + let anomalous_target = world.spawn(crate::perception::anomaly::AnomalyMarker).id(); let mut cd = CognitiveDelay::default(); // Normal entity added first diff --git a/server/src/simulation/movement.rs b/server/src/simulation/movement.rs index e0eea10fc..5fd5e7aec 100644 --- a/server/src/simulation/movement.rs +++ b/server/src/simulation/movement.rs @@ -917,12 +917,14 @@ mod tests { // Entity with lower bits processes first and claims the target assert_eq!( - pos_lower, target, + pos_lower, + target, "entity with lower Entity::to_bits() ({}) should win the tile", lower.to_bits() ); assert_eq!( - pos_higher, higher_origin, + pos_higher, + higher_origin, "entity with higher Entity::to_bits() ({}) should stay at origin", higher.to_bits() ); diff --git a/server/tests/bridge_tcp.rs b/server/tests/bridge_tcp.rs index d189b8f2f..93b8d884d 100644 --- a/server/tests/bridge_tcp.rs +++ b/server/tests/bridge_tcp.rs @@ -86,7 +86,10 @@ fn input_roundtrip_over_tcp() { match bridge.receive_inputs() { Ok(inputs) if !inputs.is_empty() => break inputs, Ok(_) => { - assert!(std::time::Instant::now() < deadline, "timed out waiting for inputs"); + assert!( + std::time::Instant::now() < deadline, + "timed out waiting for inputs" + ); thread::sleep(std::time::Duration::from_millis(1)); } Err(e) => panic!("failed to receive inputs: {}", e), @@ -150,7 +153,10 @@ fn tcp_bridge_eof_returns_error() { match bridge.receive_inputs() { Ok(inputs) if inputs.is_empty() => { // WouldBlock — client hasn't disconnected yet, retry - assert!(std::time::Instant::now() < deadline, "timed out waiting for EOF"); + assert!( + std::time::Instant::now() < deadline, + "timed out waiting for EOF" + ); thread::sleep(std::time::Duration::from_millis(1)); } Ok(inputs) => panic!("expected Disconnected error, got {} inputs", inputs.len()), diff --git a/server/tests/determinism.rs b/server/tests/determinism.rs index a71164e04..06d17090d 100644 --- a/server/tests/determinism.rs +++ b/server/tests/determinism.rs @@ -16,8 +16,8 @@ use settled_reach_server::knowledge::registry::EntityRegistry; use settled_reach_server::knowledge::{KnowledgeGraph, KnowledgePlugin}; use settled_reach_server::npc::relationships::{RelationshipEdge, RelationshipGraph}; use settled_reach_server::npc::{ - Contentment, DailyRoutine, Npc, NpcPlugin, RelationshipKind, RoutineEntry, - ToleranceThreshold, Want, WantKind, + Contentment, DailyRoutine, Npc, NpcPlugin, RelationshipKind, RoutineEntry, ToleranceThreshold, + Want, WantKind, }; use settled_reach_server::perception::cognitive_delay::CognitiveDelay; use settled_reach_server::perception::vision_cone::Facing; @@ -213,10 +213,7 @@ fn build_deterministic_app(seed: u64) -> App { /// Run the simulation for a fixed number of ticks with predetermined inputs. /// Returns serialized snapshots for each tick. -fn run_simulation( - seed: u64, - inputs: &[Vec], -) -> Vec> { +fn run_simulation(seed: u64, inputs: &[Vec]) -> Vec> { let mut app = build_deterministic_app(seed); let mut snapshots = Vec::with_capacity(inputs.len()); @@ -334,7 +331,8 @@ fn gauntlet_deterministic_replay() { for (tick, (s1, s2)) in run1.iter().zip(run2.iter()).enumerate() { assert_eq!( - s1, s2, + s1, + s2, "snapshot at tick {} differs between runs ({} vs {} bytes)", tick, s1.len(), diff --git a/server/tests/gen_fixtures.rs b/server/tests/gen_fixtures.rs index f6461afd9..7becb342b 100644 --- a/server/tests/gen_fixtures.rs +++ b/server/tests/gen_fixtures.rs @@ -279,11 +279,11 @@ fn generate_msgpack_fixtures() { // Tests that GDScript can decode full ObserverSnapshot structs when the tick // field crosses encoding format boundaries. let boundary_snapshots: [(u64, &str); 5] = [ - (0, "snapshot_boundary_tick_0"), // pos fixint - (127, "snapshot_boundary_tick_127"), // pos fixint max - (32767, "snapshot_boundary_tick_32767"), // int16/uint16 asymmetry + (0, "snapshot_boundary_tick_0"), // pos fixint + (127, "snapshot_boundary_tick_127"), // pos fixint max + (32767, "snapshot_boundary_tick_32767"), // int16/uint16 asymmetry (2147483647, "snapshot_boundary_tick_2b31m1"), // int32/uint32 asymmetry - (4294967296, "snapshot_boundary_tick_2b32"), // int64 minimum + (4294967296, "snapshot_boundary_tick_2b32"), // int64 minimum ]; for (tick, name) in &boundary_snapshots { diff --git a/server/tests/serialization.rs b/server/tests/serialization.rs index 28650c31f..7efdae655 100644 --- a/server/tests/serialization.rs +++ b/server/tests/serialization.rs @@ -139,8 +139,9 @@ fn all_fixtures_deserialize() { if name.starts_with("snapshot_boundary") { // Boundary snapshot fixtures (#472): tick may exceed PROTOCOL_VERSION check - rmp_serde::from_slice::(&bytes) - .unwrap_or_else(|e| panic!("deserialize boundary snapshot fixture {}: {}", name, e)); + rmp_serde::from_slice::(&bytes).unwrap_or_else(|e| { + panic!("deserialize boundary snapshot fixture {}: {}", name, e) + }); } else if name.starts_with("snapshot") { let snap = rmp_serde::from_slice::(&bytes) .unwrap_or_else(|e| panic!("deserialize snapshot fixture {}: {}", name, e)); @@ -694,19 +695,48 @@ fn nearby_interaction_contradicted_roundtrip() { /// int 32 (-32769 to -2^31), int 64 (-2^31-1 to -2^63). const BOUNDARY_VALUES: [i64; 41] = [ // Positive boundaries (25 values) - 0, 1, 126, 127, // pos fixint - 128, 129, 254, 255, // uint 8 - 256, 257, 32766, 32767, // int 16 / uint 16 asymmetry - 32768, 32769, 65534, 65535, // uint 16 - 65536, 65537, 2147483646, 2147483647, // int 32 / uint 32 asymmetry - 2147483648, 4294967294, 4294967295, // uint 32 - 4294967296, i64::MAX, // int 64 + 0, + 1, + 126, + 127, // pos fixint + 128, + 129, + 254, + 255, // uint 8 + 256, + 257, + 32766, + 32767, // int 16 / uint 16 asymmetry + 32768, + 32769, + 65534, + 65535, // uint 16 + 65536, + 65537, + 2147483646, + 2147483647, // int 32 / uint 32 asymmetry + 2147483648, + 4294967294, + 4294967295, // uint 32 + 4294967296, + i64::MAX, // int 64 // Negative boundaries (16 values) - -1, -31, -32, // neg fixint - -33, -34, -127, -128, // int 8 - -129, -130, -32767, -32768, // int 16 - -32769, -2147483647, -2147483648, // int 32 - -2147483649, i64::MIN, // int 64 + -1, + -31, + -32, // neg fixint + -33, + -34, + -127, + -128, // int 8 + -129, + -130, + -32767, + -32768, // int 16 + -32769, + -2147483647, + -2147483648, // int 32 + -2147483649, + i64::MIN, // int 64 ]; #[test]