Adds Serialize/Deserialize to SelectedBookmark and wires it into SaveStateV1 so a loaded game remembers which bookmark and starting location the player picked. Replaces the TODO at bookmark/mod.rs:95 (originally deferred to Sprint 37 alongside #614). Also refactors BookmarkPlugin to accept an injected BookmarkRegistry via BookmarkPlugin::new(registry) (#862). The Default constructor still wires the canonical tycoon registry — injection is for tests and future TOML loading. Flagged in PR #132 review as a follow-up. Updates bookmark spec §4.4 to remove the v0.2-deferred scope note. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ use crate::npc::{
|
||||
PersonalityTraits, Relationships, Secret, SecretSeverity, SkillSet, TellSystem,
|
||||
ToleranceThreshold, Want, WantKind,
|
||||
};
|
||||
use crate::bookmark::SelectedBookmark;
|
||||
use crate::simulation::modification::Modification;
|
||||
use crate::simulation::movement::TilePosition;
|
||||
use crate::simulation::time::TickRate;
|
||||
@@ -122,6 +123,11 @@ pub struct SaveStateV1 {
|
||||
/// `None` if no activation yet. Persisted alongside `activated_count`.
|
||||
#[serde(default)]
|
||||
pub last_activation_tick: Option<u64>,
|
||||
/// Bookmark and starting location confirmed by the player at session start (#863, #614).
|
||||
/// Both fields are `None` in saves created before #863 or before character creation
|
||||
/// completes. Defaults to `SelectedBookmark::default()` for backward compatibility.
|
||||
#[serde(default)]
|
||||
pub selected_bookmark: SelectedBookmark,
|
||||
}
|
||||
|
||||
/// Per-NPC state snapshot for `SaveStateV1`.
|
||||
@@ -435,6 +441,7 @@ mod tests {
|
||||
contamination_active: false,
|
||||
activated_count: 0,
|
||||
last_activation_tick: None,
|
||||
selected_bookmark: crate::bookmark::SelectedBookmark::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -854,6 +861,7 @@ mod tests {
|
||||
contamination_active: false,
|
||||
activated_count: 0,
|
||||
last_activation_tick: None,
|
||||
selected_bookmark: crate::bookmark::SelectedBookmark::default(),
|
||||
};
|
||||
|
||||
let bytes = save.to_bytes().expect("serialize");
|
||||
@@ -878,6 +886,60 @@ mod tests {
|
||||
assert!(result.is_err(), "must panic without StableEntityId");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// SelectedBookmark round-trip (#863)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn selected_bookmark_with_values_survives_roundtrip() {
|
||||
// Spec (#863): SelectedBookmark persisted in SaveStateV1 must survive a
|
||||
// full MessagePack serialize → deserialize cycle with field values intact.
|
||||
let mut state = minimal_save_state();
|
||||
state.selected_bookmark = crate::bookmark::SelectedBookmark {
|
||||
bookmark_id: Some("tycoon".to_string()),
|
||||
starting_location_id: Some("GJ 35".to_string()),
|
||||
};
|
||||
|
||||
let bytes = state.to_bytes().expect("serialize");
|
||||
let recovered = SaveStateV1::from_bytes(&bytes).expect("deserialize");
|
||||
|
||||
assert_eq!(
|
||||
recovered.selected_bookmark.bookmark_id,
|
||||
Some("tycoon".to_string()),
|
||||
"bookmark_id must survive roundtrip"
|
||||
);
|
||||
assert_eq!(
|
||||
recovered.selected_bookmark.starting_location_id,
|
||||
Some("GJ 35".to_string()),
|
||||
"starting_location_id must survive roundtrip"
|
||||
);
|
||||
|
||||
// Idempotent re-serialize: bytes must be stable
|
||||
let bytes2 = recovered.to_bytes().expect("re-serialize");
|
||||
assert_eq!(bytes, bytes2, "SelectedBookmark roundtrip must be idempotent");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_bookmark_default_survives_roundtrip() {
|
||||
// Spec (#863): saves from before Sprint 37 (both fields None) must load
|
||||
// cleanly via serde(default) on the SaveStateV1 field.
|
||||
let state = minimal_save_state();
|
||||
assert!(state.selected_bookmark.bookmark_id.is_none());
|
||||
assert!(state.selected_bookmark.starting_location_id.is_none());
|
||||
|
||||
let bytes = state.to_bytes().expect("serialize");
|
||||
let recovered = SaveStateV1::from_bytes(&bytes).expect("deserialize");
|
||||
|
||||
assert!(
|
||||
recovered.selected_bookmark.bookmark_id.is_none(),
|
||||
"default bookmark_id (None) must roundtrip"
|
||||
);
|
||||
assert!(
|
||||
recovered.selected_bookmark.starting_location_id.is_none(),
|
||||
"default starting_location_id (None) must roundtrip"
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Modifications stub round-trip (#567, D-111/D-112)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user