tecs.sequence
Snapshot-safe programs, waits, ownership, actions, and tween timelines.
Sequences store control flow as data. A snapshot, a rewind, or a reload can therefore preserve a playback's program, instruction pointer, waits, bindings, and parameters. A coroutine cannot be serialized, so the sequencer compiles authored steps into instructions and stores each playback's cursor, wait, and branch as ordinary data. nupp.tasks owns the world's logical update; it cannot own a playback that has to survive a save file.
Programs and actions#
Register an action on the world, name it from a program, and bind each playback to the entities it acts on:
tecs.sequence.registerAction(world, "game.lockControls", function(_world, _context): nil
controlsLocked = true
end)
local intro <const> = tecs.sequence.define("game.bossIntro", {
tecs.sequence.call("game.lockControls"),
tecs.sequence.wait(1.5),
tecs.sequence.emit("boss.ready"),
})
local playback <const> = tecs.sequence.play(world, intro, {owner = encounter, bindings = {boss = bossEntity}})Program names are a snapshot compatibility surface. Use stable qualified names. Redefining a name publishes a new version: running playbacks keep the version they started on, and later play calls use the new one. Action bodies resolve by name when they run, so a reload can replace action code without moving a live instruction pointer.
Actions run synchronously and cannot suspend. An action that raises faults its playback after preserving the mutations it already completed.
Clocks and waits#
"fixed" advances once per fixed step and serves deterministic gameplay. "frame" advances once per gameplay frame and serves scripted input, which needs exactly one decision per frame however many fixed steps that frame runs. "presentation" advances once per frame carrying the frame's real elapsed time, and serves values the simulation never reads.
Durations are seconds. Step and tick values count whole advances of the program's clock. Every positive duration waits at least one tick, and waitSteps(0) yields until the next tick and gives a loop a fresh instruction budget.
ecs.newWorld({nominalFrameTime = seconds}), or Application.Config.world, sets the duration used to convert frame and presentation waits to ticks. It is positive and finite and defaults to 1/60 in both windowed and headless sessions. setNominalFrameTime(world, seconds) changes future conversions in that world only; already scheduled tick deadlines do not move. Restore a saved playback into a world configured for its intended cadence.
sourceKey(key, component, fields) resolves a moving tween destination through World.byKey on each evaluation, including after snapshot load or a new entity claims the key. TrackingTarget.key does the same when its entity is zero. An explicit entity id takes precedence and a stale id does not fall back to the key. A missing source contributes zero, as other missing tracking sources do.
Ownership#
owner ties a playback to an entity, and despawning that entity cancels the playback. A named channel gives one owner a replaceable slot, so starting a second playback on the same channel cancels the first.
Pause uses named holders, and a playback resumes only after every holder releases it. A branch inherits owner, bindings, parameters, budget, and pause state, and cannot outlive its parent.
Each playback receives a bounded instruction budget per tick and faults with budgetExceeded rather than hanging the frame.
Timelines#
A timeline compiles tweens into the same runtime and keeps the same ownership, clock, channel, pause, and snapshot rules. Targets interpolate component fields. A parallel block shares a start time, and a sequential block starts after the one before it ends.
Program construction#
A sequence is written by calling the node constructors. The API has no generic row decoder or shared data schema. A game that stores sequences as data decodes its own schema into these constructors.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Action | type | Defines a registered effect that runs synchronously and cannot suspend. |
ActionContext | record | Provides the context a registered action receives. |
Awaitable | record | Defines the response an await step's provider must give. |
ClockId | type | Selects the clock a program runs against. |
DefineOptions | type | Configures define. |
EasingFunction | type | Maps normalized input progress to eased output progress. |
EasingName | type | Names a built-in easing curve. |
Emission | record | The payload an emit step delivers at address zero. |
EntityRef | record | References an entity supplied at play time. |
Evaluator | record | Defines the evaluator an eval step runs every tick. |
FaultReason | type | Explains why a cursor stopped running. |
Handle | type | Identifies one playback through a generation-checked reference that remains meaningful across a snapshot load. |
Node | record | Represents one authored step produced by a node constructor. |
PlaybackMode | type | Selects how a timeline repeats. |
PlaybackState | type | Describes the lifecycle state of one playback. |
PlayOptions | type | Configures play. |
Program | record | Represents a compiled, immutable program shared by every playback of it. |
QueryCondition | type | Describes the condition awaited by a waitQuery step. |
RunOptions | type | Configures a nested tweenRun operation. |
Status | record | Reports a playback's current state. |
Step | record | Describes one step a playback reaches without branching. |
Target | record | Describes the component and one to four numeric fields a timeline operation writes. |
TargetName | type | Names a built-in component-field target. |
TimelineNode | record | Represents one authored timeline operation. |
TimelineOptions | type | Configures timeline. |
TrackingTarget | record | Selects a dynamic tracking-source entity. |
TrackSource | record | Provides a live component-field tracking source. |
TweenOutcome | type | Reports how the playback awaited by waitTween ended. |
Functions
| Function | Kind | Description |
|---|---|---|
activeCount | function | Returns the number of live playbacks, for tests and diagnostics. |
await | function | Builds a step that waits for work outside the sequencer to finish. |
bind | function | References an entity supplied through PlayOptions.bindings. |
call | function | Builds a step that runs a registered action. |
cancel | function | Stops a playback and releases its cursor. |
cancelOwnedBy | function | Cancels every playback owned by an entity. |
currentStep | function | Returns a clock's current tick as counted by the sequencer. |
define | function | Compiles a program under a stable symbolic name. |
disassemble | function | Renders a program as readable instructions. |
easing | function | Returns a built-in easing curve by name. |
emit | function | Builds a step that emits a sequence event at address zero. |
eval | function | Builds a step that evaluates a registered evaluator every tick until it finishes. |
fork | function | Builds a step that starts a branch alongside the rest of the program. |
hasAction | function | Reports whether a world has registered an action name. |
hasQuery | function | Reports whether a world has registered a query name. |
join | function | Builds a step that waits for every branch not yet joined. |
loop | function | Builds a step that repeats a block. |
parallel | function | Builds a step that forks several blocks and waits for all of them. |
pause | function | Suspends a playback, its branches, and anything it started. |
play | function | Starts a program. |
playbacks | function | Returns handles for every live playback in a stable order. |
playTween | function | Builds a step that plays a registered timeline on a bound entity. |
plugin | function | Installs the sequencer into a world. |
program | function | Returns the newest version of a defined program, or a requested version. |
programNames | function | Returns the names of every defined program, sorted. |
registerAction | function | Registers an action a call step can name. |
registerAwaitable | function | Registers a provider an await step can name. |
registerEvaluator | function | Registers an evaluator an eval step can name. |
registerQuery | function | Registers a query a waitQuery step can name. |
resume | function | Releases one holder's claim on a paused playback. |
setInstructionBudget | function | Sets the per-tick instruction budget for playbacks that ask for none. |
setNominalFrameTime | function | Sets the nominal frame duration a frame or presentation wait converts seconds against. |
signal | function | Raises a named signal and wakes every playback blocked on it. |
signalOnEvent | function | Raises a signal whenever an event emits at address zero. |
sourceKey | function | Reads a moving destination from the live entity holding a world key. |
sourceOwn | function | Reads a moving destination from the animated entity itself. |
sourceRelated | function | Reads a moving destination from the entity a relationship points at. |
sourceTracking | function | Reads a moving destination from the entity named by the animated entity's TrackingTarget. |
status | function | Returns a playback's status. |
targetAngle | function | Interpolates one field as an angle, taking the short way round. |
targetField | function | Interpolates one numeric field of a component. |
targetField2 | function | Interpolates two numeric fields of a component together. |
timeline | function | Compiles a tween timeline into a program under a stable name. |
tweenAdjust | function | Builds a timeline operation that interpolates by a delta from the starting value. |
tweenEmit | function | Builds a timeline operation that emits a named sequence event. |
tweenParallel | function | Builds a timeline operation that runs blocks concurrently and ends with the longest. |
tweenRun | function | Builds a timeline operation that runs a nested timeline from its own spec. |
tweenTo | function | Builds a timeline operation that interpolates to an absolute destination. |
tweenTrack | function | Builds a timeline operation that interpolates toward a destination that keeps moving. |
tweenWait | function | Builds a timeline operation that advances the cursor without changing anything. |
upcoming | function | Returns the actions and emissions a playback will certainly perform next, and their timing. |
wait | function | Builds a step that waits a duration in seconds. |
waitingOn | function | Returns how many playbacks currently wait on a signal name. |
waitQuery | function | Builds a step that blocks until a registered query matches or stops matching. |
waitSignal | function | Builds a step that blocks until signal raises a named signal. |
waitSteps | function | Builds a step that waits a whole number of ticks of the program's clock. |
waitTween | function | Builds a step that waits for the playback the most recent playTween started. |
Values
| Value | Kind | Description |
|---|---|---|
DEFAULT_BUDGET | variable | The per-tick instruction budget a world gives a playback that asks for none. |
TrackingTarget | variable | The process-wide component a tweenTrack tracking source reads. |
Types#
Actiontype#
type Action = function(exclusive world: entityworld.World, ctx: ActionContext): nilDefines a registered effect that runs synchronously and cannot suspend.
ActionContextrecord#
record ActionContext
handle: Handle
owner: integer
args: {any}
params: {[string]: any}?
cursor: Cursor?
entity: function(borrows self: ActionContext, borrows world: entityworld.World, name: string): integer?
bind: function(exclusive self: ActionContext, name: string, entity: integer?): nil
endProvides the context a registered action receives.
Methods
entity#
entity: function(borrows self: ActionContext, borrows world: entityworld.World, name: string): integer?Resolves a named binding to a live entity.
The world is a parameter rather than a field of this record, because the sequencer reuses one context across calls and may not keep a view of the world the running tick borrows. An action already has the world it ran under.
Arguments
| Name | Type | Description |
|---|---|---|
borrows self | ActionContext | the context the running action received |
borrows world | entityworld.World | the world the running action received |
name | string | the binding name supplied through |
Returns
| Type | Description |
|---|---|
integer? | the entity id, or nil when the binding is missing or its entity is no longer alive |
bind#
bind: function(exclusive self: ActionContext, name: string, entity: integer?): nilNames an entity for the steps that follow, so an action that creates one can hand it on.
Bindings travel with the playback and survive a snapshot. Passing nil forgets the name.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive self | ActionContext | the context the running action received |
name | string | the binding name later steps resolve |
entity | integer? | the entity id to bind, or nil to forget the name |
Returns
| Type | Description |
|---|---|
nil |
Fields
handle#
handle: HandleRead-only. Identifies the playback, for status or cancel from inside an action.
args#
args: {any}Read-only. Carries the call node's constants with every entity reference already resolved to an entity id.
cursor#
cursor: Cursor?Engine-owned. Points at the running cursor. Game code uses entity and bind instead of reading this.
Awaitablerecord#
record Awaitable
isPending: function(borrows world: entityworld.World, entity: integer, key: string?): boolean
setPaused: (function(exclusive world: entityworld.World, entity: integer, key: string?, paused: boolean): nil)?
endDefines the response an await step's provider must give.
Methods
isPending#
Caller-writable. Reports whether the work named by entity and key is still going. The sequencer calls this at a fixed-step boundary for every cursor parked on the name, so it must be cheap and must not mutate the world. Reporting false for work that never started is correct: a program waiting on an animation that is already over should carry on rather than hang.
Arguments
| Name | Type | Description |
|---|---|---|
borrows world | entityworld.World | |
entity | integer | |
key | string? |
Returns
| Type | Description |
|---|---|
boolean |
Fields
ClockIdtype#
type ClockId = "fixed" | "frame" | "presentation"Selects the clock a program runs against.
DefineOptionstype#
type DefineOptions = {clock: ClockId?}Configures define.
EasingFunctiontype#
type EasingFunction = function(t: number): numberMaps normalized input progress to eased output progress.
EasingNametype#
type EasingName = "linear"
| "quadIn"
| "quadOut"
| "quadInOut"
| "quadOutIn"
| "cubicIn"
| "cubicOut"
| "cubicInOut"
| "cubicOutIn"
| "quartIn"
| "quartOut"
| "quartInOut"
| "quartOutIn"
| "quintIn"
| "quintOut"
| "quintInOut"
| "quintOutIn"
| "sineIn"
| "sineOut"
| "sineInOut"
| "sineOutIn"
| "expoIn"
| "expoOut"
| "expoInOut"
| "expoOutIn"
| "backIn"
| "backOut"
| "backInOut"
| "backOutIn"
| "elasticIn"
| "elasticOut"
| "elasticInOut"
| "elasticOutIn"
| "bounceIn"
| "bounceOut"
| "bounceInOut"
| "bounceOutIn"Names a built-in easing curve.
Emissionrecord#
The payload an emit step delivers at address zero.
Fields
args#
args: {any}Read-only. Carries the emitted constants with bindings resolved. This list is built per emission and is the observer's to keep.
EntityRefrecord#
References an entity supplied at play time.
Fields
Evaluatorrecord#
record Evaluator
resolve: (function(data: any): any)?
newState: (function(program: Program, data: any, cursor: Cursor): any)?
step: function(exclusive world: entityworld.World, cursor: Cursor, dt: number): boolean
save: (function(state: any): any)?
load: (function(program: Program, data: any, saved: any): any)?
endDefines the evaluator an eval step runs every tick.
Methods
step#
step: function(exclusive world: entityworld.World, cursor: Cursor, dt: number): booleanCaller-writable. Advances one cursor by dt and returns true once the cursor should move on to the next instruction.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | entityworld.World | |
cursor | Cursor | |
dt | number |
Returns
| Type | Description |
|---|---|
boolean |
Fields
resolve#
resolve: (function(data: any): any)?Caller-writable. Turns the constant an eval step carries into the form that is cheapest to read every tick. Runs once per program constant and the result is shared by every playback of it.
newState#
Caller-writable. Builds per-cursor working state, or nil when the evaluator needs none.
FaultReasontype#
type FaultReason = "unregisteredAction"
| "actionError"
| "budgetExceeded"
| "branchFaulted"
| "unregisteredQuery"
| "unregisteredEvaluator"
| "unregisteredAwaitable"
| "unregisteredTween"Explains why a cursor stopped running.
Handletype#
type Handle = integerIdentifies one playback through a generation-checked reference that remains meaningful across a snapshot load.
Noderecord#
record Node
kind: string
text: string?
number: number?
count: integer?
args: any
nodes: {Node}?
blocks: {{Node}}?
endRepresents one authored step produced by a node constructor.
Fields
args#
args: anyRead-only. Carries the step's constant, when it takes one. Most steps carry a list; an evaluating step carries whatever its evaluator reads.
PlaybackModetype#
type PlaybackMode = "once" | "loop" | "pingPong"Selects how a timeline repeats.
PlaybackStatetype#
type PlaybackState = "running" | "paused" | "completed" | "canceled" | "faulted"Describes the lifecycle state of one playback.
PlayOptionstype#
type PlayOptions = {
owner: integer?,
bindings: {[string]: integer}?,
params: {[string]: any}?,
budget: integer?,
channel: string?
}Configures play.
Programrecord#
record Program
name: string
version: integer
clock: ClockId
code: {integer}
consts: {any}
loopCounts: {[integer]: integer}
bindingArgs: {[integer]: boolean}
evalResolved: {[integer]: any}?
endRepresents a compiled, immutable program shared by every playback of it.
Fields
code#
code: {integer}Engine-owned. Holds the flat instruction array. Game code reads it through disassemble rather than directly.
loopCounts#
loopCounts: {[integer]: integer}Engine-owned. Maps a jump's address to the iteration count it seeds.
bindingArgs#
bindingArgs: {[integer]: boolean}Engine-owned. Marks the constant indexes whose argument list holds an entity reference, so a call with plain constants copies nothing.
evalResolved#
evalResolved: {[integer]: any}?Engine-owned. Caches one resolved constant per evaluating instruction.
QueryConditiontype#
type QueryCondition = "any" | "empty"Describes the condition awaited by a waitQuery step.
RunOptionstype#
type RunOptions = {
mode: PlaybackMode?,
count: integer?
}Configures a nested tweenRun operation.
Statusrecord#
record Status
state: PlaybackState
program: string
version: integer
pc: integer
wakeAt: integer?
waitingFor: string?
waitingQuery: string?
waitingCondition: QueryCondition?
waitingAwaitable: string?
waitingAwaitableEntity: integer?
waitingTween: Handle?
tweenOutcome: TweenOutcome?
branches: integer
joining: boolean
fault: FaultReason?
faultMessage: string?
endReports a playback's current state.
Fields
wakeAt#
wakeAt: integer?Read-only. Reports the tick this playback next runs on, or nil when it is blocked on something other than a time.
waitingCondition#
waitingCondition: QueryCondition?Read-only. Reports the query condition this playback waits for.
waitingAwaitable#
waitingAwaitable: string?Read-only. Names the awaitable provider this playback is blocked on.
waitingAwaitableEntity#
waitingAwaitableEntity: integer?Read-only. Names the entity this playback's awaitable is asked about.
branches#
branches: integerRead-only. Counts live branches this playback forked and has not joined.
Steprecord#
Describes one step a playback reaches without branching.
Fields
ticks#
ticks: integerRead-only. Counts ticks of the program's clock from the reference point until this step runs.
args#
args: {any}Read-only. Carries the step's constants, unresolved. This list is the program's own rather than a copy, so read it and do not write to it.
Targetrecord#
record Target
id: string
componentName: string
component: components.Component?
fields: {string}
mode: TargetMode
endDescribes the component and one to four numeric fields a timeline operation writes.
Fields
id#
id: stringRead-only. Identifies this target in a compiled slot. A built-in carries its TargetName; one built from a component carries the component name joined to its fields.
component#
Engine-owned. Caches the resolved component, which a target restored from a snapshot looks up on first use.
fields#
fields: {string}Read-only. Names the fields written, in the order the destinations line up with them.
TargetNametype#
type TargetName = "transform.x"
| "transform.y"
| "transform.xy"
| "transform.rotation"
| "transform.rotationShortest"
| "transform.scaleX"
| "transform.scaleY"
| "transform.scaleXY"
| "color.a"
| "color.rgba"Names a built-in component-field target.
TimelineNoderecord#
record TimelineNode
kind: OperationKind
duration: number?
easingName: EasingName?
target: Target?
source: TrackSource?
t1: number?
t2: number?
t3: number?
t4: number?
name: string?
nested: {TimelineNode}?
mode: PlaybackMode?
count: integer?
branches: {{TimelineNode}}?
endRepresents one authored timeline operation.
Fields
source#
source: TrackSource?Read-only. Selects where a tracking interpolation reads its destination.
branches#
branches: {{TimelineNode}}?Read-only. Carries one block per concurrent branch of a parallel operation.
TimelineOptionstype#
type TimelineOptions = {clock: ClockId?}Configures timeline.
TrackingTargetrecord#
Selects a dynamic tracking-source entity.
Fields
TrackSourcerecord#
record TrackSource
kind: TrackSourceKind
key: string?
relationshipName: string?
relationship: components.Component?
componentName: string
component: components.Component?
fields: {string}
endProvides a live component-field tracking source.
Fields
relationshipName#
relationshipName: string?Read-only. Names the relationship traversed by a related source.
relationship#
relationship: components.Component?Engine-owned. Caches the resolved relationship component.
TweenOutcometype#
type TweenOutcome = "completed" | "canceled" | "replaced" | "targetLost"Reports how the playback awaited by waitTween ended.
Functions#
activeCountfunction#
function activeCount(exclusive world: ecs.World): integerReturns the number of live playbacks, for tests and diagnostics.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playbacks |
Returns
| Type | Description |
|---|---|
integer | the count, with branches and |
awaitfunction#
Builds a step that waits for work outside the sequencer to finish.
A subsystem registers a provider under a name and answers whether the work is still going; this parks the playback until it is not. The sequencer never learns what the work is, which is how a program waits on a sprite animation without the sequencer requiring the renderer. A binding that is missing or dead is not a fault, and neither is a provider reporting work that never started.
Arguments
| Name | Type | Description |
|---|---|---|
provider | string | the registered provider name, checked before the binding; an unregistered name faults the playback with |
target | EntityRef | a |
key | string? | handed to the provider unread, for a provider that answers about more than one thing per entity |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the provider name is empty or the target is not a
bindreference
bindfunction#
References an entity supplied through PlayOptions.bindings.
The reference resolves when the instruction using it runs rather than at play, so a binding may name an entity that does not exist yet. A missing binding resolves to nothing instead of faulting.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the binding name, which must not be empty |
Returns
| Type | Description |
|---|---|
EntityRef | a reference to put in a |
Raises
when the name is empty
callfunction#
function call(action: string, ...: any): NodeBuilds a step that runs a registered action.
The action name resolves per world when the instruction runs, so a program may name an action registered later. A name still unregistered when the step runs faults the playback with unregisteredAction. The step costs no tick.
Arguments
| Name | Type | Description |
|---|---|---|
action | string | the registered action name |
... | any | the constants the action receives, which are the same values for every playback of this program; put anything that differs between two playbacks in |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the action name is empty
cancelfunction#
Stops a playback and releases its cursor.
This is safe on a finished handle. Cancelling takes the branches the playback forked with it, and a playback parked in a waitTween on the one canceled is told canceled and resumes. What a playTween started is a playback in its own right and is not canceled with the one that started it, unlike a pause, which cascades to it; stop it through its owner with cancelOwnedBy.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playback |
handle | Handle | the playback to stop, read against this world's cursor arena |
Returns
| Type | Description |
|---|---|
boolean | false when the handle names nothing running, whether it already finished or was already canceled |
cancelOwnedByfunction#
function cancelOwnedBy(exclusive world: ecs.World, owner: integer, reason: TweenOutcome?): integerCancels every playback owned by an entity.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playbacks |
owner | integer | an entity id; nothing owns a playback started without an owner, so no argument reaches one and only |
reason | TweenOutcome? | what a playback parked in a |
Returns
| Type | Description |
|---|---|
integer | the number of canceled playbacks, counting each cursor once and reaching branches through their inherited owner |
currentStepfunction#
Returns a clock's current tick as counted by the sequencer.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the clock counters |
clock | ClockId? | which of the three counters to read, defaulting to |
Returns
| Type | Description |
|---|---|
integer | the ticks of that one clock, counted from when this world's sequencer state was created and comparable only against another reading of the same clock |
definefunction#
function define(name: string, nodes: {Node}, options: DefineOptions?): ProgramCompiles a program under a stable symbolic name.
Defining over a name already in use publishes a new version rather than replacing the old one: playbacks already running keep the version they started on, and only later play calls see the new one.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the stable symbolic name, which is a snapshot compatibility surface |
nodes | {Node} | the authored steps, which may be empty, in which case the program completes on its first tick |
options | DefineOptions? | the clock the program's waits count, defaulting to |
Returns
| Type | Description |
|---|---|
Program | the compiled program, immutable and shared by every playback of it, so the same value plays on any number of worlds |
Raises
when the name is empty, the clock is not one of the three names, or an entry is not a step
disassemblefunction#
function disassemble(value: Program, pc: integer?): stringRenders a program as readable instructions.
Arguments
| Name | Type | Description |
|---|---|---|
value | Program | a program from |
pc | integer? | the instruction to mark, as a playback's |
Returns
| Type | Description |
|---|---|
string | a header naming the program and its version, then one line per instruction, newline separated, for reading rather than for parsing |
easingfunction#
function easing(name: EasingName): EasingFunction?Returns a built-in easing curve by name.
Arguments
| Name | Type | Description |
|---|---|---|
name | EasingName | the curve name |
Returns
| Type | Description |
|---|---|
EasingFunction? | the curve, or nil when the name names no built-in |
emitfunction#
function emit(event: string, ...: any): NodeBuilds a step that emits a sequence event at address zero.
The step occupies no tick, so the step after it runs in the same one. Each emission copies its argument list and sends it with the event, so an observer may keep it, unlike the list an action receives.
Arguments
| Name | Type | Description |
|---|---|---|
event | string | the name carried on the emission |
... | any | the constants delivered on the emission with every entity reference already resolved |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the event name is empty
evalfunction#
function eval(evaluator: string, data: any): NodeBuilds a step that evaluates a registered evaluator every tick until it finishes.
Unlike every other step this one does not hand the tick back: the playback joins its clock's active set and the runtime steps it each tick. Values that move every frame need this behavior.
Arguments
| Name | Type | Description |
|---|---|---|
evaluator | string | the registered evaluator name, resolved when the instruction runs; an unregistered name faults the playback with |
data | any | the constant the evaluator receives, passed through its |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the evaluator name is empty
forkfunction#
Builds a step that starts a branch alongside the rest of the program.
The branch is a playback of its own running the same program at a different instruction, inheriting the owner, bindings, parameters, and instruction budget of the playback that forked it. It starts once that playback next waits, joins, or ends, within the same tick. A branch never outlives its parent, and a branch that faults takes its parent with it, faulted branchFaulted, rather than leaving a join one branch short forever.
Arguments
| Name | Type | Description |
|---|---|---|
nodes | {Node} | the branch body, which must not be empty and which reads and writes the forking playback's own bindings table rather than a copy |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the block is empty
hasActionfunction#
Reports whether a world has registered an action name.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the action registry |
name | string | the action name to find |
Returns
| Type | Description |
|---|---|
boolean | false for a name this world never registered, including one another world has |
hasQueryfunction#
Reports whether a world has registered a query name.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the query registry |
name | string | the query name to find |
Returns
| Type | Description |
|---|---|
boolean | false for a name this world never registered |
joinfunction#
function join(): NodeBuilds a step that waits for every branch not yet joined.
It falls straight through when there are none, and when the last branch finishes the waiting playback resumes within that same tick.
Returns
| Type | Description |
|---|---|
Node | a step for |
loopfunction#
Builds a step that repeats a block.
Arguments
| Name | Type | Description |
|---|---|---|
count | integer? | the iterations, or nil to repeat until canceled |
nodes | {Node} | the block, which must not be empty and which is run in order and started again from its first node |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the count is not a positive whole number, or the block is empty
parallelfunction#
Builds a step that forks several blocks and waits for all of them.
This is a fork per block followed by one join. Blocks start in argument order, and each shares the playback's bindings table as a fork does.
Arguments
| Name | Type | Description |
|---|---|---|
blocks | {{Node}} | one non-empty block per branch |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when no block is supplied or one of them is empty
pausefunction#
Suspends a playback, its branches, and anything it started.
Pause is holder counted: two systems can hold the same playback for unrelated reasons, and it runs again only when the last one lets go, so whichever resumes first cannot undo the other. Its wait, if any, resumes from where it paused, and what it is waiting on stops with it when the awaitable provider knows how.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playback |
handle | Handle | the playback to pause |
holder | string? | any string, treated as a set rather than a count, so two pauses under the same holder are one hold; it defaults to |
Returns
| Type | Description |
|---|---|
boolean | whether the playback stopped, which is false when another holder already had it or the handle names nothing running |
playfunction#
function play(exclusive world: ecs.World, value: Program, options: PlayOptions?): HandleStarts a program.
The first instruction runs on the next tick of the program's own clock, never inside this call.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the new playback |
value | Program | a program from |
options | PlayOptions? | the owner, bindings, parameters, budget, and channel; taking a channel another playback on the same owner holds cancels that one first, reporting |
Returns
| Type | Description |
|---|---|
Handle | a handle to the new playback, carrying a generation so one whose playback has ended never names a later playback that took the same slot |
playbacksfunction#
Returns handles for every live playback in a stable order.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playbacks |
Returns
| Type | Description |
|---|---|
{Handle} | a caller-owned table ordered by cursor slot, which is stable within a run but differs from creation order because new playbacks reuse free slots |
playTweenfunction#
Builds a step that plays a registered timeline on a bound entity.
The step costs no tick. It starts a separate playback owned by the bound entity, running on the named program's clock and using this playback's instruction budget. A binding that is missing or dead is not a fault: nothing plays, and a following waitTween resumes at once reporting targetLost.
Arguments
| Name | Type | Description |
|---|---|---|
timeline | string | the timeline name, resolved when the instruction runs and always to the newest version; an undefined name faults the playback with |
target | EntityRef | a |
params | {[string]: any}? | the started playback's parameters, so |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the timeline name is empty or the target is not a
bindreference
pluginfunction#
function plugin(exclusive world: ecs.World): nilInstalls the sequencer into a world.
Installing twice on the same world does nothing, so a world that scripts input may install the sequencer itself rather than relying on the application having done it.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world receiving sequencer state, systems, and the snapshot handler |
Returns
| Type | Description |
|---|---|
nil |
programfunction#
Returns the newest version of a defined program, or a requested version.
Every version a playback still runs stays reachable, and the newest version of a name is never dropped.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | a name previously passed to |
version | integer? | the version to look up, starting at one, or nil for the newest |
Returns
| Type | Description |
|---|---|
Program? | the program, or nil when the name was never defined or that version has been dropped |
programNamesfunction#
function programNames(): {string}Returns the names of every defined program, sorted.
Returns
| Type | Description |
|---|---|
{string} | a caller-owned list; registration is process-wide, so this spans every world in the process |
registerActionfunction#
Registers an action a call step can name.
Actions must obey the deterministic-action contract to keep replay and rewind meaningful: no wall-clock reads, no file or network access, and randomness only from a generator whose state travels with the snapshot.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the action registry |
name | string | the action name, which is per world unlike an evaluator or an awaitable; registering it twice replaces the action, and a playback resolves the name at each |
action | Action | the body, which faults its playback with |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty
registerAwaitablefunction#
Registers a provider an await step can name.
Registration is process-wide, like an evaluator, because a provider is code. Registering a name twice replaces the provider, so a playback already parked on the name asks the new one at the next fixed step.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the provider name |
provider | Awaitable | the provider, which must carry |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty or the provider has no
isPending
registerEvaluatorfunction#
Registers an evaluator an eval step can name.
Registration is process-wide, like a program: an evaluator is code, and two worlds evaluating the same name run the same thing. Registering a name twice replaces it.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the evaluator name |
evaluator | Evaluator | the evaluator, which must carry |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty or the evaluator has no
step
registerQueryfunction#
function registerQuery(exclusive world: ecs.World, name: string, descriptor: ecs.QueryDescriptor): nilRegisters a query a waitQuery step can name.
Registering a name again re-tests every playback already parked on it, against the new query, at the next fixed step.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the query registry |
name | string | the query name |
descriptor | ecs.QueryDescriptor | the include and exclude constraints |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty
resumefunction#
Releases one holder's claim on a paused playback.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playback |
handle | Handle | the paused playback |
holder | string? | the string that took the hold, defaulting to |
Returns
| Type | Description |
|---|---|
boolean | whether the playback started running again, which is true only for the last holder to let go; a wake time that passed while it was held runs it on the next tick rather than immediately |
setInstructionBudgetfunction#
function setInstructionBudget(exclusive world: ecs.World, instructions: integer): nilSets the per-tick instruction budget for playbacks that ask for none.
The budget caps how many instructions one playback runs per tick, and exceeding it faults the playback with budgetExceeded rather than deferring the rest, so it guards against a loop with no wait in it rather than acting as a scheduler. It applies to playbacks started after this call.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world the budget applies to |
instructions | integer | the budget, which must be at least one |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the budget is less than one
setNominalFrameTimefunction#
function setNominalFrameTime(exclusive world: ecs.World, seconds: number): nilSets the nominal frame duration a frame or presentation wait converts seconds against.
The nominal duration is used rather than a frame's measured time because a wake is a tick number fixed when the wait runs and never revisited.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world whose future waits use this duration. |
seconds | number | the positive finite nominal duration in seconds. |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the duration is not positive and finite.
signalfunction#
Raises a named signal and wakes every playback blocked on it.
Delivery happens on the next fixed step. Raising a signal nothing waits on is not an error and is not remembered: a playback that reaches waitSignal afterwards keeps waiting.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the signal state |
name | string | the signal name, registered nowhere |
Returns
| Type | Description |
|---|---|
integer | the number of playbacks waiting on the name now, which may differ from the set the next fixed step wakes |
Raises
when the name is empty
signalOnEventfunction#
function signalOnEvent<E is events.Emittable>(exclusive world: ecs.World, name: string, event: Type<E>): nilRaises a signal whenever an event emits at address zero.
An ECS event is an edge, like a signal, so this wires one to the other rather than adding a separate kind of wait. Delivery follows the ordinary signal rule and waiters wake on the next fixed step.
Type parameters
| Name | Description |
|---|---|
E |
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the observer and the signal state |
name | string | the signal to raise, once per emission |
event | Type<E> | the event definition, observed at address zero and only there; the observer lasts for the life of the world |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the name is empty
sourceKeyfunction#
function sourceKey(key: string, component: ecs.Component, fields: {string}): TrackSourceReads a moving destination from the live entity holding a world key. Missing or despawned sources contribute zero until a live entity claims the key.
Arguments
| Name | Type | Description |
|---|---|---|
key | string | the non-empty key resolved on every evaluation. |
component | ecs.Component | the component to read from that entity. |
fields | {string} | the fields aligned with the target's fields. |
Returns
| Type | Description |
|---|---|
TrackSource | the keyed tracking source. |
Raises
when the key is empty.
sourceOwnfunction#
function sourceOwn(component: ecs.Component, fields: {string}): TrackSourceReads a moving destination from the animated entity itself.
Arguments
| Name | Type | Description |
|---|---|---|
component | ecs.Component | the component read |
fields | {string} | the fields read, lined up with the target's own |
Returns
| Type | Description |
|---|---|
TrackSource | the source |
sourceRelatedfunction#
function sourceRelated(relationship: ecs.Component, component: ecs.Component, fields: {string}): TrackSourceReads a moving destination from the entity a relationship points at.
Arguments
| Name | Type | Description |
|---|---|---|
relationship | ecs.Component | the relationship traversed from the animated entity |
component | ecs.Component | the component read on the target entity |
fields | {string} | the fields read, lined up with the target's own |
Returns
| Type | Description |
|---|---|
TrackSource | the source |
sourceTrackingfunction#
function sourceTracking(component: ecs.Component, fields: {string}): TrackSourceReads a moving destination from the entity named by the animated entity's TrackingTarget.
Arguments
| Name | Type | Description |
|---|---|---|
component | ecs.Component | the component read |
fields | {string} | the fields read, lined up with the target's own |
Returns
| Type | Description |
|---|---|
TrackSource | the source |
statusfunction#
Returns a playback's status.
A finished playback still answers, with state saying how it ended, until a later play takes its slot. The waiting fields are filled only while the playback lives.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playback |
handle | Handle | the playback to inspect, read against this world's cursor arena |
Returns
| Type | Description |
|---|---|
Status? | a caller-owned status, or nil for a handle this world never issued |
targetAnglefunction#
Interpolates one field as an angle, taking the short way round.
Arguments
| Name | Type | Description |
|---|---|---|
component | ecs.Component | the component whose field moves |
fieldName | string | the radian-valued field name |
Returns
| Type | Description |
|---|---|
Target | the target |
targetFieldfunction#
Interpolates one numeric field of a component.
Arguments
| Name | Type | Description |
|---|---|---|
component | ecs.Component | the component whose field moves |
fieldName | string | the field name |
Returns
| Type | Description |
|---|---|
Target | the target, which holds no per-playback state and is safe to share |
targetField2function#
Interpolates two numeric fields of a component together.
Arguments
| Name | Type | Description |
|---|---|---|
component | ecs.Component | the component whose fields move |
first | string | the field the first destination lines up with |
second | string | the field the second destination lines up with |
Returns
| Type | Description |
|---|---|
Target | the target |
timelinefunction#
function timeline(name: string, spec: {TimelineNode}, options: TimelineOptions?): ProgramCompiles a tween timeline into a program under a stable name.
The compiled slots travel in the program's constant pool, so a timeline is snapshot safe for the same reason every other program is. Play it with play, giving the entity it animates as the owner: a playback with no owner has nothing to write to and completes on its first tick.
A playback reads four parameters, all optional: mode is "once", "loop", or "pingPong"; count is the passes a finite repeat makes; speed is a playback multiplier defaulting to one; and delay is the seconds to wait before the first frame. They are read when the playback builds its state, so a speed of zero or less, a negative delay, or a repeat of a zero-length timeline faults the playback with actionError rather than raising from play.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the stable symbolic name, sharing |
spec | {TimelineNode} | the timeline operations in execution order |
options | TimelineOptions? | the clock the timeline is evaluated against, defaulting to |
Returns
| Type | Description |
|---|---|
Program | the compiled program, also reachable by name through |
Raises
when the clock is neither
"fixed"nor"presentation", or an operation is invalid
tweenAdjustfunction#
function tweenAdjust(duration: number, curve: EasingName | EasingFunction, target: TargetName | Target, t1: number, t2: number?, t3: number?, t4: number?): TimelineNodeBuilds a timeline operation that interpolates by a delta from the starting value.
Arguments
| Name | Type | Description |
|---|---|---|
duration | number | the seconds it occupies, which must be greater than zero |
curve | EasingName | EasingFunction | a name from the built-in curves, or one of those curve values |
target | TargetName | Target | which component fields move and in which order |
t1 | number | the signed change applied to the target's first field |
t2 | number? | the change applied to the second field |
t3 | number? | the change applied to the third field |
t4 | number? | the change applied to the fourth field |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
Raises
when the curve names no built-in
tweenEmitfunction#
function tweenEmit(name: string): TimelineNodeBuilds a timeline operation that emits a named sequence event.
It occupies no time, so what follows it starts at the same instant. The emission carries the entity the timeline is animating as its one argument and the timeline's own playback as its handle. It fires as the cursor passes the point going forward, so a ping-pong return leg does not fire it a second time.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the name carried on the emission |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
tweenParallelfunction#
function tweenParallel(blocks: {{TimelineNode}}): TimelineNodeBuilds a timeline operation that runs blocks concurrently and ends with the longest.
Every block starts where the operation sits, and what follows starts after the last of them ends. Two blocks writing the same component field is not an error, and the later one in argument order wins each tick.
Arguments
| Name | Type | Description |
|---|---|---|
blocks | {{TimelineNode}} | one block per concurrent branch, each run in sequence within itself |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
tweenRunfunction#
function tweenRun(spec: {TimelineNode}, options: RunOptions?): TimelineNodeBuilds a timeline operation that runs a nested timeline from its own spec.
The nested timeline occupies a window of the parent equal to its own length times its pass count, so a repeating nested run needs a finite count. Its operations keep their per-playback state separately from the parent's.
Arguments
| Name | Type | Description |
|---|---|---|
spec | {TimelineNode} | the nested operations, which this call reads rather than rewrites |
options | RunOptions? | omit to run the nested timeline once; |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
Raises
when a repeating nested run carries no finite count
tweenTofunction#
function tweenTo(duration: number, curve: EasingName | EasingFunction, target: TargetName | Target, t1: number, t2: number?, t3: number?, t4: number?): TimelineNodeBuilds a timeline operation that interpolates to an absolute destination.
The operation reads its starting values from the entity on its first tick rather than during compilation, so the same timeline played on two entities starts from wherever each of them is.
Arguments
| Name | Type | Description |
|---|---|---|
duration | number | the seconds it occupies, which must be greater than zero |
curve | EasingName | EasingFunction | a name from the built-in curves, or one of those curve values |
target | TargetName | Target | which component fields move and in which order the destinations line up with them |
t1 | number | the destination for the target's first field, in that field's own units: pixels for a translate, radians for a rotation, zero through one for a color channel |
t2 | number? | the destination for the second field |
t3 | number? | the destination for the third field |
t4 | number? | the destination for the fourth field, with anything past the target's field count ignored and an omitted field treated as zero |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
Raises
when the curve names no built-in
tweenTrackfunction#
function tweenTrack(duration: number, curve: EasingName | EasingFunction, target: TargetName | Target, from: TrackSource): TimelineNodeBuilds a timeline operation that interpolates toward a destination that keeps moving.
The first tick fixes the start as tweenTo does, and each later tick reads the destination again while the operation remains inside its window. Once the window ends the operation holds its last destination. A source whose entity or component is missing reads as zero rather than faulting.
Arguments
| Name | Type | Description |
|---|---|---|
duration | number | the seconds it occupies, which must be greater than zero |
curve | EasingName | EasingFunction | a name from the built-in curves, or one of those curve values |
target | TargetName | Target | which component fields move and which fields of the source line up with them |
from | TrackSource | where the destination is read each tick |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
Raises
when the curve names no built-in
tweenWaitfunction#
function tweenWait(duration: number): TimelineNodeBuilds a timeline operation that advances the cursor without changing anything.
Arguments
| Name | Type | Description |
|---|---|---|
duration | number | the seconds it occupies, where zero performs no work |
Returns
| Type | Description |
|---|---|
TimelineNode | an operation for a timeline spec |
upcomingfunction#
Returns the actions and emissions a playback will certainly perform next, and their timing.
The walk covers the straight-line run ahead of where the playback sits, accumulating waits, and stops at the first instruction whose successor requires execution.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the playback |
handle | Handle | the playback to inspect |
withinTicks | integer? | report only what is due within this many ticks of the playback's own clock, or nil for everything the walk can reach |
Returns
| Type | Description |
|---|---|
{Step} | a caller-owned list of the calls and emissions ahead, in order; empty for a handle that names nothing running and for one parked on a signal, a query, a join, a tween, or an evaluator, since none of those has a predictable start. A wait written in seconds also ends the walk. |
waitfunction#
function wait(seconds: number): NodeBuilds a step that waits a duration in seconds.
The duration converts to whole ticks when the instruction runs, rounded to nearest, and any non-zero duration waits at least one tick. Zero still costs a tick: the cursor resumes on the program's next tick rather than carrying on within this one.
Arguments
| Name | Type | Description |
|---|---|---|
seconds | number | the non-negative duration |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the duration is negative
waitingOnfunction#
Returns how many playbacks currently wait on a signal name.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world that owns the waiting playbacks |
name | string | the signal name to count |
Returns
| Type | Description |
|---|---|
integer | zero for a name nothing waits on; a signal raised during this step leaves its waiters in the count until the next step delivers it |
waitQueryfunction#
function waitQuery(name: string, condition: QueryCondition): NodeBuilds a step that blocks until a registered query matches or stops matching.
Unlike a signal, which is an edge, this is a condition on the world: a wait whose condition already holds resumes rather than waiting for a transition that has already happened. The condition is evaluated at the start of a fixed step rather than at the instruction, so a query wait costs at least one step.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the registered query name, resolved per world when the instruction runs |
condition | QueryCondition |
|
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the name is empty or the condition is neither value
waitSignalfunction#
Builds a step that blocks until signal raises a named signal.
The next fixed step delivers signals after signal runs, so a signal one sequence raises never runs another within the same step. A signal raised before the wait is not remembered.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | the signal name, which is registered nowhere |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the name is empty
waitStepsfunction#
function waitSteps(steps: integer): NodeBuilds a step that waits a whole number of ticks of the program's clock.
Arguments
| Name | Type | Description |
|---|---|---|
steps | integer | the non-negative tick count, where zero yields until the next tick |
Returns
| Type | Description |
|---|---|
Node | a step for |
Raises
when the count is negative or not a whole number
waitTweenfunction#
function waitTween(): NodeBuilds a step that waits for the playback the most recent playTween started.
It resumes when that specific playback completes, is canceled, loses its channel, or loses its entity, so it never waits forever, and status reports the outcome as tweenOutcome. A waitTween that no playTween preceded falls straight through without costing a tick.
Returns
| Type | Description |
|---|---|
Node | a step for |
Values#
DEFAULT_BUDGETvariable#
const DEFAULT_BUDGET: integerThe per-tick instruction budget a world gives a playback that asks for none.
TrackingTargetvariable#
const TrackingTarget: components.TableComponent<TrackingTarget>The process-wide component a tweenTrack tracking source reads.