pub struct StorySession<R: StoryRng = FastRng> { /* private fields */ }Expand description
A journaling, replayable session wrapping a Story.
Owns a Story + a SessionJournal. Stepping mirrors
Story::advance_with exactly (StepOutcome), recording inputs at the
session boundary. The wrapped story is reachable via story /
story_mut for the documented journal-bypass escape
hatch.
Implementations§
Source§impl<R: StoryRng> StorySession<R>
impl<R: StoryRng> StorySession<R>
Sourcepub fn new(story: Story<R>, seed: Option<u64>) -> Self
pub fn new(story: Story<R>, seed: Option<u64>) -> Self
Wrap story in a fresh session. seed is advisory metadata recorded in
the journal (the host is responsible for actually seeding the story via
Story::set_rng_seed before/at start).
Sourcepub fn journal(&self) -> &SessionJournal
pub fn journal(&self) -> &SessionJournal
Read-only access to the journal (for export / persistence).
Sourcepub fn export_journal(&mut self) -> SessionJournal
pub fn export_journal(&mut self) -> SessionJournal
Take the journal by value, refreshing its checkpoint first so the
exported artifact can fast-restore. The session keeps a fresh empty
journal bound to the same program (rarely needed; export usually clones
via journal).
Sourcepub fn story(&self) -> &Story<R>
pub fn story(&self) -> &Story<R>
Escape hatch: the wrapped story. Reads through here never touch the journal (they can’t mutate anyway).
Sourcepub fn story_mut(&mut self) -> &mut Story<R>
pub fn story_mut(&mut self) -> &mut Story<R>
Escape hatch: mutable access to the wrapped story. Anything done here bypasses the journal — the documented journal-bypass contract. Use for foreign / shared flows (#200) whose externals never journal.
Sourcepub fn advance(&mut self) -> Result<StepOutcome, RuntimeError>
pub fn advance(&mut self) -> Result<StepOutcome, RuntimeError>
Advance one step with the default (fallback) handler, journaling any
inline-resolved externals. Surfaces a deferred external as
StepOutcome::AwaitingExternal.
Sourcepub fn advance_with(
&mut self,
handler: &dyn ExternalFnHandler,
) -> Result<StepOutcome, RuntimeError>
pub fn advance_with( &mut self, handler: &dyn ExternalFnHandler, ) -> Result<StepOutcome, RuntimeError>
Advance one step with a custom handler, journaling any inline-resolved externals it produces (the journaling-window gate: only this frame’s externals record).
Sourcepub fn continue_single(&mut self) -> Result<Line, RuntimeError>
pub fn continue_single(&mut self) -> Result<Line, RuntimeError>
Advance until one line of content or a yield point, journaling externals.
Sourcepub fn continue_to_pause(&mut self) -> Result<Vec<Line>, RuntimeError>
pub fn continue_to_pause(&mut self) -> Result<Vec<Line>, RuntimeError>
Advance to the next pause, journaling externals. The last line is always
terminal (Done / Choices / End).
Sourcepub fn choose(&mut self, index: usize) -> Result<(), RuntimeError>
pub fn choose(&mut self, index: usize) -> Result<(), RuntimeError>
Select a choice, journaling the Choice event (with its advisory label).
Sourcepub fn resolve_external(&mut self, value: Value)
pub fn resolve_external(&mut self, value: Value)
Resolve a deferred external (out-of-band, ExternalResult::Pending),
journaling it as an External event. This is the journaling-window gate
in action: the session records here because it is the session’s own
pause that is being resolved.
Sourcepub fn has_pending_external(&self) -> bool
pub fn has_pending_external(&self) -> bool
Whether the session is parked on a deferred external.
Sourcepub fn set_var(
&mut self,
name: &str,
value: Value,
) -> Result<bool, SessionError>
pub fn set_var( &mut self, name: &str, value: Value, ) -> Result<bool, SessionError>
Set a global variable. Turn-boundary only: rejected mid-turn.
§Errors
SessionError::MutationMidTurn if the session is mid-turn (status
Active).
Sourcepub fn go_to_path(
&mut self,
path: &str,
args: &[Value],
) -> Result<(), SessionError>
pub fn go_to_path( &mut self, path: &str, args: &[Value], ) -> Result<(), SessionError>
Move the play head to a path (ink ChoosePathString). Turn-boundary
only: rejected mid-turn.
§Errors
SessionError::MutationMidTurn mid-turn; wrapped RuntimeErrors
from the jump.
Sourcepub fn load_state(&mut self, state: &SaveState) -> Result<(), SessionError>
pub fn load_state(&mut self, state: &SaveState) -> Result<(), SessionError>
Load a durable SaveState. Turn-boundary only: rejected mid-turn.
§Errors
SessionError::MutationMidTurn mid-turn.
Sourcepub fn save_state(&self) -> SaveState
pub fn save_state(&self) -> SaveState
Capture the current durable game state (does not journal).
Sourcepub fn call_function(
&mut self,
name: &str,
args: &[Value],
handler: &dyn ExternalFnHandler,
) -> Result<Value, RuntimeError>
pub fn call_function( &mut self, name: &str, args: &[Value], handler: &dyn ExternalFnHandler, ) -> Result<Value, RuntimeError>
Evaluate an ink function from engine code, journaling a Call event.
The function’s own externals resolve through the isolated (non-journaling)
Story::call_function handler path — only the top-level call journals.
§Errors
Wrapped RuntimeErrors from evaluation.
Sourcepub fn snapshot(&self) -> StateSnapshot
pub fn snapshot(&self) -> StateSnapshot
A typed snapshot of the current game state (globals with list
membership, turn counts, callstack summary). See StateSnapshot.
Sourcepub fn diff(a: &StateSnapshot, b: &StateSnapshot) -> StateDiff
pub fn diff(a: &StateSnapshot, b: &StateSnapshot) -> StateDiff
Pure diff of two snapshots (convenience; see the free diff fn).
Source§impl<R: StoryRng> StorySession<R>
impl<R: StoryRng> StorySession<R>
Sourcepub fn restore(
story: Story<R>,
journal: SessionJournal,
) -> Result<(Self, ReplayOutcome), SessionError>
pub fn restore( story: Story<R>, journal: SessionJournal, ) -> Result<(Self, ReplayOutcome), SessionError>
Fast-restore: apply a journal’s embedded checkpoint
and skip replay when the program checksum matches; otherwise fall back to
a full replay.
Returns the constructed session and the ReplayOutcome. On a checksum
match with a present checkpoint the outcome is
ReplayOutcome::Replayed with no warnings (no stepping occurred).
§Errors
SessionError::ChecksumMismatch only if the checksum differs and no
checkpoint is present to restore from (nothing safe to do).
Sourcepub fn replay(
story: Story<R>,
journal: &SessionJournal,
mode: ExternalReplayMode,
live_handler: Option<&dyn ExternalFnHandler>,
) -> (Self, ReplayOutcome)
pub fn replay( story: Story<R>, journal: &SessionJournal, mode: ExternalReplayMode, live_handler: Option<&dyn ExternalFnHandler>, ) -> (Self, ReplayOutcome)
Replay a journal against story from a fresh start. Consumes the journal
prefix event-by-event; on divergence, truncates the journal at that point
and parks at the reached position.
The session re-records as it replays, rebuilding its own journal. In
ExternalReplayMode::Recorded, the rebuilt prefix is the source
prefix: source External events are re-pushed verbatim, including any
the re-run did not actually consume (a recorded-mode mismatch falls back
to the ink fallback body rather than diverging) — the truncated prefix
is the source’s record, not a re-observed trace. In
ExternalReplayMode::Live the rebuilt journal is a re-observed
trace: live results are journaled as they resolve and source External
events are not copied.
mode selects recorded (journal-served) vs live externals. Live replay
hitting a deferred external parks with FailReason::AwaitingExternal,
retaining the un-replayed tail: resolve the external
(resolve_external) and resume with
continue_replay, which picks up the remaining
recorded inputs from the park point.
Sourcepub fn has_pending_replay(&self) -> bool
pub fn has_pending_replay(&self) -> bool
Whether a parked replay tail is pending (a live replay hit a deferred
external). Resolve it and call continue_replay.
Sourcepub fn continue_replay(
&mut self,
live_handler: Option<&dyn ExternalFnHandler>,
) -> ReplayOutcome
pub fn continue_replay( &mut self, live_handler: Option<&dyn ExternalFnHandler>, ) -> ReplayOutcome
Resume a replay parked on a deferred external
(FailReason::AwaitingExternal). Resolve the pending external first
(via resolve_external), then call this: the
session resumes consuming the retained journal tail from the park
point — it can complete (ReplayOutcome::Replayed with all warnings
accumulated across parks), park again, diverge later, or fail.
With no pending replay tail this keeps the advance-only behavior:
it steps the live story to its next pause with live_handler
(journaling externals as in normal play) and returns Replayed on
reaching the pause, or Failed if it parks or errors (at_event is
then the rebuilt journal’s current length, where the next event would
land).