Skip to main content

Event

Use the Event API to subscribe handlers and publish namespaced events.

Error Handling Pattern​

  • Success: err == nil
  • Failure: err is an error code string

Node Relationship​

  • Event is the cross-service event bus used by APIs like Node, Player, Chat, Resource, and Environment.
  • Node graph changes are observed through node-domain events (node:created, node:updated, ...), while data changes happen through the owning API (Node.*, Chat.*, etc.).
  • Use event handlers for orchestration and reactions; keep authority checks in the owning service APIs.

Function List​

FunctionDescriptionScope
AddEventHandlerRegister a handler for an exact event name or prefix wildcard.S
RemoveEventHandlerRemove one handler by id/function, or remove all handlers for a pattern.S
TriggerEventPublish an event locally or route it through the network.S

Event List​

EventDescriptionScope
player:connectedPlayer session connected.S
player:disconnectedPlayer session disconnected.S
player:updatedPlayer connection identity changed (role/account/display name/login state).S
player:kickedPlayer was kicked.S
player:bannedPlayer was banned.S
chat:messageChat message created.S
chat:editedChat message edited.S
chat:removedChat message removed.S
chat:clearedChat cleared.S
chat:mute_changedChat mute state changed.S
resource:startedResource started.S
resource:stoppedResource stopped.S
resource:restartResource restarted.S
resource:start_errorResource start/restart failed.S
resource:stop_errorResource stop failed.S
resource:syncClient resource state sync.C
client:frameLocal client frame callback with deltaSeconds / deltaMs.C
ui:createdUI element created.C
ui:updatedUI element updated.C
ui:removedUI element removed.C
ui:actionUI interaction callback event.C
node:createdNode created in node graph.S
node:updatedNode updated in node graph.S
node:deletedNode deleted in node graph.S
node:movedNode moved/reparented in node graph.S
world:environment_changedEnvironment state changed.S
world:weather_changedWeather changed.S
world:time_changedTime-of-day changed.S
world:local_override_changedLocal environment override changed.C

Routing Model​

Client runtime targets:

  • "local" or "client": local dispatch only.
  • "server": route to server.
  • Other remote targets ("all", selectors, selector arrays) are denied with ERR_PERMISSION_DENIED.

Server runtime targets:

  • "local": local dispatch only.
  • "server": server runtime only.
  • "all": broadcast to all connected clients.
  • number: one playerId (session target).
  • string prefixed c_: one clientId target.
  • string prefixed a_: one accountId target.
  • table: list of target selectors (number, c_*, a_*).

Notes:

  • Direct client-to-client routing is not supported.
  • If server receives an unsupported target value, routing falls back to local behavior.

Target selector quick reference:

  • number -> playerId (session id)
  • c_* -> clientId
  • a_* -> accountId

Event Envelope​

Handlers receive one event envelope table:

local event = {
name = "custom:example",
payload = { value = 1 },
source = "server",
target = "all",
timestamp = 1739366400000,
correlationId = "8a2dd6d901a5477c8f8627cd2a17c2dc",
version = "1.0"
}

Field meanings:

FieldTypeSet ByDescription
namestringcaller + runtime validationEvent name (namespace:event).
payloadtablecallerEvent payload data.
sourcestringruntimeTrusted origin (client, server, player:<id>).
targetstring/number/tablecaller + runtime normalizationRouting target descriptor (local/server/all or selector).
timestampnumberruntimeEvent creation timestamp (ms).
correlationIdstringcaller or runtimeCorrelates publish/ack/retry logs.
versionstringruntimeEnvelope schema version (1.0).

Transport metadata:

  • reliable and deterministic are options on publish.
  • They are not envelope fields passed to handlers.

Functions​

AddEventHandler​

S Shared (Client & Server)

local handlerId, err = AddEventHandler(eventName, handler)

Parameters:

  • eventName (string): exact name (custom:ping) or prefix wildcard (custom:*).
  • handler (function): callback receiving one envelope argument.

Returns:

  • handlerId (string | nil)
  • err (nil | string)

Wildcard matching rule:

  • Only trailing * is supported (prefix match).

RemoveEventHandler​

S Shared (Client & Server)

local err = RemoveEventHandler(eventName, handlerIdOrHandler)

