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:
2026-02-25 12:13:03 +01:00
co-authored by Claude Opus 4.6
parent 0dd33690f7
commit 6ed8d11502
22 changed files with 2561 additions and 35 deletions
+16 -4
View File
@@ -72,7 +72,19 @@ fn server_subprocess_sends_snapshot_on_connect() {
let mut reader = BufReader::new(stream.try_clone().expect("clone stream for reader"));
let mut writer = BufWriter::new(stream);
// 4. Send one PlayerInput (idle tick 0)
// 4. Read the protocol handshake (first framed message, #555)
let handshake_frame = read_framed(&mut reader)
.expect("read handshake frame")
.expect("server closed connection before sending handshake");
let handshake: HandshakeMessage =
rmp_serde::from_slice(&handshake_frame).expect("deserialize HandshakeMessage");
assert_eq!(
handshake.protocol_version, PROTOCOL_VERSION,
"handshake protocol_version mismatch: got {}, expected {}",
handshake.protocol_version, PROTOCOL_VERSION
);
// 5. Send one PlayerInput (idle tick 0)
let inputs = vec![PlayerInput {
tick: 0,
action: PlayerAction::MoveNorth,
@@ -80,14 +92,14 @@ fn server_subprocess_sends_snapshot_on_connect() {
let payload = rmp_serde::to_vec_named(&inputs).expect("serialize PlayerInput");
write_framed(&mut writer, &payload).expect("send PlayerInput to server");
// 5. Read one ObserverSnapshot
// 6. Read one ObserverSnapshot
let response = read_framed(&mut reader)
.expect("read snapshot frame")
.expect("server closed connection before sending snapshot");
let snapshot: ObserverSnapshot =
rmp_serde::from_slice(&response).expect("deserialize ObserverSnapshot");
// 6. Assert protocol correctness (D-020)
// 7. Assert protocol correctness (D-020)
assert_eq!(
snapshot.version, PROTOCOL_VERSION,
"protocol version mismatch: got {}, expected {}",
@@ -105,7 +117,7 @@ fn server_subprocess_sends_snapshot_on_connect() {
.any(|e| matches!(e.kind, EntityKind::Player));
assert!(has_player, "snapshot must contain a Player entity");
// 7. Clean up: drop connection so the server exits its game loop
// 8. Clean up: drop connection so the server exits its game loop
drop(reader);
drop(writer);