@moku-labs/game 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/app/page.d.mts +1 -1
- package/dist/app/system.d.mts +3 -3
- package/dist/app.d.mts +2 -2
- package/dist/app.mjs +1 -1
- package/dist/assets.d.mts +1 -1
- package/dist/{capture-fpd83Mi5.mjs → capture-D7XvoHdH.mjs} +78 -1
- package/dist/{commands-BrgeMP5y.d.mts → commands-Cpn2ga7s.d.mts} +5 -5
- package/dist/control.d.mts +4 -4
- package/dist/control.mjs +1 -1
- package/dist/{define-ZmWbWxon.d.mts → define-Bwp8CNkW.d.mts} +2 -2
- package/dist/{headless-BsNdJs9W.mjs → headless-BA6ffzqD.mjs} +30 -9
- package/dist/{headless-Dgxvp6Ym.d.mts → headless-IfD6_JMH.d.mts} +1 -1
- package/dist/{index-CdbE_jGs.d.mts → index-yUO4Dx2C.d.mts} +4 -4
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/inspect.d.mts +7 -7
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{memory-DJ2sPExv.d.mts → memory-D1IKoFr1.d.mts} +1 -1
- package/dist/{run-wYty_Lwo.mjs → run-Bf2hKyjZ.mjs} +151 -43
- package/dist/{runtime-6qakboTm.d.mts → runtime-CWY6co6m.d.mts} +1 -1
- package/dist/{src-CltFFKtc.mjs → src-Dgmdzl8R.mjs} +444 -128
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +2 -2
- package/dist/{types-DGIkfZB7.d.mts → types-B98iRBSG.d.mts} +4 -4
- package/dist/{types-CmuwAjkV.d.mts → types-BArxen_M.d.mts} +1 -1
- package/dist/{types-C6y8EfEB.d.mts → types-BWDESfx2.d.mts} +94 -25
- package/dist/{types-zRGJdFGX.d.mts → types-D7MGz4c-.d.mts} +2 -2
- package/dist/{types-D57SntUt.d.mts → types-InZGjvkI.d.mts} +2 -2
- package/dist/{types-B3T2X6To.d.mts → types-oQoQEj-y.d.mts} +19 -12
- package/dist/visual.d.mts +6 -6
- package/dist/visual.mjs +1 -1
- package/llms.txt +5 -4
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import { r as systemSource, t as memory } from "./memory-CWlqhS7l.mjs";
|
|
3
3
|
import { a as finishDrafts, i as enableDraftPatches, n as deepFreeze, o as openDrafts, r as dropDrafts } from "./drafts-BawpSaVp.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { d as graphHash, f as isRestCheckpoint, h as pushEntry, i as servePaused, l as findNode, m as compact, n as captureFrame, o as collectFlows, p as slotNames, r as serveCaptures, s as describeGraph, t as cancelCaptures, u as framePath } from "./capture-D7XvoHdH.mjs";
|
|
5
5
|
import { A as system, C as Exiting, D as component, E as Tree, T as Order, _ as Parent, a as visualOf$1, b as Transform, c as Text, g as NineSlice, h as Display, j as tag, k as resource, m as textFor, n as fitScaleOf, o as visualRectOf, p as readStyle, r as samePose, s as Countdown, t as CONTENT, u as builtInStyles, v as Shape, w as Layer, x as componentsFor, y as Sprite } from "./visual-dMW8iqAE.mjs";
|
|
6
6
|
import { n as isDev } from "./dev-Cc50Dk7H.mjs";
|
|
7
7
|
import { i as isComponentDefinition, n as flatten, r as defineComponent } from "./flatten-BkXuscAV.mjs";
|
|
@@ -1933,19 +1933,6 @@ function connectFlow(ctx) {
|
|
|
1933
1933
|
registerSchedule(flowCtx, fx);
|
|
1934
1934
|
}
|
|
1935
1935
|
//#endregion
|
|
1936
|
-
//#region src/plugins/flow/runner/loop-types.ts
|
|
1937
|
-
/** The stages of entering a node, in the order they run. */
|
|
1938
|
-
const stages = ["load", "scene"];
|
|
1939
|
-
/**
|
|
1940
|
-
* Does nothing. It stands in for an unsubscribe function until the real one arrives.
|
|
1941
|
-
*
|
|
1942
|
-
* @example
|
|
1943
|
-
* ```ts
|
|
1944
|
-
* const watch = { off: noop };
|
|
1945
|
-
* ```
|
|
1946
|
-
*/
|
|
1947
|
-
function noop() {}
|
|
1948
|
-
//#endregion
|
|
1949
1936
|
//#region src/plugins/flow/runner/validate.ts
|
|
1950
1937
|
/** Nodes of one flow above which `validate` warns. A flow holds 7 to 15 nodes. */
|
|
1951
1938
|
const MAX_NODES = 15;
|
|
@@ -2400,6 +2387,16 @@ function collectGraph(ctx, features) {
|
|
|
2400
2387
|
return graph.flows;
|
|
2401
2388
|
}
|
|
2402
2389
|
/**
|
|
2390
|
+
* Renders the whole graph as JSON. It reads the flows as data, so it works before `run()`.
|
|
2391
|
+
*
|
|
2392
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2393
|
+
* @param features - Features API: owners and slot contributions.
|
|
2394
|
+
* @returns The graph.
|
|
2395
|
+
*/
|
|
2396
|
+
function graphOf(ctx, features) {
|
|
2397
|
+
return describeGraph(collectGraph(ctx, features), features);
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2403
2400
|
* Validates the collected graph. Warnings are logged, problems stop `run()` in one error.
|
|
2404
2401
|
*
|
|
2405
2402
|
* @param ctx - Domain context of the flow plugin.
|
|
@@ -2412,6 +2409,169 @@ function checkGraph(ctx, features) {
|
|
|
2412
2409
|
if (report.problems.length > 0) throw new Error(report.problems.join("\n"));
|
|
2413
2410
|
}
|
|
2414
2411
|
//#endregion
|
|
2412
|
+
//#region src/plugins/flow/runner/bookmark.ts
|
|
2413
|
+
/**
|
|
2414
|
+
* Reads the main flow of the config.
|
|
2415
|
+
*
|
|
2416
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2417
|
+
* @param method - Name of the API method that needs it, for the message.
|
|
2418
|
+
* @returns The top-level flow.
|
|
2419
|
+
* @throws {Error} When no main flow was configured.
|
|
2420
|
+
*/
|
|
2421
|
+
function mainFlow(ctx, method) {
|
|
2422
|
+
const main = ctx.config.mainFlow;
|
|
2423
|
+
if (main === void 0) throw new Error(`[game] flow.${method}() needs a main flow.\n Pass it as pluginConfigs.flow.mainFlow.`);
|
|
2424
|
+
return main;
|
|
2425
|
+
}
|
|
2426
|
+
/**
|
|
2427
|
+
* Names a checkpoint of the graph, for the message of a refused bookmark. Only a rest node is
|
|
2428
|
+
* one: a transit node that carries the flag is no place a restore may be sent to.
|
|
2429
|
+
*
|
|
2430
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2431
|
+
* @param modules - Injected sibling APIs.
|
|
2432
|
+
* @returns The path of the first rest checkpoint, or the start of the main flow.
|
|
2433
|
+
*/
|
|
2434
|
+
function someCheckpoint(ctx, modules) {
|
|
2435
|
+
const main = mainFlow(ctx, "restore");
|
|
2436
|
+
for (const flow of collectGraph(ctx, modules.features).values()) for (const [name, entry] of Object.entries(flow.nodes)) if (isRestCheckpoint(entry)) return flow.id === main.id ? name : `${flow.id}/${name}`;
|
|
2437
|
+
return main.start;
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Looks up the node at a path. A sub-flow and a slot are no nodes: nothing runs at them.
|
|
2441
|
+
*
|
|
2442
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2443
|
+
* @param modules - Injected sibling APIs.
|
|
2444
|
+
* @param path - Path of a bookmark, or of its rest point.
|
|
2445
|
+
* @returns The node there, a rest node or a transit node, or `undefined` when the path names none.
|
|
2446
|
+
*/
|
|
2447
|
+
function nodeAt(ctx, modules, path) {
|
|
2448
|
+
const entry = findNode(mainFlow(ctx, "restore"), path, modules.features.contributions)?.entry;
|
|
2449
|
+
return entry?.kind === "node" ? entry : void 0;
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Finds the node a bookmark names.
|
|
2453
|
+
*
|
|
2454
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2455
|
+
* @param modules - Injected sibling APIs.
|
|
2456
|
+
* @param bookmark - The bookmark to enter.
|
|
2457
|
+
* @returns The node at the bookmark's path, a rest node or a transit node.
|
|
2458
|
+
* @throws {Error} When the path names no node of the graph.
|
|
2459
|
+
*/
|
|
2460
|
+
function bookmarkNode(ctx, modules, bookmark) {
|
|
2461
|
+
const node = nodeAt(ctx, modules, bookmark.path);
|
|
2462
|
+
if (node === void 0) throw new Error(`[game] The bookmark "${bookmark.path}" is not a node of the graph.\n Restore a rest node, for example the checkpoint "${someCheckpoint(ctx, modules)}".`);
|
|
2463
|
+
return node;
|
|
2464
|
+
}
|
|
2465
|
+
/**
|
|
2466
|
+
* Tells whether the graph is the one a bookmark was made for. The answer costs a description and
|
|
2467
|
+
* a hash of the whole graph, so one acceptance works it out once: see `askedOnce`.
|
|
2468
|
+
*
|
|
2469
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2470
|
+
* @param modules - Injected sibling APIs.
|
|
2471
|
+
* @param bookmark - The bookmark to compare with.
|
|
2472
|
+
* @returns True while the hash of `describe()` is the bookmark's.
|
|
2473
|
+
*/
|
|
2474
|
+
function sameGraph(ctx, modules, bookmark) {
|
|
2475
|
+
return graphHash(graphOf(ctx, modules.features)) === bookmark.graph;
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Makes a question that is worked out at most once: at the first ask, and never when nobody asks.
|
|
2479
|
+
*
|
|
2480
|
+
* @param ask - Works the answer out.
|
|
2481
|
+
* @returns The question. Every ask after the first gets the first answer.
|
|
2482
|
+
* @example
|
|
2483
|
+
* ```ts
|
|
2484
|
+
* const same = askedOnce(() => true);
|
|
2485
|
+
* same() && same(); // true, and the answer was worked out once
|
|
2486
|
+
* ```
|
|
2487
|
+
*/
|
|
2488
|
+
function askedOnce(ask) {
|
|
2489
|
+
const answer = { value: void 0 };
|
|
2490
|
+
return () => {
|
|
2491
|
+
answer.value ??= ask();
|
|
2492
|
+
return answer.value;
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Checks that a bookmark may be entered: a rest checkpoint always, any other node only while the
|
|
2497
|
+
* graph is the one the bookmark was made for. A transit node is never taken for a checkpoint,
|
|
2498
|
+
* whatever its flag says: it is a place inside a transition, not a place a save may stand on.
|
|
2499
|
+
*
|
|
2500
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2501
|
+
* @param modules - Injected sibling APIs.
|
|
2502
|
+
* @param bookmark - The bookmark to enter.
|
|
2503
|
+
* @param unchanged - Asks whether the graph is the bookmark's own. Not asked for a checkpoint.
|
|
2504
|
+
* @returns The node the bookmark names.
|
|
2505
|
+
* @throws {Error} When the path is no node, or the graph changed since the bookmark.
|
|
2506
|
+
*/
|
|
2507
|
+
function checkBookmark(ctx, modules, bookmark, unchanged) {
|
|
2508
|
+
const node = bookmarkNode(ctx, modules, bookmark);
|
|
2509
|
+
if (isRestCheckpoint(node)) return node;
|
|
2510
|
+
if (unchanged()) return node;
|
|
2511
|
+
throw new Error(`[game] The bookmark "${bookmark.path}" was made for another graph.\n Restore the checkpoint "${someCheckpoint(ctx, modules)}" instead.`);
|
|
2512
|
+
}
|
|
2513
|
+
/**
|
|
2514
|
+
* Builds the bookmark of the rest point before a transit node, for a bookmark that carries `rest`
|
|
2515
|
+
* and was made for another graph. That is one whose path names a transit node while the hash
|
|
2516
|
+
* differs, and one whose path names no node any more: a renamed or removed node is a changed
|
|
2517
|
+
* graph too. Only the position changes: the state stays the bookmark's.
|
|
2518
|
+
*
|
|
2519
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2520
|
+
* @param modules - Injected sibling APIs.
|
|
2521
|
+
* @param bookmark - The bookmark to enter.
|
|
2522
|
+
* @param unchanged - Asks whether the graph is the bookmark's own. Asked for a transit node only.
|
|
2523
|
+
* @returns The bookmark to enter instead, or `undefined` when the bookmark stands for itself.
|
|
2524
|
+
*/
|
|
2525
|
+
function restFallback(ctx, modules, bookmark, unchanged) {
|
|
2526
|
+
const rest = bookmark.rest;
|
|
2527
|
+
if (rest === void 0) return void 0;
|
|
2528
|
+
const node = nodeAt(ctx, modules, bookmark.path);
|
|
2529
|
+
if (node !== void 0 && (node.rest || unchanged())) return void 0;
|
|
2530
|
+
return {
|
|
2531
|
+
...bookmark,
|
|
2532
|
+
path: rest.path,
|
|
2533
|
+
input: rest.input
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Decides what the loop enters for a bookmark: the bookmark itself, or the rest point before it
|
|
2538
|
+
* when the graph changed under a bookmark that has one. Either one passes the same check, so the
|
|
2539
|
+
* fallback is entered only when it is a checkpoint, and the log says that it was taken.
|
|
2540
|
+
*
|
|
2541
|
+
* @param ctx - Domain context of the flow plugin.
|
|
2542
|
+
* @param modules - Injected sibling APIs.
|
|
2543
|
+
* @param bookmark - The bookmark to enter.
|
|
2544
|
+
* @returns The bookmark the loop enters, and whether its node is a rest node.
|
|
2545
|
+
* @throws {Error} When neither the bookmark nor its rest point may be entered.
|
|
2546
|
+
*/
|
|
2547
|
+
function acceptBookmark(ctx, modules, bookmark) {
|
|
2548
|
+
const unchanged = askedOnce(() => sameGraph(ctx, modules, bookmark));
|
|
2549
|
+
const fallback = restFallback(ctx, modules, bookmark, unchanged);
|
|
2550
|
+
const entered = fallback ?? bookmark;
|
|
2551
|
+
const node = checkBookmark(ctx, modules, entered, unchanged);
|
|
2552
|
+
if (fallback !== void 0) ctx.log.info("flow:restore-fell-back", {
|
|
2553
|
+
from: bookmark.path,
|
|
2554
|
+
to: fallback.path
|
|
2555
|
+
});
|
|
2556
|
+
return {
|
|
2557
|
+
bookmark: entered,
|
|
2558
|
+
rest: node.rest
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
//#endregion
|
|
2562
|
+
//#region src/plugins/flow/runner/loop-types.ts
|
|
2563
|
+
/** The stages of entering a node, in the order they run. */
|
|
2564
|
+
const stages = ["load", "scene"];
|
|
2565
|
+
/**
|
|
2566
|
+
* Does nothing. It stands in for an unsubscribe function until the real one arrives.
|
|
2567
|
+
*
|
|
2568
|
+
* @example
|
|
2569
|
+
* ```ts
|
|
2570
|
+
* const watch = { off: noop };
|
|
2571
|
+
* ```
|
|
2572
|
+
*/
|
|
2573
|
+
function noop() {}
|
|
2574
|
+
//#endregion
|
|
2415
2575
|
//#region src/plugins/flow/runner/position.ts
|
|
2416
2576
|
/**
|
|
2417
2577
|
* Resolves what the deepest frame points at.
|
|
@@ -3075,8 +3235,27 @@ function guideAllow(payload) {
|
|
|
3075
3235
|
};
|
|
3076
3236
|
}
|
|
3077
3237
|
/**
|
|
3238
|
+
* Refuses an effect a body starts after its node was aborted. The rejection counts as handled
|
|
3239
|
+
* from the start: a body that does not await the effect leaves no unhandled rejection behind,
|
|
3240
|
+
* and a body that awaits it still gets the reason.
|
|
3241
|
+
*
|
|
3242
|
+
* @param signal - The aborted signal of the node.
|
|
3243
|
+
* @returns A promise rejected with the abort reason.
|
|
3244
|
+
* @example
|
|
3245
|
+
* ```ts
|
|
3246
|
+
* refuseEffect(AbortSignal.abort("restore")); // a promise rejected with "restore"
|
|
3247
|
+
* ```
|
|
3248
|
+
*/
|
|
3249
|
+
function refuseEffect(signal) {
|
|
3250
|
+
const refused = Promise.reject(abortReason(signal));
|
|
3251
|
+
refused.catch(noop);
|
|
3252
|
+
return refused;
|
|
3253
|
+
}
|
|
3254
|
+
/**
|
|
3078
3255
|
* Builds the `fx` of a node context: awaited effects by call, cosmetic hints by `emit`. A `guide`
|
|
3079
|
-
* narrows the gate while it runs; the runner lifts the narrow when the node is left.
|
|
3256
|
+
* narrows the gate while it runs; the runner lifts the narrow when the node is left. A body that
|
|
3257
|
+
* was aborted keeps running, since the loop only stops waiting for it, so its `fx` starts nothing
|
|
3258
|
+
* any more: the gate and the narrow belong to the node the loop entered after it.
|
|
3080
3259
|
*
|
|
3081
3260
|
* @param modules - Injected sibling APIs.
|
|
3082
3261
|
* @param step - The node run in progress.
|
|
@@ -3084,12 +3263,13 @@ function guideAllow(payload) {
|
|
|
3084
3263
|
*/
|
|
3085
3264
|
function nodeFx(modules, step) {
|
|
3086
3265
|
/**
|
|
3087
|
-
* Awaits one effect.
|
|
3266
|
+
* Awaits one effect. After the abort of the node it rejects with the abort reason instead.
|
|
3088
3267
|
*
|
|
3089
3268
|
* @param descriptor - The descriptor the node awaits.
|
|
3090
3269
|
* @returns The answer, the handler's value, or `undefined`.
|
|
3091
3270
|
*/
|
|
3092
3271
|
const run = (descriptor) => {
|
|
3272
|
+
if (step.signal.aborted) return refuseEffect(step.signal);
|
|
3093
3273
|
if (descriptor.kind === "guide") {
|
|
3094
3274
|
const allow = guideAllow(descriptor.payload);
|
|
3095
3275
|
if (allow !== void 0) modules.gate.narrow(allow);
|
|
@@ -3599,19 +3779,23 @@ function enterSlot(ctx, modules, location, slot, safe) {
|
|
|
3599
3779
|
return "continue";
|
|
3600
3780
|
}
|
|
3601
3781
|
/**
|
|
3602
|
-
* Enters the node of a bookmark: the state is replaced, the rest
|
|
3603
|
-
* becomes the bookmark's path.
|
|
3782
|
+
* Enters the node of a bookmark, a rest node or a transit node: the state is replaced, the rest
|
|
3783
|
+
* point is marked and the position becomes the bookmark's path. The mark is made for a transit
|
|
3784
|
+
* node too. A restore leaves the whole document waiting for the provider, and a rollback of the
|
|
3785
|
+
* node would drop it unsent; and the frames of the transit node are what a failure of it must
|
|
3786
|
+
* retry, since the restored state belongs to them. `flow:rest` and the rest seam fire once for
|
|
3787
|
+
* every node entered here; only a rest node is announced as a checkpoint.
|
|
3604
3788
|
*
|
|
3605
3789
|
* @param ctx - Domain context of the flow plugin.
|
|
3606
3790
|
* @param modules - Injected sibling APIs; the features API resolves a path through a slot.
|
|
3607
3791
|
* @param bookmark - The bookmark to enter.
|
|
3608
3792
|
* @throws {Error} When the graph has no node at the bookmark's path.
|
|
3609
3793
|
*/
|
|
3610
|
-
function enterBookmark
|
|
3794
|
+
function enterBookmark(ctx, modules, bookmark) {
|
|
3611
3795
|
const state = ctx.state.runner;
|
|
3612
3796
|
const location = findNode(requireMainFlow(ctx), bookmark.path, modules.features.contributions);
|
|
3613
3797
|
loopSeam(state).restoring = void 0;
|
|
3614
|
-
if (location === void 0) throw new Error(`[game] The bookmark names no node "${bookmark.path}".\n Bookmark a
|
|
3798
|
+
if (location === void 0) throw new Error(`[game] The bookmark names no node "${bookmark.path}".\n Bookmark a node of the running graph.`);
|
|
3615
3799
|
ctx.deps.model.store.restore({
|
|
3616
3800
|
player: bookmark.player,
|
|
3617
3801
|
session: bookmark.session,
|
|
@@ -3622,10 +3806,9 @@ function enterBookmark$1(ctx, modules, bookmark) {
|
|
|
3622
3806
|
state.slotAfter = void 0;
|
|
3623
3807
|
state.restFrame = [...state.stack];
|
|
3624
3808
|
state.failures = 0;
|
|
3625
|
-
const entry = location.entry;
|
|
3626
3809
|
ctx.emit("flow:rest", {
|
|
3627
3810
|
path: bookmark.path,
|
|
3628
|
-
checkpoint:
|
|
3811
|
+
checkpoint: isRestCheckpoint(location.entry)
|
|
3629
3812
|
});
|
|
3630
3813
|
notifyRest(state, bookmark.path);
|
|
3631
3814
|
}
|
|
@@ -3643,7 +3826,7 @@ async function advance(ctx, modules, safe) {
|
|
|
3643
3826
|
if (stopped(state)) return "stop";
|
|
3644
3827
|
const restoring = state.seam?.restoring;
|
|
3645
3828
|
if (restoring !== void 0) {
|
|
3646
|
-
enterBookmark
|
|
3829
|
+
enterBookmark(ctx, modules, restoring);
|
|
3647
3830
|
return "continue";
|
|
3648
3831
|
}
|
|
3649
3832
|
const location = locateFrames(ctx, state.stack);
|
|
@@ -3685,28 +3868,55 @@ async function runLoop(ctx, modules) {
|
|
|
3685
3868
|
}
|
|
3686
3869
|
/**
|
|
3687
3870
|
* Restores a bookmark through the running loop: the active node is aborted with reason
|
|
3688
|
-
* `"restore"`, its transaction is discarded, the state is replaced and the node is entered.
|
|
3871
|
+
* `"restore"`, its transaction is discarded, the state is replaced and the node is entered. An
|
|
3872
|
+
* edge that was already committed still arrives first, and the rest point it reaches is not the
|
|
3873
|
+
* bookmark's: the promise waits for the turn in which the loop takes the bookmark. A loop that
|
|
3874
|
+
* ends before that turn ends the wait too, after a stop or a fatal error. Nothing is entered
|
|
3875
|
+
* then, and the bookmark is dropped: the seam outlives the loop, and the next `run()` would enter
|
|
3876
|
+
* a bookmark nobody waits for.
|
|
3689
3877
|
*
|
|
3690
3878
|
* @param ctx - Domain context of the flow plugin.
|
|
3691
3879
|
* @param _modules - Injected sibling APIs.
|
|
3692
3880
|
* @param bookmark - The bookmark to enter.
|
|
3693
|
-
* @returns A promise that resolves once the loop
|
|
3881
|
+
* @returns A promise that resolves once the loop entered the bookmark's node, before that node
|
|
3882
|
+
* ran: the rest seam fired. It also resolves when the loop ended first, with nothing entered.
|
|
3694
3883
|
* @throws {Error} When the loop is not running.
|
|
3695
3884
|
*/
|
|
3696
3885
|
function restorePosition(ctx, _modules, bookmark) {
|
|
3697
3886
|
const state = ctx.state.runner;
|
|
3698
|
-
|
|
3887
|
+
const running = state.running;
|
|
3888
|
+
if (running === void 0) throw new Error("[game] flow.restore() needs a running graph.\n Call flow.run() from the createApp onStart callback first.");
|
|
3699
3889
|
const seam = loopSeam(state);
|
|
3700
3890
|
const entered = new Promise((resolve) => {
|
|
3891
|
+
/**
|
|
3892
|
+
* Takes the listener off the rest seam.
|
|
3893
|
+
*
|
|
3894
|
+
* @returns True when it was still there, false when it had already left.
|
|
3895
|
+
*/
|
|
3896
|
+
const dropListener = () => {
|
|
3897
|
+
const index = seam.rest.indexOf(listener);
|
|
3898
|
+
if (index === -1) return false;
|
|
3899
|
+
seam.rest.splice(index, 1);
|
|
3900
|
+
return true;
|
|
3901
|
+
};
|
|
3701
3902
|
/**
|
|
3702
3903
|
* Resolves once, when the loop rests at the restored node.
|
|
3703
3904
|
*/
|
|
3704
3905
|
const listener = () => {
|
|
3705
|
-
|
|
3706
|
-
|
|
3906
|
+
if (seam.restoring !== void 0) return;
|
|
3907
|
+
dropListener();
|
|
3908
|
+
resolve();
|
|
3909
|
+
};
|
|
3910
|
+
/**
|
|
3911
|
+
* Ends the wait when the loop ended, resolved or rejected, before it took the bookmark.
|
|
3912
|
+
*/
|
|
3913
|
+
const finish = () => {
|
|
3914
|
+
if (!dropListener()) return;
|
|
3915
|
+
if (seam.restoring === bookmark) seam.restoring = void 0;
|
|
3707
3916
|
resolve();
|
|
3708
3917
|
};
|
|
3709
3918
|
seam.rest.push(listener);
|
|
3919
|
+
running.then(finish, finish);
|
|
3710
3920
|
});
|
|
3711
3921
|
seam.restoring = bookmark;
|
|
3712
3922
|
state.abort?.abort("restore");
|
|
@@ -3871,24 +4081,37 @@ function watchEnd(running) {
|
|
|
3871
4081
|
return end;
|
|
3872
4082
|
}
|
|
3873
4083
|
/**
|
|
3874
|
-
*
|
|
3875
|
-
* that ended
|
|
4084
|
+
* Watches for the next gate the loop opens. The listener leaves the seam when a gate wakes it. A
|
|
4085
|
+
* wait that something else ended calls `off()`: the gate that would take the listener off may
|
|
4086
|
+
* never come.
|
|
3876
4087
|
*
|
|
3877
4088
|
* @param seam - The seam of the running loop.
|
|
3878
|
-
* @returns
|
|
4089
|
+
* @returns The promise of the next open gate, and the way to stop watching.
|
|
3879
4090
|
*/
|
|
3880
4091
|
function nextGateOpen(seam) {
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
4092
|
+
const watch = { wake: void 0 };
|
|
4093
|
+
/**
|
|
4094
|
+
* Takes the listener off the seam. It may be gone already.
|
|
4095
|
+
*/
|
|
4096
|
+
const off = () => {
|
|
4097
|
+
const index = seam.gateOpen.indexOf(listener);
|
|
4098
|
+
if (index !== -1) seam.gateOpen.splice(index, 1);
|
|
4099
|
+
};
|
|
4100
|
+
/**
|
|
4101
|
+
* Wakes the waiting side once the loop opened a gate, and leaves.
|
|
4102
|
+
*/
|
|
4103
|
+
function listener() {
|
|
4104
|
+
off();
|
|
4105
|
+
watch.wake?.();
|
|
4106
|
+
}
|
|
4107
|
+
const promise = new Promise((resolve) => {
|
|
4108
|
+
watch.wake = resolve;
|
|
3891
4109
|
});
|
|
4110
|
+
seam.gateOpen.push(listener);
|
|
4111
|
+
return {
|
|
4112
|
+
promise,
|
|
4113
|
+
off
|
|
4114
|
+
};
|
|
3892
4115
|
}
|
|
3893
4116
|
/**
|
|
3894
4117
|
* Waits until the loop opens its gate. Between the edge it commits and the gate of the rest node
|
|
@@ -3902,7 +4125,41 @@ function nextGateOpen(seam) {
|
|
|
3902
4125
|
async function reachGate(ctx, seam, ended) {
|
|
3903
4126
|
if (ctx.state.gate.open !== void 0) return;
|
|
3904
4127
|
if (ended.done) return;
|
|
3905
|
-
|
|
4128
|
+
const opened = nextGateOpen(seam);
|
|
4129
|
+
try {
|
|
4130
|
+
await Promise.race([opened.promise, ended.settled]);
|
|
4131
|
+
} finally {
|
|
4132
|
+
opened.off();
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
/**
|
|
4136
|
+
* Waits until a transit node the loop was sent into shows what it waits for: the gate it opens,
|
|
4137
|
+
* or, when it opens none, the next rest point or the end of the loop. `restore` waits here after
|
|
4138
|
+
* it entered a transit bookmark, so its caller reads a state with the open gate in it. There is no
|
|
4139
|
+
* timeout: a node that opens no gate, never rests and never ends keeps the wait pending, as its
|
|
4140
|
+
* body would. Call it only after the loop entered the node: a gate opened before that belongs to
|
|
4141
|
+
* the node the restore aborted. After a loop that ended before the enter it returns at once: the
|
|
4142
|
+
* end of the loop is already there.
|
|
4143
|
+
*
|
|
4144
|
+
* @param ctx - Domain context of the flow plugin.
|
|
4145
|
+
* @returns A promise that resolves at the open gate, at a rest point or at the end of the loop.
|
|
4146
|
+
*/
|
|
4147
|
+
async function reachGateOrRest(ctx) {
|
|
4148
|
+
const running = ctx.state.runner.running;
|
|
4149
|
+
if (running === void 0 || ctx.state.gate.open !== void 0) return;
|
|
4150
|
+
const seam = loopSeam(ctx.state.runner);
|
|
4151
|
+
const opened = nextGateOpen(seam);
|
|
4152
|
+
const rested = watchRest(seam);
|
|
4153
|
+
try {
|
|
4154
|
+
await Promise.race([
|
|
4155
|
+
opened.promise,
|
|
4156
|
+
rested.next(),
|
|
4157
|
+
watchEnd(running).settled
|
|
4158
|
+
]);
|
|
4159
|
+
} finally {
|
|
4160
|
+
opened.off();
|
|
4161
|
+
rested.off();
|
|
4162
|
+
}
|
|
3906
4163
|
}
|
|
3907
4164
|
/**
|
|
3908
4165
|
* The error of a step the loop never reached, naming where it stands instead.
|
|
@@ -4020,14 +4277,17 @@ async function playRoute(ctx, modules, route, running) {
|
|
|
4020
4277
|
/**
|
|
4021
4278
|
* Walks a route through the running loop: restores `from` when given, switches to fast mode,
|
|
4022
4279
|
* waits until the loop rests at each step's `at` and answers through the gate, and substitutes
|
|
4023
|
-
* the result of every sub-flow node the route skips.
|
|
4024
|
-
*
|
|
4025
|
-
* it
|
|
4280
|
+
* the result of every sub-flow node the route skips. A rest node `from` names is entered before
|
|
4281
|
+
* the switch: the stages of that node see the mode of the caller. A transit node is entered
|
|
4282
|
+
* after the switch, so it never starts live and never shows its popup. The mode of the caller
|
|
4283
|
+
* is put back afterwards, also when the bookmark is refused or a step rejects, and a
|
|
4284
|
+
* substitution the route never reached is disarmed: it must not skip a sub-flow in the live play
|
|
4285
|
+
* that follows.
|
|
4026
4286
|
*
|
|
4027
4287
|
* @param ctx - Domain context of the flow plugin.
|
|
4028
4288
|
* @param modules - Injected sibling APIs: features, fx, gate, inbox.
|
|
4029
4289
|
* @param route - The player's answers and substituted results, in order.
|
|
4030
|
-
* @param start - Bookmark to enter first
|
|
4290
|
+
* @param start - Bookmark to enter first, the restore that enters it and the kind of its node.
|
|
4031
4291
|
* @returns The state the walk ended in.
|
|
4032
4292
|
* @throws {Error} Before `run()` was called, when the bookmark is refused, and when a step's `at`
|
|
4033
4293
|
* is never reached.
|
|
@@ -4035,10 +4295,13 @@ async function playRoute(ctx, modules, route, running) {
|
|
|
4035
4295
|
async function walkRoute(ctx, modules, route, start) {
|
|
4036
4296
|
const running = ctx.state.runner.running;
|
|
4037
4297
|
if (running === void 0) throw new Error("[game] flow.walk() needs a running graph.\n Call flow.run() from the createApp onStart callback first.");
|
|
4038
|
-
|
|
4298
|
+
const entersBeforeSwitch = start !== void 0 && !start.transit;
|
|
4299
|
+
const entersAfterSwitch = start !== void 0 && !entersBeforeSwitch;
|
|
4300
|
+
if (entersBeforeSwitch) await start.restore(start.from);
|
|
4039
4301
|
const previous = ctx.state.fx.mode;
|
|
4040
4302
|
modules.fx.setMode("fast");
|
|
4041
4303
|
try {
|
|
4304
|
+
if (entersAfterSwitch) await start.restore(start.from);
|
|
4042
4305
|
await playRoute(ctx, modules, route, running);
|
|
4043
4306
|
} finally {
|
|
4044
4307
|
loopSeam(ctx.state.runner).substitutions.clear();
|
|
@@ -4050,69 +4313,71 @@ async function walkRoute(ctx, modules, route, start) {
|
|
|
4050
4313
|
//#region src/plugins/flow/runner/api.ts
|
|
4051
4314
|
const noPayload = null;
|
|
4052
4315
|
/**
|
|
4053
|
-
*
|
|
4316
|
+
* Tells whether a frame points at a rest node.
|
|
4054
4317
|
*
|
|
4055
4318
|
* @param ctx - Domain context of the flow plugin.
|
|
4056
|
-
* @param
|
|
4057
|
-
* @returns
|
|
4058
|
-
* @throws {Error} When no main flow was configured.
|
|
4319
|
+
* @param frame - One level of a position.
|
|
4320
|
+
* @returns True when the entry of that frame is a node with `rest`.
|
|
4059
4321
|
*/
|
|
4060
|
-
function
|
|
4061
|
-
const
|
|
4062
|
-
|
|
4063
|
-
return main;
|
|
4322
|
+
function restsAt(ctx, frame) {
|
|
4323
|
+
const entry = ctx.state.runner.flows.get(frame.flow)?.nodes[frame.node];
|
|
4324
|
+
return entry !== void 0 && entry.kind === "node" && entry.rest;
|
|
4064
4325
|
}
|
|
4065
4326
|
/**
|
|
4066
|
-
*
|
|
4327
|
+
* Tells whether the graph stands on a rest node. `setMode` is legal there and before `run()`.
|
|
4067
4328
|
*
|
|
4068
4329
|
* @param ctx - Domain context of the flow plugin.
|
|
4069
|
-
* @
|
|
4070
|
-
* @returns The graph.
|
|
4330
|
+
* @returns True while the position is empty or points at a rest node.
|
|
4071
4331
|
*/
|
|
4072
|
-
function
|
|
4073
|
-
|
|
4332
|
+
function resting(ctx) {
|
|
4333
|
+
const frame = ctx.state.runner.stack.at(-1);
|
|
4334
|
+
return frame === void 0 || restsAt(ctx, frame);
|
|
4074
4335
|
}
|
|
4075
4336
|
/**
|
|
4076
|
-
*
|
|
4337
|
+
* Tells whether a transit node waits at the gate: an effect that takes answers is open inside
|
|
4338
|
+
* it. A rest node with its own gate open rests, so it does not count.
|
|
4077
4339
|
*
|
|
4078
4340
|
* @param ctx - Domain context of the flow plugin.
|
|
4079
|
-
* @
|
|
4080
|
-
* @returns The path of the first checkpoint, or the start of the main flow.
|
|
4341
|
+
* @returns True while the gate is open and the position is no rest node.
|
|
4081
4342
|
*/
|
|
4082
|
-
function
|
|
4083
|
-
|
|
4084
|
-
for (const flow of collectGraph(ctx, modules.features).values()) for (const [name, entry] of Object.entries(flow.nodes)) if (entry.kind === "node" && entry.checkpoint) return flow.id === main.id ? name : `${flow.id}/${name}`;
|
|
4085
|
-
return main.start;
|
|
4343
|
+
function waitsInTransit(ctx) {
|
|
4344
|
+
return ctx.state.gate.open !== void 0 && !resting(ctx);
|
|
4086
4345
|
}
|
|
4087
4346
|
/**
|
|
4088
|
-
*
|
|
4089
|
-
*
|
|
4347
|
+
* Reads the rest point before the transit node the graph stands in. The rest frame names a
|
|
4348
|
+
* transit node when the graph started at one or a restore entered one: no rest point came
|
|
4349
|
+
* before it then.
|
|
4090
4350
|
*
|
|
4091
4351
|
* @param ctx - Domain context of the flow plugin.
|
|
4092
|
-
* @
|
|
4093
|
-
* @param bookmark - The bookmark to enter.
|
|
4094
|
-
* @throws {Error} When the path is no rest node, or the graph changed since the bookmark.
|
|
4352
|
+
* @returns The last rest node and its input, or `undefined` when the rest frame names none.
|
|
4095
4353
|
*/
|
|
4096
|
-
function
|
|
4097
|
-
const
|
|
4098
|
-
|
|
4099
|
-
if (
|
|
4100
|
-
|
|
4101
|
-
|
|
4354
|
+
function restBefore(ctx) {
|
|
4355
|
+
const frames = ctx.state.runner.restFrame ?? [];
|
|
4356
|
+
const frame = frames.at(-1);
|
|
4357
|
+
if (frame === void 0 || !restsAt(ctx, frame)) return void 0;
|
|
4358
|
+
return {
|
|
4359
|
+
path: framePath(frames),
|
|
4360
|
+
input: frame.input
|
|
4361
|
+
};
|
|
4102
4362
|
}
|
|
4103
4363
|
/**
|
|
4104
|
-
* Makes a bookmark of the
|
|
4364
|
+
* Makes a bookmark of where the graph stands. While a transit node waits at the gate for the
|
|
4365
|
+
* answer of an effect it is that node, with the rest point before it; in every other moment it is
|
|
4366
|
+
* the last rest point. The state is the committed one in both cases: for a waiting transit node
|
|
4367
|
+
* that is the state it was entered with, since its own writes are still in the open transaction.
|
|
4105
4368
|
*
|
|
4106
4369
|
* @param ctx - Domain context of the flow plugin.
|
|
4107
4370
|
* @param modules - Injected sibling APIs.
|
|
4108
|
-
* @returns A
|
|
4371
|
+
* @returns A node plus a state that really existed there.
|
|
4109
4372
|
* @throws {Error} When the graph has no position yet.
|
|
4110
4373
|
*/
|
|
4111
4374
|
function makeBookmark(ctx, modules) {
|
|
4112
4375
|
const state = ctx.state.runner;
|
|
4113
|
-
const
|
|
4376
|
+
const waiting = waitsInTransit(ctx);
|
|
4377
|
+
const frames = waiting ? state.stack : state.restFrame ?? state.stack;
|
|
4114
4378
|
if (frames.length === 0) throw new Error("[game] flow.bookmark() needs a position.\n Call it while the graph rests at a node.");
|
|
4115
4379
|
const snapshot = ctx.deps.model.store.snapshot();
|
|
4380
|
+
const rest = waiting ? restBefore(ctx) : void 0;
|
|
4116
4381
|
return {
|
|
4117
4382
|
path: framePath(frames),
|
|
4118
4383
|
input: frames.at(-1)?.input ?? noPayload,
|
|
@@ -4122,35 +4387,71 @@ function makeBookmark(ctx, modules) {
|
|
|
4122
4387
|
seed: snapshot.rng.seed,
|
|
4123
4388
|
streams: { ...snapshot.rng.streams }
|
|
4124
4389
|
},
|
|
4125
|
-
graph: graphHash(graphOf(ctx, modules))
|
|
4390
|
+
graph: graphHash(graphOf(ctx, modules.features)),
|
|
4391
|
+
...rest === void 0 ? {} : { rest }
|
|
4126
4392
|
};
|
|
4127
4393
|
}
|
|
4128
4394
|
/**
|
|
4129
|
-
*
|
|
4130
|
-
*
|
|
4131
|
-
*
|
|
4395
|
+
* Sends the running loop to a bookmark that passed the check, the short way a walk needs: the
|
|
4396
|
+
* promise resolves as soon as the loop stands at the node, before the node ran. It stays an
|
|
4397
|
+
* `async` function of its own: the microtask turn it adds lets the loop start the stages of a
|
|
4398
|
+
* rest node before the walk goes on and switches to fast mode. A loop that ends first resolves it
|
|
4399
|
+
* too, with nothing entered: the walk then finds its route not reached.
|
|
4132
4400
|
*
|
|
4133
4401
|
* @param ctx - Domain context of the flow plugin.
|
|
4134
4402
|
* @param modules - Injected sibling APIs.
|
|
4135
|
-
* @param bookmark - The bookmark
|
|
4136
|
-
* @returns A promise that resolves once the loop
|
|
4137
|
-
* @throws {Error} When the bookmark names no rest node of this graph.
|
|
4403
|
+
* @param bookmark - The accepted bookmark.
|
|
4404
|
+
* @returns A promise that resolves once the loop entered the node, or ended first.
|
|
4138
4405
|
*/
|
|
4139
|
-
async function
|
|
4140
|
-
checkBookmark(ctx, modules, bookmark);
|
|
4406
|
+
async function enterAccepted(ctx, modules, bookmark) {
|
|
4141
4407
|
await restorePosition(ctx, modules, bookmark);
|
|
4142
4408
|
}
|
|
4143
4409
|
/**
|
|
4144
|
-
*
|
|
4410
|
+
* Walks a route from a bookmark. The bookmark passes the check `restore` runs, so both ways into
|
|
4411
|
+
* a position refuse the same bookmarks and take the same fallback. The walk is told which kind of
|
|
4412
|
+
* node it enters: a rest node is entered before the switch to fast mode, a transit node after it.
|
|
4145
4413
|
*
|
|
4146
4414
|
* @param ctx - Domain context of the flow plugin.
|
|
4147
|
-
* @
|
|
4415
|
+
* @param modules - Injected sibling APIs.
|
|
4416
|
+
* @param route - The player's answers and substituted results, in order.
|
|
4417
|
+
* @param from - The bookmark to enter first.
|
|
4418
|
+
* @returns The state the walk ended in.
|
|
4419
|
+
* @throws {Error} When the bookmark may not be entered, and when a step's `at` is never reached.
|
|
4148
4420
|
*/
|
|
4149
|
-
function
|
|
4150
|
-
const
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4421
|
+
async function walkFrom(ctx, modules, route, from) {
|
|
4422
|
+
const accepted = acceptBookmark(ctx, modules, from);
|
|
4423
|
+
return walkRoute(ctx, modules, route, {
|
|
4424
|
+
/**
|
|
4425
|
+
* Enters the accepted bookmark. The walk never waits for a gate here: it answers the gates
|
|
4426
|
+
* of its own route.
|
|
4427
|
+
*
|
|
4428
|
+
* @param bookmark - The bookmark to enter.
|
|
4429
|
+
* @returns A promise that resolves once the loop entered its node.
|
|
4430
|
+
*/
|
|
4431
|
+
restore: (bookmark) => enterAccepted(ctx, modules, bookmark),
|
|
4432
|
+
from: accepted.bookmark,
|
|
4433
|
+
transit: !accepted.rest
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
/**
|
|
4437
|
+
* Restores a bookmark, the way the plugin's `restore` does. A rest node is entered as `walk`
|
|
4438
|
+
* enters it. A transit node shows nothing until it ran up to its gate, so for one the promise
|
|
4439
|
+
* waits on: for the gate the node opens, or the rest point or the end of the loop it reaches
|
|
4440
|
+
* without one. The wait starts only after the loop entered the node, so a gate the aborted old
|
|
4441
|
+
* node still opened is not taken for the new one. A loop that ends before it entered the node
|
|
4442
|
+
* ends both waits: nothing is entered, and the wait for the gate returns at once.
|
|
4443
|
+
*
|
|
4444
|
+
* @param ctx - Domain context of the flow plugin.
|
|
4445
|
+
* @param modules - Injected sibling APIs.
|
|
4446
|
+
* @param bookmark - The bookmark to enter.
|
|
4447
|
+
* @returns A promise that resolves once a rest node is entered, or a transit node waits, or the
|
|
4448
|
+
* loop ended first.
|
|
4449
|
+
* @throws {Error} When the bookmark may not be entered, and before `run()`.
|
|
4450
|
+
*/
|
|
4451
|
+
async function restoreBookmark(ctx, modules, bookmark) {
|
|
4452
|
+
const accepted = acceptBookmark(ctx, modules, bookmark);
|
|
4453
|
+
await restorePosition(ctx, modules, accepted.bookmark);
|
|
4454
|
+
if (!accepted.rest) await reachGateOrRest(ctx);
|
|
4154
4455
|
}
|
|
4155
4456
|
/**
|
|
4156
4457
|
* Creates the runner API: `run` owns the one loop, `walk` and `restore` enter a position through
|
|
@@ -4179,21 +4480,12 @@ function createRunnerApi(ctx, modules) {
|
|
|
4179
4480
|
},
|
|
4180
4481
|
walk: (route, options) => {
|
|
4181
4482
|
const from = options?.from;
|
|
4182
|
-
if (from === void 0) return walkRoute(ctx, modules, route);
|
|
4183
|
-
return
|
|
4184
|
-
/**
|
|
4185
|
-
* Enters the bookmark the walk starts from, checked the way `restore` checks it.
|
|
4186
|
-
*
|
|
4187
|
-
* @param bookmark - The bookmark to enter.
|
|
4188
|
-
* @returns A promise that resolves once the loop rests at its node.
|
|
4189
|
-
*/
|
|
4190
|
-
restore: (bookmark) => enterBookmark(ctx, modules, bookmark),
|
|
4191
|
-
from
|
|
4192
|
-
});
|
|
4483
|
+
if (from === void 0 || state.running === void 0) return walkRoute(ctx, modules, route);
|
|
4484
|
+
return walkFrom(ctx, modules, route, from);
|
|
4193
4485
|
},
|
|
4194
4486
|
bookmark: () => makeBookmark(ctx, modules),
|
|
4195
|
-
restore: (bookmark) =>
|
|
4196
|
-
describe: () => graphOf(ctx, modules),
|
|
4487
|
+
restore: (bookmark) => restoreBookmark(ctx, modules, bookmark),
|
|
4488
|
+
describe: () => graphOf(ctx, modules.features),
|
|
4197
4489
|
state: () => readState(ctx),
|
|
4198
4490
|
history: () => [...state.journal],
|
|
4199
4491
|
setMode: (mode) => {
|
|
@@ -4521,7 +4813,7 @@ const flowPlugin = /*#__PURE__*/ createPlugin$1("flow", {
|
|
|
4521
4813
|
},
|
|
4522
4814
|
events: (register) => register.map({
|
|
4523
4815
|
"flow:edge": "An edge was taken and its state committed",
|
|
4524
|
-
"flow:rest": "The graph reached a rest node",
|
|
4816
|
+
"flow:rest": "The graph reached a rest node, or entered the node of a bookmark",
|
|
4525
4817
|
"flow:error": "A node failed and the graph rolled back"
|
|
4526
4818
|
}),
|
|
4527
4819
|
createState: createFlowState,
|
|
@@ -17236,8 +17528,32 @@ async function untilAborted(ctx, bundle, signal) {
|
|
|
17236
17528
|
if (load.failed && !signal.aborted) throw load.error;
|
|
17237
17529
|
}
|
|
17238
17530
|
/**
|
|
17531
|
+
* Takes the expected scene and clears the record, so one `expect` mounts one scene.
|
|
17532
|
+
*
|
|
17533
|
+
* @param ctx - Domain context of the plugin.
|
|
17534
|
+
* @returns The scene id that was recorded or expected, or `undefined` when there was none.
|
|
17535
|
+
*/
|
|
17536
|
+
function takePending(ctx) {
|
|
17537
|
+
const target = ctx.state.pending;
|
|
17538
|
+
ctx.state.pending = void 0;
|
|
17539
|
+
return target;
|
|
17540
|
+
}
|
|
17541
|
+
/**
|
|
17542
|
+
* Tells whether the node is a transit node of a fast walk: the walk passes through it and builds
|
|
17543
|
+
* no scene there.
|
|
17544
|
+
*
|
|
17545
|
+
* @param node - The node being entered.
|
|
17546
|
+
* @param run - The runner's mode and signal.
|
|
17547
|
+
* @returns True for a node without `rest` entered in fast mode.
|
|
17548
|
+
*/
|
|
17549
|
+
function isFastTransit(node, run) {
|
|
17550
|
+
return run.mode === "fast" && !node.rest;
|
|
17551
|
+
}
|
|
17552
|
+
/**
|
|
17239
17553
|
* Reads the scene the node asks for. In fast mode a transit node only records it: the walk passes
|
|
17240
|
-
* through many of them and the picture is built once, at the rest point.
|
|
17554
|
+
* through many of them and the picture is built once, at the rest point. In live mode a transit
|
|
17555
|
+
* node without a scene of its own takes the expected one, so a restore at a popup that waits for
|
|
17556
|
+
* its answer builds the scene under it; one with its own scene leaves the record alone.
|
|
17241
17557
|
*
|
|
17242
17558
|
* @param ctx - Domain context of the plugin.
|
|
17243
17559
|
* @param node - The node being entered.
|
|
@@ -17246,32 +17562,32 @@ async function untilAborted(ctx, bundle, signal) {
|
|
|
17246
17562
|
*/
|
|
17247
17563
|
function targetOf(ctx, node, run) {
|
|
17248
17564
|
const state = ctx.state;
|
|
17249
|
-
if (run
|
|
17250
|
-
|
|
17565
|
+
if (isFastTransit(node, run)) {
|
|
17566
|
+
state.pending = node.scene ?? state.pending;
|
|
17251
17567
|
return;
|
|
17252
17568
|
}
|
|
17253
|
-
if (!node.rest) return node.scene;
|
|
17569
|
+
if (!node.rest) return node.scene ?? takePending(ctx);
|
|
17254
17570
|
const target = node.scene ?? state.pending;
|
|
17255
17571
|
state.pending = void 0;
|
|
17256
17572
|
if (target === void 0 && state.current === void 0) ctx.log.warn("scenes: a rest node was entered with no scene", { path: node.path });
|
|
17257
17573
|
return target;
|
|
17258
17574
|
}
|
|
17259
17575
|
/**
|
|
17260
|
-
* Reads the scene an `over` node asks for. An over node
|
|
17261
|
-
*
|
|
17262
|
-
*
|
|
17576
|
+
* Reads the scene an `over` node asks for. An over node takes the expected scene and clears the
|
|
17577
|
+
* record, whatever is mounted, so a restore at a popup builds the scene under it, whether the
|
|
17578
|
+
* popup rests or waits for its answer. With nothing expected it switches nothing. In fast mode an
|
|
17579
|
+
* over transit node takes nothing and leaves the record alone: a walk builds no scene on the way.
|
|
17580
|
+
* A scene the over node names itself is only warned about.
|
|
17263
17581
|
*
|
|
17264
17582
|
* @param ctx - Domain context of the plugin.
|
|
17265
17583
|
* @param node - The over node being entered.
|
|
17584
|
+
* @param run - The runner's mode and signal.
|
|
17266
17585
|
* @returns The expected scene id, or `undefined` when nothing has to happen.
|
|
17267
17586
|
*/
|
|
17268
|
-
function overTargetOf(ctx, node) {
|
|
17269
|
-
const state = ctx.state;
|
|
17587
|
+
function overTargetOf(ctx, node, run) {
|
|
17270
17588
|
if (node.scene !== void 0) ctx.log.warn("scenes: an over node cannot name a scene", { path: node.path });
|
|
17271
|
-
if (
|
|
17272
|
-
|
|
17273
|
-
state.pending = void 0;
|
|
17274
|
-
return target;
|
|
17589
|
+
if (isFastTransit(node, run)) return void 0;
|
|
17590
|
+
return takePending(ctx);
|
|
17275
17591
|
}
|
|
17276
17592
|
/**
|
|
17277
17593
|
* Builds the scene in one synchronous block: the layers, then the projections of the old scene
|
|
@@ -17298,9 +17614,9 @@ function apply(ctx, scene) {
|
|
|
17298
17614
|
});
|
|
17299
17615
|
}
|
|
17300
17616
|
/**
|
|
17301
|
-
* The `scene` stage of entering a node: the whole switch. An `over` node
|
|
17302
|
-
* scene, a node without a scene keeps the current one, and an
|
|
17303
|
-
* touching anything.
|
|
17617
|
+
* The `scene` stage of entering a node: the whole switch. An `over` node switches only to an
|
|
17618
|
+
* expected scene, a node without a scene and with nothing expected keeps the current one, and an
|
|
17619
|
+
* aborted node returns without touching anything.
|
|
17304
17620
|
*
|
|
17305
17621
|
* @param ctx - Domain context of the plugin.
|
|
17306
17622
|
* @param node - The node being entered.
|
|
@@ -17317,7 +17633,7 @@ function apply(ctx, scene) {
|
|
|
17317
17633
|
* ```
|
|
17318
17634
|
*/
|
|
17319
17635
|
async function enterScene(ctx, node, run) {
|
|
17320
|
-
const target = node.over ? overTargetOf(ctx, node) : targetOf(ctx, node, run);
|
|
17636
|
+
const target = node.over ? overTargetOf(ctx, node, run) : targetOf(ctx, node, run);
|
|
17321
17637
|
if (target === void 0 || target === ctx.state.current) return;
|
|
17322
17638
|
const scene = ctx.state.scenes.get(target);
|
|
17323
17639
|
if (scene === void 0) throw new Error(`[game] Scene "${target}" is not registered.\n List it in the scenes of a feature.`);
|