diff --git a/server/src/simulation/save_io.rs b/server/src/simulation/save_io.rs index 0f8644cba..895f4ac75 100644 --- a/server/src/simulation/save_io.rs +++ b/server/src/simulation/save_io.rs @@ -16,7 +16,7 @@ use thiserror::Error; use crate::bridge::types::SaveLoadResultWire; use crate::bridge::types::SnapshotBuffer; -use crate::content::template::TemplateReferenceMap; +use crate::content::template::{TemplateReferenceMap, TriangleState}; use crate::knowledge::graph::KnowledgeGraph; use crate::knowledge::registry::EntityRegistry; use crate::npc::Npc; @@ -113,6 +113,13 @@ pub fn save_to_file(path: &Path, world: &mut World) -> Result<(), SaveLoadError> .cloned() .unwrap_or_default(); + // Capture TriangleState components — sorted by triangle_id for determinism (D-010). + let mut triangle_states: Vec = { + let mut q = world.query::<&TriangleState>(); + q.iter(world).cloned().collect() + }; + triangle_states.sort_by_key(|t| t.triangle_id.0); + let state = SaveStateV1 { format_version: SAVE_FORMAT_VERSION, tick, @@ -122,6 +129,7 @@ pub fn save_to_file(path: &Path, world: &mut World) -> Result<(), SaveLoadError> relationship_graph, npc_states, template_references, + triangle_states, }; let bytes = state @@ -210,6 +218,11 @@ pub fn load_from_file(path: &Path, world: &mut World) -> Result<(), SaveLoadErro // Restore simulation resources. world.insert_resource(state.relationship_graph); world.insert_resource(state.template_references); + + // Restore triangle states (#250) — spawn dedicated entities for each. + for ts in &state.triangle_states { + world.spawn(ts.clone()); + } { let mut t = world.resource_mut::(); t.tick = state.tick; @@ -508,6 +521,7 @@ mod tests { relationship_graph: RelationshipGraph::new(), npc_states: vec![], template_references: TemplateReferenceMap::default(), + triangle_states: vec![], }; let bytes = bad_state.to_bytes().expect("serialize"); let path = temp_path(); diff --git a/server/src/simulation/save_state.rs b/server/src/simulation/save_state.rs index 1a81f7a09..f6c24ef9c 100644 --- a/server/src/simulation/save_state.rs +++ b/server/src/simulation/save_state.rs @@ -39,7 +39,7 @@ use bevy_ecs::entity::Entity; use bevy_ecs::world::World; use serde::{Deserialize, Serialize}; -use crate::content::template::{TemplateOwnership, TemplateReferenceMap}; +use crate::content::template::{TemplateOwnership, TemplateReferenceMap, TriangleState}; use crate::knowledge::graph::KnowledgeGraph; use crate::knowledge::registry::StableEntityId; use crate::knowledge::types::StableId; @@ -89,6 +89,11 @@ pub struct SaveStateV1 { /// relationship metadata even when their NPCs are not in Active tier. #[serde(default)] pub template_references: TemplateReferenceMap, + /// Triangle escalation states (#250). + /// Persisted so tension/phase survive save/load. Sorted by triangle_id + /// for deterministic serialization (D-010). + #[serde(default)] + pub triangle_states: Vec, } /// Per-NPC state snapshot for `SaveStateV1`. @@ -399,6 +404,7 @@ mod tests { relationship_graph: RelationshipGraph::new(), npc_states: vec![], template_references: TemplateReferenceMap::default(), + triangle_states: vec![], } } @@ -794,6 +800,7 @@ mod tests { relationship_graph: RelationshipGraph::new(), npc_states: vec![frozen], template_references: TemplateReferenceMap::default(), + triangle_states: vec![], }; let bytes = save.to_bytes().expect("serialize"); diff --git a/server/tests/information_boundaries.rs b/server/tests/information_boundaries.rs index 98be2a175..67e8f273b 100644 --- a/server/tests/information_boundaries.rs +++ b/server/tests/information_boundaries.rs @@ -204,6 +204,7 @@ fn save_state_npc_kg_isolation() { relationship_graph: RelationshipGraph::new(), npc_states: vec![npc_a_state, npc_b_state], template_references: Default::default(), + triangle_states: vec![], }; // Roundtrip: serialize → deserialize.