@jgengine/ws 0.18.1 → 0.19.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.
- package/CHANGELOG.md +158 -7
- package/dist/createWsBackend.js +21 -2
- package/dist/host.d.ts +2 -0
- package/dist/host.js +4 -0
- package/dist/hostRouter.js +28 -4
- package/dist/protocol.d.ts +5 -0
- package/dist/protocol.js +8 -2
- package/package.json +2 -2
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/jgengine/SKILL.md +7 -5
- package/skills/jgengine/api.md +118 -89
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine/recipes/minimal-game.md +3 -0
- package/skills/jgengine-multiplayer/SKILL.md +2 -2
- package/skills/jgengine-multiplayer/api.md +85 -77
package/skills/jgengine/api.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.19.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
13
|
-
- `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it.
|
|
13
|
+
- `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. In a `defineGame({ input })`, pressing an action runs the same-named command with `{ yaw, pitch, aim }`; `repeatMs` re-fires it while held (automatic weapons).
|
|
14
14
|
- `AuthoredProvenance` (interface): interface AuthoredProvenance — A first-class entry in the scene document — the editor fully owns and persists it.
|
|
15
15
|
- `Drop` (interface): interface Drop — A resolved loot outcome — one item or currency grant with its rolled count.
|
|
16
16
|
- `GameDefinition` (interface): interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — Fully-resolved game description produced by {@link defineGameDefinition} — assets, scene, and opted-in subsystems.
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
|
|
64
64
|
- `CommandDecodeResult` (type): type CommandDecodeResult<TInput> = | { ok: true; value: TInput } | { ok: false; reason: string } — ⚠ undocumented
|
|
65
65
|
- `CommandDecoder` (type): type CommandDecoder<TInput> = (input: unknown) => CommandDecodeResult<TInput> — Parses raw `unknown` transport input into `TInput`, rejecting anything that doesn't match the command's declared shape. Runs before `validate`/`apply`, so a malformed payload never reaches game logic.
|
|
66
|
-
- `CommandDefinition` (interface): interface CommandDefinition<TState, TInput> — ⚠ undocumented
|
|
67
|
-
- `CommandRegistry` (interface): interface CommandRegistry<TState> — ⚠ undocumented
|
|
68
|
-
- `CommandRejection` (interface): interface CommandRejection — ⚠ undocumented
|
|
66
|
+
- `CommandDefinition` (interface): interface CommandDefinition<TState, TInput> { decode?: CommandDecoder<TInput>; validate?(state: TState, input: TInput): CommandRejection | null; apply(state: TState, input: TInput): TState | void } — ⚠ undocumented
|
|
67
|
+
- `CommandRegistry` (interface): interface CommandRegistry<TState> { define<TInput>(name: string, definition: CommandDefinition<TState, TInput>): void; has(name: string): boolean; names(): string[]; run(state: TState, name: string, input: unknown): CommandResult<TState> } — ⚠ undocumented
|
|
68
|
+
- `CommandRejection` (interface): interface CommandRejection { reason: string } — ⚠ undocumented · used by `validateCommandInput` (@jgengine/ws): Validates a `runCommand` input against a declared catalog.
|
|
69
69
|
- `CommandResult` (type): type CommandResult<TState> = | { status: "applied"; state: TState } | { status: "rejected"; reason: string } | { status: "unknown-command" } — ⚠ undocumented
|
|
70
70
|
- `createCommandRegistry` (function): function createCommandRegistry<TState>(): CommandRegistry<TState> — ⚠ undocumented
|
|
71
71
|
|
|
@@ -84,28 +84,28 @@
|
|
|
84
84
|
- `DevtoolsControlKind` (type): type DevtoolsControlKind = | "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum" — ⚠ undocumented
|
|
85
85
|
- `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, and formatted message.
|
|
86
86
|
- `DevtoolsLogLevel` (type): type DevtoolsLogLevel = "log" | "info" | "warn" | "error" — Severity level for a captured devtools log entry.
|
|
87
|
-
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides — ⚠ undocumented
|
|
87
|
+
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides { version: number; enabled: string[]; values: Record<string, unknown>; schemas?: Record<string, {kind: DevtoolsControlKind; schemaVersion?: number}> } — ⚠ undocumented
|
|
88
88
|
- `DevtoolsSnapshot` (interface): interface DevtoolsSnapshot — Serializable point-in-time snapshot of devtools state: frame, render, latency, logs, controls, and discovered fields.
|
|
89
89
|
- `DiscoveredEntry` (interface): interface DiscoveredEntry — A control auto-discovered by scanning an object/table, with its binding metadata and current reader.
|
|
90
|
-
- `DiscoverySkip` (interface): interface DiscoverySkip — ⚠ undocumented
|
|
90
|
+
- `DiscoverySkip` (interface): interface DiscoverySkip { readonly path: string; readonly reason: string } — ⚠ undocumented
|
|
91
91
|
- `FrameRecordSample` (interface): interface FrameRecordSample — Per-frame timing input recorded by callers: total frame time, sim time, and optional named phase durations.
|
|
92
92
|
- `FrameStats` (interface): interface FrameStats — Aggregated frame-timing statistics over the recent sampling window, including per-phase breakdown.
|
|
93
93
|
- `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — Frame duration in milliseconds above which a frame is recorded as a long-frame spike (~30fps budget).
|
|
94
94
|
- `LatencyStats` (interface): interface LatencyStats — Aggregated latency statistics (last/avg/min/max) over recorded latency samples.
|
|
95
95
|
- `LongFrameEvent` (interface): interface LongFrameEvent — Recorded spike event for a frame that exceeded the long-frame threshold, with culprit attribution.
|
|
96
|
-
- `NormalizedColor` (interface): interface NormalizedColor — ⚠ undocumented
|
|
96
|
+
- `NormalizedColor` (interface): interface NormalizedColor { readonly hex: string; readonly rgb: string; readonly alpha: number; readonly hasAlpha: boolean } — ⚠ undocumented
|
|
97
97
|
- `OVERRIDES_FORMAT_VERSION` (const): const OVERRIDES_FORMAT_VERSION: 1 — ⚠ undocumented
|
|
98
|
-
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic — ⚠ undocumented
|
|
98
|
+
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
|
|
99
99
|
- `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
|
|
100
100
|
- `PhaseStats` (interface): interface PhaseStats — Timing summary for a single named profiling phase across the sampling window.
|
|
101
101
|
- `RenderSample` (interface): interface RenderSample — Snapshot of renderer resource counts (draw calls, triangles, geometries, textures) for a frame.
|
|
102
|
-
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions — ⚠ undocumented
|
|
102
|
+
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions { kind?: DevtoolsControlKind } — ⚠ undocumented
|
|
103
103
|
- `ScanMeta` (type): type ScanMeta = Readonly<Record<string, ScanFieldMeta>> — ⚠ undocumented
|
|
104
104
|
- `Tunable` (interface): interface Tunable<T> — Typed handle to a registered control, exposing its live value and subscribe/set/reset operations.
|
|
105
105
|
- `TunableAccessor` (interface): interface TunableAccessor — Get/set accessor pair plus initial value used to bind a discovered field to a control.
|
|
106
|
-
- `TunableChoice` (interface): interface TunableChoice<T = unknown> — ⚠ undocumented
|
|
106
|
+
- `TunableChoice` (interface): interface TunableChoice<T = unknown> { readonly value: T; readonly label?: string } — ⚠ undocumented
|
|
107
107
|
- `TunableInterval` (type): type TunableInterval = { min: number; max: number } — ⚠ undocumented
|
|
108
|
-
- `TunableOptions` (interface): interface TunableOptions<T = unknown> — ⚠ undocumented
|
|
108
|
+
- `TunableOptions` (interface): interface TunableOptions<T = unknown> { min?: number; max?: number; step?: number; options?: readonly T[]; choices?: readonly TunableChoice<T>[]; label?: string; group?: string; onChange?: (value: T) => void; kind?: DevtoolsControlKind; unit?: AngleUnit; displayUnit?: AngleUnit… — ⚠ undocumented · used by `createDiscoverModule` (@jgengine/core/devtools/discover): Create the discovery subsystem that scans objects/tables for tunable fields and binds them as controls and probes.
|
|
109
109
|
- `TunableVec2` (type): type TunableVec2 = [number, number] — ⚠ undocumented
|
|
110
110
|
- `TunableVec3` (type): type TunableVec3 = [number, number, number] — ⚠ undocumented
|
|
111
111
|
- `TunableVec4` (type): type TunableVec4 = [number, number, number, number] — ⚠ undocumented
|
|
@@ -173,28 +173,28 @@
|
|
|
173
173
|
|
|
174
174
|
## @jgengine/core/devtools/transformTunables
|
|
175
175
|
|
|
176
|
-
- `TunableTransformResult` (interface): interface TunableTransformResult — ⚠ undocumented
|
|
176
|
+
- `TunableTransformResult` (interface): interface TunableTransformResult { code: string; bound: string[] } — ⚠ undocumented
|
|
177
177
|
|
|
178
178
|
## @jgengine/core/devtools/tunableSchema
|
|
179
179
|
|
|
180
180
|
- `AngleUnit` (type): type AngleUnit = "rad" | "deg" — ⚠ undocumented
|
|
181
181
|
- `CONTROL_SCHEMA_VERSION` (const): const CONTROL_SCHEMA_VERSION: 1 — ⚠ undocumented
|
|
182
182
|
- `DevtoolsControlKind` (type): type DevtoolsControlKind = | "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum" — ⚠ undocumented
|
|
183
|
-
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides — ⚠ undocumented
|
|
184
|
-
- `DiscoverySkip` (interface): interface DiscoverySkip — ⚠ undocumented
|
|
183
|
+
- `DevtoolsOverrides` (interface): interface DevtoolsOverrides { version: number; enabled: string[]; values: Record<string, unknown>; schemas?: Record<string, {kind: DevtoolsControlKind; schemaVersion?: number}> } — ⚠ undocumented
|
|
184
|
+
- `DiscoverySkip` (interface): interface DiscoverySkip { readonly path: string; readonly reason: string } — ⚠ undocumented
|
|
185
185
|
- `MAX_SCAN_DEPTH` (const): const MAX_SCAN_DEPTH: 5 — ⚠ undocumented
|
|
186
186
|
- `MAX_SCAN_TARGETS` (const): const MAX_SCAN_TARGETS: 512 — ⚠ undocumented
|
|
187
187
|
- `MAX_TABLE_ENTRIES` (const): const MAX_TABLE_ENTRIES: 64 — ⚠ undocumented
|
|
188
|
-
- `NormalizedColor` (interface): interface NormalizedColor — ⚠ undocumented
|
|
188
|
+
- `NormalizedColor` (interface): interface NormalizedColor { readonly hex: string; readonly rgb: string; readonly alpha: number; readonly hasAlpha: boolean } — ⚠ undocumented
|
|
189
189
|
- `OVERRIDES_FORMAT_VERSION` (const): const OVERRIDES_FORMAT_VERSION: 1 — ⚠ undocumented
|
|
190
|
-
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic — ⚠ undocumented
|
|
191
|
-
- `OverrideParseResult` (interface): interface OverrideParseResult — ⚠ undocumented
|
|
192
|
-
- `ResolvedAxisBounds` (interface): interface ResolvedAxisBounds — ⚠ undocumented
|
|
193
|
-
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions — ⚠ undocumented
|
|
190
|
+
- `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
|
|
191
|
+
- `OverrideParseResult` (interface): interface OverrideParseResult { readonly overrides: DevtoolsOverrides | null; readonly diagnostics: readonly string[] } — ⚠ undocumented
|
|
192
|
+
- `ResolvedAxisBounds` (interface): interface ResolvedAxisBounds { readonly labels: readonly string[]; readonly min: readonly number[]; readonly max: readonly number[]; readonly step: readonly number[] } — ⚠ undocumented
|
|
193
|
+
- `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions { kind?: DevtoolsControlKind } — ⚠ undocumented
|
|
194
194
|
- `ScanMeta` (type): type ScanMeta = Readonly<Record<string, ScanFieldMeta>> — ⚠ undocumented
|
|
195
|
-
- `TunableChoice` (interface): interface TunableChoice<T = unknown> — ⚠ undocumented
|
|
195
|
+
- `TunableChoice` (interface): interface TunableChoice<T = unknown> { readonly value: T; readonly label?: string } — ⚠ undocumented
|
|
196
196
|
- `TunableInterval` (type): type TunableInterval = { min: number; max: number } — ⚠ undocumented
|
|
197
|
-
- `TunableOptions` (interface): interface TunableOptions<T = unknown> — ⚠ undocumented
|
|
197
|
+
- `TunableOptions` (interface): interface TunableOptions<T = unknown> { min?: number; max?: number; step?: number; options?: readonly T[]; choices?: readonly TunableChoice<T>[]; label?: string; group?: string; onChange?: (value: T) => void; kind?: DevtoolsControlKind; unit?: AngleUnit; displayUnit?: AngleUnit… — ⚠ undocumented · used by `createDiscoverModule` (@jgengine/core/devtools/discover): Create the discovery subsystem that scans objects/tables for tunable fields and binds them as controls and probes.
|
|
198
198
|
- `TunableVec2` (type): type TunableVec2 = [number, number] — ⚠ undocumented
|
|
199
199
|
- `TunableVec3` (type): type TunableVec3 = [number, number, number] — ⚠ undocumented
|
|
200
200
|
- `TunableVec4` (type): type TunableVec4 = [number, number, number, number] — ⚠ undocumented
|
|
@@ -224,18 +224,22 @@
|
|
|
224
224
|
- `subscribeUrlChange` (function): function subscribeUrlChange(listener: () => void): () => void — Fires `listener` whenever the query string may have changed out from under a flag — browser back/forward (`popstate`). Manual address-bar edits reload the page, so initial reads cover those; this keeps in-app state honest across history navigation. Returns an unsubscribe.
|
|
225
225
|
- `writeUrlParam` (function): function writeUrlParam(param: string, value: string | null): void — Sets `param` to `value`, or removes it when `value` is null, rewriting the URL in place with `history.replaceState` so the rest of the query and the hash survive and no history entry is pushed. No-ops without a DOM, and skips the write when the URL already matches.
|
|
226
226
|
|
|
227
|
+
## @jgengine/core/devtools/warnOnce
|
|
228
|
+
|
|
229
|
+
- `warnOnce` (function): function warnOnce(site: string, message: string): void — Logs a dev-mode `console.warn` once per `site`, and never in a production build. Reach for it where a permissive config would otherwise fail silently — a clipped sky dome, an ignored tint.
|
|
230
|
+
|
|
227
231
|
## @jgengine/core/meta/changelog
|
|
228
232
|
|
|
229
233
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
230
234
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
231
|
-
- `VERSION` (const): const VERSION: "0.
|
|
235
|
+
- `VERSION` (const): const VERSION: "0.19.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
232
236
|
|
|
233
237
|
## @jgengine/core/runtime/adapter
|
|
234
238
|
|
|
235
|
-
- `MultiplayerAdapterConfig` (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult… — ⚠ undocumented
|
|
239
|
+
- `MultiplayerAdapterConfig` (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult… — ⚠ undocumented · used by `convex`: Convex transport.
|
|
236
240
|
- `MultiplayerAuthority` (type): type MultiplayerAuthority = "server" | "client" — Where the world simulation is authoritative.
|
|
237
|
-
- `MultiplayerTopology` (type): type MultiplayerTopology = "shared" | "lobbies" | "private" — ⚠ undocumented
|
|
238
|
-
- `ServersPoolConfig` (type): type ServersPoolConfig = { minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; } & ( | { topology: "shared"; maxServers?: number; slotsPerServer?: number } | { topology?: "rooms"; maxServers: number; slotsPerServer: number } ) — ⚠ undocumented
|
|
241
|
+
- `MultiplayerTopology` (type): type MultiplayerTopology = "shared" | "lobbies" | "private" — ⚠ undocumented · used by `convex`: Convex transport.
|
|
242
|
+
- `ServersPoolConfig` (type): type ServersPoolConfig = { minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; } & ( | { topology: "shared"; maxServers?: number; slotsPerServer?: number } | { topology?: "rooms"; maxServers: number; slotsPerServer: number } ) — ⚠ undocumented · used by `servers`: Resolve the shared-world pool to one unbounded world; room pools keep explicit capacities.
|
|
239
243
|
- `adapterOf` (function): function adapterOf(multiplayer: unknown): MultiplayerAdapterConfig | null — ⚠ undocumented
|
|
240
244
|
- `convex` (function): function convex(config?: { topology?: MultiplayerTopology; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig — Convex transport. Omitting `authority` (or passing `"client"`) is **presence-only** — prefer `convexPresence()` to name that intent explicitly. Pass `{ authority: "server" }` for a shared, host-authoritative world — see `examples/HOSTED.md`.
|
|
241
245
|
- `convexPresence` (function): function convexPresence(config?: { topology?: MultiplayerTopology }): MultiplayerAdapterConfig — Presence-only Convex transport — each client runs its own `onTick`; only presence/feeds/chat sync. Sugar for `convex({ ...config, authority: "client" })`.
|
|
@@ -255,21 +259,34 @@
|
|
|
255
259
|
|
|
256
260
|
## @jgengine/core/runtime/cameraDirector
|
|
257
261
|
|
|
258
|
-
- `
|
|
259
|
-
- `
|
|
262
|
+
- `CHASE_VIEWS` (const): const CHASE_VIEWS: readonly ChaseView[] — Default order {@link nextChaseView} cycles through.
|
|
263
|
+
- `CameraDirector` (interface): interface CameraDirector { follow(entityId: string | null): void; followedEntityId(): string | null | undefined; setCinematic(config: CinematicCameraConfig | null): void; cinematic(): CinematicCameraConfig | null; setChaseTuning(tuning: ChaseCameraTuning | null): v… — ⚠ undocumented
|
|
264
|
+
- `ChaseCameraTuning` (type): type ChaseCameraTuning = Partial< Pick< ChaseCameraConfig, | "distance" | "height" | "lookHeight" | "springDamping" | "fov" | "lead" | "bank" | "shakePerSpeed" | "velocityYaw" | "yawResponse" | "distanceBySpeed" | "pitchFollow" | "fovKick" | "lookBackAction" | "collision" | "view" > > — Runtime patch over the static `camera.chase` config — distance/height/fov retuning from gameplay events (#286.11), a whole driving-feel overlay applied only while a vehicle is piloted (#1299), or a `view` switch between chase and seat cameras.
|
|
265
|
+
- `nextChaseView` (function): function nextChaseView(current: ChaseView, views: readonly ChaseView[] = CHASE_VIEWS): ChaseView — The view after `current` in `views`, wrapping at the end; a view missing from the list starts the cycle over. Pair with `setChaseTuning` to bind a "change camera" key: `ctx.camera.setChaseTuning({ ...ctx.camera.chaseTuning(), view: nextChaseView(view) })`.
|
|
260
266
|
|
|
261
267
|
## @jgengine/core/runtime/commandInput
|
|
262
268
|
|
|
263
269
|
- `QuantityResult` (type): type QuantityResult = { ok: true; quantity: number } | { ok: false; reason: "invalid-quantity" } — Validated integer quantity or a stable input rejection.
|
|
270
|
+
- `isInputRecord` (function): function isInputRecord(value: unknown): value is Record<string, unknown> — Narrow an untrusted value to a plain object, rejecting arrays and null.
|
|
271
|
+
- `readInputArray` (function): function readInputArray<T>(value: unknown, readItem: (item: unknown) => T | null, options: { maxItems?: number } = {}): T[] | null — Read an array of at most `maxItems` entries, each accepted by `readItem`; any rejected entry rejects the whole array.
|
|
272
|
+
- `readInputNumber` (function): function readInputNumber(value: unknown, options: { min?: number; max?: number; integer?: boolean } = {}): number | null — Read a finite number inside `[min, max]`, or `null`. `NaN` and `±Infinity` are always rejected, so a coordinate can never drive an unbounded loop; `integer` also rejects fractions instead of flooring them.
|
|
273
|
+
- `readInputOneOf` (function): function readInputOneOf<const T extends string | number>(value: unknown, allowed: readonly T[]): T | null — Read one of a fixed set of values, or `null`.
|
|
274
|
+
- `readInputPoint2` (function): function readInputPoint2(value: unknown, options: { maxAbs?: number } = {}): { x: number; z: number } | null — Read an `{ x, z }` ground point whose axes are finite and within `maxAbs`, or `null`.
|
|
275
|
+
- `readInputPoint3` (function): function readInputPoint3(value: unknown, options: { maxAbs?: number } = {}): { x: number; y: number; z: number } | null — Read an `{ x, y, z }` point whose axes are finite and within `maxAbs`, or `null`.
|
|
276
|
+
- `readInputString` (function): function readInputString(value: unknown, options: { maxLength?: number } = {}): string | null — Read a non-empty string of at most `maxLength` characters, or `null`.
|
|
264
277
|
- `readQuantity` (function): function readQuantity(value: unknown, options: { min?: number; max?: number } = {}): QuantityResult — Read an untrusted whole-item quantity without coercion, truncation, or clamping.
|
|
265
278
|
|
|
266
279
|
## @jgengine/core/runtime/commandRunner
|
|
267
280
|
|
|
268
|
-
- `
|
|
281
|
+
- `CommandAccess` (type): type CommandAccess = "client" | "server" — Who may send a command: any client through the public transport, or only trusted host code.
|
|
282
|
+
- `CommandDef` (type): type CommandDef<TInput = unknown> = { /** * `"server"` when `input` carries facts only the host may decide (unlock state, prices, multipliers): * public transports then refuse it and only trusted host calls such as `helpers.runCommand` run it. * Defaults to `"client"`, so every field of `input` must… — ⚠ undocumented · used by `defineCommand`: Declare a command with a typed input: `parse` narrows the wire value once, and `scope`, `validate` and `apply` receive the parsed type.
|
|
269
283
|
- `CommandScope` (type): type CommandScope = { /** Member ids to hydrate; omit for the whole roster. */ players?: readonly string[]; /** Chunk keys to hydrate; omit for every chunk of the server, `[]` for none. */ chunkKeys?: readonly string[]; } — Which slice of a server has to be hydrated before some work runs against it. Both fields default to "everything", which costs a read per member plus one per chunk — the cost that makes a large shared world unaffordable per mutation. Narrow them when the caller knows what it will touch.
|
|
270
284
|
- `CommandScopeDefinition` (type): type CommandScopeDefinition = Omit<CommandScope, "players"> & { players?: readonly string[] | "actor" } — Declarative read scope; actor is resolved before host hydration.
|
|
271
285
|
- `CommandValidationError` (type): type CommandValidationError = { reason: string } — ⚠ undocumented
|
|
286
|
+
- `MALFORMED_COMMAND_INPUT_REASON` (const): const MALFORMED_COMMAND_INPUT_REASON: "Malformed command input" — Reason every host returns when a command's `parse` rejects its input.
|
|
272
287
|
- `RunCommandResult` (type): type RunCommandResult = | { ok: true; snapshot: GameRuntimeSnapshot } | { ok: false; reason: string } — ⚠ undocumented
|
|
288
|
+
- `SERVER_ONLY_COMMAND_REASON` (const): const SERVER_ONLY_COMMAND_REASON: "Command is server-only" — Reason a public transport returns for a {@link CommandDef} marked `access: "server"`.
|
|
289
|
+
- `defineCommand` (function): function defineCommand<TInput>(definition: CommandDef<TInput> & { parse: (input: unknown) => TInput | null }): CommandDef — Declare a command with a typed input: `parse` narrows the wire value once, and `scope`, `validate` and `apply` receive the parsed type. Returns the untyped {@link CommandDef} a runtime's command table holds.
|
|
273
290
|
|
|
274
291
|
## @jgengine/core/runtime/context/objectSlots
|
|
275
292
|
|
|
@@ -281,30 +298,30 @@
|
|
|
281
298
|
## @jgengine/core/runtime/gameContext
|
|
282
299
|
|
|
283
300
|
- `CatalogEntityRole` (type): type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle" — ⚠ undocumented
|
|
284
|
-
- `FloatTextInput` (interface): interface FloatTextInput — ⚠ undocumented
|
|
301
|
+
- `FloatTextInput` (interface): interface FloatTextInput { instanceId?: string; position?: [number, number, number]; text?: string; kind?: string; amount?: number; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
|
|
285
302
|
- `GameAudio` (interface): interface GameAudio — Reachable audio seam on `ctx.game`: `play`, `music`, and `resume` route through the `audio.play`/`audio.music`/`audio.resume` events the shell's audio engine listens on, so game code triggers sound without importing the shell. Retained `loop`/`setLoop`/`stopLoop` add id-keyed loops with live pitch/gain control over `audio.loopStart`/`audio.loopSet`/`audio.loopStop` (#1051).
|
|
286
303
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
287
|
-
- `GameContextCards` (interface): interface GameContextCards — ⚠ undocumented
|
|
288
|
-
- `GameContextCommands` (interface): interface GameContextCommands — ⚠ undocumented
|
|
289
|
-
- `GameContextContent` (interface): interface GameContextContent — ⚠ undocumented
|
|
290
|
-
- `GameContextEconomy` (interface): interface GameContextEconomy — ⚠ undocumented
|
|
291
|
-
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry — ⚠ undocumented
|
|
292
|
-
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> — ⚠ undocumented
|
|
293
|
-
- `GameContextItemEntry` (interface): interface GameContextItemEntry — ⚠ undocumented
|
|
294
|
-
- `GameContextItemUse` (interface): interface GameContextItemUse — ⚠ undocumented
|
|
295
|
-
- `GameContextLoot` (interface): interface GameContextLoot — ⚠ undocumented
|
|
304
|
+
- `GameContextCards` (interface): interface GameContextCards { pile(id: string, config?: CardPileConfig): CardPile } — ⚠ undocumented
|
|
305
|
+
- `GameContextCommands` (interface): interface GameContextCommands { define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void; has(name: string): boolean; names(): string[]; run(name: string, input: unknown): CommandResult<GameContext>; runAs(actorUserId: string, name: string,… — ⚠ undocumented
|
|
306
|
+
- `GameContextContent` (interface): interface GameContextContent { itemById?(itemId: string): GameContextItemEntry | null | undefined; entityById?(catalogId: string): GameContextEntityEntry | null | undefined; objectById?(catalogId: string): GameContextObjectEntry | null | undefined } — ⚠ undocumented
|
|
307
|
+
- `GameContextEconomy` (interface): interface GameContextEconomy { balance(userId: string, currencyId: string | CurrencyDefinition): number; grant(userId: string, currencyId: string | CurrencyDefinition, amount: number): void; charge(userId: string, currencyId: string | CurrencyDefinition, amount: number… — ⚠ undocumented
|
|
308
|
+
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry { stats?: StatCatalog; receive?: ReceiveMap; onDeath?: OnDeathSpec; movement?: PoseAllowedStates & {walkSpeed?: number}; role?: CatalogEntityRole; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; scale?: number } — ⚠ undocumented · used by `entityEntryFromCatalog` (@jgengine/core/editor): Turns an authored entity row into the runtime {@link GameContextEntityEntry} a spawn consumes: looks up `entityId` in the document's {@link …
|
|
309
|
+
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> { bind(action: keyof GameEventMap): () => void } — ⚠ undocumented
|
|
310
|
+
- `GameContextItemEntry` (interface): interface GameContextItemEntry { use?: string; weapon?: Record<string, unknown>; trade?: TradeField; rarity?: string; baseType?: string } — ⚠ undocumented
|
|
311
|
+
- `GameContextItemUse` (interface): interface GameContextItemUse { register(handlers: Record<string, ItemUseHandler<GameContext>>): void; registered(): string[]; can(input: ItemUseInput): ItemUseRejection | null; use(input: ItemUseInput): ItemUseResult<GameContext> } — ⚠ undocumented
|
|
312
|
+
- `GameContextLoot` (interface): interface GameContextLoot { register(def: LootTableDef): void; has(id: string): boolean; roll(id: string, rng?: () => number): Drop[]; grantToPlayer(userId: string, drops: Drop[], source?: string): void } — ⚠ undocumented
|
|
296
313
|
- `GameContextModels` (interface): interface GameContextModels — Per-kind render-model lookup for {@link GameContextOptions.models}; a resolved `ModelConfig` satisfies {@link ModelBodySource} structurally.
|
|
297
|
-
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry — ⚠ undocumented
|
|
298
|
-
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — ⚠ undocumented
|
|
299
|
-
- `GameContextRace` (interface): interface GameContextRace — ⚠ undocumented
|
|
300
|
-
- `GameContextTurn` (interface): interface GameContextTurn — ⚠ undocumented
|
|
301
|
-
- `GameContextWorld` (interface): interface GameContextWorld — ⚠ undocumented
|
|
302
|
-
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented
|
|
314
|
+
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry { proximityPrompt?: ProximityPrompt; breakable?: false | {baseBreakTime: number}; slotInventory?: InventoryLayout; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; halfExtents?: readonly [number, number, number] } — ⚠ undocumented
|
|
315
|
+
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> { definition: GameDefinition<TAssetRef, TMultiplayer>; content: GameContextContent; player: {userId: string; isNew: boolean}; territory?: Territory; now?: () => number; occluder?: (from: EntityPosition, to: EntityPosition) => boolean; seed?… — ⚠ undocumented
|
|
316
|
+
- `GameContextRace` (interface): interface GameContextRace { state(id: string, config?: RaceStateConfig): RaceState } — ⚠ undocumented
|
|
317
|
+
- `GameContextTurn` (interface): interface GameContextTurn { loop(id: string, config?: TurnLoopConfig): TurnLoop } — ⚠ undocumented
|
|
318
|
+
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
319
|
+
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
303
320
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
304
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext — ⚠ undocumented
|
|
305
|
-
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore — ⚠ undocumented
|
|
306
|
-
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext —
|
|
307
|
-
- `TelegraphInput` (interface): interface TelegraphInput — ⚠ undocumented
|
|
321
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setPoseConstraint: EntityStore["setPoseConstraint"]; get(ins… — ⚠ undocumented
|
|
322
|
+
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
323
|
+
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
324
|
+
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
308
325
|
- `VfxInput` (interface): interface VfxInput — Request a transient spell/ability VFX burst. The easy path is a named `preset` — `vfx({ preset: "arrow", from: caster, to: enemy })` renders a visible bolt with no color or archetype tuning; `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` and the rest of {@link vfxPresets} likewise just work. `from`/`to` accept an instance id (the shell follows its live pose) or a fixed world point. Anything you also pass — `kind`, `color` (`0xRRGGBB`), `radius`, `durationMs` — overrides the preset; supply `kind` + `color` yourself for a fully custom burst with no preset. `durationMs` defaults per `kind`.
|
|
309
326
|
- `WorldItemPickupResult` (type): type WorldItemPickupResult = | { status: "ok"; record: WorldItemRecord } | { status: "rejected"; reason: string } — ⚠ undocumented
|
|
310
327
|
- `createGameContext` (function): function createGameContext<TAssetRef extends ModelAssetRef, TMultiplayer>(options: GameContextOptions<TAssetRef, TMultiplayer>): GameContext — ⚠ undocumented
|
|
@@ -312,36 +329,36 @@
|
|
|
312
329
|
## @jgengine/core/runtime/gameContextTypes
|
|
313
330
|
|
|
314
331
|
- `CatalogEntityRole` (type): type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle" — ⚠ undocumented
|
|
315
|
-
- `FloatTextInput` (interface): interface FloatTextInput — ⚠ undocumented
|
|
332
|
+
- `FloatTextInput` (interface): interface FloatTextInput { instanceId?: string; position?: [number, number, number]; text?: string; kind?: string; amount?: number; hitType?: string; element?: string; crit?: boolean; scale?: number } — ⚠ undocumented
|
|
316
333
|
- `GameAudio` (interface): interface GameAudio — Reachable audio seam on `ctx.game`: `play`, `music`, and `resume` route through the `audio.play`/`audio.music`/`audio.resume` events the shell's audio engine listens on, so game code triggers sound without importing the shell. Retained `loop`/`setLoop`/`stopLoop` add id-keyed loops with live pitch/gain control over `audio.loopStart`/`audio.loopSet`/`audio.loopStop` (#1051).
|
|
317
334
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
318
|
-
- `GameContextCards` (interface): interface GameContextCards — ⚠ undocumented
|
|
319
|
-
- `GameContextCommands` (interface): interface GameContextCommands — ⚠ undocumented
|
|
320
|
-
- `GameContextContent` (interface): interface GameContextContent — ⚠ undocumented
|
|
321
|
-
- `GameContextEconomy` (interface): interface GameContextEconomy — ⚠ undocumented
|
|
322
|
-
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry — ⚠ undocumented
|
|
323
|
-
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> — ⚠ undocumented
|
|
324
|
-
- `GameContextItemEntry` (interface): interface GameContextItemEntry — ⚠ undocumented
|
|
325
|
-
- `GameContextItemUse` (interface): interface GameContextItemUse — ⚠ undocumented
|
|
326
|
-
- `GameContextLoot` (interface): interface GameContextLoot — ⚠ undocumented
|
|
335
|
+
- `GameContextCards` (interface): interface GameContextCards { pile(id: string, config?: CardPileConfig): CardPile } — ⚠ undocumented
|
|
336
|
+
- `GameContextCommands` (interface): interface GameContextCommands { define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void; has(name: string): boolean; names(): string[]; run(name: string, input: unknown): CommandResult<GameContext>; runAs(actorUserId: string, name: string,… — ⚠ undocumented
|
|
337
|
+
- `GameContextContent` (interface): interface GameContextContent { itemById?(itemId: string): GameContextItemEntry | null | undefined; entityById?(catalogId: string): GameContextEntityEntry | null | undefined; objectById?(catalogId: string): GameContextObjectEntry | null | undefined } — ⚠ undocumented
|
|
338
|
+
- `GameContextEconomy` (interface): interface GameContextEconomy { balance(userId: string, currencyId: string | CurrencyDefinition): number; grant(userId: string, currencyId: string | CurrencyDefinition, amount: number): void; charge(userId: string, currencyId: string | CurrencyDefinition, amount: number… — ⚠ undocumented
|
|
339
|
+
- `GameContextEntityEntry` (interface): interface GameContextEntityEntry { stats?: StatCatalog; receive?: ReceiveMap; onDeath?: OnDeathSpec; movement?: PoseAllowedStates & {walkSpeed?: number}; role?: CatalogEntityRole; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; scale?: number } — ⚠ undocumented · used by `entityEntryFromCatalog` (@jgengine/core/editor): Turns an authored entity row into the runtime {@link GameContextEntityEntry} a spawn consumes: looks up `entityId` in the document's {@link …
|
|
340
|
+
- `GameContextFeed` (interface): interface GameContextFeed extends Omit<GameFeed, "bind"> { bind(action: keyof GameEventMap): () => void } — ⚠ undocumented
|
|
341
|
+
- `GameContextItemEntry` (interface): interface GameContextItemEntry { use?: string; weapon?: Record<string, unknown>; trade?: TradeField; rarity?: string; baseType?: string } — ⚠ undocumented
|
|
342
|
+
- `GameContextItemUse` (interface): interface GameContextItemUse { register(handlers: Record<string, ItemUseHandler<GameContext>>): void; registered(): string[]; can(input: ItemUseInput): ItemUseRejection | null; use(input: ItemUseInput): ItemUseResult<GameContext> } — ⚠ undocumented
|
|
343
|
+
- `GameContextLoot` (interface): interface GameContextLoot { register(def: LootTableDef): void; has(id: string): boolean; roll(id: string, rng?: () => number): Drop[]; grantToPlayer(userId: string, drops: Drop[], source?: string): void } — ⚠ undocumented
|
|
327
344
|
- `GameContextModels` (interface): interface GameContextModels — Per-kind render-model lookup for {@link GameContextOptions.models}; a resolved `ModelConfig` satisfies {@link ModelBodySource} structurally.
|
|
328
|
-
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry — ⚠ undocumented
|
|
329
|
-
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — ⚠ undocumented
|
|
330
|
-
- `GameContextRace` (interface): interface GameContextRace — ⚠ undocumented
|
|
331
|
-
- `GameContextTurn` (interface): interface GameContextTurn — ⚠ undocumented
|
|
332
|
-
- `GameContextWorld` (interface): interface GameContextWorld — ⚠ undocumented
|
|
333
|
-
- `HitReactionInput` (interface): interface HitReactionInput — ⚠ undocumented
|
|
345
|
+
- `GameContextObjectEntry` (interface): interface GameContextObjectEntry { proximityPrompt?: ProximityPrompt; breakable?: false | {baseBreakTime: number}; slotInventory?: InventoryLayout; verbs?: readonly ContextVerb[]; colliders?: EntityColliderSet; halfExtents?: readonly [number, number, number] } — ⚠ undocumented
|
|
346
|
+
- `GameContextOptions` (interface): interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> { definition: GameDefinition<TAssetRef, TMultiplayer>; content: GameContextContent; player: {userId: string; isNew: boolean}; territory?: Territory; now?: () => number; occluder?: (from: EntityPosition, to: EntityPosition) => boolean; seed?… — ⚠ undocumented
|
|
347
|
+
- `GameContextRace` (interface): interface GameContextRace { state(id: string, config?: RaceStateConfig): RaceState } — ⚠ undocumented
|
|
348
|
+
- `GameContextTurn` (interface): interface GameContextTurn { loop(id: string, config?: TurnLoopConfig): TurnLoop } — ⚠ undocumented
|
|
349
|
+
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
350
|
+
- `HitReactionInput` (interface): interface HitReactionInput { from: string; to: string; config: HitReactionConfig | ImpactPresetName; power?: number } — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
334
351
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
335
|
-
- `SceneEntityContext` (interface): interface SceneEntityContext — ⚠ undocumented
|
|
336
|
-
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore — ⚠ undocumented
|
|
337
|
-
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext —
|
|
338
|
-
- `TelegraphInput` (interface): interface TelegraphInput — ⚠ undocumented
|
|
352
|
+
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setPoseConstraint: EntityStore["setPoseConstraint"]; get(ins… — ⚠ undocumented
|
|
353
|
+
- `SceneObjectContext` (interface): interface SceneObjectContext extends ObjectStore { catalog(instanceId: string): GameContextObjectEntry | null; raycast(input: ObjectRaycastInput): ObjectRaycastHit | null; raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[]; setColliders(instanceId: string, colliders: Entit… — ⚠ undocumented
|
|
354
|
+
- `SceneWorldItemContext` (interface): interface SceneWorldItemContext — `ctx.scene.worldItem`: items lying in the world — spawn, list, find the nearest, and pick up into a player's bag.
|
|
355
|
+
- `TelegraphInput` (interface): interface TelegraphInput { from: string; shape: TelegraphShape; at: [number, number, number]; dir?: number; windupMs: number; kind?: string; effect?: {effect: string; via?: EffectVia; radius?: number; falloff?: "linear" | "none"; los?: boolean;} } — ⚠ undocumented
|
|
339
356
|
- `VfxInput` (interface): interface VfxInput — Request a transient spell/ability VFX burst. The easy path is a named `preset` — `vfx({ preset: "arrow", from: caster, to: enemy })` renders a visible bolt with no color or archetype tuning; `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` and the rest of {@link vfxPresets} likewise just work. `from`/`to` accept an instance id (the shell follows its live pose) or a fixed world point. Anything you also pass — `kind`, `color` (`0xRRGGBB`), `radius`, `durationMs` — overrides the preset; supply `kind` + `color` yourself for a fully custom burst with no preset. `durationMs` defaults per `kind`.
|
|
340
357
|
- `WorldItemPickupResult` (type): type WorldItemPickupResult = | { status: "ok"; record: WorldItemRecord } | { status: "rejected"; reason: string } — ⚠ undocumented
|
|
341
358
|
|
|
342
359
|
## @jgengine/core/runtime/gameRuntime
|
|
343
360
|
|
|
344
|
-
- `GameRuntime` (type): type GameRuntime = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; /** * Whether this runtime declares `loop.onTick`. A host's tick cron reads it to skip hydrating and * persisting a server whose `tick` is a no-op by construction — the difference between a world that * costs a ful… — ⚠ undocumented
|
|
361
|
+
- `GameRuntime` (type): type GameRuntime = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; /** * Whether this runtime declares `loop.onTick`. A host's tick cron reads it to skip hydrating and * persisting a server whose `tick` is a no-op by construction — the difference between a world that * costs a ful… — ⚠ undocumented · used by `initialPlayerState`: Seed a fresh player through the same onNewPlayer hook as joining, without hydrating or modifying a world.
|
|
345
362
|
- `GameRuntimeDefinition` (type): type GameRuntimeDefinition = { gameId: string; topology?: "shared" | "rooms"; save: SaveConfig; commands: Record<string, CommandDef>; loop?: ServerLoopHooks; } — ⚠ undocumented
|
|
346
363
|
- `HydrateInput` (type): type HydrateInput = { gameId: string; serverId: string; serverRow: RuntimeServerRow; playersByUserId: Record<string, RuntimePlayerRow>; chunksByKey: Record<string, RuntimeChunkRow>; revision?: number; /** Host wall clock for `onInit`, in ms. Defaults to `Date.now()`. */ nowMs?: number; } — ⚠ undocumented
|
|
347
364
|
- `RuntimeInitContext` (type): type RuntimeInitContext = { snapshot: GameRuntimeSnapshot; setSnapshot: (snapshot: GameRuntimeSnapshot) => void; /** * Host wall clock at the start of this call, in ms. The host already knows it, so anything keyed to * real time — a UTC date rollover, a `lastTickAt` anchor other code paths read, a s… — ⚠ undocumented
|
|
@@ -355,27 +372,27 @@
|
|
|
355
372
|
- `HeadlessCommandInterface` (interface): interface HeadlessCommandInterface — Canvas-free command/intent surface for {@link HeadlessRunner} — invoke a game's registered UI commands and read the resulting reactive state off `ctx`.
|
|
356
373
|
- `HeadlessInput` (interface): interface HeadlessInput — One step's worth of player intent handed to {@link HeadlessRunner.step} — the held-action set and pointer state the shell would otherwise publish from the browser each frame.
|
|
357
374
|
- `HeadlessRunner` (interface): interface HeadlessRunner — A renderer-free driver for a game loop: builds a {@link GameContext} from a {@link GameDefinition}, runs the init hooks, then advances the simulation one step at a time from injected input. No React, R3F, or three.js — the whole play path (time, input, `onTick`, behaviour nav, optional player movement) runs from `core` primitives alone, so a non-React host (a server tick, a test, a CLI replay) can play a real game and read its world snapshot. The shell's FrameDriver is one such driver bolted to `useFrame`; this is the same step distilled out of the render tree.
|
|
358
|
-
- `HeadlessRunnerOptions` (interface): interface HeadlessRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — ⚠ undocumented
|
|
375
|
+
- `HeadlessRunnerOptions` (interface): interface HeadlessRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> { definition: GameDefinition<TAssetRef, TMultiplayer>; content?: GameContextContent; loop?: GameLoop<GameContext>; player?: {userId: string; isNew: boolean}; now?: () => number; maxStepSeconds?: number; playerMovement?: boolean; movement?: … — ⚠ undocumented
|
|
359
376
|
- `createHeadlessRunner` (function): function createHeadlessRunner<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HeadlessRunnerOptions<TAssetRef, TMultiplayer>): HeadlessRunner — ⚠ undocumented
|
|
360
377
|
|
|
361
378
|
## @jgengine/core/runtime/hostPersistence
|
|
362
379
|
|
|
363
380
|
- `FEED_RING_LIMIT` (const): const FEED_RING_LIMIT: 20 — ⚠ undocumented
|
|
364
381
|
- `GameServerRecord` (type): type GameServerRecord = { serverId: string; gameId: string; status: GameServerStatus; mode?: string; modeConfig?: unknown; label?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; memberUserIds: string[]; slotsPerServer: number; save: SaveConfig; serverState: RuntimeServer… — ⚠ undocumented
|
|
365
|
-
- `GameServerStatus` (type): type GameServerStatus = "open" | "running" | "closed" — ⚠ undocumented
|
|
366
|
-
- `HostPersistence` (type): type HostPersistence = { savePlan?: (plan: ServerPersistPlan) => Promise<void>; resetScenario?: (reset: NormalizedScenarioReset) => Promise<void>; loadServer: (serverId: string) => Promise<GameServerRecord | null>; saveServer: (record: GameServerRecord) => Promise<void>; listServers: (gameId: string… — ⚠ undocumented
|
|
382
|
+
- `GameServerStatus` (type): type GameServerStatus = "open" | "running" | "closed" — ⚠ undocumented · used by `statusAfterLeave` (@jgengine/core/runtime/hostPolicy): Status after a leave: empty rooms reopen; non-empty rooms keep their current status.
|
|
383
|
+
- `HostPersistence` (type): type HostPersistence = { savePlan?: (plan: ServerPersistPlan) => Promise<void>; resetScenario?: (reset: NormalizedScenarioReset) => Promise<void>; loadServer: (serverId: string) => Promise<GameServerRecord | null>; saveServer: (record: GameServerRecord) => Promise<void>; listServers: (gameId: string… — ⚠ undocumented · used by `memoryPersistence` (@jgengine/node): Creates an in-memory `HostPersistence` implementation, useful for tests and ephemeral hosts.
|
|
367
384
|
- `LEADERBOARD_PENDING_KEY` (const): const LEADERBOARD_PENDING_KEY: "leaderboardPending" — ⚠ undocumented
|
|
368
385
|
- `LEADERBOARD_TOP_LIMIT` (const): const LEADERBOARD_TOP_LIMIT: 100 — ⚠ undocumented
|
|
369
386
|
- `LeaderboardEntry` (type): type LeaderboardEntry = { userId: string; value: number; } — ⚠ undocumented
|
|
370
387
|
- `LeaderboardIncrement` (type): type LeaderboardIncrement = { userId: string; stat: string; scope: LeaderboardScope; serverId?: string; by: number; } — ⚠ undocumented
|
|
371
|
-
- `LeaderboardRow` (type): type LeaderboardRow = { gameId: string; stat: string; scope: LeaderboardScope; serverId?: string; userId: string; value: number; updatedAt: number; } — ⚠ undocumented
|
|
388
|
+
- `LeaderboardRow` (type): type LeaderboardRow = { gameId: string; stat: string; scope: LeaderboardScope; serverId?: string; userId: string; value: number; updatedAt: number; } — ⚠ undocumented · used by `createLeaderboard` (@jgengine/core/game/leaderboard): Ranked score tracking across global, server, and per-profile scopes, with top-N queries and per-profile lookups.
|
|
372
389
|
- `OPEN_SERVER_LISTING_LIMIT` (const): const OPEN_SERVER_LISTING_LIMIT: 20 — ⚠ undocumented
|
|
373
390
|
- `OPEN_SERVER_LISTING_MAX` (const): const OPEN_SERVER_LISTING_MAX: 100 — ⚠ undocumented
|
|
374
391
|
- `PlayerProfileRecord` (type): type PlayerProfileRecord = { userId: string; gameId: string; playerState: RuntimePlayerRow; revision: number; updatedAt: number; } — ⚠ undocumented
|
|
375
392
|
- `ServerListing` (type): type ServerListing = { serverId: string; status: GameServerStatus; memberCount: number; slotsPerServer: number; mode?: string; label?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; updatedAt: number; } — ⚠ undocumented
|
|
376
393
|
- `ServerPersistPlan` (type): type ServerPersistPlan = { server: GameServerRecord; profiles: PlayerProfileRecord[]; chunks: WorldChunkRecord[]; deletedChunks: string[]; leaderboard: LeaderboardIncrement[]; /** * Which fields of the server row the plan actually moved. A store that pays per written field — * or whose subscribers r… — ⚠ undocumented
|
|
377
394
|
- `SessionAttributes` (type): type SessionAttributes = { label?: string; mode?: string; visibility?: SessionVisibility; joinCode?: string; tags?: string[]; } — ⚠ undocumented
|
|
378
|
-
- `SessionVisibility` (type): type SessionVisibility = "public" | "private" — ⚠ undocumented
|
|
395
|
+
- `SessionVisibility` (type): type SessionVisibility = "public" | "private" — ⚠ undocumented · used by `isAutoJoinCandidate` (@jgengine/core/runtime/hostPolicy): Auto-match candidate when no `serverId` is supplied: already a member, or a public room with free capacity.
|
|
379
396
|
- `ToServerListingOptions` (type): type ToServerListingOptions = { includeJoinCode?: boolean; } — ⚠ undocumented
|
|
380
397
|
- `WorldChunkRecord` (type): type WorldChunkRecord = { serverId: string; chunkKey: string; snapshot: RuntimeChunkRow; updatedAt: number; } — ⚠ undocumented
|
|
381
398
|
- `isSnapshotClean` (function): function isSnapshotClean(snapshot: GameRuntimeSnapshot): boolean — True when a snapshot carries no unwritten mutation — nothing for a persist to do.
|
|
@@ -432,12 +449,23 @@
|
|
|
432
449
|
## @jgengine/core/runtime/inputSnapshot
|
|
433
450
|
|
|
434
451
|
- `InputFrame` (interface): interface InputFrame — One client's input for a tick — the semantic held-action set plus pointer state, the serializable, over-the-wire counterpart of {@link InputSnapshot} the host stores per connected player.
|
|
435
|
-
- `InputSnapshot` (interface): interface InputSnapshot — ⚠ undocumented
|
|
452
|
+
- `InputSnapshot` (interface): interface InputSnapshot { rumble(userId: string, options: {strong: number; weak: number; ms: number}): Promise<boolean>; haptics(userId: string): HapticChannels; publish(held: readonly string[]): void; publishPointer(state: PointerAxisState | null): void; publishA… — ⚠ undocumented · used by `GamepadSource` (@jgengine/shell/input/gamepadSource): Poll browser gamepads and feed semantic actions into the shell tracker.
|
|
436
453
|
- `createInputSnapshot` (function): function createInputSnapshot(): InputSnapshot — ⚠ undocumented
|
|
437
454
|
|
|
455
|
+
## @jgengine/core/runtime/localPlayers
|
|
456
|
+
|
|
457
|
+
- `LocalPlayerAssignment` (interface): interface LocalPlayerAssignment — Result of {@link LocalPlayers.assign}.
|
|
458
|
+
- `LocalPlayerSlot` (interface): interface LocalPlayerSlot — One local seat on a shared screen: a stable slot id, the user id the game spawns for it, and the device driving it.
|
|
459
|
+
- `LocalPlayers` (interface): interface LocalPlayers — Seats for couch co-op: devices hot-join into slots, each slot has its own input.
|
|
460
|
+
- `LocalPlayersConfig` (interface): interface LocalPlayersConfig — Retunable seat policy.
|
|
461
|
+
- `LocalPlayersOptions` (interface): interface LocalPlayersOptions extends LocalPlayersConfig — Options for {@link createLocalPlayers}.
|
|
462
|
+
- `LocalPlayersSnapshot` (interface): interface LocalPlayersSnapshot — Serializable seat table.
|
|
463
|
+
- `createLocalPlayers` (function): function createLocalPlayers(options: LocalPlayersOptions): LocalPlayers — Seat table for local multiplayer on one screen.
|
|
464
|
+
- `localPlayers` (function): function localPlayers(ctx: GameContext): LocalPlayers — The seat table of a game context, created on first use with one seat for `ctx.player` and `ctx.input`. The shell retunes it from `defineGame({ localPlayers })` and hot-joins pads into it.
|
|
465
|
+
|
|
438
466
|
## @jgengine/core/runtime/motionIntents
|
|
439
467
|
|
|
440
|
-
- `MotionIntentBatch` (interface): interface MotionIntentBatch — ⚠ undocumented
|
|
468
|
+
- `MotionIntentBatch` (interface): interface MotionIntentBatch { impulses: readonly number[]; horizontalImpulses: readonly (readonly [number, number])[]; verticalVelocity: number | null; y: number | null } — ⚠ undocumented
|
|
441
469
|
- `MotionIntents` (interface): interface MotionIntents — Seam for game code to reach the motion the shell's FrameDriver otherwise owns privately (#162.4). Game code calls `impulse`, `pushHorizontal`, `setVerticalVelocity`, and/or `setY` from `onTick` or commands; the shell calls `takePending()` once per frame, before integrating gravity, to drain what accumulated. `setY` wins over physics for that frame; impulses add to the velocity the driver is about to integrate; a later `setVerticalVelocity` replaces that velocity outright. Horizontal pushes compose with the walk controller (#282.4): they add to its horizontal velocity and decay naturally as it re-blends toward input — knockback, dashes, explosion shoves without raw `setPose` offsets.
|
|
442
470
|
|
|
443
471
|
## @jgengine/core/runtime/objectRows
|
|
@@ -451,11 +479,11 @@
|
|
|
451
479
|
|
|
452
480
|
## @jgengine/core/runtime/persistenceScope
|
|
453
481
|
|
|
454
|
-
- `NormalizedScenarioReset` (interface): interface NormalizedScenarioReset — ⚠ undocumented
|
|
482
|
+
- `NormalizedScenarioReset` (interface): interface NormalizedScenarioReset { gameId: string; serverId: string | null; wipeChunks: boolean; wipeServerSession: boolean; resetPlayers: "run" | "none"; runFields: readonly string[] } — ⚠ undocumented
|
|
455
483
|
- `PersistenceScope` (type): type PersistenceScope = "run" | "meta" — ⚠ undocumented
|
|
456
|
-
- `ScenarioReset` (interface): interface ScenarioReset — ⚠ undocumented
|
|
457
|
-
- `ScopeSchema` (interface): interface ScopeSchema — ⚠ undocumented
|
|
458
|
-
- `ScopedState` (interface): interface ScopedState<T = Record<string, unknown>> — ⚠ undocumented
|
|
484
|
+
- `ScenarioReset` (interface): interface ScenarioReset { gameId: string; serverId?: string; wipeChunks?: boolean; wipeServerSession?: boolean; resetPlayers?: PersistenceScope | "none"; runFields?: readonly string[] } — ⚠ undocumented
|
|
485
|
+
- `ScopeSchema` (interface): interface ScopeSchema { run: readonly string[] } — ⚠ undocumented
|
|
486
|
+
- `ScopedState` (interface): interface ScopedState<T = Record<string, unknown>> { meta: Partial<T>; run: Partial<T> } — ⚠ undocumented
|
|
459
487
|
- `applyRunReset` (function): function applyRunReset(profile: PlayerProfileRecord, runFields: readonly string[], now: number): PlayerProfileRecord — ⚠ undocumented
|
|
460
488
|
- `clearRunFields` (function): function clearRunFields(player: RuntimePlayerRow, runFields: readonly string[]): RuntimePlayerRow — ⚠ undocumented
|
|
461
489
|
- `mergeScopes` (function): function mergeScopes<T extends Record<string, unknown>>(scoped: ScopedState<T>): Partial<T> — ⚠ undocumented
|
|
@@ -490,7 +518,7 @@
|
|
|
490
518
|
|
|
491
519
|
## @jgengine/core/runtime/save
|
|
492
520
|
|
|
493
|
-
- `SaveConfig` (type): type SaveConfig = | "none" | { auto: string; scope: SaveScope; } — ⚠ undocumented
|
|
521
|
+
- `SaveConfig` (type): type SaveConfig = | "none" | { auto: string; scope: SaveScope; } — ⚠ undocumented · used by `persistServerSnapshot` (@jgengine/convex): Write a snapshot back: server row, dirty player profiles, dirty chunks, and drained leaderboard increments, all under `save`.
|
|
494
522
|
- `SaveScope` (type): type SaveScope = "player" | "chunks" | "player+chunks" — ⚠ undocumented
|
|
495
523
|
|
|
496
524
|
## @jgengine/core/runtime/simContext
|
|
@@ -519,13 +547,13 @@
|
|
|
519
547
|
|
|
520
548
|
## @jgengine/core/runtime/snapshot
|
|
521
549
|
|
|
522
|
-
- `GameRuntimeSnapshot` (type): type GameRuntimeSnapshot = { version: number; gameId: string; serverId: string; server: RuntimeServerRow; players: Record<string, RuntimePlayerRow>; chunks: Record<string, RuntimeChunkRow>; revision: number; dirty: { server: boolean; players: string[]; chunks: string[]; }; } — ⚠ undocumented
|
|
550
|
+
- `GameRuntimeSnapshot` (type): type GameRuntimeSnapshot = { version: number; gameId: string; serverId: string; server: RuntimeServerRow; players: Record<string, RuntimePlayerRow>; chunks: Record<string, RuntimeChunkRow>; revision: number; dirty: { server: boolean; players: string[]; chunks: string[]; }; } — ⚠ undocumented · used by `isSnapshotClean` (@jgengine/core/runtime/hostPersistence): True when a snapshot carries no unwritten mutation — nothing for a persist to do.
|
|
523
551
|
- `RUNTIME_SNAPSHOT_VERSION` (const): const RUNTIME_SNAPSHOT_VERSION: 1 — ⚠ undocumented
|
|
524
|
-
- `RuntimeChunkRow` (type): type RuntimeChunkRow = { /** Cell key to owner id, persisted with its spatial chunk. */ territory?: Record<string, string>; territoryReceipts?: Record<string, { claimedAt: number; costPaid: number }>; chunkKey: string; objects: RuntimeObjectRow[]; entities: RuntimeEntityRow[]; flags?: Record<string,… — ⚠ undocumented
|
|
552
|
+
- `RuntimeChunkRow` (type): type RuntimeChunkRow = { /** Cell key to owner id, persisted with its spatial chunk. */ territory?: Record<string, string>; territoryReceipts?: Record<string, { claimedAt: number; costPaid: number }>; chunkKey: string; objects: RuntimeObjectRow[]; entities: RuntimeEntityRow[]; flags?: Record<string,… — ⚠ undocumented · used by `createEmptyChunkRow` (@jgengine/core/runtime/worldChunks): An empty chunk row for `chunkKey` — the starting value for a cell nothing has been placed in yet.
|
|
525
553
|
- `RuntimeEntityRow` (type): type RuntimeEntityRow = { instanceId: string; catalogId: string; position?: [number, number, number]; rotationY?: number; parentSpace?: string; group?: string; stats?: Record<string, { current: number; max: number; min?: number }>; targetInstanceId?: string | null; userId?: string; } — ⚠ undocumented
|
|
526
554
|
- `RuntimeInventorySlot` (type): type RuntimeInventorySlot = { item: string; count: number; slot?: number; } — ⚠ undocumented
|
|
527
555
|
- `RuntimeObjectRow` (type): type RuntimeObjectRow = { instanceId: string; catalogId: string; position: [number, number, number]; rotationY?: number; parentSpace?: string; /** Opaque per-instance state; the persisted name for `SceneObject.state`. */ flags?: Record<string, unknown>; /** Container contents for a catalog `slotInve… — The persisted form of a placed `SceneObject`: identity, placement, per-instance state, and slot contents. Convert with `toRuntimeObjectRow`/`fromRuntimeObjectRow` (`runtime/objectRows`).
|
|
528
|
-
- `RuntimePlayerRow` (type): type RuntimePlayerRow = { territoryOwnedCount?: number; ownedTerritoryChunkKeys?: string[]; userId: string; inventories: Record<string, RuntimeInventorySlot[]>; economy: Record<string, number>; unlocks: string[]; quests?: unknown; social?: unknown; leaderboard?: Record<string, number>; session?: Rec… — ⚠ undocumented
|
|
556
|
+
- `RuntimePlayerRow` (type): type RuntimePlayerRow = { territoryOwnedCount?: number; ownedTerritoryChunkKeys?: string[]; userId: string; inventories: Record<string, RuntimeInventorySlot[]>; economy: Record<string, number>; unlocks: string[]; quests?: unknown; social?: unknown; leaderboard?: Record<string, number>; session?: Rec… — ⚠ undocumented · used by `initialPlayerState` (@jgengine/core/runtime/gameRuntime): Seed a fresh player through the same onNewPlayer hook as joining, without hydrating or modifying a world.
|
|
529
557
|
- `RuntimeProfileRow` (type): type RuntimeProfileRow = { userId: string; gameId: string; player: RuntimePlayerRow; updatedAt: number; } — ⚠ undocumented
|
|
530
558
|
- `RuntimeServerRow` (type): type RuntimeServerRow = { entities: RuntimeEntityRow[]; objects: RuntimeObjectRow[]; session: Record<string, unknown>; feeds?: Record<string, unknown[]>; } — ⚠ undocumented
|
|
531
559
|
|
|
@@ -544,18 +572,19 @@
|
|
|
544
572
|
- `GameRuntimeFeeds` (type): type GameRuntimeFeeds = { subscribeServer: ( serverId: string, onChange: (view: GameRuntimeServerView | null) => void, ) => FeedUnsubscribe; subscribePlayer: ( args: { serverId: string }, onChange: (view: GameRuntimePlayerView | null) => void, ) => FeedUnsubscribe; subscribeFeed: ( args: { serverId:… — ⚠ undocumented
|
|
545
573
|
- `GameRuntimePlayerView` (type): type GameRuntimePlayerView = { userId: string; gameId: string; playerState: unknown; updatedAt: number; } — ⚠ undocumented
|
|
546
574
|
- `GameRuntimeServerView` (type): type GameRuntimeServerView = { serverId: string; gameId: string; revision: number; memberUserIds: string[]; serverState: unknown | WorldSyncFrame; updatedAt: number; } — ⚠ undocumented
|
|
547
|
-
- `GameRuntimeTransport` (type): type GameRuntimeTransport = { joinServer: (args: { gameId: string; serverId?: string; role?: MultiplayerRole
|
|
575
|
+
- `GameRuntimeTransport` (type): type GameRuntimeTransport = { /** * Join a server. `sessionId` names this client session (one tab, one mounted view) so the host keeps * the user a member while any of their sessions is still live. */ joinServer: (args: { gameId: string; serverId?: string; role?: MultiplayerRole; sessionId?: string … — ⚠ undocumented · used by `useServerSession` (@jgengine/react/useServerSession): Joins a host and exposes a retryable blocking state until membership is confirmed.
|
|
548
576
|
- `JoinServerOutcome` (type): type JoinServerOutcome = | (JoinServerResult & { ok: true }) | { ok: false; reason: "full" | "closed" | "unauthorized" } — Join failures are returned to the caller so the triggering surface can offer retry.
|
|
549
577
|
- `JoinServerResult` (type): type JoinServerResult = { serverId: string; isNew: boolean; resumeTicket?: ResumeTicket; } — ⚠ undocumented
|
|
550
578
|
- `LiveGameBackend` (type): type LiveGameBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = GameBackend<TPresenceRow, TPresenceLocation, TGameId> & { presenceSync: PresenceSync; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown }) => Promise<void>; chatSyncFor… — ⚠ undocumented
|
|
551
579
|
- `MultiplayerRole` (type): type MultiplayerRole = "player" | "spectator" — Connection role used for authoritative world access; spectators are read-only.
|
|
552
580
|
- `MultiplayerSession` (type): type MultiplayerSession = { gameId: string; userId: string; backend: LiveGameBackend; feedActions: string[]; } — ⚠ undocumented
|
|
553
|
-
- `PresencePoseRow` (type): type PresencePoseRow = { userId: string; /** Set when the host tracks presence per session, so one user can hold two rows. */ sessionId?: string; /** Actor class, e.g. `"player"` / `"agent"` — a host may clamp each differently. */ kind?: string; /** Display name carried on the row, so a nameplate ne… — ⚠ undocumented
|
|
581
|
+
- `PresencePoseRow` (type): type PresencePoseRow = { userId: string; /** Set when the host tracks presence per session, so one user can hold two rows. */ sessionId?: string; /** Actor class, e.g. `"player"` / `"agent"` — a host may clamp each differently. */ kind?: string; /** Display name carried on the row, so a nameplate ne… — ⚠ undocumented · used by `useShellMultiplayerSync` (@jgengine/shell/useShellMultiplayerSync): Joins the multiplayer server for the live context and wires presence, feed relay, and chat sync until teardown.
|
|
554
582
|
- `PresenceSync` (type): type PresenceSync = { subscribe: (serverId: string, onChange: (rows: PresencePoseRow[]) => void) => FeedUnsubscribe; syncPose: (serverId: string, pose: PlayerPose) => void; } — ⚠ undocumented
|
|
555
583
|
- `ResumeTicket` (type): type ResumeTicket = { userId: string; serverId: string; token: string; } — A short-lived credential allowing a disconnected client to reclaim one server membership.
|
|
556
584
|
- `RunCommandArgs` (type): type RunCommandArgs = { serverId: string; command: string; input: unknown; } — ⚠ undocumented
|
|
557
585
|
- `TransportRunCommandResult` (type): type TransportRunCommandResult = | { ok: true } | { ok: false; reason: string } — ⚠ undocumented
|
|
558
586
|
- `WorldSyncFrame` (type): type WorldSyncFrame = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; revision: number; diff: WorldDiff } — A revision-stamped world baseline or compact diff sent to a server-authoritative client.
|
|
587
|
+
- `createTransportSessionId` (function): function createTransportSessionId(): string — Fresh id for one client session's join/leave pair (see {@link GameRuntimeTransport.joinServer}).
|
|
559
588
|
|
|
560
589
|
## @jgengine/core/runtime/visibility
|
|
561
590
|
|
|
@@ -616,7 +645,7 @@
|
|
|
616
645
|
|
|
617
646
|
## @jgengine/shell/defineGame
|
|
618
647
|
|
|
619
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented
|
|
648
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
620
649
|
- `defineGame` (function): function defineGame<TAssetRef extends ModelAssetRef = ModelAssetRef>(config: GameConfig<TAssetRef>): PlayableGame — The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, authored scene) into a `PlayableGame` ready for `GameHost`. Defaults to solo/offline multiplayer; `editorLayers` auto-mounts the authored scene document.
|
|
621
650
|
|
|
622
651
|
## @jgengine/shell/gameKit
|
|
@@ -628,7 +657,7 @@
|
|
|
628
657
|
- `EditorDocument` (interface): interface EditorDocument — The full authored scene: every marker, volume, path, note, and sculpted terrain for a game.
|
|
629
658
|
- `EditorSummonModule` (interface): interface EditorSummonModule — Structural shape of the module `GameHost`'s `editor` loader resolves — `import("@jgengine/editor")` satisfies it.
|
|
630
659
|
- `GameCameraConfig` (interface): interface GameCameraConfig — Camera tuning for the shell's rig stack: pick the rig via `rig`, then tune it through its matching config block. All fields optional — the default is the third-person orbit rig.
|
|
631
|
-
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented
|
|
660
|
+
- `GameConfig` (type): type GameConfig<TAssetRef extends ModelAssetRef = ModelAssetRef> = EngineFields<TAssetRef> & PresentationFields — ⚠ undocumented · used by `defineGame`: The one public authoring entry point: compose engine fields (systems, world, physics, input) and presentation fields (camera, HUD, audio, au…
|
|
632
661
|
- `GameContext` (interface): interface GameContext — The live engine handle a game's loop, systems, commands, and UI read and mutate — entities, objects, the reactive store, opted-in `game.*` subsystems, world queries, and the sim clock. One context = one running world.
|
|
633
662
|
- `GameHost` (function): function GameHost({ playable, gameId, wsUrl, multiplayer, resolveMultiplayer, editor }: GameHostProps): React.JSX.Element — The one documented mount: resolves multiplayer for the playable and renders the shell. With the `editor` loader prop it also owns the whole editor summon (F2+E, `?mode=editor`, dev save endpoint).
|
|
634
663
|
- `GameHostProps` (interface): interface GameHostProps — Props for {@link GameHost}: the playable, optional multiplayer overrides, and the optional editor loader that enables the engine-owned F2+E summon.
|
|
@@ -12,6 +12,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
12
12
|
|
|
13
13
|
- `collectAuthoredTriggers` (function) · `import { collectAuthoredTriggers } from "@jgengine/shell/gameKit"`
|
|
14
14
|
|
|
15
|
+
## chase-view-cycle — cycle a vehicle camera between chase, hood and cockpit views at runtime
|
|
16
|
+
|
|
17
|
+
- `nextChaseView` (function) · `import { nextChaseView } from "@jgengine/core/runtime/cameraDirector"`
|
|
18
|
+
|
|
15
19
|
## client-side — input prediction with replay reconciliation
|
|
16
20
|
|
|
17
21
|
- `createPredictionBuffer` (function) · `import { createPredictionBuffer } from "@jgengine/core/runtime/prediction"`
|
|
@@ -20,6 +24,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
20
24
|
|
|
21
25
|
- `GameContextModels` (interface) · `import { GameContextModels } from "@jgengine/core/runtime/gameContext"`
|
|
22
26
|
|
|
27
|
+
## command-number — validate finite, bounded numbers and integers at command boundaries
|
|
28
|
+
|
|
29
|
+
- `readInputNumber` (function) · `import { readInputNumber } from "@jgengine/core/runtime/commandInput"`
|
|
30
|
+
|
|
31
|
+
## command-parse — parse untrusted command input once so every command stage sees typed, bounded values
|
|
32
|
+
|
|
33
|
+
- `defineCommand` (function) · `import { defineCommand } from "@jgengine/core/runtime/commandRunner"`
|
|
34
|
+
|
|
23
35
|
## command-quantity — validate bounded whole-item counts at command boundaries
|
|
24
36
|
|
|
25
37
|
- `readQuantity` (function) · `import { readQuantity } from "@jgengine/core/runtime/commandInput"`
|
|
@@ -40,6 +52,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
40
52
|
|
|
41
53
|
- `EditorCatalogDefinition` (interface) · `import { EditorCatalogDefinition } from "@jgengine/shell/gameKit"`
|
|
42
54
|
|
|
55
|
+
## fire-input — bind a key or mouse button to a named action that runs the same-named command with the aim; repeatMs auto-fires while held
|
|
56
|
+
|
|
57
|
+
- `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/authoring"`
|
|
58
|
+
|
|
43
59
|
## game-system — declare a composable capability with its own schedule and lifecycle
|
|
44
60
|
|
|
45
61
|
- `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/shell/gameKit"`
|
|
@@ -96,6 +112,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
96
112
|
|
|
97
113
|
- `createInputRecorder` (function) · `import { createInputRecorder } from "@jgengine/core/runtime/inputRecorder"`
|
|
98
114
|
|
|
115
|
+
## local-players — Couch co-op seats: devices hot-join into slots, each with its own user id and input snapshot.
|
|
116
|
+
|
|
117
|
+
- `createLocalPlayers` (function) · `import { createLocalPlayers } from "@jgengine/core/runtime/localPlayers"`
|
|
118
|
+
|
|
99
119
|
## loot-table — validate a loot table definition for use with the registry
|
|
100
120
|
|
|
101
121
|
- `lootTable` (function) · `import { lootTable } from "@jgengine/core/authoring"`
|
|
@@ -178,6 +198,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
178
198
|
|
|
179
199
|
- `defineStore` (function) · `import { defineStore } from "@jgengine/shell/gameKit"`
|
|
180
200
|
|
|
201
|
+
## warn-once — emit a dev-only warning a single time per call site
|
|
202
|
+
|
|
203
|
+
- `warnOnce` (function) · `import { warnOnce } from "@jgengine/core/devtools/warnOnce"`
|
|
204
|
+
|
|
181
205
|
## world-chunk-key — map world positions to persisted chunk keys at a declared cell size
|
|
182
206
|
|
|
183
207
|
- `chunkKeyOf` (function) · `import { chunkKeyOf } from "@jgengine/core/runtime/worldChunks"`
|
|
@@ -193,3 +217,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
193
217
|
## world-environment — compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
|
|
194
218
|
|
|
195
219
|
- `environment` (function) · `import { environment } from "@jgengine/shell/gameKit"`
|
|
220
|
+
|
|
221
|
+
## world-item-pickup — pick up dropped items: ctx.scene.worldItem nearestInRadius → pickup into the player's inventory
|
|
222
|
+
|
|
223
|
+
- `SceneWorldItemContext` (interface) · `import { SceneWorldItemContext } from "@jgengine/core/runtime/gameContext"`
|
|
@@ -45,6 +45,9 @@ spatial place. When the game *is* a place, declare only substrate + laws with `w
|
|
|
45
45
|
`@jgengine/core/world/place` (e.g. `{ id, ground: { mode: "flat", size: { x: Infinity, z: Infinity } }, physics: { gravity: -24 } }`,
|
|
46
46
|
or `mode: "board"` for 2D). Never seed, sky, or vegetation in `world` — the engine renders its
|
|
47
47
|
default sky until you author one in the editor, and foliage is editor scatter over the ground.
|
|
48
|
+
A created 3D game instead ships hills as a terrain sculpt in `editor.scene.json` and a `world.ts`
|
|
49
|
+
that consumes it with `environment({ sculpt })` via `environmentContentFromDocument`; the file
|
|
50
|
+
holds only the ground palette. Reshape the hills in the editor, not in code.
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
```ts
|
|
@@ -55,7 +55,7 @@ Use [capabilities.md](capabilities.md) for intent-to-import discovery, [api.md](
|
|
|
55
55
|
|
|
56
56
|
Use `createGameServerFunctions({ topology: "shared", runtimes })` for one persistent world. Shared topology defaults to singleton matchmaking. Capacity is not a throughput guarantee; verify the command and subscription read sets under crowd load. Subscribe to `getServerCapacity` for shared membership/status without volatile world revision reads.
|
|
57
57
|
|
|
58
|
-
Every gameplay command declares `scope(input)`: use `{ players: "actor", chunkKeys: [] }` for player-only work and `chunkKeysAround(position, 1)` for spatial work. The omitted-command default is actor-only with no chunks; an explicit `{}` requests the whole world and needs a justified bounded workload. Hosts use `helpers.runCommand` and its `beforePersist` callback to pair game writes with membership and revision checks in one transaction. Never copy that check/apply/persist loop into a game.
|
|
58
|
+
Every gameplay command declares `scope(input)`: use `{ players: "actor", chunkKeys: [] }` for player-only work and `chunkKeysAround(position, 1)` for spatial work. The omitted-command default is actor-only with no chunks; an explicit `{}` requests the whole world and needs a justified bounded workload. Hosts use `helpers.runCommand` and its `beforePersist` callback to pair game writes with membership and revision checks in one transaction. Never copy that check/apply/persist loop into a game. Declare commands with `defineCommand({ parse, scope, validate, apply })` and build `parse` from the `readInput*` readers in `@jgengine/core/runtime/commandInput` (finite, bounded numbers and points, capped strings and arrays): the runner refuses malformed input before `scope`, `validate` or `apply` run, so those stages never re-parse or trust raw values. Treat every field of a command's `input` as client-controlled: a command whose input carries facts only the host may decide (unlock state, prices, multipliers, whether a target exists) declares `access: "server"`, so the public `runCommand` and WebSocket clients are refused and only a host mutation calling `helpers.runCommand` (or `host.runCommand({ trusted: true })`) runs it. Read time from the `nowMs` argument, never from `input`.
|
|
59
59
|
|
|
60
60
|
Every presence query is neighborhood-scoped. Pass `PresenceSession.viewerChunkKey` through `createConvexPresenceTransport`. Hosts with custom authentication, bot ownership, or spawn policy use `createWorldPresenceStore` from `@jgengine/convex/worldPresence`: resolve trusted identities and access first, then call `ensure`, `sync`, `revoke`, `active`, `nearby`, and `reap`. Store rows in `jgPoses`, never a game-owned presence table. Optional snapshot callbacks persist game-owned last-position fields at the store's cadence. Bot identity may differ from its authorized owner; preserve that mapping explicitly. Resident rows implement `PresenceResidentRow.actorExternalId` and `ownerActorId` exactly.
|
|
61
61
|
|
|
@@ -67,6 +67,6 @@ A cron never collects a whole table. Use presence-gated `forEachOnlinePlayer` wi
|
|
|
67
67
|
|
|
68
68
|
Pass runtimes to `jgengineCronSpecs` so no tick cron registers when no runtime declares `onTick`; set per-spec `intervalSeconds` deliberately. Retention sweeps and one-shot backfills are bounded, report `remaining`, and stop at convergence. Delete completed migration crons instead of leaving hourly table scans installed forever.
|
|
69
69
|
|
|
70
|
-
Join failures return typed `full`, `closed`, or `unauthorized` outcomes. Surface them through a retry gate; returning a rejection without visible UI is unfinished. Browser adapters leave sessions on `pagehide`.
|
|
70
|
+
Join failures return typed `full`, `closed`, or `unauthorized` outcomes. Surface them through a retry gate; returning a rejection without visible UI is unfinished. Browser adapters leave sessions on `pagehide`. Membership is per client session: pass a fresh `sessionId` (`createTransportSessionId`) to each `joinServer` and its matching `leaveServer`, so a closed tab or a disposed mount never evicts a live one. A host mutation acting for a user who may have no live session calls `helpers.ensureJoined` before `helpers.runCommand`.
|
|
71
71
|
|
|
72
72
|
For the explicit shared-builder composition, `jgengine create "World Name" --shape shared-world-builder` generates the connected Convex shell, runtime scope declarations, claim/accrual examples, chat, indexed presence, and an online batch pipeline. The generated README owns setup; replace the demonstration economy and author buildable world content in the editor.
|