Skip to content

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

MIGRATIONS: dict[int, Callable[[dict], dict]] = {1: _migrate_1_to_2}

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 save_game.

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 "party" document (the pre-session party; the session re-assigns the same ids).

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 "save" document.

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.