feat(simulation): error handling and recovery — panic supervision, state hash, structured errors (#85)
Protocol v17: add state_hash (desync detection) and sim_errors (structured error reporting) to ObserverSnapshot. Add SimError, SimErrorKind, SimErrorBuffer types. Wrap main loop app.update() in catch_unwind — on panic, send a final SimError snapshot before exit. Report recoverable deserialization errors to client via SimErrorBuffer. Compute per-tick state hash from player position + NPC count + tick. Update all test fixtures and golden files for protocol v17. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,8 @@ fn test_snapshot(tick: u64, entities: Vec<VisibleEntity>) -> ObserverSnapshot {
|
||||
player_knowledge: None,
|
||||
save_result: None,
|
||||
triangle_crisis_events: vec![],
|
||||
state_hash: None,
|
||||
sim_errors: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +298,8 @@ fn snapshot_v2_fields_roundtrip() {
|
||||
player_knowledge: None,
|
||||
save_result: None,
|
||||
triangle_crisis_events: vec![],
|
||||
state_hash: None,
|
||||
sim_errors: vec![],
|
||||
};
|
||||
|
||||
let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize");
|
||||
@@ -350,7 +354,7 @@ fn protocol_version_constant_matches_snapshot() {
|
||||
let snapshot = test_snapshot(0, vec![]);
|
||||
assert_eq!(snapshot.version, PROTOCOL_VERSION);
|
||||
assert_eq!(
|
||||
PROTOCOL_VERSION, 16,
|
||||
PROTOCOL_VERSION, 17,
|
||||
"bump this assertion when protocol version changes"
|
||||
);
|
||||
}
|
||||
@@ -401,6 +405,8 @@ fn all_facing_direction_variants_roundtrip() {
|
||||
player_knowledge: None,
|
||||
save_result: None,
|
||||
triangle_crisis_events: vec![],
|
||||
state_hash: None,
|
||||
sim_errors: vec![],
|
||||
};
|
||||
let bytes = rmp_serde::to_vec_named(&snapshot).expect("serialize");
|
||||
let decoded: ObserverSnapshot = rmp_serde::from_slice(&bytes).expect("deserialize");
|
||||
|
||||
Reference in New Issue
Block a user