Node
Node provides the runtime graph API for world/map objects, replicated player view data, client-only visuals, and client UI.
Planned Role In Maps And Editor Workflows​
Node is the runtime graph API for map/world content.
Use Node for:
- placed static props
- triggers
- checkpoints
- spawn markers
- replicated map/world nodes
- editor-authored world entities
Do not plan a separate Object API for the same domain unless there is a hard runtime boundary that Node cannot cover.
Recommended ownership split:
Map:- loads/unloads baked maps
- owns baked static world content lifecycle
Node:- represents the static/dynamic world graph objects after load
Environment:- remains the runtime environment authority
Vehicle:- owns deterministic vehicle entities
This keeps the public API surface smaller and avoids overlapping object systems.
Error Handling Pattern​
- Success:
err == nil - Failure:
erris an error code string
Domain Model​
NodeSim: deterministic gameplay/simulation objects (server authoritative).NodeSharedVisual: replicated visual objects approved by server.NodePlayers: replicated player view model (read-only on client).NodeClientVisual: client-only visual objects (not replicated).NodeUI: client-only UI objects (not replicated).- Runtime-owned baseline world visuals (for example
Systems/World/Always/OceanRoot/Ocean) are outside script-created node domains.
Access Matrix​
| Domain | Server Read | Server Write | Client Read | Client Write | Replicated |
|---|---|---|---|---|---|
NodeSim | Yes | Yes | Yes | No | Yes |
NodeSharedVisual | Yes | Yes | Yes | No | Yes |
NodePlayers | Yes | Yes | Yes | No | Yes |
NodeClientVisual | No | No | Yes | Yes | No |
NodeUI | No | No | Yes | Yes | No |
Notes:
- Client writes to server domains must go through
Node.RequestSim(...). - Resource scripts can only write inside their own resource subtree.
Function List​
| Function | Description | Scope |
|---|---|---|
Node.GetRoot | Get restricted root handle for node domains. | S |
Node.Get | Read one node by path or id. | S |
Node.List | List child nodes with optional filters. | S |
Node.Create | Create a node in an allowed domain. | S |
Node.Update | Patch mutable node fields. | S |
Node.Delete | Delete a node. | S |
Node.Subscribe | Subscribe to node graph changes. | S |
Node.Unsubscribe | Remove a subscription. | S |
Node.RequestSim | Client request for server-side sim mutations. | C |
Node.GetSpawnPoints | Read spawn point definitions. | S |
Event List​
| Event | Description | Scope |
|---|---|---|
node:created | Fired after node creation. | S |
node:updated | Fired after node update. | S |
node:deleted | Fired after node deletion. | S |
node:moved | Fired after node parent/path change. | S |
Path and Ownership Model​
Canonical roots:
/NodeSim/resources/{resourceName}/.../NodeSharedVisual/resources/{resourceName}/.../NodePlayers/{playerId}/.../NodeClientVisual/resources/{resourceName}/.../NodeUI/resources/{resourceName}/...
Ownership rules:
- A resource can write only to
/.../resources/{itsOwnResourceName}/.... - Cross-resource writes return
ERR_RESOURCE_SCOPE. - Client reads
NodePlayersfor remote-player visuals; this does not grant authority. - Runtime-managed systems may create internal nodes in
NodeClientVisual; scripts should treat unknown runtime-owned nodes as read-only.
Identifier bridge:
NodePlayersis keyed by sessionplayerIdin path.- if your script starts with
accountIdorclientId, resolve current session viaConnection.GetPlayer(target)first, then read/NodePlayers/{playerId}.
Node Data Contract​
Common node fields:
id(string)path(string)name(string)domain(string)resource(string ornil)kind(string)parentId(string ornil)revision(number)createdAt(number, ms epoch)updatedAt(number, ms epoch)tags(table ornil)
Spatial fields (when applicable):
transform.position(x,y,z)transform.rotation(x,y,z)transform.scale(x,y,z)
Player view nodes should expose:
playerId(session id)clientId(stable install id)accountId(persistent account id ornil)
Concurrency and Revision Model​
Write operations (Create/Update/Delete) support optimistic concurrency:
options.expectedRevisioncan be passed by caller.- mismatch returns
ERR_REVISION_CONFLICT.
Use revision from the latest read before updating.
Functions​
Node.GetRoot​
S Shared (Client & Server)
local root, err = Node.GetRoot()
Returns:
root(table):NodeSim,NodeSharedVisual,NodePlayers,NodeClientVisual,NodeUIerr(nil| string)
Node.Get​
S Shared (Client & Server)
local node, err = Node.Get(pathOrId)
Parameters:
pathOrId(string)
Returns:
node(table | nil)err(nil| string)
Node.List​
S Shared (Client & Server)
local nodes, err = Node.List(path, options)
Parameters:
path(string)options(table, optional):recursive(boolean, defaultfalse)limit(number)kind(string | table)resource(string)
Returns:
nodes(table | nil)err(nil| string)
Node.Create​
S Shared (Domain-Restricted)
local nodeId, err = Node.Create(domain, parentPath, kind, props, options)
Parameters:
domain(NodeSim|NodeSharedVisual|NodePlayers|NodeClientVisual|NodeUI)parentPath(string)kind(string)props(table)options(table, optional):expectedRevision(number)
Returns:
nodeId(string | nil)err(nil| string)
Node.Update​
S Shared (Domain-Restricted)
local err = Node.Update(pathOrId, patch, options)
Parameters:
pathOrId(string)patch(table)options(table, optional):expectedRevision(number)
Returns:
err(nil| string)
Node.Delete​
S Shared (Domain-Restricted)
local err = Node.Delete(pathOrId, options)
Parameters:
pathOrId(string)options(table, optional):expectedRevision(number)
Returns:
err(nil| string)
Node.Subscribe​
S Shared (Client & Server)
local subId, err = Node.Subscribe(pathOrPattern, handler, options)
Parameters:
pathOrPattern(string)handler(function): receives standard event envelope.options(table, optional):events(table): subset ofnode:created,node:updated,node:deleted,node:moved
Returns:
subId(string | nil)err(nil| string)
Node.Unsubscribe​
S Shared (Client & Server)
local err = Node.Unsubscribe(subId)
Parameters:
subId(string)
Returns:
err(nil| string)
Node.RequestSim​
C Client Only
local requestId, err = Node.RequestSim(action, payload, options)
Parameters:
action(string): server-defined sim mutation action.payload(table)options(table, optional):reliable(boolean, defaulttrue)correlationId(string)
Returns:
requestId(string | nil)err(nil| string)
Node.GetSpawnPoints​
S Shared (Client & Server)
local points, err = Node.GetSpawnPoints(options)
Parameters:
options(table, optional):group(string)resource(string)
Returns:
points(table | nil)err(nil| string)
Events​
All node events use the standard event envelope:
event.nameevent.payloadevent.sourceevent.targetevent.timestampevent.correlationIdevent.version
node:created (Event Bus)​
Payload:
id,path,domain,resource,kind,revision
node:updated (Event Bus)​
Payload:
id,path,domain,patch,revision
node:deleted (Event Bus)​
Payload:
id,path,domain,revision
node:moved (Event Bus)​
Payload:
id,oldPath,newPath,oldParentId,newParentId,revision
Deterministic Policy​
NodeSimis deterministic simulation state.- Client direct writes to
NodeSimare denied (ERR_PERMISSION_DENIED). - Server should apply
NodeSimmutations in deterministic tick flow. NodeSharedVisual,NodeClientVisual, andNodeUIare non-deterministic visual domains.
Related APIs​
Environmentfor sky/weather/time world visuals.Camerafor camera lifecycle/attach/view-mode APIs (internally backed by client visual nodes).UIfor high-level client interface trees (internally backed byNodeUI).Playerfor moderation and control operations.Connectionfor online roster and identity lookup.Authfor account identity/roles.
Error Codes​
ERR_INVALIDERR_NOT_FOUNDERR_PERMISSION_DENIEDERR_RESOURCE_SCOPEERR_REVISION_CONFLICTERR_DETERMINISTIC_POLICYERR_INVALID_DOMAINERR_INVALID_PATH
Quick Examples​
Server create deterministic obstacle:
local nodeId, err = Node.Create("NodeSim", "/NodeSim/resources/map_race01", "box_collider", {
name = "obstacle_01",
transform = {
position = { x = 120, y = 4, z = 30 },
rotation = { x = 0, y = 0, z = 0 },
scale = { x = 2, y = 2, z = 2 }
}
})
Client create local HUD widget:
local uiId, err = Node.Create("NodeUI", "/NodeUI/resources/my_hud", "button", {
name = "spectate_btn",
text = "Spectate"
})
Client request server-side sim mutation:
local requestId, err = Node.RequestSim("spawn_checkpoint", {
map = "race01",
checkpointId = "cp_12"
})
Resolve accountId -> NodePlayers path:
local player, err = Connection.GetPlayer("a_9d4d7c2f")
if err then return end
local node, nodeErr = Node.Get("/NodePlayers/" .. tostring(player.playerId))
if not nodeErr and node then
local pos = node.transform and node.transform.position
end