osrlib.persistence
Save/load and replay: two paths to a running session, guaranteed to agree.
A save, produced by save_game, is a
stamp_document envelope of kind "save"
carrying the full session state: party, the adventure's own content (a save is
self-contained and needs no other files to load), dungeon state, clock, ledger,
allocator, registry monsters, flags, listener state, mode, crawl counters, exported
RNG stream states, the master seed, the accepted-command log always, and the event
log optionally. A session restores from that state alone — the command and event
logs are records of what happened, not dependencies of the restore.
load_game rebuilds a session directly from a save's
state, migrating older schema versions on the way in.
replay_game rebuilds a session the other way, by
re-executing the master seed and the accepted-command log from scratch; it is valid
only under the identical engine version that recorded the log, and raises
ReplayVersionError on a mismatch. Loading a save
and replaying its command log from the same seed always reach the identical session
state.
MIGRATIONS
module-attribute
Ordered save migrations: MIGRATIONS[n] rewrites a version-n payload to n+1.
load_game
load_game(document: Mapping[str, object]) -> GameSession
Restore a session from a save document.
Runs check_document, then the ordered
migration chain, then rebuilds the session and restores the RNG streams via
RngStream.restore. Event-log entries
whose types this process doesn't know are preserved as raw records that
reserialize losslessly — the log is a record, never re-derived, never lossy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
document
|
Mapping[str, object]
|
A document produced by |
required |
Returns:
| Type | Description |
|---|---|
GameSession
|
The restored session (listeners must be re-registered by the game). |
Raises:
| Type | Description |
|---|---|
ContentValidationError
|
If the envelope or payload is malformed. |
SaveVersionError
|
If the document's schema version is newer than this library understands. |
Examples:
from osrlib.core.alignment import Alignment
from osrlib.core.character import CHARACTER_CREATION_STREAM, create_character
from osrlib.core.rng import RngStreams
from osrlib.core.ruleset import Ruleset
from osrlib.crawl.adventure import Adventure, TownSpec
from osrlib.crawl.dungeon import DungeonSpec, Edge, EdgeKind, LevelSpec
from osrlib.crawl.party import Party
from osrlib.crawl.session import GameSession
from osrlib.persistence import load_game, save_game
rules = Ruleset()
roll = RngStreams(master_seed=7).get(CHARACTER_CREATION_STREAM)
pc = create_character(name="Hild", class_id="fighter", alignment=Alignment.LAWFUL, ruleset=rules, stream=roll)
party = Party(members=[pc.character])
level = LevelSpec(number=1, width=2, height=1, entrance=(0, 0), edges={"1,0:west": Edge(kind=EdgeKind.OPEN)})
crypt = DungeonSpec(id="crypt", name="The Old Crypt", levels=(level,))
town = TownSpec(name="Threshold", travel_turns={"crypt": 1})
adventure = Adventure(name="A First Delve", town=town, dungeons=(crypt,))
session = GameSession.new(party, adventure, seed=7)
document = save_game(session)
restored = load_game(document)
assert save_game(restored) == document
replay_game
replay_game(
seed: int,
party_document: Mapping[str, object],
adventure: Adventure,
ruleset: Ruleset,
commands: Sequence[Command | Mapping[str, object]],
*,
recorded_engine_version: str | None = None
) -> GameSession
Re-execute a command log from the seed — the determinism contract exercised.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed
|
int
|
The master seed the session ran under. |
required |
party_document
|
Mapping[str, object]
|
The starting party as a stamped |
required |
adventure
|
Adventure
|
The frozen adventure content. |
required |
ruleset
|
Ruleset
|
The ruleset the session ran under. |
required |
commands
|
Sequence[Command | Mapping[str, object]]
|
The accepted-command log, as commands or their serialized forms. |
required |
recorded_engine_version
|
str | None
|
The engine version the log was recorded under, when known (a save's stamp); a mismatch raises. |
None
|
Returns:
| Type | Description |
|---|---|
GameSession
|
The replayed session, in the exact state the original reached. |
Raises:
| Type | Description |
|---|---|
ReplayVersionError
|
If the log was recorded under a different engine version — replays are valid only under the identical engine. |
ContentValidationError
|
If a command fails to parse, or a logged command is rejected on replay (divergence — the log holds accepted commands only). |
save_game
save_game(session: GameSession, *, include_event_log: bool = True) -> dict
Serialize a session to a stamped save document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The session to save. |
required |
include_event_log
|
bool
|
False compacts the save (state plus command log only). |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
The stamped |
session_state
session_state(session: GameSession, *, include_event_log: bool = True) -> dict
Serialize a session's full state (the save payload, sans envelope).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The session to serialize. |
required |
include_event_log
|
bool
|
False compacts the save to state plus the command log. |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
The JSON-ready state dict. |