Game Session & Save/Load
Relevant source files
The following files were used as context for generating this wiki page:
The Game Session & Save/Load system manages the persistence, restoration, and lifecycle of the game state in Cyrus. It provides a boundary between live gameplay and serialized data, ensuring atomic operations for saving progress and asynchronous loading of assets during world transitions.
Session Boundary Architecture
The SessionBoundary acts as the primary coordinator for session state transitions. It manages whether the system is currently idle, capturing live state, or committing a loaded snapshot to the active game world.
- Capture: The boundary records the current state of all session-managed entities, including actors, global flags, and dialogue progress, into a
RuntimeSessionSnapshot. - Commit: When a session is loaded, the boundary applies the snapshot data to the live Bevy world, replacing existing actors and resetting global state as required.
- Isolation: The system operates "off-live" during preparation to prevent blocking the main game simulation thread.
Sources: crates/cyrus-session/src/lib.rs:25-80, crates/cyrus/src/session.rs:18-35
Session State Transitions
The following diagram illustrates the lifecycle of a session operation managed by the boundary.
The diagram shows how the SessionBoundary moves from an Idle state through various active states before returning a terminal result.
Sources: crates/cyrus-session/src/lib.rs:55-110
Save Slot Management
Cyrus manages save games through a SlotStore. Saved games are stored in the user's config directory under a slots/ subfolder. Each slot contains an opaque Cyrus session document and associated metadata.
- Slot Identity: Slots are identified by a
u16index. Manual slots typically range from 2 to 299. - Revisions: The system tracks revisions for each slot. Operations like
OverwriteorDeleterequire providing the specific revision string to ensure consistency. - Atomic Writing: The system uses a "temporary candidate" pattern. It writes data to a
.tmpfile, syncs it to disk, and then performs an atomic rename to the target path.
Sources: crates/cyrus/src/session.rs:182-250, crates/cyrus-config/src/file.rs:24-60, crates/cyrus/README.md:145-160
Save Operation Sequence
This sequence shows the interaction between the UI, the session host, and the underlying storage for a save operation.
Sources: crates/cyrus/src/session.rs:278-315, crates/cyrus/src/session.rs:370-405
Session Data Structure
The RuntimeSessionSnapshot is the primary data structure for serialization. It is encoded into JSON documents for storage on disk.
| Field | Description |
|---|---|
current_world | The ID of the currently active world (e.g., START, ISLAND). |
actors | A collection of actor states, including positions, orientations, and script variables. |
flags | Global story and gameplay flags managed by the session. |
dialogue | State of the dialogue system, including required labels and active lines. |
music | Information about the currently playing CD track and playback progress. |
Sources: crates/cyrus-session/src/document.rs:10-50, crates/cyrus/src/session.rs:520-560
Asynchronous Loading Workflow
Loading a session involves a multi-stage asynchronous process to prepare assets before they are committed to the live world.
- Preparation: A worker thread reads the session document and identifies all required assets (worlds, HUD artwork, sound samples).
- Asset Export: The worker ensures that the required original game assets are exported to the local cache (e.g.,
.glbfor worlds,.wavfor audio). - Requesting: The
NativeSessionLoadhost requests these assets from the BevyAssetServer. - Committing: Once all dependencies are loaded, the
SessionBoundaryapplies the snapshot to the live world.
Sources: crates/cyrus/src/session.rs:50-100, crates/cyrus-session/src/lifecycle.rs:20-60
Staging and Replacement
During the commit phase, the SessionStaging resource tracks the replacement of live entities. It identifies which entities should be removed and which new instances need to be spawned based on the snapshot.
- World Replacement: Changing the world clears existing holds and restores original meshes.
- Actor Restoration: The system restores the selected player's role and state while ensuring dialogue speakers do not outlive their world.
Sources: crates/cyrus-session/src/staging.rs:15-80, crates/cyrus/README.md:120-135
Atomic Document Storage
The cyrus-config crate provides utility functions for safe document storage. This prevents data corruption during unexpected crashes.
// crates/cyrus-config/src/file.rs:24-52
pub fn write(path: &Path, bytes: &[u8]) -> Result<()> {
// ... creates parent directories ...
// ... creates a .tmp candidate file ...
file.write_all(bytes)?;
file.sync_all()?;
drop(file);
std::fs::rename(&temporary, path)?;
Ok(())
}
This function ensures that a file is only replaced if the new version is successfully written and synchronized to the storage medium.
Sources: crates/cyrus-config/src/file.rs:24-52
Summary
The Cyrus session system provides a robust framework for managing game state. By decoupling the live simulation from persistence logic through the SessionBoundary and employing atomic file operations, it ensures that player progress is reliably saved and restored across play sessions. The use of worker threads for asset preparation enables smooth transitions even when loading complex world data.