NET FORGE DOCS

@netforge/engine/net API reference

Last updated Checked against @netforge/engine 0.2.0View as Markdown

Net Forge, the open-source multiplayer browser game engine built for AI coding agents, is published on npm under @netforge with source at github.com/netnetdev/net-forge.

The client half of the netcode and the wire protocol: prediction and reconciliation, interpolation, delta snapshots, input packets, the client clock and the SimAdapter seam a room host drives.

import { InputQueue, encodeSnapshot } from "@netforge/engine/net";

Generated from packages/engine/src/net/index.ts by apps/forge-docs/scripts/api.mjs. Signatures are what the TypeScript compiler reports for the current source; long types are cut and say so.

KindCount
Functions23
Classes11
Constants9
Types47

Functions

bitsForSigned

Bits needed for a zigzagged signed value of magnitude up to maxAbs.

function bitsForSigned(maxAbs: number): number;

Source: packages/engine/src/net/bits.ts

connectReservation

function connectReservation<W, I>(sim: SimAdapter<W, I>, endpoint: string, reservation: Parameters<Client["consumeSeatReservation"]>[0]): Promise<RoomConnection<W, I>>;

Source: packages/engine/src/net/room-client.ts

decodeInputPacket

function decodeInputPacket<I>(codec: InputCodec<I>, bytes: Uint8Array): InputPacket<I>;

Source: packages/engine/src/net/input.ts

decodeSnapshot

Decodes a snapshot. baselineFor returns the client's stored entity set for a tick it previously decoded.

function decodeSnapshot(schema: EntitySchema, bytes: Uint8Array, baselineFor: (tick: number) => ReadonlyMap<number, QEntity> | undefined): DecodedSnapshot;

Source: packages/engine/src/net/snapshot.ts

dequantize

Wire integer back to the real value.

function dequantize(f: FieldSpec, q: number): number;

Source: packages/engine/src/net/schema.ts

dequantizeValues

function dequantizeValues(schema: EntitySchema, q: readonly number[]): number[];

Source: packages/engine/src/net/schema.ts

encodeInputPacket

function encodeInputPacket<I>(codec: InputCodec<I>, p: InputPacket<I>): Uint8Array;

Source: packages/engine/src/net/input.ts

encodeSnapshot

Encodes current (any order) against baseline (null for a full snapshot).

function encodeSnapshot(schema: EntitySchema, header: SnapshotHeader, current: readonly QEntity[], baseline: ReadonlyMap<number, QEntity> | null): Uint8Array;

Source: packages/engine/src/net/snapshot.ts

fieldBits

function fieldBits(f: FieldSpec): number;

Source: packages/engine/src/net/schema.ts

fieldDeltaBits

Default bit width for a "small change" delta of a field, or 0 for none.

function fieldDeltaBits(f: FieldSpec): number;

Source: packages/engine/src/net/schema.ts

guestId

This browser's guest id (made on first use). Storage blocked: one per page load.

function guestId(): string;

Source: packages/engine/src/net/profile-client.ts

interpolateField

Interpolate one field between a and b (real values).

function interpolateField(f: FieldSpec, a: number, b: number, t: number): number;

Source: packages/engine/src/net/schema.ts

joinRoom

function joinRoom<W, I>(sim: SimAdapter<W, I>, p: JoinParams): Promise<RoomConnection<W, I>>;

Source: packages/engine/src/net/room-client.ts

kindFieldIndices

Field indices carried on the wire for an entity kind (all fields unless the schema narrows that kind).

function kindFieldIndices(schema: EntitySchema, kind: number): readonly number[];

Source: packages/engine/src/net/snapshot.ts

pageGame

The page's ?game= slug (set by Net Forge on a launched game's frame), or null outside a browser or without one.

function pageGame(): string | null;

Source: packages/engine/src/net/room-client.ts

quantize

Real value to the integer the wire carries.

function quantize(f: FieldSpec, v: number): number;

