@jgengine/convex 0.49.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,15 @@ Use [capabilities.md](capabilities.md) for intent-to-import discovery, [api.md](
39
39
  - Persistence adapters accept structural interfaces and keep backend dependencies out of core.
40
40
  - Deterministic ids and commands make retries/reconnect safe.
41
41
 
42
+ ## Rates and wire defaults
43
+
44
+ - Every host ticks at 20 Hz (`DEFAULT_HOST_TICK_MS`, 50 ms) unless the game passes `tickMs`; `resolveHostTickMs` rejects values above 1000 ms. The Convex hosted default (`JG_HOSTED_TICK_MS`) is the same constant, but a Convex world only advances as often as its cron fires.
45
+ - `createWsBackend` speaks the binary delta codec by default: per-entity field deltas against the previous frame, quantised poses, varint entity ids. Pass `codec: "json"` for readable frames while debugging. Precision is the host's: `createHostRouter({ precision: { position: 2, angle: 3 } })` (decimal places, 0 to 6).
46
+ - A viewer's interest is the game's `replication: { aoiRadius, aoiAlways }`. The host asks the scene spatial index for entities within `aoiRadius` of the viewer's own entity, adds `aoiAlways` ids (a list or a function), and sends each connection a diff of only what entered, changed or left its view. A viewer with no entity yet holds every entity.
47
+ - A hosted world auto-saves a changed world at most once per `saveIntervalMs` (the node server and the browser host default to `DEFAULT_HOST_SAVE_INTERVAL_MS`, 1000); `0` saves after every changed tick, which copies the whole world 20 times a second. `bun scripts/multiplayer-load.ts` reports host ms per tick, and bytes allocated per tick under `BUN_JSC_useGC=false`.
48
+ - A module that throttles itself with `priority` still gets a projected baseline each tick instead of diffs.
49
+ - Measure with `bun scripts/multiplayer-load.ts` (bytes and host ms per tick for N clients and E entities) and `bun scripts/multiplayer-two-clients.ts` (a real game, two clients).
50
+
42
51
  ## Traps
43
52
 
44
53
  - Local loopback success does not prove authority or privacy.
@@ -5,27 +5,27 @@
5
5
  ## @jgengine/convex
6
6
 
7
7
  - `ConvexBackend` (type): type ConvexBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = LiveGameBackend<TPresenceRow, TPresenceLocation, TGameId> & { leaderboard: ConvexLeaderboardReads; } — ⚠ undocumented
8
- - `ConvexBackendOptions` (type): type ConvexBackendOptions<TRawPresenceRow extends { actorExternalId: string }, TPresenceRow> = { client: ConvexReactClient; gameId: string; userId: string; api?: ConvexGameApi; poseTuning?: PoseSyncTuning; presence?: { functions: ConvexPresenceFunctions; mapRow: (row: TRawPresenceRow) => TPresenceRo… — ⚠ undocumented
8
+ - `ConvexBackendOptions` (type): type ConvexBackendOptions<TRawPresenceRow extends { actorExternalId: string }, TPresenceRow> = { client: ConvexReactClient; gameId: string; userId: string; api?: ConvexGameApi | undefined; poseTuning?: PoseSyncTuning | undefined; presence?: { functions: ConvexPresenceFunctions; mapRow: (row: TRawPre… — ⚠ undocumented
9
9
  - `ConvexChatFunctions` (interface): interface ConvexChatFunctions { messages: FunctionReference<"query">; sendMessage: FunctionReference<"mutation"> } — ⚠ undocumented · used by `createConvexChatTransport`: Wires a game's Convex chat functions into the engine's ChatTransport contract: one live query per subscribed channel (the channel's recent h…
10
- - `ConvexGameApi` (type): type ConvexGameApi = { runtime: { joinServer: FunctionReference< "mutation", "public", { gameId: string; serverId?: string; mode?: string; visibility?: "public" | "private"; joinCode?: string; externalId?: string; sessionId?: string; }, JoinServerOutcome >; leaveServer: FunctionReference<"mutation",… — ⚠ undocumented
10
+ - `ConvexGameApi` (type): type ConvexGameApi = { runtime: { joinServer: FunctionReference< "mutation", "public", { gameId: string; serverId?: string | undefined; mode?: string; visibility?: "public" | "private"; joinCode?: string; externalId?: string | undefined; sessionId?: string; }, JoinServerOutcome >; leaveServer: Funct… — ⚠ undocumented
11
11
  - `ConvexGameClient` (type): type ConvexGameClient = Pick<ConvexReactClient, "mutation" | "watchQuery"> — Structural client seam avoids requiring the engine and game to share a class instance type.
12
12
  - `ConvexGameTransportConfig` (type): type ConvexGameTransportConfig = { gameId: string; userId?: string; } — ⚠ undocumented
13
13
  - `ConvexLeaderboardReads` (type): type ConvexLeaderboardReads = ReturnType<typeof createConvexLeaderboardReads> — ⚠ undocumented
14
14
  - `ConvexPresenceFunctions` (interface): interface ConvexPresenceFunctions — The Convex functions a **game** supplies for a world it owns: presence keyed by the game's own ids, including which residents exist while offline, which is game content and has no engine analogue. Not satisfied by `createPresenceFunctions` (`@jgengine/convex/server`) — that is the pose lane of a hosted `jgGameServers` server, backing `PresenceSync` rather than `PresenceTransport`.
15
- - `ConvexRunCommandArgs` (type): type ConvexRunCommandArgs = RunCommandArgs & { externalId?: string; /** * How much of the server to hydrate before applying the command. Overrides whatever the * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world. */ scope?: LoadSnapshotScope; /** * Trusted hos… — Arguments of the `runCommand` mutation, and of the equivalent {@link GameServerHelpers.runCommand} helper.
15
+ - `ConvexRunCommandArgs` (type): type ConvexRunCommandArgs = RunCommandArgs & { externalId?: string | undefined; /** * How much of the server to hydrate before applying the command. Overrides whatever the * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world. */ scope?: LoadSnapshotScope; /** *… — Arguments of the `runCommand` mutation, and of the equivalent {@link GameServerHelpers.runCommand} helper.
16
16
  - `ConvexSaveBackendOptions` (interface): interface ConvexSaveBackendOptions — Config for {@link createConvexSaveBackend} — the Convex client, optional function refs (defaults to the `saves.*` convention), and an optional key `namespace`.
17
17
  - `ConvexSaveFunctions` (interface): interface ConvexSaveFunctions — The three Convex functions a save backend calls — a `query` that returns the stored string (or `null`) and two `mutation`s. Point them at your app's own module, or lean on the default `saves.*` convention.
18
18
  - `DEFAULT_CONVEX_POSE_RULES` (const): const DEFAULT_CONVEX_POSE_RULES: PoseSyncRules — ⚠ undocumented
19
19
  - `DEFAULT_MAX_SERVERS_PER_TICK` (const): const DEFAULT_MAX_SERVERS_PER_TICK: 32 — How many running servers one `tickActiveServers` transaction may hydrate, tick, and persist.
20
20
  - `EnsureJoinedOutcome` (type): type EnsureJoinedOutcome = | { ok: true; serverId: GenericId<"jgGameServers">; isNew: boolean } | { ok: false; reason: "full" | "closed" | "unauthorized" } — Outcome of a join: the server joined and whether the player's profile was created, or why it was refused.
21
- - `GameServerHelpers` (type): type GameServerHelpers = { ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>; loadSnapshot: ( ctx: JGMutationCtx, serverId: string, scope?: LoadSnapshotScope, ) => Promise<LoadedServerSnapshot | null>; applyCommand: (args: { gameId: string; loadedRevision: number; currentRevis… — The plain-function half of {@link createGameServerFunctions}, bound to the same runtime registry and auth mode as its mutations. Reach for it when a host mutation must pair snapshot work with its own table writes in one transaction, which a pre-registered mutation cannot do.
21
+ - `GameServerHelpers` (type): type GameServerHelpers = { ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>; /** The shared singleton world `ensureServer` would return, read without creating it, so a query can scope reads to it. `null` until a player or host mutation creates it. */ findServer: (ctx: { db: P… — The plain-function half of {@link createGameServerFunctions}, bound to the same runtime registry and auth mode as its mutations. Reach for it when a host mutation must pair snapshot work with its own table writes in one transaction, which a pre-registered mutation cannot do.
22
22
  - `HostedGameConfig` (interface): interface HostedGameConfig — One game the hosted path can serve, bound per `gameId`: its definition plus content lookup.
23
23
  - `HostedWorldInvocation` (interface): interface HostedWorldInvocation<T> — Everything one stateless invocation reconstructs a hosted world from: the persisted record via `store`, plus the member roster and held inputs the snapshot doesn't carry, and the op to apply to the fresh session.
24
24
  - `HostedWorldOutcome` (interface): interface HostedWorldOutcome<T> — What one stateless invocation produced: the op's value, the resulting revision, whether the world changed (and was saved back), and the post-op roster.
25
25
  - `JGDataModel` (type): type JGDataModel = DataModelFromSchemaDefinition<typeof schemaForTypes> — Data model of the {@link jgengineTables} schema — the shape a host's own Convex ctx must satisfy to call the exported persistence helpers.
26
26
  - `JGMutationCtx` (type): type JGMutationCtx = GenericMutationCtx<JGDataModel> — Mutation ctx accepted by {@link loadServerSnapshot} / {@link persistServerSnapshot}.
27
27
  - `JGQueryCtx` (type): type JGQueryCtx = GenericQueryCtx<JGDataModel> — Read-only context accepted by engine host queries.
28
- - `JG_HOSTED_TICK_MS` (const): const JG_HOSTED_TICK_MS: 1000 — Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`.
28
+ - `JG_HOSTED_TICK_MS` (const): const JG_HOSTED_TICK_MS: 50 — Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`.
29
29
  - `JG_MAX_MEMBERS_PER_SERVER` (const): const JG_MAX_MEMBERS_PER_SERVER: 256 — Room-topology ceiling on `slotsPerServer`; shared membership uses indexed rows instead.
30
30
  - `JG_RUNTIME_TICK_MS` (const): const JG_RUNTIME_TICK_MS: 1000 — ⚠ undocumented
31
31
  - `JgAuthMode` (type): type JgAuthMode = "anonymous" | "required" — ⚠ undocumented
@@ -36,6 +36,7 @@
36
36
  - `MAX_MEMBER_SESSIONS` (const): const MAX_MEMBER_SESSIONS: 16 — Most live client sessions one shared-topology membership remembers; the oldest drop first.
37
37
  - `MatchmakingMode` (type): type MatchmakingMode = "auto" | "singleton" — How auto-match behaves when no `serverId` is supplied.
38
38
  - `OnlinePlayer` (type): type OnlinePlayer = { serverId: string; userId: string; homeGameId?: string } — One active player delivered to an online-system batch.
39
+ - `PresenceCheckpoint` (type): type PresenceCheckpoint = { userId: string; homeGameId?: string; position: { x: number; y: number; z: number }; rotationY: number; } — A live pose row handed to a `checkpoint` writer, so a host can persist its own last-position state.
39
40
  - `PresenceListRow` (type): type PresenceListRow = { userId: string; sessionId?: string; kind?: string; label?: string; position: { x: number; y: number; z: number }; rotationY: number; rotationPitch: number; lastSeenAt: number; } — One member's pose as `list` reports it.
40
41
  - `PresenceSyncResult` (type): type PresenceSyncResult = { pose: { x: number; y: number; z: number; rotationY: number; rotationPitch: number }; lastSeenAt: number; displaced: boolean; } — What `sync` hands back: the pose the server holds after clamping, and whether this session had been displaced.
41
42
  - `REVISION_CONFLICT_REASON` (const): const REVISION_CONFLICT_REASON: "Revision conflict" — ⚠ undocumented
@@ -62,7 +63,7 @@
62
63
  - `randomConvexPlayerId` (function): function randomConvexPlayerId(): string — ⚠ undocumented
63
64
  - `rateLimit` (function): function rateLimit(ctx: JGMutationCtx, args: { key: string; windowMs: number; max: number; nowMs?: number }): Promise<{ ok: boolean; retryAfterMs: number }> — Consume one request in a fixed rate window; concurrent callers serialize on the indexed key.
64
65
  - `recentChatMessages` (function): function recentChatMessages(ctx: { db: Pick<JGQueryCtx["db"], "query"> }, args: { serverId: string; channelId?: string; limit?: number }): Promise<{ _id: Id<"jgChatMessages">; _creationTime: number; authorName?: string | undefined; serverId: string; userId: string; channelId: string; body: string; a… — Bounded recent chat for a resolved server; authorization belongs to the calling query.
65
- - `resolveConvexMultiplayer` (function): function resolveConvexMultiplayer(args: { game: GameDefinition; gameId: string; url?: string; client?: ConvexReactClient; api?: ConvexGameApi; userId?: string; force?: boolean; feedActions?: string[]; poseTuning?: PoseSyncTuning; }): MultiplayerSession | null — ⚠ undocumented
66
+ - `resolveConvexMultiplayer` (function): function resolveConvexMultiplayer(args: { game: GameDefinition; gameId: string; url?: string | undefined; client?: ConvexReactClient; api?: ConvexGameApi; userId?: string; force?: boolean; feedActions?: string[]; poseTuning?: PoseSyncTuning; }): MultiplayerSession | null — ⚠ undocumented
66
67
  - `sendChatMessage` (function): function sendChatMessage(ctx: JGMutationCtx, args: { serverId: string; userId: string; body: string; channelId?: string; authorName?: string; maxBodyLength?: number; minIntervalMs?: number }): Promise<ChatSendOutcome> — Write validated chat in a host transaction after the caller resolves its actor and world.
67
68
  - `validateSlotsPerServer` (function): function validateSlotsPerServer(slotsPerServer: number, topology: "rooms" | "shared" = "rooms"): { ok: true } | { ok: false; reason: string } — Whether a `slotsPerServer` value is a capacity a single hosted server row can actually hold.
68
69
  - `watchConvexQuery` (function): function watchConvexQuery<TArgs extends DefaultFunctionArgs, TResult, TView>(client: ConvexGameClient, query: FunctionReference<"query", "public", TArgs, TResult>, args: TArgs, toView: (result: TResult) => TView, onChange: (view: TView) => void): () => void — ⚠ undocumented
@@ -87,13 +88,13 @@
87
88
  ## @jgengine/convex/createConvexBackend
88
89
 
89
90
  - `ConvexBackend` (type): type ConvexBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = LiveGameBackend<TPresenceRow, TPresenceLocation, TGameId> & { leaderboard: ConvexLeaderboardReads; } — ⚠ undocumented
90
- - `ConvexBackendOptions` (type): type ConvexBackendOptions<TRawPresenceRow extends { actorExternalId: string }, TPresenceRow> = { client: ConvexReactClient; gameId: string; userId: string; api?: ConvexGameApi; poseTuning?: PoseSyncTuning; presence?: { functions: ConvexPresenceFunctions; mapRow: (row: TRawPresenceRow) => TPresenceRo… — ⚠ undocumented
91
+ - `ConvexBackendOptions` (type): type ConvexBackendOptions<TRawPresenceRow extends { actorExternalId: string }, TPresenceRow> = { client: ConvexReactClient; gameId: string; userId: string; api?: ConvexGameApi | undefined; poseTuning?: PoseSyncTuning | undefined; presence?: { functions: ConvexPresenceFunctions; mapRow: (row: TRawPre… — ⚠ undocumented
91
92
  - `ConvexLeaderboardReads` (type): type ConvexLeaderboardReads = ReturnType<typeof createConvexLeaderboardReads> — ⚠ undocumented
92
93
  - `createConvexBackend` (function): function createConvexBackend<TRawPresenceRow extends { actorExternalId: string } = { actorExternalId: string }, TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string>(options: ConvexBackendOptions<TRawPresenceRow, TPresenceRow>): ConvexBackend<TPresenceRow, TPresenceLo… — ⚠ undocumented
93
94
 
94
95
  ## @jgengine/convex/createConvexGameTransport
95
96
 
96
- - `ConvexGameApi` (type): type ConvexGameApi = { runtime: { joinServer: FunctionReference< "mutation", "public", { gameId: string; serverId?: string; mode?: string; visibility?: "public" | "private"; joinCode?: string; externalId?: string; sessionId?: string; }, JoinServerOutcome >; leaveServer: FunctionReference<"mutation",… — ⚠ undocumented
97
+ - `ConvexGameApi` (type): type ConvexGameApi = { runtime: { joinServer: FunctionReference< "mutation", "public", { gameId: string; serverId?: string | undefined; mode?: string; visibility?: "public" | "private"; joinCode?: string; externalId?: string | undefined; sessionId?: string; }, JoinServerOutcome >; leaveServer: Funct… — ⚠ undocumented
97
98
  - `ConvexGameClient` (type): type ConvexGameClient = Pick<ConvexReactClient, "mutation" | "watchQuery"> — Structural client seam avoids requiring the engine and game to share a class instance type.
98
99
  - `ConvexGameTransportConfig` (type): type ConvexGameTransportConfig = { gameId: string; userId?: string; } — ⚠ undocumented
99
100
  - `createConvexChatSync` (function): function createConvexChatSync(client: ConvexGameClient, api: ConvexGameApi, config: ConvexGameTransportConfig, serverId: string): ChatSync — ⚠ undocumented
@@ -110,7 +111,7 @@
110
111
  - `HostedGameConfig` (interface): interface HostedGameConfig — One game the hosted path can serve, bound per `gameId`: its definition plus content lookup.
111
112
  - `HostedWorldInvocation` (interface): interface HostedWorldInvocation<T> — Everything one stateless invocation reconstructs a hosted world from: the persisted record via `store`, plus the member roster and held inputs the snapshot doesn't carry, and the op to apply to the fresh session.
112
113
  - `HostedWorldOutcome` (interface): interface HostedWorldOutcome<T> — What one stateless invocation produced: the op's value, the resulting revision, whether the world changed (and was saved back), and the post-op roster.
113
- - `JG_HOSTED_TICK_MS` (const): const JG_HOSTED_TICK_MS: 1000 — Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`.
114
+ - `JG_HOSTED_TICK_MS` (const): const JG_HOSTED_TICK_MS: 50 — Default minimum elapsed ms before `tickHostedWorlds` advances a world — override via the factory's `tickMs`.
114
115
 
115
116
  ## @jgengine/convex/occ
116
117
 
@@ -119,15 +120,15 @@
119
120
  ## @jgengine/convex/resolveConvexMultiplayer
120
121
 
121
122
  - `randomConvexPlayerId` (function): function randomConvexPlayerId(): string — ⚠ undocumented
122
- - `resolveConvexMultiplayer` (function): function resolveConvexMultiplayer(args: { game: GameDefinition; gameId: string; url?: string; client?: ConvexReactClient; api?: ConvexGameApi; userId?: string; force?: boolean; feedActions?: string[]; poseTuning?: PoseSyncTuning; }): MultiplayerSession | null — ⚠ undocumented
123
+ - `resolveConvexMultiplayer` (function): function resolveConvexMultiplayer(args: { game: GameDefinition; gameId: string; url?: string | undefined; client?: ConvexReactClient; api?: ConvexGameApi; userId?: string; force?: boolean; feedActions?: string[]; poseTuning?: PoseSyncTuning; }): MultiplayerSession | null — ⚠ undocumented
123
124
 
124
125
  ## @jgengine/convex/server
125
126
 
126
- - `ConvexRunCommandArgs` (type): type ConvexRunCommandArgs = RunCommandArgs & { externalId?: string; /** * How much of the server to hydrate before applying the command. Overrides whatever the * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world. */ scope?: LoadSnapshotScope; /** * Trusted hos… — Arguments of the `runCommand` mutation, and of the equivalent {@link GameServerHelpers.runCommand} helper.
127
+ - `ConvexRunCommandArgs` (type): type ConvexRunCommandArgs = RunCommandArgs & { externalId?: string | undefined; /** * How much of the server to hydrate before applying the command. Overrides whatever the * {@link CommandDef} declares through its own `scope`; omit both to hydrate the whole world. */ scope?: LoadSnapshotScope; /** *… — Arguments of the `runCommand` mutation, and of the equivalent {@link GameServerHelpers.runCommand} helper.
127
128
  - `DEFAULT_CONVEX_POSE_RULES` (const): const DEFAULT_CONVEX_POSE_RULES: PoseSyncRules — ⚠ undocumented
128
129
  - `DEFAULT_MAX_SERVERS_PER_TICK` (const): const DEFAULT_MAX_SERVERS_PER_TICK: 32 — How many running servers one `tickActiveServers` transaction may hydrate, tick, and persist.
129
130
  - `EnsureJoinedOutcome` (type): type EnsureJoinedOutcome = | { ok: true; serverId: GenericId<"jgGameServers">; isNew: boolean } | { ok: false; reason: "full" | "closed" | "unauthorized" } — Outcome of a join: the server joined and whether the player's profile was created, or why it was refused.
130
- - `GameServerHelpers` (type): type GameServerHelpers = { ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>; loadSnapshot: ( ctx: JGMutationCtx, serverId: string, scope?: LoadSnapshotScope, ) => Promise<LoadedServerSnapshot | null>; applyCommand: (args: { gameId: string; loadedRevision: number; currentRevis… — The plain-function half of {@link createGameServerFunctions}, bound to the same runtime registry and auth mode as its mutations. Reach for it when a host mutation must pair snapshot work with its own table writes in one transaction, which a pre-registered mutation cannot do.
131
+ - `GameServerHelpers` (type): type GameServerHelpers = { ensureServer: (ctx: JGMutationCtx, gameId: string) => Promise<ServerDoc>; /** The shared singleton world `ensureServer` would return, read without creating it, so a query can scope reads to it. `null` until a player or host mutation creates it. */ findServer: (ctx: { db: P… — The plain-function half of {@link createGameServerFunctions}, bound to the same runtime registry and auth mode as its mutations. Reach for it when a host mutation must pair snapshot work with its own table writes in one transaction, which a pre-registered mutation cannot do.
131
132
  - `JGDataModel` (type): type JGDataModel = DataModelFromSchemaDefinition<typeof schemaForTypes> — Data model of the {@link jgengineTables} schema — the shape a host's own Convex ctx must satisfy to call the exported persistence helpers.
132
133
  - `JGMutationCtx` (type): type JGMutationCtx = GenericMutationCtx<JGDataModel> — Mutation ctx accepted by {@link loadServerSnapshot} / {@link persistServerSnapshot}.
133
134
  - `JGQueryCtx` (type): type JGQueryCtx = GenericQueryCtx<JGDataModel> — Read-only context accepted by engine host queries.
@@ -141,6 +142,7 @@
141
142
  - `MAX_MEMBER_SESSIONS` (const): const MAX_MEMBER_SESSIONS: 16 — Most live client sessions one shared-topology membership remembers; the oldest drop first.
142
143
  - `MatchmakingMode` (type): type MatchmakingMode = "auto" | "singleton" — How auto-match behaves when no `serverId` is supplied.
143
144
  - `OnlinePlayer` (type): type OnlinePlayer = { serverId: string; userId: string; homeGameId?: string } — One active player delivered to an online-system batch.
145
+ - `PresenceCheckpoint` (type): type PresenceCheckpoint = { userId: string; homeGameId?: string; position: { x: number; y: number; z: number }; rotationY: number; } — A live pose row handed to a `checkpoint` writer, so a host can persist its own last-position state.
144
146
  - `PresenceListRow` (type): type PresenceListRow = { userId: string; sessionId?: string; kind?: string; label?: string; position: { x: number; y: number; z: number }; rotationY: number; rotationPitch: number; lastSeenAt: number; } — One member's pose as `list` reports it.
145
147
  - `PresenceSyncResult` (type): type PresenceSyncResult = { pose: { x: number; y: number; z: number; rotationY: number; rotationPitch: number }; lastSeenAt: number; displaced: boolean; } — What `sync` hands back: the pose the server holds after clamping, and whether this session had been displaced.
146
148
  - `RunCommandOutcome` (type): type RunCommandOutcome = { ok: true } | { ok: false; reason: string } — Outcome of a runtime command: applied, or refused with a reason (unknown server, non-member, validation, revision conflict).
@@ -200,7 +202,7 @@
200
202
  - `createFeedWriteGate` (function): function createFeedWriteGate(allowedActions: readonly string[] = []): FeedWriteGate — ⚠ undocumented
201
203
  - `createLocalVoiceTransport` (function): function createLocalVoiceTransport(options?: { userId?: string }): { transport: VoiceTransport; participants(channelId: string): readonly VoiceParticipant[]; } — ⚠ undocumented
202
204
  - `createPoseSyncGate` (function): function createPoseSyncGate(tuning: PoseSyncTuning = DEFAULT_POSE_SYNC_TUNING): PoseSyncGate — ⚠ undocumented
203
- - `createPushToTalk` (function): function createPushToTalk(config?: { mode?: PushToTalkMode; onChange?: (transmitting: boolean) => void; }): PushToTalk — ⚠ undocumented
205
+ - `createPushToTalk` (function): function createPushToTalk(config?: { mode?: PushToTalkMode | undefined; onChange?: (transmitting: boolean) => void; }): PushToTalk — ⚠ undocumented
204
206
  - `findByJoinCode` (function): function findByJoinCode(listings: readonly SessionListing[], code: string): SessionListing | null — ⚠ undocumented
205
207
  - `fromRuntimeObjectRow` (function): function fromRuntimeObjectRow(row: RuntimeObjectRow): SceneObject — Inverse of {@link toRuntimeObjectRow}: rebuild the live placed object a host persisted.
206
208
  - `normalizeJoinCode` (function): function normalizeJoinCode(code: string): string — ⚠ undocumented
@@ -286,7 +288,7 @@
286
288
  - `DEFAULT_FEED_ACTIONS` (const): const DEFAULT_FEED_ACTIONS: string[] — ⚠ undocumented
287
289
  - `PeerShellFactories` (interface): interface PeerShellFactories — Alternate peer factories for hosts that provide their own WebRTC implementation or transport pipe.
288
290
  - `ResolvePeerShellMultiplayerArgs` (interface): interface ResolvePeerShellMultiplayerArgs — Peer bootstrap options; passing a playable hosts its authoritative GameContext world.
289
- - `ResolveShellMultiplayerArgs` (type): type ResolveShellMultiplayerArgs = { game: GameDefinition; gameId: string; url?: string | undefined; userId?: string; force?: boolean; feedActions?: string[]; } — ⚠ undocumented
291
+ - `ResolveShellMultiplayerArgs` (type): type ResolveShellMultiplayerArgs = { game: GameDefinition; gameId: string; url?: string | undefined; userId?: string | undefined; force?: boolean; feedActions?: string[]; } — ⚠ undocumented
290
292
  - `ShellMultiplayer` (type): type ShellMultiplayer = MultiplayerSession — ⚠ undocumented · used by `resolvePeerShellMultiplayer`: Opens a peer session.
291
293
  - `randomPlayerId` (function): function randomPlayerId(): string — ⚠ undocumented
292
294
  - `resolvePeerShellMultiplayer` (function): function resolvePeerShellMultiplayer(args: ResolvePeerShellMultiplayerArgs): Promise<ShellMultiplayer & { close: () => Promise<void> }> — Opens a peer session. With `playable`, one host runs the game loop and guests mirror its shared world. Await `close()` to finish authoritative persistence. Same-origin rooms use BroadcastChannel signaling.
@@ -319,7 +321,7 @@
319
321
  - `ActivePresenceResolution` (interface): interface ActivePresenceResolution<T> { active: T | null; extras: T[] } — ⚠ undocumented
320
322
  - `DEFAULT_POSE_SYNC_RULES` (const): const DEFAULT_POSE_SYNC_RULES: PoseSyncRules — Canonical client-authoritative pose-sync tuning shared by every host transport (WS, Convex).
321
323
  - `DEFAULT_SPAWN_ORIGIN` (const): const DEFAULT_SPAWN_ORIGIN: { readonly x: 0; readonly y: 0; readonly z: 0; } — ⚠ undocumented
322
- - `IncomingPose` (interface): interface IncomingPose { position: {x: number; z: number; y?: number}; rotationY?: number; rotationPitch?: number; appearance?: Record<string, string> } — ⚠ undocumented
324
+ - `IncomingPose` (interface): interface IncomingPose { position: {x: number; z: number; y?: number}; rotationY?: number; rotationPitch?: number | undefined; appearance?: Record<string, string> } — ⚠ undocumented
323
325
  - `PoseSyncDecision` (interface): interface PoseSyncDecision { position: {x: number; y: number; z: number}; rotationY: number; rotationPitch: number; appearance?: Record<string, string> | undefined; changed: boolean; refreshKeepAlive: boolean } — ⚠ undocumented
324
326
  - `PoseSyncRules` (interface): interface PoseSyncRules { maxSpeed: number; minIntervalMs?: number; maxVerticalOffset: number; floorY?: number; minElapsedSec: number; maxElapsedSec: number; keepAliveRefreshMs: number } — ⚠ undocumented · used by `DEFAULT_POSE_SYNC_RULES`: Canonical client-authoritative pose-sync tuning shared by every host transport (WS, Convex).
325
327
  - `PresencePoseState` (interface): interface PresencePoseState { position: {x: number; y: number; z: number}; rotationY: number; rotationPitch?: number; lastSeenAtMs?: number; appearance?: Record<string, string> | undefined } — ⚠ undocumented
@@ -354,7 +356,7 @@
354
356
  - `VoiceRoute` (interface): interface VoiceRoute { fromUserId: string; channelId: string; gain: number } — ⚠ undocumented
355
357
  - `VoiceTransport` (interface): interface VoiceTransport — Signaling seam for voice: who is in a channel and which media stream descriptor they published. The media plane (WebRTC, SFU, or anything else that moves audio bytes) stays behind this seam, host-supplied — the engine never touches it. subscribers delivers the channel roster on every change, starting with the current roster.
356
358
  - `createLocalVoiceTransport` (function): function createLocalVoiceTransport(options?: { userId?: string }): { transport: VoiceTransport; participants(channelId: string): readonly VoiceParticipant[]; } — ⚠ undocumented
357
- - `createPushToTalk` (function): function createPushToTalk(config?: { mode?: PushToTalkMode; onChange?: (transmitting: boolean) => void; }): PushToTalk — ⚠ undocumented
359
+ - `createPushToTalk` (function): function createPushToTalk(config?: { mode?: PushToTalkMode | undefined; onChange?: (transmitting: boolean) => void; }): PushToTalk — ⚠ undocumented
358
360
 
359
361
  ## @jgengine/core/runtime/worldProjection
360
362
 
@@ -378,7 +380,7 @@
378
380
  - `RewoundPosition` (type): type RewoundPosition = { userId: string; x: number; y: number; z: number; } — A player's interpolated position sampled from history at a past timestamp.
379
381
  - `ShutdownHook` (interface): interface ShutdownHook — A live signal listener installed by {@link installShutdownHook}; call `remove()` to uninstall it (tests, embedders opting out).
380
382
  - `SocketIoLikeServer` (type): type SocketIoLikeServer = { on: (event: "connection", listener: (socket: SocketIoLikeServerSocket) => void) => unknown; } — ⚠ undocumented
381
- - `SocketIoLikeServerSocket` (type): type SocketIoLikeServerSocket = { on: (event: string, listener: (payload: string) => void) => unknown; send: (data: string) => unknown; disconnect: (close?: boolean) => unknown; } — ⚠ undocumented
383
+ - `SocketIoLikeServerSocket` (type): type SocketIoLikeServerSocket = { on: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; send: (data: string | Uint8Array) => unknown; disconnect: (close?: boolean) => unknown; } — ⚠ undocumented
382
384
  - `WebHandler` (type): type WebHandler = (request: Request) => Promise<Response> — ⚠ undocumented
383
385
  - `WorldGameServer` (interface): interface WorldGameServer — A runnable ws host for GameContext worlds: {@link createWorldGameHost} + {@link createGameWsServer} + a tick loop, with a manual `tick(dt)` seam so a fake clock can drive it in tests.
384
386
  - `WorldGameServerOptions` (interface): interface WorldGameServerOptions extends Omit<GameWsServerOptions, "host"> — Config for {@link createWorldGameServer}: how to resolve a game by id, the tick cadence, and the underlying ws-server/router options (minus `host`, which the server builds).
@@ -449,7 +451,7 @@
449
451
  - `GameSocketIoServerOptions` (type): type GameSocketIoServerOptions = HostRouterOptions & { io: SocketIoLikeServer } — ⚠ undocumented
450
452
  - `RewoundPosition` (type): type RewoundPosition = { userId: string; x: number; y: number; z: number; } — A player's interpolated position sampled from history at a past timestamp.
451
453
  - `SocketIoLikeServer` (type): type SocketIoLikeServer = { on: (event: "connection", listener: (socket: SocketIoLikeServerSocket) => void) => unknown; } — ⚠ undocumented
452
- - `SocketIoLikeServerSocket` (type): type SocketIoLikeServerSocket = { on: (event: string, listener: (payload: string) => void) => unknown; send: (data: string) => unknown; disconnect: (close?: boolean) => unknown; } — ⚠ undocumented
454
+ - `SocketIoLikeServerSocket` (type): type SocketIoLikeServerSocket = { on: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; send: (data: string | Uint8Array) => unknown; disconnect: (close?: boolean) => unknown; } — ⚠ undocumented
453
455
  - `attachGameSocketIoServer` (function): function attachGameSocketIoServer(options: GameSocketIoServerOptions): GameSocketIoServer — ⚠ undocumented
454
456
 
455
457
  ## @jgengine/node/testFixtures
@@ -526,8 +528,8 @@
526
528
  - `HostRouter` (type): type HostRouter = { connect: (transport: HostRouterTransport) => HostRouterConnection; rewind: (args: { serverId: string; atMs: number }) => RewoundPosition[]; close: () => void; /** Wait for accepted message handlers and subscription reads to settle. Close first to prevent new work. */ drain: () =>… — ⚠ undocumented
527
529
  - `HostRouterAuthenticate` (type): type HostRouterAuthenticate = (args: { userId: string; token?: string | undefined; }) => Promise<string | null> | string | null — ⚠ undocumented
528
530
  - `HostRouterConnection` (type): type HostRouterConnection = { handleRaw: (raw: unknown) => void; close: () => void; } — ⚠ undocumented
529
- - `HostRouterOptions` (type): type HostRouterOptions = { host: GameServerHost; authenticate?: HostRouterAuthenticate; allowAnonymous?: boolean; singleSession?: boolean; poseRules?: PoseSyncRules; positionHistoryMs?: number; chatRateLimit?: ChatRateLimit; chatHistoryLimit?: number; chatMaxBodyLength?: number; /** Per-op rate limi… — ⚠ undocumented
530
- - `HostRouterTransport` (type): type HostRouterTransport = { send: (data: string) => void; close: () => void; } — ⚠ undocumented
531
+ - `HostRouterOptions` (type): type HostRouterOptions = { host: GameServerHost; authenticate?: HostRouterAuthenticate; allowAnonymous?: boolean; singleSession?: boolean; poseRules?: PoseSyncRules; positionHistoryMs?: number; chatRateLimit?: ChatRateLimit; chatHistoryLimit?: number; chatMaxBodyLength?: number; /** Quantisation of … — ⚠ undocumented
532
+ - `HostRouterTransport` (type): type HostRouterTransport = { send: (data: WsFrame) => void; close: () => void; } — ⚠ undocumented
531
533
  - `HttpReads` (type): type HttpReads = { getTop: (args: { stat: string; scope: LeaderboardScope; serverId?: string; limit?: number; }) => Promise<LeaderboardEntry[]>; getLeaderboardProfile: (userId: string) => Promise<Record<string, number>>; getPlayerProfile: (userId: string) => Promise<GameRuntimePlayerView | null>; li… — ⚠ undocumented
532
534
  - `HttpReadsOptions` (type): type HttpReadsOptions = { baseUrl: string; gameId: string; fetchImpl?: typeof fetch; } — ⚠ undocumented
533
535
  - `MAX_APPEARANCE_ENTRIES` (const): const MAX_APPEARANCE_ENTRIES: 32 — Max number of keys in a pose `appearance` tag map.
@@ -550,10 +552,11 @@
550
552
  - `ReadsPersistence` (type): type ReadsPersistence = Pick< HostPersistence, "listServers" | "loadProfile" | "getLeaderboardTop" | "getLeaderboardProfile" > — ⚠ undocumented
551
553
  - `RewoundPosition` (type): type RewoundPosition = { userId: string; x: number; y: number; z: number; } — A player's interpolated position sampled from history at a past timestamp.
552
554
  - `SocketIoBackendOptions` (type): type SocketIoBackendOptions = Omit<WsBackendOptions, "url" | "pipe" | "webSocketFactory"> & { socket: SocketIoLikeSocket; } — ⚠ undocumented
553
- - `SocketIoLikeSocket` (type): type SocketIoLikeSocket = { connected: boolean; on: (event: string, listener: (payload: string) => void) => unknown; off: (event: string, listener: (payload: string) => void) => unknown; send: (data: string) => unknown; disconnect: () => unknown; } — ⚠ undocumented
554
- - `TransportPipe` (type): type TransportPipe = { send: (data: string) => void; close: () => void; } — ⚠ undocumented
555
+ - `SocketIoLikeSocket` (type): type SocketIoLikeSocket = { connected: boolean; on: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; off: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; send: (data: string | Uint8Array<ArrayBuffer>) => unknown; disconnect: () => unknown; } — ⚠ undocumented
556
+ - `TransportData` (type): type TransportData = string | Uint8Array<ArrayBuffer> — One transport message: JSON text, or bytes from the binary codec.
557
+ - `TransportPipe` (type): type TransportPipe = { send: (data: TransportData) => void; close: () => void; } — ⚠ undocumented
555
558
  - `TransportPipeFactory` (type): type TransportPipeFactory = (handlers: TransportPipeHandlers) => TransportPipe — ⚠ undocumented
556
- - `TransportPipeHandlers` (type): type TransportPipeHandlers = { onOpen: () => void; onMessage: (data: string) => void; onClose: () => void; } — ⚠ undocumented
559
+ - `TransportPipeHandlers` (type): type TransportPipeHandlers = { onOpen: () => void; onMessage: (data: string | Uint8Array) => void; onClose: () => void; } — ⚠ undocumented
557
560
  - `VoiceChannelDef` (interface): interface VoiceChannelDef { id: VoiceChannelId; positional: boolean; falloff?: AudioFalloffConfig; gain?: number } — ⚠ undocumented
558
561
  - `VoiceChannelId` (type): type VoiceChannelId = string — ⚠ undocumented
559
562
  - `VoiceChannelRouter` (interface): interface VoiceChannelRouter { registerChannel(def: VoiceChannelDef): void; join(userId: VoiceMemberId, channelId: VoiceChannelId): void; leave(userId: VoiceMemberId, channelId: VoiceChannelId): void; leaveAll(userId: VoiceMemberId): void; updatePosition(userId: VoiceM… — ⚠ undocumented
@@ -564,13 +567,15 @@
564
567
  - `WorldGameHostOptions` (interface): interface WorldGameHostOptions — Config for {@link createWorldGameHost}: how to resolve a hosted world's authoritative session per server.
565
568
  - `WsAppearance` (type): type WsAppearance = Record<string, string | number | boolean> — Client-set cosmetic/state tags carried alongside a pose (skin, mount, emote, ...). Primitive values only.
566
569
  - `WsBackend` (type): type WsBackend = GameBackend & { rtt: { sampleMs: number; smoothedMs: number }; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown }) => Promise<void>; browse: (args: { gameId: string; filter?: MatchFilter; limit?: number }) => Promise<SessionListing[]>; joinByCode: (args: { ga… — ⚠ undocumented
567
- - `WsBackendOptions` (type): type WsBackendOptions = { url?: string; pipe?: TransportPipeFactory; userId: string; token?: string | undefined; webSocketFactory?: (url: string) => WebSocket; reconnectDelayMs?: number; maxReconnectDelayMs?: number; rpcTimeoutMs?: number; poseTuning?: PoseSyncTuning; now?: () => number; setTimeoutF… — ⚠ undocumented
570
+ - `WsBackendOptions` (type): type WsBackendOptions = { url?: string; pipe?: TransportPipeFactory; userId: string; token?: string | undefined; /** Wire codec: `binary` (default, per-entity deltas with quantised poses) or `json` text frames for debugging. */ codec?: WsCodecName; webSocketFactory?: (url: string) => WebSocket; reco… — ⚠ undocumented
568
571
  - `WsBrowseResult` (type): type WsBrowseResult = SessionListing[] — ⚠ undocumented
569
572
  - `WsChannel` (type): type WsChannel = "server" | "player" | "feed" | "presence" | "chat" | "voice" — ⚠ undocumented
570
573
  - `WsChatMessage` (type): type WsChatMessage = { id: string; channelId: string; fromUserId: string; body: string; at: number; } — ⚠ undocumented
571
574
  - `WsChatSync` (type): type WsChatSync = { subscribe: ( serverId: string, channelId: string, onChange: (messages: WsChatMessage[]) => void, ) => () => void; send: (serverId: string, channelId: string, body: string) => Promise<ChatSendOutcome>; } — ⚠ undocumented
572
- - `WsClientMessage` (type): type WsClientMessage = | { v: 1; t: "ping"; id: number; at: number } | { v: 1; t: "hello"; id: number; userId: string; token?: string | undefined } | { v: 1; t: "join"; id: number; gameId: string; serverId?: string | undefined; attributes?: SessionAttributes | undefined; code?: string | undefined; r… — ⚠ undocumented
575
+ - `WsClientMessage` (type): type WsClientMessage = | { v: 1; t: "ping"; id: number; at: number } | { v: 1; t: "hello"; id: number; userId: string; token?: string | undefined; codec?: WsCodecName | undefined } | { v: 1; t: "join"; id: number; gameId: string; serverId?: string | undefined; attributes?: SessionAttributes | undefi… — ⚠ undocumented
576
+ - `WsCodecName` (type): type WsCodecName = "json" | "binary" — Wire codec a client asks for in `hello`: `binary` (default for {@link createWsBackend}) or `json` for debugging.
573
577
  - `WsDecodeFailure` (type): type WsDecodeFailure = { reason: string; id?: number | undefined; } — ⚠ undocumented
578
+ - `WsFrame` (type): type WsFrame = string | Uint8Array<ArrayBuffer> — One transport frame: JSON text, or a binary frame from {@link encodeWsFrame}.
574
579
  - `WsJoinByCodeResult` (type): type WsJoinByCodeResult = JoinServerResult | null — ⚠ undocumented
575
580
  - `WsJoinResult` (type): type WsJoinResult = JoinServerResult — ⚠ undocumented
576
581
  - `WsPose` (type): type WsPose = PlayerPose & { appearance?: WsAppearance | undefined } — ⚠ undocumented
@@ -605,6 +610,15 @@
605
610
  - `validateCommandInput` (function): function validateCommandInput(catalog: CommandCatalog | undefined, command: string, input: unknown): CommandRejection | null — Validates a `runCommand` input against a declared catalog. `undefined` catalog means "no declarations" — everything passes through unchanged.
606
611
  - `webSocketPipe` (function): function webSocketPipe(url: string, webSocketFactory: (url: string) => WebSocket = (target) => new WebSocket(target)): TransportPipeFactory — ⚠ undocumented
607
612
 
613
+ ## @jgengine/ws/binaryWire
614
+
615
+ - `DEFAULT_WIRE_PRECISION` (const): const DEFAULT_WIRE_PRECISION: { readonly position: 2; readonly angle: 3; } — Default {@link WirePrecision}.
616
+ - `WIRE_DESYNC` (const): const WIRE_DESYNC: "desync" — Marks a binary frame the decoder could not apply to its state.
617
+ - `WIRE_MAGIC` (const): const WIRE_MAGIC: 177 — Binary wire format for the ws protocol. Every message is a JSON-shaped value, so one tagged value codec carries all of them; world frames add an entity-delta section that sends only the fields that changed against the last frame on that connection, with quantised poses and varint entity ids.
618
+ - `WireDecoder` (type): type WireDecoder = { /** The decoded message, `null` for a malformed frame, or `"desync"` when a world delta does not follow the last frame it saw (the receiver must resubscribe for a baseline). */ decode(frame: Uint8Array): unknown; } — Stateful binary decoder for one connection's incoming messages.
619
+ - `WireEncoder` (type): type WireEncoder = { encode(message: unknown): Uint8Array<ArrayBuffer> } — Stateful binary encoder for one connection's outgoing messages.
620
+ - `WirePrecision` (type): type WirePrecision = { /** Decimal places kept for positions and velocities. Default 2 (centimetres). */ position?: number | undefined; /** Decimal places kept for rotations. Default 3 (about a milliradian). */ angle?: number | undefined; } — Quantisation applied to replicated poses on the binary wire, in decimal places.
621
+
608
622
  ## @jgengine/ws/commandMiddleware
609
623
 
610
624
  - `CommandAuthorize` (type): type CommandAuthorize = (args: { userId: string; op: HostCommandOp; serverId?: string | undefined; command?: string | undefined; }) => boolean | Promise<boolean> — Per-command authorization hook: return `false` to reject. Defaults to allow-all when omitted.
@@ -626,7 +640,7 @@
626
640
  ## @jgengine/ws/createWsBackend
627
641
 
628
642
  - `WsBackend` (type): type WsBackend = GameBackend & { rtt: { sampleMs: number; smoothedMs: number }; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown }) => Promise<void>; browse: (args: { gameId: string; filter?: MatchFilter; limit?: number }) => Promise<SessionListing[]>; joinByCode: (args: { ga… — ⚠ undocumented
629
- - `WsBackendOptions` (type): type WsBackendOptions = { url?: string; pipe?: TransportPipeFactory; userId: string; token?: string | undefined; webSocketFactory?: (url: string) => WebSocket; reconnectDelayMs?: number; maxReconnectDelayMs?: number; rpcTimeoutMs?: number; poseTuning?: PoseSyncTuning; now?: () => number; setTimeoutF… — ⚠ undocumented
643
+ - `WsBackendOptions` (type): type WsBackendOptions = { url?: string; pipe?: TransportPipeFactory; userId: string; token?: string | undefined; /** Wire codec: `binary` (default, per-entity deltas with quantised poses) or `json` text frames for debugging. */ codec?: WsCodecName; webSocketFactory?: (url: string) => WebSocket; reco… — ⚠ undocumented
630
644
  - `WsChatSync` (type): type WsChatSync = { subscribe: ( serverId: string, channelId: string, onChange: (messages: WsChatMessage[]) => void, ) => () => void; send: (serverId: string, channelId: string, body: string) => Promise<ChatSendOutcome>; } — ⚠ undocumented
631
645
  - `WsPresenceSync` (type): type WsPresenceSync = { subscribe: (serverId: string, onChange: (rows: WsPresenceRow[]) => void) => () => void; /** `pose.appearance`, when provided, is forwarded to the host as-is and surfaces on every subscriber's presence row for that user. */ syncPose: (serverId: string, pose: WsPose) => void; } — ⚠ undocumented
632
646
  - `WsVoiceSync` (type): type WsVoiceSync = { subscribe: ( serverId: string, channelId: string, onChange: (participants: WsVoiceParticipant[]) => void, ) => () => void; join: (serverId: string, channelId: string, streamId?: string) => Promise<void>; leave: (serverId: string, channelId: string) => Promise<void>; publish: (se… — ⚠ undocumented
@@ -648,8 +662,8 @@
648
662
  - `HostRouter` (type): type HostRouter = { connect: (transport: HostRouterTransport) => HostRouterConnection; rewind: (args: { serverId: string; atMs: number }) => RewoundPosition[]; close: () => void; /** Wait for accepted message handlers and subscription reads to settle. Close first to prevent new work. */ drain: () =>… — ⚠ undocumented
649
663
  - `HostRouterAuthenticate` (type): type HostRouterAuthenticate = (args: { userId: string; token?: string | undefined; }) => Promise<string | null> | string | null — ⚠ undocumented
650
664
  - `HostRouterConnection` (type): type HostRouterConnection = { handleRaw: (raw: unknown) => void; close: () => void; } — ⚠ undocumented
651
- - `HostRouterOptions` (type): type HostRouterOptions = { host: GameServerHost; authenticate?: HostRouterAuthenticate; allowAnonymous?: boolean; singleSession?: boolean; poseRules?: PoseSyncRules; positionHistoryMs?: number; chatRateLimit?: ChatRateLimit; chatHistoryLimit?: number; chatMaxBodyLength?: number; /** Per-op rate limi… — ⚠ undocumented
652
- - `HostRouterTransport` (type): type HostRouterTransport = { send: (data: string) => void; close: () => void; } — ⚠ undocumented
665
+ - `HostRouterOptions` (type): type HostRouterOptions = { host: GameServerHost; authenticate?: HostRouterAuthenticate; allowAnonymous?: boolean; singleSession?: boolean; poseRules?: PoseSyncRules; positionHistoryMs?: number; chatRateLimit?: ChatRateLimit; chatHistoryLimit?: number; chatMaxBodyLength?: number; /** Quantisation of … — ⚠ undocumented
666
+ - `HostRouterTransport` (type): type HostRouterTransport = { send: (data: WsFrame) => void; close: () => void; } — ⚠ undocumented
653
667
  - `MAX_QUEUED_MESSAGES` (const): const MAX_QUEUED_MESSAGES: 64 — Cap on frames queued behind a connection's in-flight message; beyond this a flood gets rejected instead of piling up unbounded promises.
654
668
  - `RewoundPosition` (type): type RewoundPosition = { userId: string; x: number; y: number; z: number; } — A player's interpolated position sampled from history at a past timestamp.
655
669
  - `createHostRouter` (function): function createHostRouter(options: HostRouterOptions): HostRouter — ⚠ undocumented
@@ -680,9 +694,10 @@
680
694
 
681
695
  ## @jgengine/ws/pipe
682
696
 
683
- - `TransportPipe` (type): type TransportPipe = { send: (data: string) => void; close: () => void; } — ⚠ undocumented
697
+ - `TransportData` (type): type TransportData = string | Uint8Array<ArrayBuffer> — One transport message: JSON text, or bytes from the binary codec.
698
+ - `TransportPipe` (type): type TransportPipe = { send: (data: TransportData) => void; close: () => void; } — ⚠ undocumented
684
699
  - `TransportPipeFactory` (type): type TransportPipeFactory = (handlers: TransportPipeHandlers) => TransportPipe — ⚠ undocumented
685
- - `TransportPipeHandlers` (type): type TransportPipeHandlers = { onOpen: () => void; onMessage: (data: string) => void; onClose: () => void; } — ⚠ undocumented
700
+ - `TransportPipeHandlers` (type): type TransportPipeHandlers = { onOpen: () => void; onMessage: (data: string | Uint8Array) => void; onClose: () => void; } — ⚠ undocumented
686
701
  - `webSocketPipe` (function): function webSocketPipe(url: string, webSocketFactory: (url: string) => WebSocket = (target) => new WebSocket(target)): TransportPipeFactory — ⚠ undocumented
687
702
 
688
703
  ## @jgengine/ws/protocol
@@ -697,8 +712,10 @@
697
712
  - `WsBrowseResult` (type): type WsBrowseResult = SessionListing[] — ⚠ undocumented
698
713
  - `WsChannel` (type): type WsChannel = "server" | "player" | "feed" | "presence" | "chat" | "voice" — ⚠ undocumented
699
714
  - `WsChatMessage` (type): type WsChatMessage = { id: string; channelId: string; fromUserId: string; body: string; at: number; } — ⚠ undocumented
700
- - `WsClientMessage` (type): type WsClientMessage = | { v: 1; t: "ping"; id: number; at: number } | { v: 1; t: "hello"; id: number; userId: string; token?: string | undefined } | { v: 1; t: "join"; id: number; gameId: string; serverId?: string | undefined; attributes?: SessionAttributes | undefined; code?: string | undefined; r… — ⚠ undocumented
715
+ - `WsClientMessage` (type): type WsClientMessage = | { v: 1; t: "ping"; id: number; at: number } | { v: 1; t: "hello"; id: number; userId: string; token?: string | undefined; codec?: WsCodecName | undefined } | { v: 1; t: "join"; id: number; gameId: string; serverId?: string | undefined; attributes?: SessionAttributes | undefi… — ⚠ undocumented
716
+ - `WsCodecName` (type): type WsCodecName = "json" | "binary" — Wire codec a client asks for in `hello`: `binary` (default for {@link createWsBackend}) or `json` for debugging.
701
717
  - `WsDecodeFailure` (type): type WsDecodeFailure = { reason: string; id?: number | undefined; } — ⚠ undocumented
718
+ - `WsFrame` (type): type WsFrame = string | Uint8Array<ArrayBuffer> — One transport frame: JSON text, or a binary frame from {@link encodeWsFrame}.
702
719
  - `WsJoinByCodeResult` (type): type WsJoinByCodeResult = JoinServerResult | null — ⚠ undocumented
703
720
  - `WsJoinResult` (type): type WsJoinResult = JoinServerResult — ⚠ undocumented
704
721
  - `WsPose` (type): type WsPose = PlayerPose & { appearance?: WsAppearance | undefined } — ⚠ undocumented
@@ -719,7 +736,7 @@
719
736
  ## @jgengine/ws/socketIoPipe
720
737
 
721
738
  - `SocketIoBackendOptions` (type): type SocketIoBackendOptions = Omit<WsBackendOptions, "url" | "pipe" | "webSocketFactory"> & { socket: SocketIoLikeSocket; } — ⚠ undocumented
722
- - `SocketIoLikeSocket` (type): type SocketIoLikeSocket = { connected: boolean; on: (event: string, listener: (payload: string) => void) => unknown; off: (event: string, listener: (payload: string) => void) => unknown; send: (data: string) => unknown; disconnect: () => unknown; } — ⚠ undocumented
739
+ - `SocketIoLikeSocket` (type): type SocketIoLikeSocket = { connected: boolean; on: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; off: (event: string, listener: (payload: string | Uint8Array) => void) => unknown; send: (data: string | Uint8Array<ArrayBuffer>) => unknown; disconnect: () => unknown; } — ⚠ undocumented
723
740
  - `createSocketIoBackend` (function): function createSocketIoBackend(options: SocketIoBackendOptions): WsBackend — ⚠ undocumented
724
741
  - `socketIoPipe` (function): function socketIoPipe(socket: SocketIoLikeSocket): TransportPipeFactory — ⚠ undocumented
725
742
 
@@ -18,7 +18,7 @@ await session.close();
18
18
 
19
19
  The resolver owns one tick timer, signaling, peers and persistence teardown.
20
20
  Ticks skip worlds with no active members; only the canonical game/server id is
21
- admitted. `tickMs` defaults to 1000 / 30. Automatic saves default to a 1000 ms interval (`saveIntervalMs`); commands and
21
+ admitted. `tickMs` defaults to 50 (20 Hz, `DEFAULT_HOST_TICK_MS`). Automatic saves default to a 1000 ms interval (`saveIntervalMs`); commands and
22
22
  close retain their immediate save boundaries. `store` accepts an asynchronous
23
23
  `HostedWorldStore` for restart recovery; omitted storage is memory-only.
24
24
  `slotsPerServer` caps players inside the serialized world join queue; known live