playerthree/protocol

PlayerThree message conventions — transport-agnostic.

PlayerThree does not own the game's netcode. A game carries these messages over whatever it already uses (the playerthree/colyseus interop maps them onto Colyseus room messages; a custom WebSocket game can do the same). Agents normally connect through MCP instead (see mcp/), which needs no game transport at all.

Inbound messages are untrusted: size-capped, strictly shaped, rate-limited. Outbound game data always passes the closed-vocabulary guard first.

Interfaces

ObserverAgentCard

Defined in: packages/playerthree/src/protocol.ts:31

Public series-scoped identity only; never a private commander or owner identifier.

Properties


ObserverCommentary

Defined in: packages/playerthree/src/protocol.ts:22

Only voluntarily disclosed agent-emitted game chat or summaries, never owner messages or harness logs.

Properties


ObserverFrame

Defined in: packages/playerthree/src/protocol.ts:42

One released observer frame. A frame carries exactly one of world (a keyframe: the full projection, sent on join, perspective change, resume beyond the batch bound or resynchronization) or delta (playerthree/delta from frame sequence - 1 of the same perspective, sent only directly after it). Frames are shared by every observer at the same cursor and are immutable.

Properties


ObserverRequest

Defined in: packages/playerthree/src/protocol.ts:63

Observer transport request; the server, never the client, chooses the projection and release watermark.

Properties

PropertyTypeDefined in
after?numberpackages/playerthree/src/protocol.ts:65
perspectivestringpackages/playerthree/src/protocol.ts:64

ViewState

Defined in: packages/playerthree/src/protocol.ts:159

A reader's state of one team's view stream.

Properties


WatchMsg

Defined in: packages/playerthree/src/protocol.ts:128

Transport-level spectator message (not routed to PlayerThreeMatch.handleMessage).

Properties

Type Aliases

ClientMsg

type ClientMsg = 
  | {
  args?: unknown;
  method: ControlMethod;
  op: "call";
  role: string;
  seq: number;
}
  | {
  detail?: DetailRequest;
  everyTicks: number;
  op: "stream";
  role: string;
  seq: number;
}
  | {
  mandate: unknown;
  op: "mandate";
  role: string;
  seq: number;
}
  | {
  op: "scopes";
  role: string;
  scopes: Scope[];
  seq: number;
}
  | {
  approve: boolean;
  id: string;
  op: "resolveApproval";
  role: string;
  seq: number;
}
  | {
  controller: Controller;
  op: "controller";
  role: string;
  seq: number;
}
  | {
  op: "mint";
  role: string;
  seq: number;
}
  | {
  op: "tell";
  ping?: unknown;
  roles: string[] | "all";
  seq: number;
  text: string;
};

Defined in: packages/playerthree/src/protocol.ts:108

Union Members

Type Literal
{
  args?: unknown;
  method: ControlMethod;
  op: "call";
  role: string;
  seq: number;
}

Control-plane RPC for a seat (agents: their seat; owners: any seat of their team). Omitted args means {}.


Type Literal
{
  detail?: DetailRequest;
  everyTicks: number;
  op: "stream";
  role: string;
  seq: number;
}

detail: observation sections pushed in full besides the role's defaults, or 'all' (unknown → invalid).


Type Literal
{
  mandate: unknown;
  op: "mandate";
  role: string;
  seq: number;
}

Owner only.


Type Literal
{
  op: "scopes";
  role: string;
  scopes: Scope[];
  seq: number;
}

Owner only: replace this live seat's agent permissions.


Type Literal
{
  approve: boolean;
  id: string;
  op: "resolveApproval";
  role: string;
  seq: number;
}

Owner only: resolve a supervised proposal.


Type Literal
{
  controller: Controller;
  op: "controller";
  role: string;
  seq: number;
}

Owner only.


Type Literal
{
  op: "mint";
  role: string;
  seq: number;
}

Owner only: mint a seat token for an agent.


Type Literal
{
  op: "tell";
  ping?: unknown;
  roles: string[] | "all";
  seq: number;
  text: string;
}

