Runtime

schedule

Schedule definitions and explicit visibility-boundary markers.

Schedules are the execution plan of the game. They decide not only which systems run, but also when staged world changes become observable to later systems in the same frame.

That makes this module the place where ECS timing becomes intentional:

  • deferred commands flush only at applyDeferred()
  • emitted events become readable only at updateEvents()
  • lifecycle buffers advance only at updateLifecycle()
  • machine transitions commit only at transition markers

Reach for this module whenever you need to explain the order of gameplay, setup, reset, host sync, or state transitions in one frame.

Examples

// Run simulation first while mutations are still staged.
const update = Game.Schedule(
  simulateMovement,
  resolveCollisions,
  // Make queued spawns, inserts, and despawns visible.
  Game.Schedule.applyDeferred(),
  // Advance lifecycle buffers before host sync reacts to changes.
  Game.Schedule.updateLifecycle(),
  createSprites,
  syncTransforms
)

Functions

Public constructors for schedules and explicit schedule marker steps.

applyDeferred

Source

Creates an explicit command-application marker step.

Systems before this marker can enqueue commands. Systems after it see the fully applied world changes.

This is the normal boundary between setup/simulation work and any later system that depends on spawned entities, inserted components, or queued despawns becoming visible in the current schedule.

const update = Game.Schedule(
  simulateSystem,
  // Flush queued world mutation before the observer runs.
  Game.Schedule.applyDeferred(),
  observeSpawnedSystem
)

updateEvents

Source

Creates an explicit event/message update marker step.

Systems before this marker can write events. Systems after it read the committed readable event buffers for the current schedule execution.

Use this when a later system in the same schedule should observe events that earlier systems just emitted. If those event payloads carry entity handles, later systems should re-resolve them through lookup.getHandle(...) after this marker.

const update = Game.Schedule(
  emitTickSystem,
  Game.Schedule.updateEvents(),
  observeTickSystem
)

updateLifecycle

Source

Creates an explicit lifecycle update marker step.

This commits readable added, changed, removed, and despawned lifecycle buffers for later systems in the same schedule.

This is the required boundary before lifecycle-driven host sync. Systems using Game.Query.added(...), Game.Query.changed(...), Game.System.readRemoved(...), or Game.System.readDespawned() only observe the current schedule's structural changes after this marker. extend is the preferred wrapper when the host slice is just a prefix or suffix around a headless gameplay schedule.

const browserUpdate = Game.Schedule(
  simulationSystem,
  Game.Schedule.applyDeferred(),
  // Commit readable added/changed/removed views for the host sync slice.
  Game.Schedule.updateLifecycle(),
  destroyNodesSystem,
  createNodesSystem,
  syncTransformsSystem
)

updateRelationFailures

Source

Creates an explicit relation-failure update marker step.

Deferred relation mutation failures become readable only after this marker.

transitions

Source

Creates a typed reusable transition bundle.

Use bundles to group multiple machine transition schedules and then attach them to one applyStateTransitions(...) marker.

fragment

Source

Creates a reusable explicit schedule fragment.

Fragments are the only reusable authoring unit for explicit schedules. They can contain systems, explicit boundary markers, and nested fragments.

const hostMirror = Game.Schedule.fragment({
  schema,
  entries: [
    Game.Schedule.updateLifecycle(),
    destroyNodesSystem,
    createNodesSystem,
    syncTransformsSystem
  ]
})

phase

Source

Creates a reusable explicit schedule phase.

Use this for repeated explicit step slices such as host-sync tails or other lifecycle-driven suffixes that should stay visible in authored schedules.

const hostMirrorPhase = Game.Schedule.phase({
  steps: [
    Game.Schedule.updateLifecycle(),
    destroyNodesSystem,
    createNodesSystem,
    syncTransformsSystem
  ]
})

build

Source

Builds one final executable schedule from explicit entries.

build(...) is the only final schedule constructor. Systems are derived from the authored plan and kept in that exact order.

compose

Source

Flattens mixed schedule entries into one { systems, steps } pair.

This is the preferred way to compose systems, markers, and reusable phases without manually keeping systems and steps in sync.

const plan = Game.Schedule.compose({
  entries: [
    captureInputSystem,
    gameplaySystem,
    Game.Schedule.applyDeferred(),
    hostMirrorPhase
  ]
})

const update = Game.Schedule.build(...plan.steps)

applyStateTransitions

Source

Creates an explicit machine-transition application marker step.

Queued machine writes are committed only at this boundary. If a transition bundle is provided, matching enter/exit/transition schedules run as part of the same boundary.

This is the canonical restart/reset boundary: queue the next phase with Game.System.nextState(...), then let applyStateTransitions(...) commit the new current state and run any attached reset schedules.

const transitions = Game.Schedule.transitions(
  Game.Schedule.onEnter(Phase, "Playing", [ResetWorldSystem])
)

const update = Game.Schedule(
  QueueRestartSystem,
  GameplaySystem,
  Game.Schedule.applyDeferred(),
  Game.Schedule.applyStateTransitions(transitions)
)

Schedule

Source

Creates one explicit executable schedule from authored plan entries.

isSystemStep

Source

Runtime check used to distinguish system steps from schedule markers.