@tpgames/game-kit
v0.5.1
Published
Renderer-free authoring helpers for Toilet Paper Games.
Readme
@tpgames/game-kit
Renderer-free authoring helpers for Toilet Paper Games.
Use this package when building iframe-hosted game surfaces. It re-exports the
core TPG game APIs and provides bootIframeGame, which creates the shell
postMessage bridge internally before booting @tpgames/runtime-game.
bun add @tpgames/game-kitimport { bootIframeGame, defineSimpleGame, dispatchLocalPreviewLifecycle } from "@tpgames/game-kit";
interface SharedState {
round: number;
}
interface PlayerState {
answer: string;
}
const game = defineSimpleGame<SharedState, PlayerState>({
ready(api) {
void api.reportLoading(true);
}
});
const api = bootIframeGame(game, {
context: {
surfaceKind: "controller",
surfaceId: "controller"
}
});
const sharedState = api.getSharedState();
const playerSnapshot = api.getPlayerStateSnapshot();
const mutation = await api.setPlayerState(
{ answer: "paper" },
undefined,
{ expectedRevision: playerSnapshot?.revision ?? 0 }
);
if (mutation.status === "rejected") {
console.warn(mutation.message);
}
const connectedControllerIds = api.controllerIds({ connectedOnly: true });
await api.sendGameAction({ type: "round.submit", answer: "paper" });
await api.leaveRoom();
if (window.parent === window) {
dispatchLocalPreviewLifecycle();
}The shared and player state types declared with defineSimpleGame flow through
bootIframeGame. The returned API combines typed state access and controller
helpers with iframe-only actions such as sendGameAction and leaveRoom.
Subscribe to lifecycle, participant, settings, shared-state, and player-state
updates through their typed runtime API methods. Advanced defineGame
definitions continue to receive the generic low-level runtime API.
Lightweight DOM surfaces can also share the kit's infrastructure helpers:
import {
createIdempotentSurfaceReadyReporter,
escapeHtml
} from "@tpgames/game-kit";
let runtimeApi: ReturnType<typeof bootIframeGame> | undefined;
const reportSurfaceReady = createIdempotentSurfaceReadyReporter(() => runtimeApi);
container.innerHTML = `<h1>${escapeHtml(title)}</h1>`;
reportSurfaceReady();escapeHtml is for text and attribute interpolation into HTML strings.
createIdempotentSurfaceReadyReporter waits until the runtime API is available
and calls reportLoading(true) at most once.
Only api.context().isAuthority may commit shared state. A controller or
spectator can update only its own player state. State mutations return an
explicit applied, accepted, or rejected result; snapshot revisions can be
passed as expectedRevision to reject stale read-modify-write attempts.
Every mounted iframe runs its own game definition. Shell lifecycle hooks and
surface-readiness hooks are separate, surfacesLoading/surfacesReady can
repeat, and state setters become locally visible only after the shell echoes a
snapshot. See the SDK runtime guide for idempotency and
channel-selection rules.
Rendering is intentionally out of scope. Bring React, Three.js, Phaser, Pixi, p5, Kaplay, or any other renderer separately.