Parameters:

  • eventName (string): same name/pattern used for registration.
  • handlerIdOrHandler (string | function | nil):
  • string: remove by handler id.
  • function: remove by function reference.
  • nil: remove all handlers for eventName.

Returns:

  • err (nil | string)

TriggerEvent​

S Shared (Client & Server)

local err = TriggerEvent(eventName, payload, options)

Parameters:

  • eventName (string): event name.
  • payload (table, optional): payload object.
  • options (table, optional):
  • target ("local" | "client" | "server" | "all" | selector | list of selectors, default "local")
  • reliable (boolean, default true)
  • deterministic (boolean, default false)
  • correlationId (string)

Returns:

  • err (nil | string)

Deterministic and Reliable Rules​

Deterministic rules:

  • deterministic=true requires reliable=true.
  • Client runtime cannot publish deterministic events to remote targets.
  • Server runtime can publish deterministic events only during deterministic tick execution.
  • Remote inbound deterministic events are rejected.

Reliable delivery (reliable=true):

  • Uses system:event_ack and system:event_timeout control-plane events.
  • Retries: interval 1000ms, timeout 5000ms, max retries 3.

Namespaces​

Allowed namespaces:

  • resource
  • player
  • chat
  • storage
  • ui
  • system
  • node
  • world
  • auth
  • command
  • camera
  • input
  • custom

Unscoped names are treated as custom.

Built-in Event Domains​

Built-in event-bus domains currently cover:

  • player lifecycle event-bus: player:connected, player:disconnected, player:updated
  • player moderation event-bus: player:kicked, player:banned
  • chat lifecycle event-bus: chat:message, chat:edited, chat:removed, chat:cleared, chat:mute_changed
  • resource lifecycle event-bus: resource:started, resource:stopped, resource:restart, resource:start_error, resource:stop_error, resource:sync
  • ui lifecycle/action event-bus: ui:created, ui:updated, ui:removed, ui:action
  • node graph lifecycle event-bus: node:created, node:updated, node:deleted, node:moved
  • world environment event-bus: world:environment_changed, world:weather_changed, world:time_changed, world:local_override_changed

Not built-in by default:

  • camera state changes like view mode/attach changes
  • auth/account workflow events (auth:*)
  • command observability events (command:*)
  • storage workflow events (storage:*)
  • input/camera workflow helper events (input:*, camera:*)

For those, publish custom events with TriggerEvent(...) and handle them with AddEventHandler(...).

Player-event payload guidance:

  • player events should include playerId, clientId, accountId when available.
  • use playerId for session-scoped runtime routing.
  • use accountId/clientId for persistence and long-term moderation identity.
  • for spatial/visual state, read NodePlayers via Node.* (not from event payload alone).

Error Codes​

  • ERR_INVALID_EVENT_NAME
  • ERR_NAMESPACE_NOT_ALLOWED
  • ERR_INVALID_HANDLER
  • ERR_HANDLER_NOT_FOUND
  • ERR_PAYLOAD_INVALID
  • ERR_INVALID_OPTIONS
  • ERR_PERMISSION_DENIED
  • ERR_TRANSPORT_FAILED
  • ERR_NOT_CONNECTED
  • ERR_NOT_FOUND
  • ERR_DETERMINISTIC_POLICY

Quick Examples​

Register and trigger a local event:

local id, addErr = AddEventHandler("custom:ui_refresh", function(event)
print("refresh reason=" .. tostring(event.payload and event.payload.reason))
end)

local runErr = TriggerEvent("custom:ui_refresh", {
reason = "manual"
})

Client -> server event:

local err = TriggerEvent("custom:profile:save", {
slot = 1,
value = "new-value"
}, {
target = "server",
reliable = true
})

Server -> one player:

local err = TriggerEvent("custom:reward_granted", {
amount = 250
}, {
target = "a_9d4d7c2f",
reliable = true
})

Player event + NodePlayers correlation:

AddEventHandler("player:connected", function(event)
local pid = event.payload.playerId
local node, nodeErr = Node.Get("/NodePlayers/" .. tostring(pid))

if not nodeErr and node then
-- use node.transform etc. for visual/runtime read-model context
end
end)