Describe Keep state on Run behavior (#229)

Behaviors gets the Keep state point, State machines notes that kept calls keep their local machines, Development counts 1385 checks.
admin committed 2026-09-25 14:41:32 +02:00
1 parent 010259abb3
commit 151ea288ab
3 files changed
+15 -7

No files matched your search

+10 -3
@@ -211,9 +211,16 @@ shown on the node and editable in the inspector.
removing takes the edges hanging on it in *every* graph that calls the behavior. A call is a removing takes the edges hanging on it in *every* graph that calls the behavior. A call is a
**call, not an inlining**: the nested graph runs in its own pulse cache with a fresh copy of its **call, not an inlining**: the nested graph runs in its own pulse cache with a fresh copy of its
variable defaults, so values travel in and out through the ports and nothing else. That makes a variable defaults, so values travel in and out through the ports and nothing else. That makes a
call repeatable and keeps a nested behavior free of hidden state — for state, use a behavior call repeatable and keeps a nested behavior free of hidden state. A behavior that would run
object in the scene (see *Cross-object state* below). A behavior that would run itself raises an itself raises an error instead of looping; there is one start and one end node per behavior.
error instead of looping; there is one start and one end node per behavior. - **Keep state** (a checkbox on the calling node, off by default): the nested behavior then picks
up its variables and its local state machines where the last call **from this node, on this
object** left them. Two call nodes running the same behavior keep a state each, and so do two
objects running the same graph; a kept call inside a kept call has one per outer node. It follows
today's declarations: a variable added since starts at its default, a removed one is dropped, and
one whose kind changed starts over. The state lives as long as the object does in the run and is
never saved. For state shared between objects, use a Variables asset or a behavior object in the
scene (see *Cross-object state* below).
Three ways in and out of a call: **drag** a behavior out of the asset panel onto the graph and it Three ways in and out of a call: **drag** a behavior out of the asset panel onto the graph and it
lands as a call where you let go; **double-click** a call node to open the graph it runs. The lands as a call where you let go; **double-click** a call node to open the graph it runs. The
way down stays above the graph as a **breadcrumb** (`Game > Move > Other`): click a crumb to go way down stays above the graph as a **breadcrumb** (`Game > Move > Other`): click a crumb to go
+1 -1
@@ -14,7 +14,7 @@ npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output ./build.sh # the same under `set -o pipefail`, for callers that pipe the output
php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts) php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts)
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job) php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job)
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1384 editor checks, screenshots in /tmp/gm-e2e-*.png node tools/seed-e2e.mjs && node tools/e2e.mjs # 1385 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result ./e2e.sh status # live progress, or the last run's result
+4 -3
@@ -146,8 +146,9 @@ The rules, all of them worth knowing before wiring a graph on them:
whose events keep moving the machine stops the run with an endless-loop error, like the step limit. whose events keep moving the machine stops the run with an endless-loop error, like the step limit.
- Who hears it follows the **scope**: a local machine is heard by the instance that moved it alone, - Who hears it follows the **scope**: a local machine is heard by the instance that moved it alone,
a global one by **every** instance with a matching event node — which is the point of sharing a a global one by **every** instance with a matching event node — which is the point of sharing a
state. A move a nested `Run behavior` makes to a *local* machine dies with the call and is not state. A move a nested `Run behavior` makes to a *local* machine is not reported, and it dies
reported; a move it makes to a global one is a move like any other. with the call unless the call node has **Keep state** on (see *Behaviors*); a move it makes to a
global one is a move like any other.
## The current state while playing ## The current state while playing
@@ -162,7 +163,7 @@ gone the moment the run stops. Play repaints the tab the way it repaints the Beh
**A local machine's current state is the behavior instance's own**, kept beside its variables **A local machine's current state is the behavior instance's own**, kept beside its variables
(`NodeContext.states`, machine id → state id): two crates running the same behavior are in (`NodeContext.states`, machine id → state id): two crates running the same behavior are in
states of their own, and a behavior run through `Run behavior` starts in no state, as it starts states of their own, and a behavior run through `Run behavior` starts in no state, as it starts
with fresh variables. A **global** one is kept for the run instead (`SharedState.states`, built with fresh variables, unless the call keeps its state. A **global** one is kept for the run instead (`SharedState.states`, built
once per run in `core/graph/sharedState.ts`) and every instance reads and moves that one — which once per run in `core/graph/sharedState.ts`) and every instance reads and moves that one — which
bag a node touches is decided by the machine's scope, not by the node (`stateBag`). Neither bag a node touches is decided by the machine's scope, not by the node (`stateBag`). Neither
survives a stop: both begin the next Play at the start state again, or in no state where there is survives a stop: both begin the next Play at the start state again, or in no state where there is