@table-games-engine/client-sdk
v0.1.3
Published
Browser SDK for table-games-engine matches on Nakama: state sync, dispatch, optimistic prediction, reconnect.
Maintainers
Readme
@table-games-engine/client-sdk
Framework-agnostic browser SDK for connecting to a Nakama-hosted match built on @table-games-engine/core. It keeps a local PlayerView in sync with the server, sends actions with idempotent retries, optionally predicts moves optimistically, and reconnects automatically.
Most game authors don't install this directly: store games talk to the host through the bridge in @table-games-engine/game-kit. Use this package if you build your own web client for a self-hosted Nakama server.
Install
npm install @table-games-engine/client-sdkESM only, targets ES2022 and browser globals; no Node polyfills needed.
Connect and dispatch
Provide a ClientTransport (an adapter over the Nakama JS client). InMemoryTransport / createInMemoryTransportPair are bundled for tests.
import { MatchClient, createInMemoryTransportPair, type PlayerId } from '@table-games-engine/client-sdk';
const { transport } = createInMemoryTransportPair();
const client = new MatchClient({
transport,
viewer: 'player-uuid' as PlayerId,
optimistic: true, // opt in
reconnect: { initialBackoffMs: 250, maxBackoffMs: 30_000 },
});
await client.join('match-id'); // the constructor has no side effects; join() connects
const result = await client.dispatch({
kind: 'action',
action: { type: 'action', actionTypeId: 'fire-bolt', targets: {}, payload: {} },
});
if (!result.accepted) console.warn('rejected', result.error);pass, concede, choose-targets and order-triggers go through the same dispatch. The SDK fills in the idempotent clientId and last-seen state version; retries reuse the clientId, so the server absorbs duplicates.
Subscribe
const unsub = client.subscribe((view) => render(view)); // fires on every view replacement
client.subscribeBroadcasts((event) => console.log(event.type)); // e.g. PlayerJoined
client.subscribeMatchEnded((result) => console.log(result));
client.getState(); // current view, or null before the first message
await client.leave(); // stops reconnection and disconnectsOptimistic prediction
Best effort, based only on the viewer's filtered PlayerView. Pass is predicted; generic actions are queued as a no-op "in flight" shadow; concede, choose-targets and order-triggers are not predicted. When the server's transition arrives the client rebases: server wins.
Reconnection
With reconnect options, a closed connection triggers exponential-backoff retries (default 250 ms start, 2x, capped at 30 s), rejoins the match, calls onReconnected, and replays pending optimistic actions with their original clientId.
Other exports
Connection, StateStore, applyServerTransition, dispatch, generateClientId, predictOptimistically, reconcile, ReconnectController, wire-format helpers (parseInbound, serializeOutbound, ...), OPCODES, and type re-exports from core (PlayerView, ActionInput, MatchState, StateTransition, ...), so you do not need core as a direct dependency.
Licence
Apache-2.0