Owner only (chat mode): message own agents — some roles of the owner's team, or all.


ObserverGrant

type ObserverGrant = 
  | {
  delayMs: number;
  kind: "team";
  team: string;
}
  | {
  delayMs: number;
  kind: "omniscient";
  teams: readonly string[];
};

Defined in: packages/playerthree/src/protocol.ts:18


ObserverMessage

type ObserverMessage = object;

Defined in: packages/playerthree/src/protocol.ts:54

Properties

frame?
frame?: ObserverFrame;

Defined in: packages/playerthree/src/protocol.ts:59

grant?
grant?: ObserverGrant;

Defined in: packages/playerthree/src/protocol.ts:58

Admission metadata only: never contains a live tick or completion signal.

op
op: "observer";

Defined in: packages/playerthree/src/protocol.ts:55

state
state: "buffering" | "ready" | "closed";

Defined in: packages/playerthree/src/protocol.ts:56


Principal

type Principal = 
  | {
  kind: "team";
  match: string;
  team: string;
}
  | {
  kind: "human";
  match: string;
  role: string;
  team: string;
}
  | {
  kind: "seat";
  match: string;
  role: string;
  team: string;
}
  | {
  kind: "spectator";
  match: string;
  team?: string;
}
  | {
  grant: ObserverGrant;
  kind: "observer";
  match: string;
};

Defined in: packages/playerthree/src/protocol.ts:70

A token encodes exactly one principal. Presented once when joining (e.g. Colyseus join options).

Union Members

Type Literal
{
  kind: "team";
  match: string;
  team: string;
}

Trusted private team-root capability (every seat of the team); never issue to a public-lobby participant.


Type Literal
{
  kind: "human";
  match: string;
  role: string;
  team: string;
}

Human owner capability for exactly one seat, with own-side fogged viewing.


Type Literal
{
  kind: "seat";
  match: string;
  role: string;
  team: string;
}

Agent bound to exactly one seat. Cannot change controllers or mint tokens.


Type Literal
{
  kind: "spectator";
  match: string;
  team?: string;
}

Read-only; receives per-team fogged views.


Type Literal
{
  grant: ObserverGrant;
  kind: "observer";
  match: string;
}

Observer admission is independent of player, agent and operator authority.


ServerErrorCode

type ServerErrorCode = 
  | "bad_message"
  | "bad_token"
  | "forbidden"
  | "out_of_scope"
  | "approval_not_found"
  | "approval_pending"
  | "approval_failed"
  | "mode_unavailable"
  | "rate_limited"
  | "invalid"
  | "busy"
  | "internal"
  | "not_in_match"
  | "paused";

Defined in: packages/playerthree/src/protocol.ts:133


ServerMsg

type ServerMsg = 
  | ObserverMessage
  | {
  data: unknown;
  ok: true;
  op: "result";
  seq: number;
}
  | {
  code: ServerErrorCode;
  ok: false;
  op: "result";
  seq: number;
}
  | ViewMessage
  | {
  data: unknown;
  op: "obs";
  role: string;
}
  | {
  op: "seats";
  seats: SeatInfo[];
}
  | {
  cards: PublicSeatCard[];
  op: "cards";
}
  | {
  data: unknown;
  op: "report";
  team: string;
}
  | {
  op: "said";
  role: string;
  text: string;
  tick: number;
};

Defined in: packages/playerthree/src/protocol.ts:175

Union Members

ObserverMessage


Type Literal
{
  data: unknown;
  ok: true;
  op: "result";
  seq: number;
}

Type Literal
{
  code: ServerErrorCode;
  ok: false;
  op: "result";
  seq: number;
}

ViewMessage

Pushed to team owners/spectators at the view rate.


Type Literal
{
  data: unknown;
  op: "obs";
  role: string;
}

Stream mode push.


Type Literal
{
  op: "seats";
  seats: SeatInfo[];
}

Seats of the recipient's team (owners) or of the watched teams (spectators).


Type Literal
{
  cards: PublicSeatCard[];
  op: "cards";
}

