From 8f983802614766b96e9e444a2fbbf5f18964c4bc Mon Sep 17 00:00:00 2001 From: m3ultra Date: Mon, 27 Jul 2026 18:27:04 +1000 Subject: [PATCH] export: byte-stable bundles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bundle is full of HashMaps — rooms, scripts, sprites, every prop's msgs — and Rust randomizes their iteration per process, so exporting an unchanged game twice produced two different files. These are committed build outputs; a diff should mean the game changed. Serializing through serde_json::Value fixes it at every depth in one hop, because Value is BTreeMap-backed by default. Three consecutive exports are now byte-identical, and the golden replay is unchanged. Co-Authored-By: Claude Opus 5 --- mrpci-core/src/bin/mrpci-headless/main.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mrpci-core/src/bin/mrpci-headless/main.rs b/mrpci-core/src/bin/mrpci-headless/main.rs index b54db7a..0b7df36 100644 --- a/mrpci-core/src/bin/mrpci-headless/main.rs +++ b/mrpci-core/src/bin/mrpci-headless/main.rs @@ -77,7 +77,17 @@ fn main() { if let Some(path) = get("--export-bundle") { let bundle = gs.world.to_bundle(); - let j = serde_json::to_string(&bundle).expect("bundle serializes"); + // Through Value first, so the output is byte-stable. + // + // A bundle is full of HashMaps — rooms, scripts, sprites, every + // prop's msgs — and Rust randomizes their iteration per process, so + // exporting the same unchanged game twice produced two different + // files. These are committed build outputs; a diff should mean the + // game changed. serde_json's Value is BTreeMap-backed by default, so + // this one hop sorts every key at every depth. + let j = serde_json::to_value(&bundle) + .and_then(|v| serde_json::to_string(&v)) + .expect("bundle serializes"); std::fs::write(&path, &j).expect("write bundle"); println!("bundle exported: {} ({} rooms, {} scripts, {} sprites, {} bytes)", path, bundle.rooms.len(), bundle.scripts.len(), bundle.sprites.len(), j.len());