Source: packages/engine/src/net/schema.ts

quantizeEntity

Quantize an entity's values. With only (the kind's wire fields), the other fields are left 0: they never go on the wire for that kind.

function quantizeEntity(schema: EntitySchema, e: EntityState, only?: readonly number[]): number[];

Source: packages/engine/src/net/schema.ts

rotateGuestId

A fresh guest id: after a guest profile merges into a wallet, the old id is spent.

function rotateGuestId(): string;

Source: packages/engine/src/net/profile-client.ts

savedSession

The saved session token for a rooms server, if it has not expired.

function savedSession(endpoint: string): { token: string; address: string; } | null;

Source: packages/engine/src/net/profile-client.ts

saveSession

function saveSession(endpoint: string, s: { token: string; address: string; expiresAt: number; } | null): void;

Source: packages/engine/src/net/profile-client.ts

signInWithWallet

Sign in to a rooms server with a wallet: its nonce, an EIP-4361 message for its domain, one personal_sign (no gas), a session token. Saved for this endpoint until it expires.

function signInWithWallet(endpoint: string, provider: Eip1193, statement?: string): Promise<{ token: string; address: string; expiresAt: number; }>;

Source: packages/engine/src/net/profile-client.ts

unzigzag

function unzigzag(z: number): number;

Source: packages/engine/src/net/bits.ts

zigzag

function zigzag(n: number): number;

Source: packages/engine/src/net/bits.ts

Classes

BaselineMissingError

class BaselineMissingError {
  constructor(baselineTick: number);
}

Source: packages/engine/src/net/snapshot.ts

BitReader

class BitReader {
  constructor(buf: Uint8Array);
  readBits(bits: number): number;
  readBool(): boolean;
  readSigned(bits: number): number;
  readVarUint(): number;
  get remainingBits(): number;
}

Source: packages/engine/src/net/bits.ts

BitWriter

class BitWriter {
  constructor(initialBytes?: number);
  writeBits(value: number, bits: number): void;
  writeBool(b: boolean): void;
  writeSigned(value: number, bits: number): void;
  writeVarUint(value: number): void;
  get bitLength(): number;
  finish(): Uint8Array;
}

Source: packages/engine/src/net/bits.ts

InputQueue

Server-side per-player input queue with speed-hack protection.

class InputQueue {
  constructor<I>(opts?: InputQueueOptions);
  lastAppliedSeq: number;
  dropped: number;
  starved: number;
  receive(commands: readonly InputCommand<I>[]): void;
  consume(): InputCommand<I>[];
  get pending(): number;
}

Source: packages/engine/src/net/input.ts

InterpolationBuffer

class InterpolationBuffer {
  constructor(schema: EntitySchema, capacity?: number);
  push(tick: number, entities: ReadonlyMap<number, QEntity>): void;
  sample(renderTick: number, exclude?: number): InterpolatedEntity[];
  get newestTick(): number;
}

Source: packages/engine/src/net/interpolation.ts

NetClient

class NetClient {
  constructor<W, I>(sim: SimAdapter<W, I>, player: PlayerId, selfEntity: number, send: (bytes: Uint8Array) => void, opts?: NetClientOptions);
  readonly stats: ClientStats;
  get nextSeq(): number;
  receiveSnapshot(bytes: Uint8Array, nowMs: number): void;
  tick(input: I, nowMs: number): void;
  tickIntervalMs(): number;
  get serverInputBuffer(): number | null;
  renderTick(nowMs: number): number;
  render(nowMs: number): { self: EntityState | undefined; others: InterpolatedEntity[]; };
  authoritativeSelf(): EntityState | undefined;
  interpolatedSelf(nowMs: number): InterpolatedEntity | undefined;
  predicted(): EntityState | undefined;
  get ready(): boolean;
  get corrections(): number;
  get lastCorrection(): number;
  get pendingInputs(): number;
}

Source: packages/engine/src/net/client.ts

