Savegames hold only the game state: no settings, no camera (settings stay in local storage)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-06 07:54:36 +01:00
1 parent 68e878265c
commit 7709226a58
4 files changed
+30 -30

No files matched your search

+18 -18
View File
@@ -1,9 +1,9 @@
// Savegames: JSON with format tag and version, containing the game state, the camera
// and the player's settings (settings are always applied when a savegame is loaded).
// Savegames: JSON with format tag and version, containing the game state - nothing else. The
// player's settings live in the browser's local storage; settings in older savegames are
// ignored. Only the screenshot scenes (scenes.rs) also carry a camera.
use crate::game::Game;
use crate::render3d::Cam;
use crate::settings::Settings;
use serde::{Deserialize, Serialize};
pub const FORMAT: &str = "dungeoncraft-save";
@@ -15,8 +15,8 @@ struct SaveOut<'a> {
format: &'a str,
version: u32,
game: &'a Game,
camera: &'a Cam,
settings: &'a Settings,
#[serde(skip_serializing_if = "Option::is_none")]
camera: Option<&'a Cam>,
}
#[derive(Deserialize)]
@@ -25,17 +25,21 @@ struct SaveIn {
version: u32,
game: Game,
camera: Option<Cam>,
settings: Option<Settings>,
}
pub struct Loaded {
pub game: Game,
pub camera: Option<Cam>,
pub settings: Option<Settings>,
}
pub fn to_json(game: &Game, camera: &Cam, settings: &Settings) -> String {
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera, settings }).unwrap_or_default()
pub fn to_json(game: &Game) -> String {
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera: None }).unwrap_or_default()
}
// a screenshot scene: the game and where the camera looks
#[allow(dead_code)] // scenes.rs (tests) only
pub fn to_json_with_camera(game: &Game, camera: &Cam) -> String {
serde_json::to_string(&SaveOut { format: FORMAT, version: VERSION, game, camera: Some(camera) }).unwrap_or_default()
}
// Err contains a language key describing the problem
@@ -50,7 +54,7 @@ pub fn from_bytes(data: &[u8]) -> Result<Loaded, &'static str> {
let mut game = s.game;
game.map.activate();
game.recount();
Ok(Loaded { game, camera: s.camera, settings: s.settings })
Ok(Loaded { game, camera: s.camera })
}
#[cfg(test)]
@@ -65,18 +69,14 @@ mod tests {
for _ in 0..3000 {
g.update(0.05);
}
let cam = Cam::new(10.0, 20.0);
let mut settings = Settings::default();
settings.language = "de".into();
let json = to_json(&g, &cam, &settings);
let json = to_json(&g);
println!("savegame size: {} KB", json.len() / 1024);
let l = from_bytes(json.as_bytes()).expect("load");
assert_eq!(l.game.time, g.time);
assert_eq!(l.game.ents.len(), g.ents.len());
assert_eq!(l.game.gold, g.gold);
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.kind == b.kind && a.owner == b.owner && a.marked == b.marked));
assert_eq!(l.settings.unwrap().language, "de");
assert_eq!(l.camera.unwrap().tx, 10.0);
assert!(l.camera.is_none() && !json.contains("\"settings\"") && !json.contains("\"camera\""), "only the game state");
assert_eq!((l.game.map.w, l.game.map.h), (96, 128));
assert_eq!(l.game.map.portals.len(), g.map.portals.len());
assert!(l.game.map.t.iter().zip(g.map.t.iter()).all(|(a, b)| a.var == b.var && a.hp == b.hp && a.food == b.food && a.gold == b.gold));
@@ -107,7 +107,7 @@ mod tests {
g.update(0.05);
}
let sim = t1.elapsed();
let json = to_json(&g, &Cam::new(0.0, 0.0), &Settings::default());
let json = to_json(&g);
println!("256x256: generate {:?}, 2 min simulated in {:?}, savegame {} KB", gen, sim, json.len() / 1024);
let l = from_bytes(json.as_bytes()).expect("load");
assert_eq!(l.game.map.t.len(), 256 * 256);
@@ -132,7 +132,7 @@ mod tests {
let (ex, ey) = (cx + 1, cy + 1);
let mut cam = Cam::new(ex as f32 + 0.5, ey as f32 + 0.5);
cam.zoom = 12.0;
std::fs::write(path, to_json(&g, &cam, &Settings { language: "en".into(), ..Settings::default() })).unwrap();
std::fs::write(path, to_json_with_camera(&g, &cam)).unwrap();
}
}