Core

System

System declarations, typed requirements, and execution context.

Systems are the main unit of gameplay behavior in the library. A system declares everything it is allowed to touch up front, and the runtime derives a context that exposes exactly that surface and nothing more.

This module is where ECS logic becomes explicit and reviewable:

  • queries describe which entities may be visited
  • resources, events, services, and machines are requested by name
  • change detection is per system: each run sees what changed since its previous run
  • hidden ambient world access is impossible through the public API

Reach for this module whenever you are writing gameplay, simulation, reset, input, host sync, or transition logic that should run inside a schedule.

Examples

// Define one simulation step with explicit world access.
const Move = Game.System("Move", {
  queries: {
    moving: Game.Query({
      selection: {
        position: Game.Query.write(Position),
        velocity: Game.Query.read(Velocity)
      }
    })
  },
  resources: {
    dt: Game.System.readResource(DeltaTime)
  }
}, ({ queries, resources }) => {
  const dt = resources.dt.get()

  for (const { data } of queries.moving.each()) {
    const velocity = data.velocity.get()
    data.position.update((position) => ({
      x: position.x + velocity.x * dt,
      y: position.y + velocity.y * dt
    }))
  }
})

Functions

Public system authoring helpers for resources, events, lifecycle reads, and typed system definitions.

service

Source

Declares that a system needs a service from the external runtime environment.

Use this for host capabilities that should not live in ECS world storage: clocks, random generators, render bridges, audio sinks, persistence APIs, or network clients. The matching implementation must be supplied when the runtime is created with Game.Runtime.services(...).

readResource

Source

Creates a resource-read declaration for a system spec.

Use this for world-level singleton data the system needs to observe but must not mutate, such as delta time, score snapshots, configuration, or aggregated frame input. The resulting context slot is a read-only ReadCell.

writeResource

Source

Creates a resource-write declaration for a system spec.

Use this when the system owns mutation of one world-level singleton, such as score, UI summaries, accumulated damage, or frame-local caches. Declaring it here makes that authority visible in the system contract before the body is read.

const CountUp = Game.System("CountUp", {
  resources: {
    score: Game.System.writeResource(Score)
  }
}, ({ resources }) => {
  // Mutate the singleton through the explicit write cell.
  resources.score.update((score) => score + 1)
})

readEvent

Source

Creates an event-read declaration for a system spec.

Each run of the reading system sees the events published since its own previous completed run, once, in emission order: events from earlier systems in the same schedule, and events emitted after it ran last time.

Events are kept until every system that reads them has run, so a reader in a schedule ticked less often than the emitter's (a fixed update below the render rate) still receives all of them. Events nobody has read yet are kept for the current and previous runtime.tick(...) call; a reader's first run sees whatever is still kept. A system skipped by its run conditions discards the events published meanwhile. Each stream is capped at Runtime.streamCapacity entries; a reader that missed dropped ones sees lagged() === true.

This is the usual second half of a cross-system flow: one system emits an event, and a later system reads it and re-validates any handles or lookups it needs.

writeEvent

Source

Creates an event-write declaration for a system spec.

Emitted events are published when the system completes successfully; a failed run publishes nothing. Readers later in the same schedule see them, and so do readers that run in a later tick.

If the payload needs to name an entity for later work, emit a durable Game.Entity.handle(...) (optionally with an intent component) and let the later reader re-resolve it through lookup.getHandle(...).

const EmitHit = Game.System("EmitHit", {
  events: {
    hit: Game.System.writeEvent(Hit)
  }
}, ({ events }) => {
  events.hit.emit({ amount: 1 })
})

machine

Source

Declares read access to the current committed value of a finite-state machine.

Use this when gameplay logic needs to branch on the current committed phase, but should not see queued next-state writes early. Machines are the intended default for menus, rounds, encounters, pause flows, and other discrete modes whose transition boundary matters.

nextState

Source

Declares queued write access to the next value of a finite-state machine.

This is the system-side request channel for a future phase change. It does not immediately switch the committed state; the queued value is applied only at an explicit Game.Schedule.applyStateTransitions(...) boundary.

Use this instead of writing a plain resource when the transition timing itself is part of the gameplay model, such as restarting a round, leaving a menu, or entering a results screen after reset/setup schedules run.

transition

Source

Declares read access to the last applied transition payload of a machine.

readTransitionEvent

Source

Declares read access to committed transition events for one machine.

Transition events are published when a transition commits and read like normal events: each run sees those published since its previous run.

This is one of the clearest signs that the modeled value should be a machine rather than a plain state descriptor.

readRemoved

Source

Declares read access to removed-component lifecycle records.

Each run returns the entities whose component was removed since the system's previous run (removals are applied when commands are). Records are kept until every system that reads them has run, so a reader in a schedule ticked less often than the one that removes (rendering after several fixed updates) still sees every removal. A system skipped by its run conditions keeps its position, like added/changed, and sees the removals when it runs again. Each log is capped at Runtime.streamCapacity entries; drops show up as missed reads in the debug trace. Systems usually pair this with host cleanup such as removing renderer-owned nodes. readDespawned complements this for whole-entity teardown.

const DestroyRenderNodesSystem = Game.System("DestroyRenderNodes", {
  removed: {
    renderables: Game.System.readRemoved(Renderable)
  }
}, ({ removed }) => {
  for (const entityId of removed.renderables.all()) {
    // destroy host-owned node here
  }
})

readRelationFailures

Source

Declares read access to relation-mutation failure records.

readDespawned

Source

Declares read access to despawned-entity lifecycle records.

Each run returns the entities despawned since the system's previous run. Records are retained like readRemoved records. Use it when host-owned state must be destroyed even if no single removed component is the canonical trigger. readRemoved is often used alongside this in authoritative host mirrors.

const DestroyNodesSystem = Game.System("DestroyNodes", {
  despawned: {
    entities: Game.System.readDespawned()
  }
}, ({ despawned }) => {
  for (const entityId of despawned.entities.all()) {
    // destroy host-owned node here
  }
})

System

Source

No description provided yet.