Predictor

class Predictor {
  constructor<W, I>(sim: SimAdapter<W, I>, player: PlayerId, selfEntity: number, seed?: number, firstSeq?: number);
  lastCorrection: number;
  corrections: number;
  get upcomingSeq(): number;
  get isReady(): boolean;
  predict(input: I, viewTick16: number): InputCommand<I> | null;
  unacked(): readonly InputCommand<I>[];
  reconcile(ackSeq: number, serverSelf: EntityState): void;
  state(): EntityState | undefined;
  renderState(dtMs: number): EntityState | undefined;
}

Source: packages/engine/src/net/prediction.ts

ProfileApi

The room server's profile routes for one game.

class ProfileApi {
  constructor(endpoint: string, game: string);
  get(auth: ProfileAuth): Promise<GameProfile>;
  importLocal(guest: string, local: LocalProgress): Promise<GameProfile>;
  claim(session: string, guest: string): Promise<GameProfile>;
  setSettings(auth: ProfileAuth, settings: Readonly<Record<string, unknown>>): Promise<GameProfile>;
  setName(auth: ProfileAuth, displayName: string): Promise<GameProfile>;
  redeem(session: string, item: string, key: string): Promise<GameProfile>;
}

Source: packages/engine/src/net/profile-client.ts

ServerClock

Tracks server time from snapshot arrivals. The offset (server ms minus local ms) is taken from the freshest-looking arrival, eased toward later samples so jitter does not make the render time jump.

class ServerClock {
  constructor(tickMs: number);
  observe(tick: number, localMs: number): void;
  serverTick(localMs: number): number;
  get ready(): boolean;
}

Source: packages/engine/src/net/interpolation.ts

SnapshotHistory

Per-client history of what was sent, keyed by tick, so the next snapshot can be encoded against whatever the client last acked.

class SnapshotHistory {
  constructor(capacity?: number);
  put(tick: number, entities: readonly QEntity[] | ReadonlyMap<number, QEntity>): void;
  get(tick: number): ReadonlyMap<number, QEntity> | undefined;
  pruneBefore(tick: number): void;
  get size(): number;
}

Source: packages/engine/src/net/snapshot.ts

VirtualNet

class VirtualNet {
  constructor(seed?: number);
  now: number;
  sent: number;
  dropped: number;
  random(): number;
  send(link: LinkOptions, deliver: () => void): void;
  advanceTo(t: number): void;
}

Source: packages/engine/src/net/netsim.ts

Constants

DEFAULT_CLIENT

const DEFAULT_CLIENT: NetClientOptions;

Source: packages/engine/src/net/client.ts

DEFAULT_INPUT_QUEUE

const DEFAULT_INPUT_QUEUE: InputQueueOptions;

Source: packages/engine/src/net/input.ts

GAME_SLUG_RE

A game slug as the GameFactory and the room server take it.

const GAME_SLUG_RE: RegExp;

Source: packages/engine/src/net/protocol.ts

GUEST_ID_RE

Guest profile ids: 16-80 characters of A-Z, a-z, 0-9, _ or - (the same rule as Launcher guest drafts).

const GUEST_ID_RE: RegExp;

Source: packages/engine/src/net/protocol.ts

GUEST_KEY

Storage key for the guest id: shared by every game on one origin, like a Launcher guest.

const GUEST_KEY: "netnet:guest:v1";

Source: packages/engine/src/net/profile-client.ts

MSG_INPUT

const MSG_INPUT: 1;

Source: packages/engine/src/net/protocol.ts

MSG_SNAPSHOT

const MSG_SNAPSHOT: 2;

Source: packages/engine/src/net/protocol.ts

REDUNDANCY

How many unacked commands each packet repeats.

const REDUNDANCY: 4;

Source: packages/engine/src/net/input.ts

ROOM_NAME

const ROOM_NAME: "instance";

Source: packages/engine/src/net/protocol.ts

Types

