Event
Use the Event API to subscribe handlers and publish namespaced events.
Error Handling Pattern​
- Success:
err == nil - Failure:
erris an error code string
Node Relationship​
Eventis the cross-service event bus used by APIs likeNode,Player,Chat,Resource, andEnvironment.- 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​
| Function | Description | Scope |
|---|---|---|
AddEventHandler | Register a handler for an exact event name or prefix wildcard. | S |
RemoveEventHandler | Remove one handler by id/function, or remove all handlers for a pattern. | S |
TriggerEvent | Publish an event locally or route it through the network. | S |
Event List​
| Event | Description | Scope |
|---|---|---|
player:connected | Player session connected. | S |
player:disconnected | Player session disconnected. | S |
player:updated | Player connection identity changed (role/account/display name/login state). | S |
player:kicked | Player was kicked. | S |
player:banned | Player was banned. | S |
chat:message | Chat message created. | S |
chat:edited | Chat message edited. | S |
chat:removed | Chat message removed. | S |
chat:cleared | Chat cleared. | S |
chat:mute_changed | Chat mute state changed. | S |
resource:started | Resource started. | S |
resource:stopped | Resource stopped. | S |
resource:restart | Resource restarted. | S |
resource:start_error | Resource start/restart failed. | S |
resource:stop_error | Resource stop failed. | S |
resource:sync | Client resource state sync. | C |
client:frame | Local client frame callback with deltaSeconds / deltaMs. | C |
ui:created | UI element created. | C |
ui:updated | UI element updated. | C |
ui:removed | UI element removed. | C |
ui:action | UI interaction callback event. | C |
node:created | Node created in node graph. | S |
node:updated | Node updated in node graph. | S |
node:deleted | Node deleted in node graph. | S |
node:moved | Node moved/reparented in node graph. | S |
world:environment_changed | Environment state changed. | S |
world:weather_changed | Weather changed. | S |
world:time_changed | Time-of-day changed. | S |
world:local_override_changed | Local 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 withERR_PERMISSION_DENIED.
Server runtime targets:
"local": local dispatch only."server": server runtime only."all": broadcast to all connected clients.number: oneplayerId(session target).stringprefixedc_: oneclientIdtarget.stringprefixeda_: oneaccountIdtarget.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_*->clientIda_*->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:
| Field | Type | Set By | Description |
|---|---|---|---|
name | string | caller + runtime validation | Event name (namespace:event). |
payload | table | caller | Event payload data. |
source | string | runtime | Trusted origin (client, server, player:<id>). |
target | string/number/table | caller + runtime normalization | Routing target descriptor (local/server/all or selector). |
timestamp | number | runtime | Event creation timestamp (ms). |
correlationId | string | caller or runtime | Correlates publish/ack/retry logs. |
version | string | runtime | Envelope schema version (1.0). |
Transport metadata:
reliableanddeterministicare 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 foreventName.
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, defaulttrue)deterministic(boolean, defaultfalse)correlationId(string)
Returns:
err(nil| string)
Deterministic and Reliable Rules​
Deterministic rules:
deterministic=truerequiresreliable=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_ackandsystem:event_timeoutcontrol-plane events. - Retries: interval
1000ms, timeout5000ms, max retries3.
Namespaces​
Allowed namespaces:
resourceplayerchatstorageuisystemnodeworldauthcommandcamerainputcustom
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,accountIdwhen available. - use
playerIdfor session-scoped runtime routing. - use
accountId/clientIdfor persistence and long-term moderation identity. - for spatial/visual state, read
NodePlayersviaNode.*(not from event payload alone).
Error Codes​
ERR_INVALID_EVENT_NAMEERR_NAMESPACE_NOT_ALLOWEDERR_INVALID_HANDLERERR_HANDLER_NOT_FOUNDERR_PAYLOAD_INVALIDERR_INVALID_OPTIONSERR_PERMISSION_DENIEDERR_TRANSPORT_FAILEDERR_NOT_CONNECTEDERR_NOT_FOUNDERR_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)