Actor state
An actor is a concrete character instance in the world — the thing that walks around in a specific game (app), at a position (chunk). An actor optionally references an avatar (the reusable "class"/identity), and carries its own state.
Like avatars, actors follow the rule: only the owner can write; everyone in that app, with that app's token, can read the public parts. A session token or another game's app token is answered as not found — these are game-plane fields and require an app token for the actor's app.
Actors vs avatars: an avatar is a position-less identity a player owns and can reuse across games; an actor is one in-world instance of a character in a single game, with a position and its own per-instance state.
The actor record
| Field | Meaning |
|---|---|
uuid | The actor's id — a 32-byte string used by the realtime protocol (not a hyphenated UUID) |
appId | The game this actor lives in |
userId | The owner |
avatarId | Optional link to one of the owner's avatars |
chunk | The chunk the actor is in ({ x, y, z }) |
publicState | Visible to everyone in the actor's app who holds that app's token (base64 binary) |
privateState | Visible to the owner only (base64 binary) |
Only createActor (below) creates an actor record. The realtime layer never
creates, moves or deletes one: from the actor updates a client sends it keeps
live presence (which actors are where right now), which host election and the
nearby-actor queries read, and that presence expires a few seconds after the
actor goes quiet. See GraphQL UDP Proxy API.
An actor uuid is unique within an app. createActor with a uuid the app already
has is refused with ACTOR_EXISTS (HTTP 409), whose extensions.ownedByCaller
says whether the existing record is yours: if it is, keep using it
(updateActor / updateActorState); if not, pick another uuid.
The realtime layer does not check that the actor uuid a client puts in a spatial message belongs to that client, so another player's client can send messages under your actor's uuid. Treat it as a label for display and interpolation. For anything that matters, trust the identity your server code is given (a hub's caller) and the owner on the actor record.
Creating an actor
mutation {
createActor(input: {
appId: "1",
uuid: "0123456789abcdef0123456789abcdef", # exactly 32 bytes (UTF-8)
avatarId: "5", # optional
chunk: { x: "0", y: "0", z: "0" },
publicState: "AAEC",
privateState: "BQYH"
}) { uuid appId chunk { x y z } }
}
You can only attach an avatarId that you own.
Writing state (owner only)
Use updateActorState for state-only changes, or updateActor to also move the
actor or change its avatar:
mutation {
updateActorState(uuid: "0123...", input: {
publicState: "AAEC",
privateState: "BQYH"
}) { uuid }
}
deleteActor(uuid) removes an actor you own.
Reading state
| Operation | Returns |
|---|---|
actor(uuid) | One actor in the caller's app. The owner sees privateState; other players in that app see public state only. Session or another app's token → not found. |
actors(filter) / actorsConnection | Your own actors (filter by appId, avatarId, uuid, or chunk). Under an app token, only the ones in that app — with or without filter.appId (a filter.appId for another app is refused). A session token lists your actors across all your games. |
batchLookupActors(input: { uuids }) | Public state for the given actors in the caller's app — uuids in other apps are omitted. Session token → not found. |
query {
actor(uuid: "0123...") { uuid chunk { x y z } publicState privateState }
}
query {
batchLookupActors(input: { uuids: ["0123...", "89ab..."] }) {
uuid publicState
}
}
Realtime movement vs persisted state
The publicState / privateState on the actor record are persisted state
you set with the mutations above. A player's fast-changing live transform
(position within the chunk, rotation, animation) flows over the realtime channel
via sendActorUpdate and is broadcast to nearby players — see
GraphQL UDP Proxy API. Sending actor updates is
also what makes a player a host candidate.
Use persisted actor state for things that should survive and be queryable (equipment, status, customization); use the realtime channel for per-frame motion.
Grid permissions and actors
When grid permissions are active in your world, what an actor may do as it moves
is governed by grid permissions: a player needs access
to enter/act in a region, update_voxel_data to build there, and use_voice_chat
for voice. See Permissions overview.
Reference
See the Game API GraphQL reference for the exact inputs and return types.