ClientStats

interface ClientStats {
  snapshots: number;
  snapshotBytes: number;
  inputPackets: number;
  inputBytes: number;
  baselineMisses: number;
  staleSnapshots: number;
}

Source: packages/engine/src/net/client.ts

CommandMessage

Client -> server: a store hook (paid revive, cosmetic). The room verifies before the sim applies it.

interface CommandMessage {
  readonly name: string;
  readonly value?: number;
  readonly proof?: string;
}

Source: packages/engine/src/net/protocol.ts

CommandResult

Server -> client: the outcome of a command.

interface CommandResult {
  readonly name: string;
  readonly ok: boolean;
  readonly reason?: string;
}

Source: packages/engine/src/net/protocol.ts

CreateOptions

interface CreateOptions {
  readonly place: string;
  readonly template: string;
  readonly game?: string;
}

Source: packages/engine/src/net/protocol.ts

DecodedSnapshot

interface DecodedSnapshot {
  readonly header: SnapshotHeader;
  readonly entities: ReadonlyMap<number, QEntity>;
  readonly removed: readonly number[];
}

Source: packages/engine/src/net/snapshot.ts

EarnEvent

Something the authoritative sim says a player earned.

interface EarnEvent {
  readonly type: 'earn';
  readonly player: PlayerId;
  readonly rule: string;
  readonly units: number;
}

Source: packages/engine/src/net/sim-adapter.ts

EarnMessage

interface EarnMessage {
  readonly rule: string;
  readonly units: number;
}

Source: packages/engine/src/net/protocol.ts

EarnRefusal

type EarnRefusal = 'guest' | 'wallet-busy' | 'session';

Source: packages/engine/src/net/protocol.ts

Eip1193

The slice of an EIP-1193 provider sign-in needs.

interface Eip1193 {
  request(args: { method: string; params?: unknown[] }): Promise<unknown>;
}

Source: packages/engine/src/net/profile-client.ts

EntitySchema

interface EntitySchema {
  readonly fields: readonly FieldSpec[];
  readonly xField: number;
  readonly yField: number;
  readonly unitsPerWorld: number;
  readonly kindFields?: Readonly<Record<number, readonly number[]>>;
}

Source: packages/engine/src/net/schema.ts

EntityState

One entity as the sim reports it: values are real (dequantized) numbers.

interface EntityState {
  readonly id: number;
  readonly kind: number;
  readonly owner: number;
  readonly values: readonly number[];
}

Source: packages/engine/src/net/schema.ts

FieldSpec

type FieldSpec =
  | { readonly name: string; readonly kind: 'uint'; readonly bits: number; readonly interp?: Interp; readonly deltaBits?: number }
  | { readonly name: string; readonly kind: 'int'; readonly bits: number; readonly interp?: Interp; readonly deltaBits?: number }
  | {
      readonly name: string;
      readonly kind: 'quant';
      readonly min: number;
      readonly max: number;
      readonly bits: number;
      readonly interp?: Interp;
      readonly deltaBits?: number;
    }
  | { readonly name: string; readonly kind: 'angle16'; readonly deltaBits?: number }
  | { readonly name: string; readonly kind: 'bool' };

Source: packages/engine/src/net/schema.ts

GameProfile

A player's saved progress in one game, kept by the room server: the same on every device the player signs in on. Achievements, goals and titles come from the authoritative sim only.

interface GameProfile {
  readonly game: string;
  readonly kind: 'wallet' | 'guest' | null;
  readonly wallet: string | null;
  readonly displayName: string | null;
  readonly achievements: readonly { readonly id: string; readonly at: number; readonly source: 'sim' | 'import' }[];
  readonly keepers: readonly string[];
  readonly owned: readonly string[];
  readonly cosmetics: readonly number[];
  readonly meta: Readonly<Record<string, number>>;
  readonly goals: { readonly day: number; readonly today: readonly string[]; readonly done: readonly string[] };
  readonly titles: Readonly<Record<string, number>>;
  readonly settings: Readonly<Record<string, unknown>>;
  readonly store: readonly ProfileStoreItem[];
  readonly imported: boolean;
  readonly credit: number;
  readonly totals: { readonly achievements: number; readonly titles: number; readonly games: number };
}

