diff --git a/server/src/main.rs b/server/src/main.rs index 8cb12aa60..89e409881 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -184,9 +184,6 @@ fn main() { tracing::info!("Simulation server shutting down"); } -/// Print bevy_ecs schedule graph and exit. -/// Invoked by --dump-schedule CLI flag (#346). -/// /// Print bevy_ecs schedule graph and exit. /// Invoked by --dump-schedule CLI flag (#346). /// diff --git a/server/tests/serialization.rs b/server/tests/serialization.rs index 7addfa2e7..e945e1201 100644 --- a/server/tests/serialization.rs +++ b/server/tests/serialization.rs @@ -1187,6 +1187,80 @@ fn v8_payload_deserializes_into_v9_struct() { ); } +/// rng_seed round-trips through MessagePack (#527). +/// Verifies Some(seed) survives the wire and None is omitted. +#[test] +fn rng_seed_roundtrip() { + let mut snapshot = test_snapshot(0, vec![]); + snapshot.rng_seed = Some(123456789); + let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize"); + let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes).expect("deserialize"); + assert_eq!(decoded.rng_seed, Some(123456789)); + + // None case: skip_serializing_if omits the field, default restores it + let mut snapshot_none = test_snapshot(0, vec![]); + snapshot_none.rng_seed = None; + let bytes_none = rmp_serde::to_vec_named(&snapshot_none).expect("serialize"); + let decoded_none: ObserverSnapshot = rmp_serde::from_slice(&bytes_none).expect("deserialize"); + assert_eq!(decoded_none.rng_seed, None); +} + +/// v9 payloads (without rng_seed) must deserialize into the v10 struct +/// via #[serde(default)]. Guards backwards compat during migration (#527). +#[test] +fn v9_payload_deserializes_into_v10_struct() { + #[derive(serde::Serialize)] + struct ObserverSnapshotV9 { + version: u8, + tick: u64, + game_time: GameTime, + player_facing: FacingDirection, + player_stance: MovementStance, + player_inventory: Vec, + entities: Vec, + visible_tiles: Vec, + nearby_interactions: Vec, + current_monologue: Option, + pending_recognitions: Vec, + dialogue_response: Option, + blocked_entities: Vec, + scan_events: Vec, + } + + let v9 = ObserverSnapshotV9 { + version: 9, + tick: 200, + game_time: GameTime { + day: 0, + time_of_day: 0, + day_phase: DayPhase::Morning, + tick_rate: TickRate::Full, + }, + player_facing: FacingDirection::North, + player_stance: MovementStance::Walk, + player_inventory: vec![], + entities: vec![], + visible_tiles: vec![], + nearby_interactions: vec![], + current_monologue: None, + pending_recognitions: vec![], + dialogue_response: None, + blocked_entities: vec![], + scan_events: vec![], + }; + + let bytes = rmp_serde::to_vec_named(&v9).expect("serialize v9"); + let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes) + .expect("v9 payload should deserialize into v10 struct via serde(default)"); + + assert_eq!(decoded.version, 9, "version field preserved from v9"); + assert_eq!(decoded.tick, 200); + assert_eq!( + decoded.rng_seed, None, + "missing rng_seed should default to None" + ); +} + /// NearbyInteraction.object_type round-trips through MessagePack (#422). /// Verifies object_type=Some(Container) survives the wire. #[test]