@vgai/engine 0.5.7 → 0.5.8
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/dist/core/game-scoped-slot.d.ts +14 -0
- package/dist/core/game-scoped-slot.d.ts.map +1 -0
- package/dist/core/game-scoped-slot.js +24 -0
- package/dist/core/seeded-random.d.ts.map +1 -1
- package/dist/core/seeded-random.js +3 -2
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +3 -2
- package/dist/runtime/debug-registry.d.ts +2 -2
- package/dist/runtime/debug-registry.d.ts.map +1 -1
- package/dist/runtime/debug-registry.js +4 -3
- package/dist/runtime/game.js +2 -2
- package/dist/runtime/gameplay-rng-trap.d.ts.map +1 -1
- package/dist/runtime/gameplay-rng-trap.js +2 -1
- package/package.json +1 -1
- package/src/core/game-scoped-slot.ts +28 -0
- package/src/core/seeded-random.ts +3 -2
- package/src/core/sim-clock.ts +3 -2
- package/src/runtime/debug-registry.ts +4 -3
- package/src/runtime/game.ts +2 -2
- package/src/runtime/gameplay-rng-trap.ts +4 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A non-enumerable slot carried by the game object itself.
|
|
3
|
+
*
|
|
4
|
+
* Packaged editor play can legitimately involve two copies of `@vgai/engine`:
|
|
5
|
+
* the editor bundle creates the Game, while the project's Vite graph creates
|
|
6
|
+
* its root adapter. Module-local WeakMaps cannot cross that boundary. A
|
|
7
|
+
* registry-backed symbol can, while keeping the internal value off the public
|
|
8
|
+
* string-keyed Game surface.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createGameScopedSlot<T>(name: string): {
|
|
11
|
+
set(owner: object, value: T): void;
|
|
12
|
+
get(owner: object): T | null;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=game-scoped-slot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"game-scoped-slot.d.ts","sourceRoot":"","sources":["../../src/core/game-scoped-slot.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG;IACrD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACnC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC;CAC9B,CAeA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A non-enumerable slot carried by the game object itself.
|
|
3
|
+
*
|
|
4
|
+
* Packaged editor play can legitimately involve two copies of `@vgai/engine`:
|
|
5
|
+
* the editor bundle creates the Game, while the project's Vite graph creates
|
|
6
|
+
* its root adapter. Module-local WeakMaps cannot cross that boundary. A
|
|
7
|
+
* registry-backed symbol can, while keeping the internal value off the public
|
|
8
|
+
* string-keyed Game surface.
|
|
9
|
+
*/
|
|
10
|
+
export function createGameScopedSlot(name) {
|
|
11
|
+
const key = Symbol.for(`@vgai/engine/game-scoped/${name}`);
|
|
12
|
+
return {
|
|
13
|
+
set(owner, value) {
|
|
14
|
+
Object.defineProperty(owner, key, {
|
|
15
|
+
configurable: true,
|
|
16
|
+
enumerable: false,
|
|
17
|
+
value,
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
get(owner) {
|
|
21
|
+
return owner[key] ?? null;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seeded-random.d.ts","sourceRoot":"","sources":["../../src/core/seeded-random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;
|
|
1
|
+
{"version":3,"file":"seeded-random.d.ts","sourceRoot":"","sources":["../../src/core/seeded-random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C;;;;;;gFAMgF;AAChF,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,MAAM,CAAC;IACX,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CAAC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAsBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CAgDpE;AAeD;yEACyE;AACzE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAE9E;AAED;;;2DAG2D;AAC3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAElE"}
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* stay orthogonal; only the generator function itself is shared).
|
|
27
27
|
*/
|
|
28
28
|
import { createMulberry32 } from '../runtime/render-seed';
|
|
29
|
+
import { createGameScopedSlot } from './game-scoped-slot';
|
|
29
30
|
/** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
|
|
30
31
|
export const GAMEPLAY_STREAM = 'gameplay';
|
|
31
32
|
/** The fixed fallback seed a `SeededRandom` boots with when no explicit seed
|
|
@@ -98,7 +99,7 @@ export function createSeededRandom(initialSeed) {
|
|
|
98
99
|
}
|
|
99
100
|
// ---------------------------------------------------------------------------
|
|
100
101
|
// Game-scoped registry — mirrors `runtime/debug-registry.ts`'s
|
|
101
|
-
// `registerDebugRegistry`/`getDebugRegistry`
|
|
102
|
+
// `registerDebugRegistry`/`getDebugRegistry` game-slot pattern exactly, but
|
|
102
103
|
// keyed on a bare `object` (not `Game`) so this module never needs to import
|
|
103
104
|
// `runtime/game.ts` even as a type — `core/` stays independent of `runtime/`
|
|
104
105
|
// except for the one explicit, documented `render-seed.ts` reuse above.
|
|
@@ -106,7 +107,7 @@ export function createSeededRandom(initialSeed) {
|
|
|
106
107
|
// (the `GameInternal` shell) as the key, exactly like it does for
|
|
107
108
|
// `registerDebugRegistry(gameInternal, debugRegistry)`.
|
|
108
109
|
// ---------------------------------------------------------------------------
|
|
109
|
-
const registryByOwner =
|
|
110
|
+
const registryByOwner = createGameScopedSlot('seeded-random');
|
|
110
111
|
/** Called once by `createGame`, right after both the seeded-random surface
|
|
111
112
|
* and the Game shell object exist — mirrors `registerDebugRegistry`. */
|
|
112
113
|
export function registerSeededRandom(owner, random) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sim-clock.d.ts","sourceRoot":"","sources":["../../src/core/sim-clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiHG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"sim-clock.d.ts","sourceRoot":"","sources":["../../src/core/sim-clock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiHG;AAEH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AAGpC,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3C;AAED,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,wEAAwE;IACxE,GAAG,IAAI,MAAM,CAAC;IACd,2EAA2E;IAC3E,OAAO,IAAI,MAAM,CAAC;IAClB;;;;;OAKG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAAC;IAClF;;;;;OAKG;IACH,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC;CACpE;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAiB,SAAQ,QAAQ;IAChD;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC;CACjD;AAyBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,gBAAgB,CAiJzE;AAWD,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;;+EAQ+E;AAC/E,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAElE"}
|
package/dist/core/sim-clock.js
CHANGED
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
* - **No wall clock.** This module reads no system timer and creates no
|
|
113
113
|
* host timeout; every time value comes from the runtime's own accumulator.
|
|
114
114
|
*/
|
|
115
|
+
import { createGameScopedSlot } from './game-scoped-slot';
|
|
115
116
|
const NOOP_HANDLE = { cancel() { } };
|
|
116
117
|
/** `fetch`-shaped abort rejection: a `DOMException` named `AbortError`. */
|
|
117
118
|
function abortError(message) {
|
|
@@ -268,11 +269,11 @@ export function createSimClock(options) {
|
|
|
268
269
|
}
|
|
269
270
|
// ---------------------------------------------------------------------------
|
|
270
271
|
// Game-scoped registry — mirrors `core/seeded-random.ts`'s
|
|
271
|
-
// `registerSeededRandom`/`getSeededRandom`
|
|
272
|
+
// `registerSeededRandom`/`getSeededRandom` slot pattern exactly, keyed on a
|
|
272
273
|
// bare `object` (not `Game`) so `core/` never imports `runtime/`. `createGame`
|
|
273
274
|
// is the one real registrant, passing the `GameInternal` shell as the key.
|
|
274
275
|
// ---------------------------------------------------------------------------
|
|
275
|
-
const clockByOwner =
|
|
276
|
+
const clockByOwner = createGameScopedSlot('sim-clock');
|
|
276
277
|
/** Called once by `createGame`, right after the clock and the Game shell exist. */
|
|
277
278
|
export function registerSimClock(owner, clock) {
|
|
278
279
|
clockByOwner.set(owner, clock);
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* SAME registry via {@link DebugRegistry.forRoot}, so a name a game registers is
|
|
5
5
|
* visible (and name-collision-checked) across every world, not just the one that
|
|
6
6
|
* registered it. `createGame` (`runtime/game.ts`) constructs the registry
|
|
7
|
-
* alongside the state bridge and files it in
|
|
8
|
-
*
|
|
7
|
+
* alongside the state bridge and files it in a non-enumerable game-scoped slot;
|
|
8
|
+
* later consumers (the react `useDebugProvider`/
|
|
9
9
|
* `useDebugCommand` hooks, editor panels) reach it via {@link getDebugRegistry}
|
|
10
10
|
* rather than threading it through every call site.
|
|
11
11
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/debug-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"debug-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/debug-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* SAME registry via {@link DebugRegistry.forRoot}, so a name a game registers is
|
|
5
5
|
* visible (and name-collision-checked) across every world, not just the one that
|
|
6
6
|
* registered it. `createGame` (`runtime/game.ts`) constructs the registry
|
|
7
|
-
* alongside the state bridge and files it in
|
|
8
|
-
*
|
|
7
|
+
* alongside the state bridge and files it in a non-enumerable game-scoped slot;
|
|
8
|
+
* later consumers (the react `useDebugProvider`/
|
|
9
9
|
* `useDebugCommand` hooks, editor panels) reach it via {@link getDebugRegistry}
|
|
10
10
|
* rather than threading it through every call site.
|
|
11
11
|
*
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* by convention, so collision messages stay meaningful in practice.
|
|
21
21
|
*/
|
|
22
22
|
import { z } from 'zod';
|
|
23
|
+
import { createGameScopedSlot } from '../core/game-scoped-slot';
|
|
23
24
|
/** Engine-local error for the debug seam — mirrors the `code` + `data` shape
|
|
24
25
|
* `packages/vgai-sdk/src/errors.ts` uses (no import: the engine does not
|
|
25
26
|
* depend on `@vgai/sdk`). `code` is always machine-readable; nothing reading
|
|
@@ -514,7 +515,7 @@ export function createDebugRegistry(opts) {
|
|
|
514
515
|
},
|
|
515
516
|
};
|
|
516
517
|
}
|
|
517
|
-
const registryByGame =
|
|
518
|
+
const registryByGame = createGameScopedSlot('debug-registry');
|
|
518
519
|
/** Called once by `createGame`, right after both the registry and the Game
|
|
519
520
|
* shell object exist, to file the association {@link getDebugRegistry} reads. */
|
|
520
521
|
export function registerDebugRegistry(game, registry) {
|
package/dist/runtime/game.js
CHANGED
|
@@ -297,7 +297,7 @@ export function createGame(opts) {
|
|
|
297
297
|
// same `advanced` guard as the two bumps above, so a paused/frozen frame
|
|
298
298
|
// fires no timers). Every world's `ctx.clock` is THIS instance, reached the
|
|
299
299
|
// same way `ctx.random`/`ctx.debug` reach their game-scoped singletons —
|
|
300
|
-
// `getSimClock(host.game)` over the
|
|
300
|
+
// `getSimClock(host.game)` over the game-scoped slot filed below.
|
|
301
301
|
//
|
|
302
302
|
// The disposer is supplied HERE rather than inside the clock because
|
|
303
303
|
// `core/sim-clock.ts` deliberately knows nothing about Rapier or shared
|
|
@@ -979,7 +979,7 @@ export function createGame(opts) {
|
|
|
979
979
|
debugRegistry.strip();
|
|
980
980
|
},
|
|
981
981
|
};
|
|
982
|
-
// Filed AFTER the shell exists (the
|
|
982
|
+
// Filed AFTER the shell exists (the slot lives on the Game object
|
|
983
983
|
// itself) so `getDebugRegistry(game)` — the react hooks' and any later
|
|
984
984
|
// consumer's reach-in — works from the moment `createGame` returns.
|
|
985
985
|
registerDebugRegistry(gameInternal, debugRegistry);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gameplay-rng-trap.d.ts","sourceRoot":"","sources":["../../src/runtime/gameplay-rng-trap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"gameplay-rng-trap.d.ts","sourceRoot":"","sources":["../../src/runtime/gameplay-rng-trap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,MAAM,IAAI,IAAI,CAAC;IACf;;;0CAGsC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB;AAcD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAG1B,GACA,eAAe,CA6CjB;AAaD,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAID;+BAC+B;AAC/B,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAEN;AAED;4CAC4C;AAC5C,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAEtF"}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* Never throws: a cosmetic third-party draw is legal, just outside the
|
|
20
20
|
* contract (the design doc is explicit about this — "warn, never throw").
|
|
21
21
|
*/
|
|
22
|
+
import { createGameScopedSlot } from '../core/game-scoped-slot';
|
|
22
23
|
/** Pull a single representative "call site" line out of a captured stack —
|
|
23
24
|
* the warn-once dedupe key. `stack[0]` is the `Error:` header line,
|
|
24
25
|
* `stack[1]` is this trap's OWN wrapper frame — the caller's frame is the
|
|
@@ -82,7 +83,7 @@ export function createGameplayRngTrap(warn = (message) => {
|
|
|
82
83
|
},
|
|
83
84
|
};
|
|
84
85
|
}
|
|
85
|
-
const controlByOwner =
|
|
86
|
+
const controlByOwner = createGameScopedSlot('gameplay-rng-trap');
|
|
86
87
|
/** Called once by `createGame`, alongside `registerSeededRandom`/
|
|
87
88
|
* `registerDebugRegistry`. */
|
|
88
89
|
export function registerGameplayRngTrapControl(owner, control) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/engine",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.8",
|
|
6
6
|
"description": "Readable TypeScript game engine and universal host for Three.js, PixiJS, and React games.",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"game-engine",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A non-enumerable slot carried by the game object itself.
|
|
3
|
+
*
|
|
4
|
+
* Packaged editor play can legitimately involve two copies of `@vgai/engine`:
|
|
5
|
+
* the editor bundle creates the Game, while the project's Vite graph creates
|
|
6
|
+
* its root adapter. Module-local WeakMaps cannot cross that boundary. A
|
|
7
|
+
* registry-backed symbol can, while keeping the internal value off the public
|
|
8
|
+
* string-keyed Game surface.
|
|
9
|
+
*/
|
|
10
|
+
export function createGameScopedSlot<T>(name: string): {
|
|
11
|
+
set(owner: object, value: T): void;
|
|
12
|
+
get(owner: object): T | null;
|
|
13
|
+
} {
|
|
14
|
+
const key = Symbol.for(`@vgai/engine/game-scoped/${name}`);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
set(owner, value) {
|
|
18
|
+
Object.defineProperty(owner, key, {
|
|
19
|
+
configurable: true,
|
|
20
|
+
enumerable: false,
|
|
21
|
+
value,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
get(owner) {
|
|
25
|
+
return (owner as { [key: symbol]: T | undefined })[key] ?? null;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import { createMulberry32 } from '../runtime/render-seed';
|
|
30
|
+
import { createGameScopedSlot } from './game-scoped-slot';
|
|
30
31
|
|
|
31
32
|
/** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
|
|
32
33
|
export const GAMEPLAY_STREAM = 'gameplay';
|
|
@@ -135,7 +136,7 @@ export function createSeededRandom(initialSeed: number): SeededRandom {
|
|
|
135
136
|
|
|
136
137
|
// ---------------------------------------------------------------------------
|
|
137
138
|
// Game-scoped registry — mirrors `runtime/debug-registry.ts`'s
|
|
138
|
-
// `registerDebugRegistry`/`getDebugRegistry`
|
|
139
|
+
// `registerDebugRegistry`/`getDebugRegistry` game-slot pattern exactly, but
|
|
139
140
|
// keyed on a bare `object` (not `Game`) so this module never needs to import
|
|
140
141
|
// `runtime/game.ts` even as a type — `core/` stays independent of `runtime/`
|
|
141
142
|
// except for the one explicit, documented `render-seed.ts` reuse above.
|
|
@@ -144,7 +145,7 @@ export function createSeededRandom(initialSeed: number): SeededRandom {
|
|
|
144
145
|
// `registerDebugRegistry(gameInternal, debugRegistry)`.
|
|
145
146
|
// ---------------------------------------------------------------------------
|
|
146
147
|
|
|
147
|
-
const registryByOwner =
|
|
148
|
+
const registryByOwner = createGameScopedSlot<SeededRandom>('seeded-random');
|
|
148
149
|
|
|
149
150
|
/** Called once by `createGame`, right after both the seeded-random surface
|
|
150
151
|
* and the Game shell object exist — mirrors `registerDebugRegistry`. */
|
package/src/core/sim-clock.ts
CHANGED
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
*/
|
|
115
115
|
|
|
116
116
|
import type * as THREE from 'three';
|
|
117
|
+
import { createGameScopedSlot } from './game-scoped-slot';
|
|
117
118
|
|
|
118
119
|
/** Cancel handle returned by {@link SimClock.after}/{@link SimClock.disposeAfter}. */
|
|
119
120
|
export interface SimTimerHandle {
|
|
@@ -362,12 +363,12 @@ export function createSimClock(options: SimClockOptions): SimClockInternal {
|
|
|
362
363
|
|
|
363
364
|
// ---------------------------------------------------------------------------
|
|
364
365
|
// Game-scoped registry — mirrors `core/seeded-random.ts`'s
|
|
365
|
-
// `registerSeededRandom`/`getSeededRandom`
|
|
366
|
+
// `registerSeededRandom`/`getSeededRandom` slot pattern exactly, keyed on a
|
|
366
367
|
// bare `object` (not `Game`) so `core/` never imports `runtime/`. `createGame`
|
|
367
368
|
// is the one real registrant, passing the `GameInternal` shell as the key.
|
|
368
369
|
// ---------------------------------------------------------------------------
|
|
369
370
|
|
|
370
|
-
const clockByOwner =
|
|
371
|
+
const clockByOwner = createGameScopedSlot<SimClockInternal>('sim-clock');
|
|
371
372
|
|
|
372
373
|
/** Called once by `createGame`, right after the clock and the Game shell exist. */
|
|
373
374
|
export function registerSimClock(owner: object, clock: SimClockInternal): void {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* SAME registry via {@link DebugRegistry.forRoot}, so a name a game registers is
|
|
5
5
|
* visible (and name-collision-checked) across every world, not just the one that
|
|
6
6
|
* registered it. `createGame` (`runtime/game.ts`) constructs the registry
|
|
7
|
-
* alongside the state bridge and files it in
|
|
8
|
-
*
|
|
7
|
+
* alongside the state bridge and files it in a non-enumerable game-scoped slot;
|
|
8
|
+
* later consumers (the react `useDebugProvider`/
|
|
9
9
|
* `useDebugCommand` hooks, editor panels) reach it via {@link getDebugRegistry}
|
|
10
10
|
* rather than threading it through every call site.
|
|
11
11
|
*
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { z } from 'zod';
|
|
24
24
|
import type { DebugAdapter, DebugCommandInfo, TickStampedEvent } from '../adapter/system-adapter';
|
|
25
|
+
import { createGameScopedSlot } from '../core/game-scoped-slot';
|
|
25
26
|
import type { GameLoopLiveness } from '../core/types';
|
|
26
27
|
import type { Game } from './game';
|
|
27
28
|
import type { DebugCommandArgs, DebugCtxSurface, DebugRoomHandle } from './types';
|
|
@@ -865,7 +866,7 @@ export function createDebugRegistry(opts: {
|
|
|
865
866
|
};
|
|
866
867
|
}
|
|
867
868
|
|
|
868
|
-
const registryByGame =
|
|
869
|
+
const registryByGame = createGameScopedSlot<DebugRegistry>('debug-registry');
|
|
869
870
|
|
|
870
871
|
/** Called once by `createGame`, right after both the registry and the Game
|
|
871
872
|
* shell object exist, to file the association {@link getDebugRegistry} reads. */
|
package/src/runtime/game.ts
CHANGED
|
@@ -862,7 +862,7 @@ export function createGame(opts: {
|
|
|
862
862
|
// same `advanced` guard as the two bumps above, so a paused/frozen frame
|
|
863
863
|
// fires no timers). Every world's `ctx.clock` is THIS instance, reached the
|
|
864
864
|
// same way `ctx.random`/`ctx.debug` reach their game-scoped singletons —
|
|
865
|
-
// `getSimClock(host.game)` over the
|
|
865
|
+
// `getSimClock(host.game)` over the game-scoped slot filed below.
|
|
866
866
|
//
|
|
867
867
|
// The disposer is supplied HERE rather than inside the clock because
|
|
868
868
|
// `core/sim-clock.ts` deliberately knows nothing about Rapier or shared
|
|
@@ -1568,7 +1568,7 @@ export function createGame(opts: {
|
|
|
1568
1568
|
},
|
|
1569
1569
|
};
|
|
1570
1570
|
|
|
1571
|
-
// Filed AFTER the shell exists (the
|
|
1571
|
+
// Filed AFTER the shell exists (the slot lives on the Game object
|
|
1572
1572
|
// itself) so `getDebugRegistry(game)` — the react hooks' and any later
|
|
1573
1573
|
// consumer's reach-in — works from the moment `createGame` returns.
|
|
1574
1574
|
registerDebugRegistry(gameInternal, debugRegistry);
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
* contract (the design doc is explicit about this — "warn, never throw").
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
import { createGameScopedSlot } from '../core/game-scoped-slot';
|
|
24
|
+
|
|
23
25
|
export interface GameplayRngTrap {
|
|
24
26
|
/** Wrap `Math.random` for the duration of one frame — call at frame start. */
|
|
25
27
|
enable(): void;
|
|
@@ -100,7 +102,7 @@ export function createGameplayRngTrap(
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
// ---------------------------------------------------------------------------
|
|
103
|
-
// Game-scoped registry — same
|
|
105
|
+
// Game-scoped registry — same slot-on-owner-object pattern as
|
|
104
106
|
// `core/seeded-random.ts`'s `registerSeededRandom`/`getSeededRandom` (and
|
|
105
107
|
// `debug-registry.ts`'s `registerDebugRegistry`/`getDebugRegistry`): the
|
|
106
108
|
// trap's enable/disable calls live inside `game.ts`'s `runFrameImpl`, gated
|
|
@@ -115,7 +117,7 @@ export interface GameplayRngTrapControl {
|
|
|
115
117
|
readonly enabled: boolean;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
const controlByOwner =
|
|
120
|
+
const controlByOwner = createGameScopedSlot<GameplayRngTrapControl>('gameplay-rng-trap');
|
|
119
121
|
|
|
120
122
|
/** Called once by `createGame`, alongside `registerSeededRandom`/
|
|
121
123
|
* `registerDebugRegistry`. */
|