Source: packages/engine/src/net/protocol.ts

Hitbox

interface Hitbox {
  readonly entity: number;
  readonly owner: PlayerId;
  readonly x: number;
  readonly y: number;
  readonly r: number;
}

Source: packages/engine/src/net/sim-adapter.ts

HitscanRequest

A hitscan the sim wants resolved against lag-compensated hitboxes.

interface HitscanRequest {
  readonly shooter: PlayerId;
  readonly ox: number;
  readonly oy: number;
  readonly dx: number;
  readonly dy: number;
  readonly range: number;
}

Source: packages/engine/src/net/sim-adapter.ts

InputCodec

interface InputCodec<I> {
  write(w: BitWriter, input: I): void;
  read(r: BitReader): I;
  readonly idle: I;
}

Source: packages/engine/src/net/sim-adapter.ts

InputCommand

interface InputCommand<I> {
  readonly seq: number;
  readonly viewTick16: number;
  readonly input: I;
}

Source: packages/engine/src/net/input.ts

InputPacket

interface InputPacket<I> {
  readonly ackTick: number;
  readonly commands: readonly InputCommand<I>[];
}

Source: packages/engine/src/net/input.ts

InputQueueOptions

interface InputQueueOptions {
  readonly maxQueue: number;
  readonly maxCredits: number;
  readonly targetDepth?: number;
}

Source: packages/engine/src/net/input.ts

Interp

type Interp = 'lerp' | 'step';

Source: packages/engine/src/net/schema.ts

InterpolatedEntity

interface InterpolatedEntity {
  readonly id: number;
  readonly kind: number;
  readonly owner: number;
  readonly values: readonly number[];
}

Source: packages/engine/src/net/interpolation.ts

JoinOptions

interface JoinOptions {
  readonly wallet?: string;
  readonly session?: string;
  readonly guest?: string;
}

Source: packages/engine/src/net/protocol.ts

JoinParams

interface JoinParams {
  readonly endpoint: string; // http(s)://host:port
  readonly place: string;
  readonly template: string;
  readonly game?: string;
  readonly wallet?: string;
  readonly session?: string;
  readonly guest?: string;
  readonly friendRoomId?: string;
}

Source: packages/engine/src/net/room-client.ts

LinkOptions

interface LinkOptions {
  readonly latencyMs: number;
  readonly jitterMs: number;
  readonly loss: number;
}

Source: packages/engine/src/net/netsim.ts

LocalProgress

interface LocalProgress {
  readonly achievements?: readonly string[];
  readonly goals?: { readonly day: number; readonly done: readonly string[] };
  readonly settings?: Readonly<Record<string, unknown>>;
}

Source: packages/engine/src/net/profile-client.ts

NetClientOptions

interface NetClientOptions {
  readonly interpDelayMs: number;
  readonly inputBufferTarget?: number;
  readonly firstSeq?: number;
}

Source: packages/engine/src/net/client.ts

PlayerId

Small integer assigned by the room to each connected player (1..65535).

type PlayerId = number;

Source: packages/engine/src/net/sim-adapter.ts

PlayerRights

What a player's profile lets them choose (the room checks it on every input).

interface PlayerRights {
  readonly keepers: readonly number[] | null;
}

Source: packages/engine/src/net/sim-adapter.ts

PotMessage

