narraleaf-react
Version:
A React visual novel player framework
122 lines (121 loc) • 6.9 kB
TypeScript
import { Awaitable } from "../../../util/data";
import { LiveGame } from "../common/game";
import { CalledActionResult, StackModelWaiting } from "../gameTypes";
import { LogicAction } from "./logicAction";
export declare enum StackModelItemType {
Action = "action",
Link = "link"
}
export type StackModelRawData = ({
type: StackModelItemType.Action;
actionType: string | null;
action: string | null;
} | {
type: StackModelItemType.Link;
actionType: string | null;
action: string | null;
stacks: StackModelRawData[];
stackWaitType: StackModelWaiting["type"] | null;
})[];
/**
* Nested Stack Model is a new concept designed to control serialization/deserialization of complex nested operations
*
* Core concepts for saving state:
* 1. Do not save operations that cannot be immediately resolved, such as Awaitables
* 2. If an action returns an Awaitable, to prevent re-execution of the previous operation after deserialization:
* - Store the operation in waitingAction and add it to the tail stack during serialization
* - When restoring data, retrying the tail stack operation will retry this operation
* - Awaitables should not be saved due to their scope and complex behavior, instead save their parent operation
* 3. If an action returns a regular child (synchronous operation), add it to the tail stack
* - The child will continue on the next stack operation
* 4. If an action returns multiple children, add them sequentially to the tail stack
* - These children are treated as having a call relationship, e.g. in [a,b], a waits for b to complete before continuing
* - This requires all elements except the stack top to be fully synchronous operations
* 5. If an action returns a StackAction (not yet implemented), wait according to the StackAction definition
* - This operation is considered semi-synchronous since it contains child information
* - Serialization mechanism: treat as synchronous operation, including async info and stack contents
* - When restored, operation remains at stack top and continues waiting for stack operations to complete
* - This ensures stack operations are not abnormally re-executed or skipped after deserialization
*
* Example scenarios:
* 1. Action returns Awaitable:
* - Async operation: add Awaitable to tail stack, set sync operation as waitingAction
* - During save: exclude Awaitable, add waitingAction to stack for retry on deserialize
* - During runtime: wait for resolution, pop self and add return value to stack
* 2. Action returns direct child:
* - Sync operation: add operation to tail stack
* - During save: add operation to stack
* - During runtime: pop self and add child to stack
* 3. Action returns multiple children:
* - Sync/async nature determined by last child
* - Push all children to stack in order, last child on top
* - Save behavior follows above rules
* 4. Action returns StackAction:
* - Semi-sync operation treated as sync, includes async info and stack contents
* - During save: includes direct children, wait info (type e.g. any, all) and stack
* - Runtime with non-empty stack: continue waiting for stack operations
* - Runtime with empty stack: resolve operation, pop self and add direct children
* 5. Action returns direct child but async executes StackModel:
* - Serialize StackModel and execute directly on deserialize
*/
export declare class StackModel {
private liveGame;
__tag: string | undefined;
static isStackModel(action: CalledActionResult | Awaitable<CalledActionResult> | StackModel): action is StackModel;
static createStackModel(liveGame: LiveGame, data: StackModelRawData, actionMap: Map<string, LogicAction.Actions>): StackModel;
static isCalledActionResult(action: CalledActionResult | Awaitable<CalledActionResult> | StackModel | undefined | null): action is CalledActionResult;
static fromAction(action: LogicAction.Actions): CalledActionResult;
static executeStackModelGroup(type: StackModelWaiting["type"], stackModels: StackModel[]): Awaitable<void>;
static isStackModelsAwaiting(type: StackModelWaiting["type"], stackModels: StackModel[]): boolean;
private stack;
private waitingAction;
constructor(liveGame: LiveGame, tag?: string | undefined);
/**
* Executes the next operation in the stack
*
* Main responsibilities:
* 1. Check and handle waiting states at the top of the stack
* 2. Execute current operation and handle its results
* 3. Manage asynchronous operations and nested stack models
*
* Execution flow:
* 1. If stack is empty, return null
* 2. Check top element:
* - If it's an unsettled Awaitable, return the Awaitable
* - If it's a waiting operation (with nested stack models), check nested stack status
* 3. Pop and execute current operation:
* - If it's an Awaitable, wait for completion and handle result
* - If it's a regular operation, execute and handle return value
*
* @returns One of the following:
* - CalledActionResult: Execution result (returned a synchronous operation)
* - Awaitable<CalledActionResult>: Asynchronous operation
* - null: No more operations if the stack is empty, or the top element is exited
*/
rollNext(): CalledActionResult | Awaitable<CalledActionResult> | null;
execute(): Awaitable<void>;
abortStackTop(): CalledActionResult | null;
getTopSync(): CalledActionResult | null;
executeActions(result: CalledActionResult): CalledActionResult | Awaitable<CalledActionResult> | null;
isWaiting(): boolean;
/**
* Serialize current StackModel into a plain JSON-serialisable structure.
*
* @param frozen - When true (default), the snapshot also contains the
* action currently executing at the top of the stack
* (waitingAction). This is required by save/load so that
* reloading a save resumes exactly at the current dialog or
* async node, keeping the runtime state self-consistent.
* When false, waitingAction is excluded and the snapshot
* reflects the state *before* the current action started.
* The undo/history system will then re-insert the action
* manually (see LiveGame.undo) to avoid having two copies
* of the same action after deserialisation.
* @returns Snapshot that can be passed to {@link StackModel.deserialize}.
*/
serialize(frozen?: boolean): StackModelRawData;
reset(): void;
deserialize(data: StackModelRawData, actionMap: Map<string, LogicAction.Actions>): this;
isEmpty(): boolean;
push(...items: (CalledActionResult | Awaitable<CalledActionResult>)[]): this;
}