feat(simulation): Sprint 19 — 7 server systems
Protocol handshake (#555): HandshakeMessage as first IPC frame, HandshakeState resource, forward-compatible input handling. State serialization (#96): serialize_npc_to_frozen/deserialize with full D-024 axis coverage (10 new optional fields on NpcSaveState). Scope tags (#98): ScopeTagKind enum, ScopePinned marker, automatic assignment from KnowledgeGraph and RelationshipGraph. Timestamp eviction (#97): LastInteractionTick, SimSpacePressure, BinaryHeap LRU eviction respecting ScopePinned entities. Save/load (#553): save_to_file/load_from_file via MessagePack, SaveGame/LoadGame IPC commands, SaveLoadResultWire on snapshot. Test infrastructure (#200): Layer 3 integration test entry point, three-layer architecture documented per D-030. Information boundary tests (#272): 4 negative tests proving no passive KG leakage, LOS fog holds, tier boundary holds, save isolation per NPC. 1063 tests passing, 0 failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,38 @@ impl EntityRegistry {
|
||||
self.next_id = target;
|
||||
}
|
||||
|
||||
/// Register an entity with a specific pre-existing StableId (used during save/load).
|
||||
///
|
||||
/// Unlike `register`, this does NOT advance `next_id`. After bulk-registering
|
||||
/// all restored entities, call `advance_past(max_stable_id)` so future `register()`
|
||||
/// calls produce IDs that don't conflict with the restored set.
|
||||
///
|
||||
/// No-op if the entity is already mapped to the same `stable_id`.
|
||||
/// Panics in debug builds if `stable_id` is already mapped to a different entity.
|
||||
pub fn register_existing(&mut self, entity: Entity, stable_id: StableId) {
|
||||
if let Some(&existing) = self.by_stable_id.get(&stable_id) {
|
||||
debug_assert_eq!(
|
||||
existing, entity,
|
||||
"register_existing: StableId {:?} already mapped to a different entity",
|
||||
stable_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
self.by_stable_id.insert(stable_id, entity);
|
||||
self.by_entity.insert(entity, stable_id);
|
||||
}
|
||||
|
||||
/// Advance `next_id` past `id` so future `register()` calls don't conflict.
|
||||
///
|
||||
/// Unlike `reserve_up_to`, this never panics: if the counter is already past `id`,
|
||||
/// this is a no-op. Use after `register_existing` bulk-load to position the counter.
|
||||
pub fn advance_past(&mut self, id: u64) {
|
||||
let target = id.saturating_add(1);
|
||||
if target > self.next_id {
|
||||
self.next_id = target;
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of registered entities.
|
||||
pub fn len(&self) -> usize {
|
||||
self.by_entity.len()
|
||||
|
||||
Reference in New Issue
Block a user