Games client plus its
publisher/subscription handles from there.
publishState ↔ publish_state) and keyword vs options
style. Both sit on the same MoQT substrate, so a TS player and a Python authority
interoperate on the wire.
Games — the client
One Games instance per (room, player) — or (room, server) for the
authority. The session is opened lazily on the first publish or subscribe and
reused for every channel.
- TypeScript
- Python
Bearer token for the relay session, scoped to
(tenant, room, player?).The room every channel binds to. All three channels live under
game/<roomId>/….Player identity. Omit for the authoritative server. Required to
publishInput or publishEvent — it becomes the from header on every frame.Relay hostname. Defaults to the platform relay.
Connection-state callback.
state is a ConnectionState
(0 Connecting · 1 Connected · 2 Reconnecting · 3 Closed · 4 Failed).Inject a custom WebTransport factory (for a Node polyfill or tests). Optional.
Namespace accessors
Read-only helpers if you need to inspect or interop with the raw tracks:state — server → all
publishState(args?) → StatePublisher
Authority-only. Opens the state track and returns a StatePublisher. State
frames carry no from header (the source is always the authority) and ride
the datagram lane at priority 100.
- TypeScript
- Python
Tick-rate hint for the relay’s admission control and the recorder. Does not
pace your writes — you drive the loop. Optional.
subscribeState(cb) → GamesSubscription
Player-side. Subscribes to the authority’s state and invokes your callback with
each snapshot’s raw bytes. Returns a GamesSubscription you can close().
StatePublisher
Publish one state snapshot.
priority defaults to 100. Timestamps are stamped
for you from a monotonic clock.Close the state track.
input — each player → server
publishInput() → FromPublisher
Player-only. Returns a FromPublisher that prefixes every frame with this
client’s playerId. Rides the datagram lane at priority 100. Throws if the
client has no playerId.
subscribeInputs(cb) → GamesSubscription
Authority-side. Receives every player’s input on one callback. The SDK
decodes the from header, so you get (playerId, bytes) per frame — no
per-player subscription bookkeeping.
Malformed input frames (bad
from header) are dropped silently rather than
throwing into your callback, so one bad packet can’t stall the tick loop. The
relay logs them as wire-format rejects.event — any → any (reliable)
publishEvent(args) → FromPublisher
Either role. Opens a reliable, ordered event channel and returns a
FromPublisher. Events ride a subgroup stream at priority 50 —
guaranteed delivery, in order. The server’s events are stamped from = "_authority"; a player’s events carry the player’s id.
- TypeScript
- Python
Event channel name —
chat, ready, rpc.use_item, etc. Each channel is its
own track under game/<room>/event/<channel>.subscribeEvents(args, cb) → GamesSubscription
Either role. Subscribes to one event channel; the callback gets the decoded
sender id and the raw event bytes.
FromPublisher
The handle returned by both publishInput and publishEvent. Prefixes every
write with the caller’s from id.
Publish one frame.
priority defaults to the channel default (100 for input,
50 for events). The from header is added for you.Close the underlying track.
Handles & lifecycle
GamesSubscription
Returned by every subscribe* method. Read-only ns / name plus:
Stop receiving on this channel.
Games.close()
close() the next publish/subscribe re-opens a fresh
session.
Connection events
There is no separate event emitter — connection lifecycle is delivered through theonState / on_state callback you pass to the constructor. The underlying
session auto-reconnects with capped backoff and replays every publish and
subscribe on reconnect, so your channels reattach without any code on your
side.
ConnectionState | Meaning |
|---|---|
0 Connecting | dialling the relay |
1 Connected | session up; channels (re)attached |
2 Reconnecting | link dropped; retrying with backoff |
3 Closed | you called close() |
4 Failed | unrecoverable (e.g. bad URL or auth) |
Wire helpers (advanced)
For interop tests or callers that bypass the high-level client but stay wire-compatible, thefrom-header codec is exported:
Other languages
The Go and Rust SDKs expose the same(room, player) model and the same three
channels over the shared MoqtClient, with method names in each language’s
idiomatic case. Package coordinates: github.com/clutchcall/clutchcall-sdk/go, clutchcall. Unity
games use the Netcode transport drop-in rather
than calling this client directly.
