05. Actor relocation¶
This category covers the external entry points ZLinkActorManager (ZLINK_ACTOR_MANAGER)/
ZLinkActorClient (ZLINK_ACTOR_CLIENT) provide, the entry point for joining a Spot from inside
Actor code via ZLinkActorContext, and relocation policy selection. The exact signatures are
owned by the
Actor and session binding exact interface
(Korean-only).
ZLinkActorManager.create¶
Always creates a new Actor.
const created = await actorManager
.create("player-1", "player")
.inMesh("play")
.request(new SpawnPlayer("player-1"))
.submit();
Options. This call carries the following modifiers.
| Modifier | Default | Meaning |
|---|---|---|
.inMesh(meshName) |
Optional if exactly one Mesh has Object Client/Server role | The Mesh to create the Actor in. Omitting it with two or more candidates completes with InvalidOperation; none completes with NotConfigured; a nonexistent specified Mesh completes with NotFound |
.request(request) |
None (empty request) | The request passed at Actor factory creation time |
.timeout(timeoutMs) |
5 seconds | The deadline covering resolve/reservation/factory/Ready barrier altogether |
.submit(signal?) |
terminal (pick one) | Waits until creation completes |
.yield(signal?) |
terminal (pick one) | Only valid inside a SpotWide handler |
Completion result. ZLinkActorCreateResult (a discriminated union) completes as one of
status: "created" (newly created) or status: "rejected" (the factory rejected it). If a
Ready incarnation of the same ActorId already exists, it completes with an AlreadyExists error
rather than either status — status: "existing" only exists for getOrCreate. If a Ready
incarnation exists but its stable type differs, it is TypeMismatch.
When to use. Use this when a new Actor is always needed. Use getOrCreate to reuse an
existing one and only create when there is none.
ZLinkActorManager.getOrCreate¶
Returns the Ready Actor with the same ActorId if it exists, and creates a new one otherwise.
const existingOrCreated = await actorManager
.getOrCreate("player-1", "player")
.inMesh("play")
.request(new SpawnPlayer("player-1"))
.submit();
Options. The same as create — .inMesh(...), .request(...), .timeout(...), terminal
.submit(signal?) or .yield(signal?).
Completion result. status: "existing" returns the already-existing Actor and ignores
request. Contending with a creating attempt waits for that result and joins it; a distinct
operation receives "existing" after Ready and does not share the earlier reply.
When to use. Use this when an idempotent "use if it exists, create if it doesn't" by ActorId is needed.
find / findSpot / destroy (manager)¶
Queries an existing Actor, queries the Spot it currently participates in, or terminates the exact incarnation.
const actor = await actorManager.find("player-1");
const spot = await actorManager.findSpot("player-1");
if (actor) {
const destroyed = await actorManager.destroy(actor);
}
Options. None of the three calls has modifiers — all only take the target identifier and an
optional signal.
Completion result. find returns undefined if there is no Ready Actor. findSpot returns
undefined if there is no current User Spot membership. destroy returns false if the
incarnation does not exist, completes with InvalidOperation if the generation differs, and
Unavailable while a pre-commit seal is in progress.
When to use. Use this when you need to check current existence/membership, or explicitly terminate an Actor.
sendToActor / requestToActor (ZLinkActorClient)¶
Sends a one-way message, or exchanges a typed request/reply, to a single global ActorId. Used from an external client.
await actorClient.sendToActor("player-1", new GrantItem("sword")).submit();
const reply = await actorClient
.requestToActor("player-1", new GetInventory())
.timeout(3_000)
.submit<Inventory>();
Options. sendToActor only has .metadata(...) and terminal .submit(signal?).
requestToActor additionally has the following.
| Modifier | Default | Meaning |
|---|---|---|
.timeout(timeoutMs) |
The MeshNode's request default timeout | The upper bound for waiting on the reply |
.submit<TReply>(signal?) |
terminal (pick one) | Waits until the reply arrives |
.yield<TReply>(signal?) |
terminal (pick one) | Only valid inside a SpotWide handler |
Completion result. No ActorId completes with NotFound. The remaining completion kinds
follow the same common rules as the messaging-execution category.
When to use. Use sendToActor if no reply is needed, and requestToActor if one is.
joinSpot / joinEntrySpot (inside Actor code)¶
Joins the current Actor to a User Spot or an Entry Spot. Called via
ZLinkActorContext.joinSpot(...)/joinEntrySpot(...) — unlike other entries, the only terminal
here is defer(), not submit/yield.
Options. This call carries the following modifiers.
| Modifier | Default | Meaning |
|---|---|---|
.timeout(timeoutMs) |
5 seconds | A monotonic absolute deadline |
.defer() |
Required terminal | A synchronous call with no result. Only registers the join intent and an inactive barrier — it does not start the target lookup immediately |
Completion result. defer() itself has no return value. If the current handler ends
normally, the barrier activates and executes the Join; if the handler fails, the barrier is
discarded. If the handler used yield(...), the barrier is not activated until the last
continuation ends. The actual result (accepted/rejected/failed) is delivered asynchronously via
the onJoinCompleted(...) callback carrying the same ZLinkActorJoinOperationId — a
discriminated union of status: "accepted"/"rejected"/"failed".
When to use. Use this to move an Actor to a different Spot, or return it to an Entry Spot.
Calling it from an Actor in an Entry Spot or a PerActor User Spot completes with
invalidConfiguration.
Relocation policy selection (at Actor factory registration time)¶
Choose exactly one, in the configure callback of addActorFactory(actorType, factoryType,
configure) (topology-discovery category).
| Policy | Behavior on cross-node move | When to use |
|---|---|---|
disableRelocation() |
Rejects the move itself before Capture | When this Actor must never be moved to another node |
recreateOnRelocation() |
Recreates the same logical identity via the target factory. Does not restore application state | When an Actor may be recreated without state |
preserveStateWith(adapterType) |
Moves an opaque Uint8Array via ZLinkActorRelocationAdapter<TActor>.capture/restore |
When state must be preserved across the move |
Completion result. preserveStateWith's capture(...) result is capped at 64 MiB. Returning
null/undefined or a non-Uint8Array value is treated as an adapter failure, not converted to
an empty payload. Capture/Restore can each be called multiple times within the same relocation, so
both callbacks must be retry-safe — they must not depend on an external side effect executing
exactly once.
When to use. Which of the three policies you choose determines this Actor type's entire relocation behavior — it is decided once, at factory registration time, and cannot be changed per call afterward.
See the Actor and session binding exact interface (Korean-only) for the full rationale.