Type Literal
{
  data: unknown;
  op: "report";
  team: string;
}

Guarded after-action report for team (manifest.report).


Type Literal
{
  op: "said";
  role: string;
  text: string;
  tick: number;
}

An agent's reply, to that team's owners only. Agent-authored: plain text.


ViewMessage

type ViewMessage = 
  | {
  op: "view";
  team: string;
  version: number;
  view: unknown;
}
  | {
  delta: JsonDelta;
  op: "view";
  team: string;
  version: number;
};

Defined in: packages/playerthree/src/protocol.ts:154

One step of a team's view stream: a keyframe (view, the full guarded view at version) or a playerthree/delta structural delta from version - 1 of the same team's stream. Built once, shared by every reader of the team, frozen.

Variables

MAX_MESSAGE_BYTES

const MAX_MESSAGE_BYTES: number;

Defined in: packages/playerthree/src/protocol.ts:194

Inbound message cap. Policy uploads are the largest legitimate message.


MSG

const MSG: object;

Defined in: packages/playerthree/src/protocol.ts:83

Message type names on the game transport.

Type Declaration

NameTypeDefault valueDescriptionDefined in
call"playerthree:call"'playerthree:call'-packages/playerthree/src/protocol.ts:84
cards"playerthree:cards"'playerthree:cards'-packages/playerthree/src/protocol.ts:97
controller"playerthree:controller"'playerthree:controller'-packages/playerthree/src/protocol.ts:90
mandate"playerthree:mandate"'playerthree:mandate'-packages/playerthree/src/protocol.ts:87
mint"playerthree:mint"'playerthree:mint'-packages/playerthree/src/protocol.ts:91
obs"playerthree:obs"'playerthree:obs'-packages/playerthree/src/protocol.ts:94
observer"playerthree:observer"'playerthree:observer'-packages/playerthree/src/protocol.ts:85
report"playerthree:report"'playerthree:report'Server → owners/spectators: a team's after-action report once its outcome is decided.packages/playerthree/src/protocol.ts:99
resolveApproval"playerthree:resolveApproval"'playerthree:resolveApproval'-packages/playerthree/src/protocol.ts:89
result"playerthree:result"'playerthree:result'-packages/playerthree/src/protocol.ts:92
said"playerthree:said"'playerthree:said'Server → that team's owners only: an agent's reply (Said).packages/playerthree/src/protocol.ts:105
scopes"playerthree:scopes"'playerthree:scopes'-packages/playerthree/src/protocol.ts:88
seats"playerthree:seats"'playerthree:seats'Server → owners/spectators: seat list (controllers, liveness, policy versions) whenever it changes.packages/playerthree/src/protocol.ts:96
stream"playerthree:stream"'playerthree:stream'-packages/playerthree/src/protocol.ts:86
tell"playerthree:tell"'playerthree:tell'Owner → own agents (chat mode).packages/playerthree/src/protocol.ts:103
view"playerthree:view"'playerthree:view'-packages/playerthree/src/protocol.ts:93
watch"playerthree:watch"'playerthree:watch'Spectator → transport: choose which teams' views to receive (WatchMsg). Handled by the transport binding.packages/playerthree/src/protocol.ts:101

PROTOCOL_VERSION

const PROTOCOL_VERSION: 1 = 1;

Defined in: packages/playerthree/src/protocol.ts:17


TOKEN_RE

const TOKEN_RE: RegExp;

Defined in: packages/playerthree/src/protocol.ts:192

Tokens: 32 chars base64url (24 random bytes). Validated as an opaque format before lookup.

Functions

applyView()

function applyView(state, message): ViewState | undefined;

Defined in: packages/playerthree/src/protocol.ts:169

Apply one playerthree:view message: a keyframe replaces the state, a delta applies only to exactly the previous version. Returns undefined for a delta without its base version (a protocol error: rejoin for a keyframe). state is not mutated and unchanged subtrees are shared with it, so a reader MUST treat views as immutable.

Parameters

ParameterType
stateViewState | undefined
messageViewMessage

Returns

ViewState | undefined