Server -> client ('pot'): 'in' this round's pot holds the player's entry (pot is the whole pot so far); 'waiting' the entry is placed for the next round; 'closed' the round split the pot (paid is this player's part); 'refused' (reason). Whole coins.

interface PotMessage {
  readonly state: 'in' | 'waiting' | 'closed' | 'refused';
  readonly entry: number;
  readonly pot?: number;
  readonly paid?: number;
  readonly reason?: string;
}

Source: packages/engine/src/net/protocol.ts

ProfileAuth

Who is asking: a signed-in session (bearer) or a guest id.

type ProfileAuth = { readonly session: string } | { readonly guest: string };

Source: packages/engine/src/net/profile-client.ts

ProfileStoreItem

One item of a game's permanent store as a profile sees it (cosmetics, unlocks, meta levels).

interface ProfileStoreItem {
  readonly id: string;
  readonly kind: 'cosmetic' | 'unlock' | 'meta' | 'gear';
  readonly coins: number | null;
  readonly owned: boolean;
  readonly cosmetic?: number;
  readonly keeper?: string;
  readonly gear?: string;
  readonly loseBelow?: number;
  readonly stat?: string;
  readonly step?: number;
  readonly level?: number;
  readonly levels?: number;
  readonly bonus?: number;
}

Source: packages/engine/src/net/protocol.ts

ProgressEvent

The sim says a player reached something their profile keeps: an achievement, one of today's goals, or an end-screen title. The room writes it to the player's profile on the server; the browser never grants these.

interface ProgressEvent {
  readonly type: 'progress';
  readonly player: PlayerId;
  readonly kind: 'achievement' | 'goal' | 'title';
  readonly id: string;
}

Source: packages/engine/src/net/sim-adapter.ts

QEntity

One entity on the wire, values already quantized.

interface QEntity {
  readonly id: number;
  readonly kind: number;
  readonly owner: number;
  readonly q: readonly number[];
}

Source: packages/engine/src/net/snapshot.ts

RebaseMessage

Server -> client: the room rebuilt its world at a safe point (between rounds): a new logic module (hot swap, version changed) or a fresh verification segment. The player keeps its id; its entity id may change, baselines are void and the next snapshot is full.

interface RebaseMessage {
  readonly entity: number;
  readonly sim: string;
  readonly version: string;
}

Source: packages/engine/src/net/protocol.ts

RoomConnection

interface RoomConnection<W, I> {
  readonly room: Room;
  readonly net: NetClient<W, I>;
  readonly welcome: WelcomeMessage;
  readonly roomId: string;
  bytesIn(): number;
  messagesIn(): number;
  onEarn(cb: (e: EarnMessage) => void): void;
  onRun(cb: (info: unknown) => void): void;
  latestRun(): unknown;
  onRunId(cb: (m: RunIdMessage) => void): void;
  onRebase(cb: (m: RebaseMessage) => void): void;
  signIn(session: string): Promise<SignInResult>;
  onProfile(cb: (p: GameProfile) => void): void;
  latestProfile(): GameProfile | null;
  stake(amount?: number): void;
  onStake(cb: (m: StakeMessage) => void): void;
  latestStake(): StakeMessage | null;
  pot(): void;
  onPot(cb: (m: PotMessage) => void): void;
  command(cmd: CommandMessage): Promise<CommandResult>;
  claim(): Promise<unknown>;
  leave(): Promise<void>;
}

Source: packages/engine/src/net/room-client.ts

RunIdMessage

Server -> client: a fresh referee run started (new runId after a run reset).

interface RunIdMessage {
  readonly runId: string | null;
}

Source: packages/engine/src/net/protocol.ts

RunResetEvent

The sim started a fresh run in place (game over or victory screen ended). Referee runs restart.

interface RunResetEvent {
  readonly type: 'run.reset';
}

Source: packages/engine/src/net/sim-adapter.ts

SignInMessage

Client -> server: sign in mid-session (a guest who just signed the SIWE message).

interface SignInMessage {
  readonly session: string;
}

Source: packages/engine/src/net/protocol.ts

SignInResult

Server -> client: the outcome of a mid-session sign-in.

interface SignInResult {
  readonly ok: boolean;
  readonly runId: string | null;
  readonly earnRefused?: EarnRefusal;
}

Source: packages/engine/src/net/protocol.ts

SimAdapter

interface SimAdapter<W, I> {
  readonly name: string;
  readonly version: string;
  readonly tickHz: number;
  readonly schema: EntitySchema;
  readonly input: InputCodec<I>;
  readonly maxInputsPerTick?: number;
  createWorld(seed: number, role: 'server' | 'client'): W;
  addPlayer(world: W, player: PlayerId): number;
  removePlayer(world: W, player: PlayerId): void;
  step(world: W, inputsByPlayer: ReadonlyMap<PlayerId, readonly I[]>): readonly SimEvent[];
  snapshot(world: W): readonly EntityState[];
  applySnapshot(world: W, entities: readonly EntityState[]): void;
  hitscans?(world: W, player: PlayerId, input: I): HitscanRequest | null;
  hitboxes?(world: W): readonly Hitbox[];
  applyHit?(world: W, shooter: PlayerId, target: Hitbox): readonly SimEvent[];
  info?(world: W): unknown;
  infoFor?(world: W, player: PlayerId): unknown;
  readonly infoEveryTicks?: number;
  restrictInput?(input: I, rights: PlayerRights): I;
  command?(world: W, player: PlayerId, cmd: SimCommand): boolean;
  startRun?(world: W, player: PlayerId): void;
  disposeWorld?(world: W): void;
}

Source: packages/engine/src/net/sim-adapter.ts

SimCommand

A room-level command from a player (store hooks): a paid revive, a cosmetic.

interface SimCommand {
  readonly name: string;
  readonly value?: number;
}

Source: packages/engine/src/net/sim-adapter.ts

SimEvent

type SimEvent = EarnEvent | RunResetEvent | ProgressEvent;

Source: packages/engine/src/net/sim-adapter.ts

SnapshotHeader

interface SnapshotHeader {
  readonly tick: number;
  readonly baselineTick: number;
  readonly ackSeq: number;
  readonly self: number;
  readonly inputBuffer?: number;
}

Source: packages/engine/src/net/snapshot.ts

StakeMessage

Server -> client ('stake'): 'live' the run in progress is staked; 'waiting' a stake is placed for the next run; 'closed' a staked run ended (paid is its stake share); 'refused' the stake was not taken (reason). Amounts are whole coins.

interface StakeMessage {
  readonly state: 'live' | 'waiting' | 'closed' | 'refused';
  readonly stake: number;
  readonly ceiling?: number;
  readonly paid?: number;
  readonly reason?: string;
  readonly gear?: readonly string[];
  readonly lost?: readonly string[];
}

Source: packages/engine/src/net/protocol.ts

StakeRequest

Client -> server ('stake'): the player spent a stake (the Kit's coins.spend(amount, "stake"), committed). The room fixes the stake from its own ledger when a run starts: always the NEXT run (sign-in or the next round; a run in progress never restarts), and a staked run starts from zero earn progress. amount (whole coins) is read only by a room whose store runs in mock mode (local play).

interface StakeRequest {
  readonly amount?: number;
}

Source: packages/engine/src/net/protocol.ts

StakeTermsView

A game's buy-in run terms as a client shows them (whole coins).

interface StakeTermsView {
  readonly min: number;
  readonly max: number;
  readonly multiple: number;
}

Source: packages/engine/src/net/protocol.ts

WelcomeMessage

interface WelcomeMessage {
  readonly player: number;
  readonly entity: number;
  readonly tickHz: number;
  readonly snapshotHz: number;
  readonly sim: string;
  readonly version: string;
  readonly place: string;
  readonly template: string;
  readonly runId: string | null;
  readonly earnLive: boolean;
  readonly earnRefused?: EarnRefusal;
  readonly stake?: StakeTermsView;
  readonly pot?: { readonly entry: number; readonly recycle: number };
}

Source: packages/engine/src/net/protocol.ts