@kvyverse/world-runtime 0.3.2 → 0.5.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/README.md +12 -5
- package/dist/{RapierPhysics-zLwVZpwF.js → RapierPhysics-CU6X--c7.js} +2 -1
- package/dist/World.d.ts +2 -0
- package/dist/WorldRuntime.d.ts +8 -4
- package/dist/addons/AddonsRuntime.d.ts +17 -15
- package/dist/addons/builtin-libs.d.ts +20 -0
- package/dist/addons/lib-registry.d.ts +36 -10
- package/dist/addons/required-libs.d.ts +9 -0
- package/dist/addons/types.d.ts +3 -3
- package/dist/behaviours/physics/_required-patch.d.ts +5 -7
- package/dist/behaviours/registry.d.ts +9 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +238 -122
- package/dist/lib-registry-D1VyVPBg.js +142 -0
- package/dist/modules/HtmlHudModule.d.ts +5 -1
- package/dist/modules/contracts/addons.module-api.d.ts +8 -8
- package/dist/modules/contracts/html.module-api.d.ts +18 -6
- package/dist/modules/optional-module-descriptor.d.ts +3 -0
- package/dist/utils/lazy.d.ts +7 -0
- package/dist/version.gen.d.ts +1 -1
- package/dist/world-stores.d.ts +39 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -35,9 +35,9 @@ in your `node_modules`:
|
|
|
35
35
|
| Package | When |
|
|
36
36
|
| --- | --- |
|
|
37
37
|
| `three`, `three-start` | always |
|
|
38
|
-
| `howler`, `vanjs-core`, `eventemitter3` | always — npm 7+ installs them for you |
|
|
39
|
-
| `@dimforge/rapier3d-compat` | worlds with physics |
|
|
40
|
-
| `@tweenjs/tween.js`, `animejs`, `nipplejs` | worlds that enable these
|
|
38
|
+
| `howler`, `vanjs-core`, `eventemitter3`, `nanostores` | always — npm 7+ installs them for you |
|
|
39
|
+
| `@dimforge/rapier3d-compat` | worlds with physics (a collider on the scene is enough) |
|
|
40
|
+
| `@tweenjs/tween.js`, `animejs`, `nipplejs`, `stats.js` | worlds that enable these optional libs |
|
|
41
41
|
|
|
42
42
|
The optional ones are listed in the dialog only when the world actually uses
|
|
43
43
|
them. Skip one it does need and the build fails with a clear
|
|
@@ -82,8 +82,15 @@ world.unmount(), world.runLoop(), world.stopLoop(), world.dispose()
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
On top of that: `world.getUrl(path)` resolves an asset URL the same way scripts
|
|
85
|
-
inside the world do,
|
|
86
|
-
`world.events` your scripts use).
|
|
85
|
+
inside the world do, `world.events` is the world lifecycle emitter (the same
|
|
86
|
+
`world.events` your scripts use), and `world.stores` is the world's shared
|
|
87
|
+
reactive state (the same `world.stores`) — your host page can read it, write it
|
|
88
|
+
and subscribe to keys the world's scripts use:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
world.stores.listen("score", (value) => hudElement.textContent = value);
|
|
92
|
+
world.stores.set("difficulty", "hard");
|
|
93
|
+
```
|
|
87
94
|
|
|
88
95
|
## Good to know
|
|
89
96
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as LibId } from "./lib-registry-D1VyVPBg.js";
|
|
1
2
|
import { ContextModule, ThreeContextEvents, defineProps, readOnly } from "three-start";
|
|
2
3
|
//#region packages/world-runtime/src/modules/physics/RapierPhysics.ts
|
|
3
4
|
var RapierPhysics = class RapierPhysics extends ContextModule {
|
|
@@ -23,7 +24,7 @@ var RapierPhysics = class RapierPhysics extends ContextModule {
|
|
|
23
24
|
this.timeStep = timeStep;
|
|
24
25
|
}
|
|
25
26
|
onAwake() {
|
|
26
|
-
const Rapier = this.modules.addons.
|
|
27
|
+
const Rapier = this.modules.addons.lib(LibId.Rapier);
|
|
27
28
|
if (!Rapier) throw new Error("RapierPhysics: the physics engine is not loaded — `addons` has no rapier");
|
|
28
29
|
const [x, y, z] = this._gravity;
|
|
29
30
|
console.log(`Rapier version: ${Rapier.version()}`);
|
package/dist/World.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare class World extends ThreeStart {
|
|
|
14
14
|
getUrl: (source: string | object) => string;
|
|
15
15
|
/** World lifecycle event bus — the same one as `world.events` in scripts. */
|
|
16
16
|
get events(): WorldRuntime["emitter"];
|
|
17
|
+
/** Shared reactive state — the same one as `world.stores` in scripts. */
|
|
18
|
+
get stores(): WorldRuntime["stores"];
|
|
17
19
|
start(): this;
|
|
18
20
|
dispose(): void;
|
|
19
21
|
}
|
package/dist/WorldRuntime.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import EventEmitter from "eventemitter3";
|
|
2
|
-
import * as THREE from "three/webgpu";
|
|
3
2
|
import { AddonsRuntime } from "./addons/AddonsRuntime";
|
|
3
|
+
import { WorldStores } from "./world-stores";
|
|
4
|
+
import type * as THREE from "three/webgpu";
|
|
4
5
|
import type { Object3DBehaviourConstructor, ThreeContext, ThreeStart } from "three-start";
|
|
5
6
|
import type { SceneBloom } from "./rendering/SceneBloom";
|
|
6
7
|
import type { LoadWorldOptions, RuntimeApi, WorldDefinition } from "./types";
|
|
@@ -15,6 +16,8 @@ export declare class WorldRuntime implements RuntimeApi {
|
|
|
15
16
|
readonly options: LoadWorldOptions;
|
|
16
17
|
/** World lifecycle event bus (`world.events` in scripts). */
|
|
17
18
|
readonly emitter: EventEmitter<string | symbol, any>;
|
|
19
|
+
/** Shared reactive state of the world (`world.stores` in scripts). */
|
|
20
|
+
readonly stores: WorldStores;
|
|
18
21
|
private readonly _addons;
|
|
19
22
|
private readonly _executedScripts;
|
|
20
23
|
private readonly _builtins;
|
|
@@ -23,6 +26,7 @@ export declare class WorldRuntime implements RuntimeApi {
|
|
|
23
26
|
/** Reverse index for `getUrl(resource)` — play mode resolves objects too. */
|
|
24
27
|
private readonly _pathByResource;
|
|
25
28
|
private _ctx;
|
|
29
|
+
private _starter;
|
|
26
30
|
private _bloom;
|
|
27
31
|
private _scriptContext;
|
|
28
32
|
constructor(definition: RuntimeDefinitionSlice, options: LoadWorldOptions);
|
|
@@ -32,12 +36,12 @@ export declare class WorldRuntime implements RuntimeApi {
|
|
|
32
36
|
get ctx(): ThreeContext;
|
|
33
37
|
/** @internal Set by bootstrap. */
|
|
34
38
|
setContext(ctx: ThreeContext): void;
|
|
39
|
+
/** @internal Set by bootstrap — backs `world.addModules` (register before start). */
|
|
40
|
+
setStarter(starter: ThreeStart): void;
|
|
35
41
|
/** @internal World bloom, if enabled in the renderer config. */
|
|
36
42
|
setBloom(bloom: SceneBloom): void;
|
|
37
|
-
/** @internal
|
|
43
|
+
/** @internal Libs (config + required by modules/behaviours) and externals. */
|
|
38
44
|
loadAddons(): Promise<void>;
|
|
39
|
-
/** Physics is on in the config, or a built-in behaviour on the scene needs it. */
|
|
40
|
-
private _usesPhysics;
|
|
41
45
|
/** @internal World resources by asset path — result of `loadAssets`. */
|
|
42
46
|
setResources(resources: Record<string, unknown>): void;
|
|
43
47
|
reportProgress(stage: string, details?: string): void;
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
import { ContextModule } from "three-start";
|
|
2
2
|
import type { AddonsModuleApi } from "../modules/contracts/addons.module-api";
|
|
3
|
+
import type { LibId } from "./lib-registry";
|
|
3
4
|
import type { AddonsConfig, WorldImports } from "./types";
|
|
4
|
-
import type RAPIER from "@dimforge/rapier3d-compat";
|
|
5
5
|
/**
|
|
6
|
-
* External dependencies of the world:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* External dependencies of the world: libs and external scripts. Built-in libs
|
|
7
|
+
* are registered right away (the runtime bundles them), optional ones are loaded
|
|
8
|
+
* before the world starts — enabled in `settings.addons.json → libs` or declared
|
|
9
|
+
* by a module/behaviour through `requiredLibs`. Scripts reach all of them by
|
|
10
|
+
* `require("<key>")`.
|
|
9
11
|
*
|
|
10
12
|
* This is the `addons` ctx module, but it also works without a ctx: launcher
|
|
11
13
|
* worlds have no 3d context while `require` still has to work, so there the
|
|
12
|
-
* instance is used directly. How to import packages is up to the
|
|
13
|
-
* (see `WorldImports`).
|
|
14
|
+
* instance is used directly. How to import optional packages is up to the
|
|
15
|
+
* runtime owner (see `WorldImports`).
|
|
14
16
|
*/
|
|
15
17
|
export declare class AddonsRuntime extends ContextModule implements AddonsModuleApi {
|
|
16
18
|
private readonly _imports;
|
|
17
19
|
/** libs — by requireKey (`@tweenjs/tween.js`), externals — by accessor (`Tone`). */
|
|
18
20
|
private readonly _byKey;
|
|
21
|
+
private readonly _byLibId;
|
|
19
22
|
private readonly _scriptTags;
|
|
20
|
-
private _rapier;
|
|
21
23
|
constructor(_imports?: WorldImports);
|
|
22
|
-
/** `
|
|
23
|
-
load(addons: AddonsConfig,
|
|
24
|
-
|
|
25
|
-
}): Promise<void>;
|
|
26
|
-
/** Resolves a require key: enabled lib or external accessor; undefined otherwise. */
|
|
24
|
+
/** `usedBuiltinUuids` — built-in behaviours on the scene; they may require libs. */
|
|
25
|
+
load(addons: AddonsConfig, usedBuiltinUuids?: readonly string[]): Promise<void>;
|
|
26
|
+
/** Resolves a require key: a lib or an external accessor; undefined otherwise. */
|
|
27
27
|
resolve(key: string): unknown;
|
|
28
|
-
/** The
|
|
29
|
-
|
|
28
|
+
/** The lib itself, for runtime code that knows what it needs (physics → rapier). */
|
|
29
|
+
lib<T>(id: LibId): T | undefined;
|
|
30
|
+
/** Libs bundled with the runtime: no importer, no config, always available. */
|
|
31
|
+
private _registerBuiltins;
|
|
30
32
|
private _loadLibs;
|
|
31
|
-
private
|
|
33
|
+
private _register;
|
|
32
34
|
private _loadExternals;
|
|
33
35
|
private _mountScript;
|
|
34
36
|
dispose(): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as THREE from "three/webgpu";
|
|
2
|
+
import * as TSL from "three/tsl";
|
|
3
|
+
import { LibId } from "./lib-registry";
|
|
4
|
+
/**
|
|
5
|
+
* Libraries the runtime bundles itself: `require("<key>")` hands these back in
|
|
6
|
+
* every world, no config and no importer involved. Metadata (keys, labels) is
|
|
7
|
+
* in `lib-registry`; the modules live here so the registry stays importable
|
|
8
|
+
* from the editor without dragging three & co along.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BUILTIN_LIB_MODULES: Partial<Record<LibId, unknown>>;
|
|
11
|
+
/**
|
|
12
|
+
* The subset of built-ins that is also a bare global in script scope — same
|
|
13
|
+
* objects as above, so `THREE === require("three")`. Typed literally: the
|
|
14
|
+
* script context contract depends on these exact shapes.
|
|
15
|
+
*/
|
|
16
|
+
export declare const BUILTIN_GLOBALS: {
|
|
17
|
+
readonly THREE: typeof THREE;
|
|
18
|
+
readonly TSL: typeof TSL;
|
|
19
|
+
readonly van: import("vanjs-core").Van;
|
|
20
|
+
};
|
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registry of
|
|
2
|
+
* Registry of the libraries scripts can `require`. One entry describes one
|
|
3
|
+
* library; the flags say how it gets into the world:
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* - **built-in** (`builtin: true`) — the runtime already bundles it, so it is
|
|
6
|
+
* available in every world with no config and no importer. Some of them are
|
|
7
|
+
* additionally exposed as a bare global (`globalName`) — that is the only
|
|
8
|
+
* difference between `THREE` and, say, `nanostores`.
|
|
9
|
+
* - **optional** — enabled in `settings.addons.json → libs`, or pulled in by
|
|
10
|
+
* something that declares it in `requiredLibs` (physics → rapier). The
|
|
11
|
+
* importer comes from the runtime owner (`WorldImports`), never from the
|
|
12
|
+
* package itself — see `./optional-imports.ts`.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* `id` is the config key under `libs`, `requireKey` is what the user writes in
|
|
15
|
+
* `require(...)` (and the import specifier importers are built from). Single
|
|
16
|
+
* source for: the app's importer map, the `imports.libs` block of an exported
|
|
17
|
+
* world, the JSON schema and the editor's require typings.
|
|
13
18
|
*/
|
|
14
19
|
export declare enum LibId {
|
|
20
|
+
Three = "three",
|
|
21
|
+
Tsl = "tsl",
|
|
22
|
+
ThreeStart = "threeStart",
|
|
23
|
+
Van = "van",
|
|
24
|
+
Nanostores = "nanostores",
|
|
25
|
+
EventEmitter = "eventemitter3",
|
|
26
|
+
Howler = "howler",
|
|
15
27
|
Tween = "tween",
|
|
16
28
|
Anime = "anime",
|
|
17
29
|
Nipplejs = "nipplejs",
|
|
18
|
-
Stats = "stats"
|
|
30
|
+
Stats = "stats",
|
|
31
|
+
Rapier = "rapier",
|
|
32
|
+
Css2d = "css2d",
|
|
33
|
+
Css3d = "css3d"
|
|
19
34
|
}
|
|
20
35
|
export interface LibDescriptor {
|
|
21
36
|
readonly id: LibId;
|
|
@@ -25,10 +40,21 @@ export interface LibDescriptor {
|
|
|
25
40
|
readonly label: string;
|
|
26
41
|
/** npm package to install; defaults to `requireKey` (differs for subpaths). */
|
|
27
42
|
readonly packageName?: string;
|
|
43
|
+
/** Bundled with the runtime: always available, no importer, nothing to install. */
|
|
44
|
+
readonly builtin?: boolean;
|
|
45
|
+
/** Built-in libs that are also bare globals in script scope. */
|
|
46
|
+
readonly globalName?: string;
|
|
28
47
|
/** The package exposes its API as the default export — unwrap it on load. */
|
|
29
48
|
readonly defaultExport?: boolean;
|
|
49
|
+
/** Post-import step, e.g. wasm init. Returns what `require` should hand back. */
|
|
50
|
+
readonly init?: (module: unknown) => Promise<unknown>;
|
|
30
51
|
}
|
|
31
52
|
export declare const LIBS: Readonly<Record<LibId, LibDescriptor>>;
|
|
32
53
|
export declare const LIB_IDS: LibId[];
|
|
33
54
|
export declare const LIB_DESCRIPTORS: LibDescriptor[];
|
|
55
|
+
/** Bundled with the runtime — available in every world. */
|
|
56
|
+
export declare const BUILTIN_LIB_DESCRIPTORS: LibDescriptor[];
|
|
57
|
+
/** Enabled in `settings.addons.json → libs` or required by a module/behaviour. */
|
|
58
|
+
export declare const OPTIONAL_LIB_DESCRIPTORS: LibDescriptor[];
|
|
59
|
+
export declare const OPTIONAL_LIB_IDS: LibId[];
|
|
34
60
|
export declare function libByRequireKey(requireKey: string): LibDescriptor | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LibId } from "./lib-registry";
|
|
2
|
+
import type { AddonsConfig } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Libs the world needs beyond what `libs` lists: an enabled module or a built-in
|
|
5
|
+
* behaviour on the scene declares them itself (`requiredLibs`), so physics keeps
|
|
6
|
+
* working without a line in `settings.addons.json`. Same answer for the game and
|
|
7
|
+
* for the export — the build emits importers from it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function collectRequiredLibs(addons: AddonsConfig | undefined, usedBuiltinUuids?: readonly string[]): LibId[];
|
package/dist/addons/types.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export type PackageImport = () => Promise<unknown>;
|
|
|
12
12
|
* `imports` in `defineWorld`.
|
|
13
13
|
*/
|
|
14
14
|
export interface WorldImports {
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/** Optional libs by `LibId` — enabled in `settings.addons.json → libs` or
|
|
16
|
+
* required by a module/behaviour (physics → rapier). Built-in libs are
|
|
17
|
+
* bundled with the runtime and need no importer. */
|
|
18
18
|
libs?: Partial<Record<LibId, PackageImport>>;
|
|
19
19
|
}
|
|
20
20
|
/** `settings.addons.json` — pluggable modules, built-in libs, external scripts. */
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
+
import type { RequiredPatch } from "../registry";
|
|
1
2
|
import type { RapierPhysics } from "../../modules/physics/RapierPhysics";
|
|
2
3
|
import type { NormalizedPhysics } from "../../modules/physics/physics.module-descriptor";
|
|
3
|
-
import type { ThreeStart } from "three-start";
|
|
4
4
|
/** The only place the physics module is created — called by the registry
|
|
5
5
|
* descriptor. The module itself takes rapier from `ctx.modules.addons` on awake. */
|
|
6
6
|
export declare function createRapierPhysicsModule(config: NormalizedPhysics): Promise<RapierPhysics>;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* Idempotent, and strictly before `start()` — three-start forbids `addModules`
|
|
12
|
-
* afterwards.
|
|
8
|
+
* What a physics behaviour needs on top of its class: the rapier lib (loaded
|
|
9
|
+
* with the other addons, before the world starts) and the physics ctx module.
|
|
10
|
+
* A collider on the scene is enough — no line in `settings.addons.json`.
|
|
13
11
|
*/
|
|
14
|
-
export declare
|
|
12
|
+
export declare const rapierPhysicsPatch: RequiredPatch;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Object3DBehaviourConstructor, ThreeStart } from "three-start";
|
|
2
|
+
import type { LibId } from "../addons/lib-registry";
|
|
2
3
|
/**
|
|
3
4
|
* Built-in behaviour: uuid is its identity in user worlds — it must never
|
|
4
5
|
* change. The class loads lazily, so a world without physics doesn't pull rapier.
|
|
@@ -7,8 +8,14 @@ export interface BuiltinBehaviour {
|
|
|
7
8
|
uuid: string;
|
|
8
9
|
name: string;
|
|
9
10
|
load: () => Promise<Object3DBehaviourConstructor>;
|
|
10
|
-
/**
|
|
11
|
-
requiredPatch?:
|
|
11
|
+
/** ctx modules (and their libs) the behaviour cannot work without. */
|
|
12
|
+
requiredPatch?: RequiredPatch;
|
|
13
|
+
}
|
|
14
|
+
/** What a behaviour needs beyond its own class: libs to load before the world
|
|
15
|
+
* starts, and ctx modules to register strictly before `start()`. */
|
|
16
|
+
export interface RequiredPatch {
|
|
17
|
+
requiredLibs?: LibId[];
|
|
18
|
+
apply: (starter: ThreeStart) => Promise<void>;
|
|
12
19
|
}
|
|
13
20
|
/** Registry of built-in behaviours by uuid — source of truth for both game and export. */
|
|
14
21
|
export declare const BUILTIN_BEHAVIOURS: Record<string, BuiltinBehaviour | undefined>;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ export type { InstantiateOptions, PrefabFactory } from "./WorldRuntime";
|
|
|
4
4
|
export { setColor } from "./values";
|
|
5
5
|
export { RUNTIME_VERSION, WORLD_FORMAT_VERSION } from "./version";
|
|
6
6
|
export { WorldEvent } from "./world-events";
|
|
7
|
+
export { lazyProxy } from "./utils/lazy";
|
|
8
|
+
export { WorldStores } from "./world-stores";
|
|
9
|
+
export type { WorldStore } from "./world-stores";
|
|
7
10
|
export { createWorldModules, EnvModule, HtmlHudModule, InputSystemModule, isMobileDevice, SoundsModule, UtilsModule, } from "./modules";
|
|
8
11
|
export type { EnvMode, WorldModules } from "./modules";
|
|
9
12
|
export { DEFAULT_RESOURCE_IDS, DefaultResourceId, getDefaultResource, isDefaultResourceId, } from "./resources/default-resources";
|
|
@@ -19,8 +22,8 @@ export { AudioSource, createAudio } from "./resources/audio";
|
|
|
19
22
|
export { createVideo, VideoSource } from "./resources/video";
|
|
20
23
|
export { createFont, FontSource, fontFormatFromMime } from "./resources/font";
|
|
21
24
|
export { BUILTIN_BEHAVIOUR_LIST, BUILTIN_BEHAVIOURS, getBuiltinBehaviour, } from "./behaviours/registry";
|
|
22
|
-
export type { BuiltinBehaviour } from "./behaviours/registry";
|
|
23
|
-
export {
|
|
25
|
+
export type { BuiltinBehaviour, RequiredPatch } from "./behaviours/registry";
|
|
26
|
+
export { rapierPhysicsPatch } from "./behaviours/physics/_required-patch";
|
|
24
27
|
export { AddonsRuntime } from "./addons/AddonsRuntime";
|
|
25
28
|
export { applyRendererConfig } from "./rendering/renderer-apply";
|
|
26
29
|
export { enableSceneEmissiveMrt, SceneBloom } from "./rendering/SceneBloom";
|
|
@@ -28,7 +31,8 @@ export { createTslNode, TslNode } from "./rendering/tsl-node";
|
|
|
28
31
|
export type { TslNodeBuilder } from "./rendering/tsl-node";
|
|
29
32
|
export { createUniformNode, isUniformType, UNIFORM_DEFAULTS, UNIFORM_TYPES, uniformType, } from "./rendering/tsl-uniforms";
|
|
30
33
|
export type { UniformType } from "./rendering/tsl-uniforms";
|
|
31
|
-
export { LIB_DESCRIPTORS, LIB_IDS, LIBS, LibId, libByRequireKey } from "./addons/lib-registry";
|
|
34
|
+
export { BUILTIN_LIB_DESCRIPTORS, LIB_DESCRIPTORS, LIB_IDS, LIBS, LibId, OPTIONAL_LIB_DESCRIPTORS, OPTIONAL_LIB_IDS, libByRequireKey, } from "./addons/lib-registry";
|
|
35
|
+
export { collectRequiredLibs } from "./addons/required-libs";
|
|
32
36
|
export type { LibDescriptor } from "./addons/lib-registry";
|
|
33
37
|
export { OPTIONAL_MODULE_IDS, OptionalModuleId } from "./modules/optional-module-descriptor";
|
|
34
38
|
export { OPTIONAL_MODULES, getOptionalModuleDescriptor } from "./modules/optional-modules";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { a as OPTIONAL_MODULE_IDS, i as normalizeChat, n as CHAT_INPUT_MODES, o as OptionalModuleId, r as chatModuleDescriptor } from "./chat.module-descriptor-BTsep-Kq.js";
|
|
2
|
+
import { a as LibId, c as libByRequireKey, i as LIB_IDS, n as LIBS, o as OPTIONAL_LIB_DESCRIPTORS, r as LIB_DESCRIPTORS, s as OPTIONAL_LIB_IDS, t as BUILTIN_LIB_DESCRIPTORS } from "./lib-registry-D1VyVPBg.js";
|
|
2
3
|
import { n as getRadiusScale, t as getHalfheightRadiusScale } from "./get-scale-from-obj-B7JlVPni.js";
|
|
3
4
|
import * as THREE from "three/webgpu";
|
|
4
5
|
import { BufferGeometryLoader, LoadingManager, TextureLoader } from "three/webgpu";
|
|
@@ -7,10 +8,13 @@ import { ContextModule, ThreeContextEvents, ThreeStart } from "three-start";
|
|
|
7
8
|
import * as TSL from "three/tsl";
|
|
8
9
|
import { emissive, mrt, output } from "three/tsl";
|
|
9
10
|
import van from "vanjs-core";
|
|
11
|
+
import * as howler from "howler";
|
|
10
12
|
import { Howl, Howler } from "howler";
|
|
11
13
|
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils.js";
|
|
12
14
|
import { clone } from "three/examples/jsm/utils/SkeletonUtils.js";
|
|
13
15
|
import EventEmitter from "eventemitter3";
|
|
16
|
+
import * as nanostores from "nanostores";
|
|
17
|
+
import { atom } from "nanostores";
|
|
14
18
|
//#region packages/world-runtime/src/world-events.ts
|
|
15
19
|
/**
|
|
16
20
|
* World lifecycle events, emitted on `game.emitter` (`world.events` in scripts)
|
|
@@ -46,6 +50,10 @@ var World = class extends ThreeStart {
|
|
|
46
50
|
get events() {
|
|
47
51
|
return this._runtime.emitter;
|
|
48
52
|
}
|
|
53
|
+
/** Shared reactive state — the same one as `world.stores` in scripts. */
|
|
54
|
+
get stores() {
|
|
55
|
+
return this._runtime.stores;
|
|
56
|
+
}
|
|
49
57
|
start() {
|
|
50
58
|
const wasStarted = this.isStarted;
|
|
51
59
|
super.start();
|
|
@@ -59,6 +67,26 @@ var World = class extends ThreeStart {
|
|
|
59
67
|
};
|
|
60
68
|
//#endregion
|
|
61
69
|
//#region packages/world-runtime/src/utils/lazy.ts
|
|
70
|
+
/**
|
|
71
|
+
* Object that forwards every access to `resolve()`. For script globals that are
|
|
72
|
+
* shortcuts into something created later (`modules` → `ctx.modules`): the script
|
|
73
|
+
* bag is destructured eagerly, so a plain reference or a getter would be read
|
|
74
|
+
* before the target exists.
|
|
75
|
+
*/
|
|
76
|
+
function lazyProxy(resolve) {
|
|
77
|
+
return new Proxy({}, {
|
|
78
|
+
get: (_target, key) => Reflect.get(resolve(), key),
|
|
79
|
+
has: (_target, key) => Reflect.has(resolve(), key),
|
|
80
|
+
ownKeys: () => Reflect.ownKeys(resolve()),
|
|
81
|
+
getOwnPropertyDescriptor: (_target, key) => {
|
|
82
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), key);
|
|
83
|
+
return descriptor && {
|
|
84
|
+
...descriptor,
|
|
85
|
+
configurable: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
62
90
|
/** Lazy cached value: the factory is called once on the first `get()`. */
|
|
63
91
|
function lazy(factory) {
|
|
64
92
|
let has = false;
|
|
@@ -414,18 +442,24 @@ var ControlsModule = class extends ContextModule {
|
|
|
414
442
|
};
|
|
415
443
|
//#endregion
|
|
416
444
|
//#region packages/world-runtime/src/behaviours/physics/_required-patch.ts
|
|
417
|
-
var lazyRapierPhysicsModule = lazy(async () => (await import("./RapierPhysics-
|
|
445
|
+
var lazyRapierPhysicsModule = lazy(async () => (await import("./RapierPhysics-CU6X--c7.js")).default);
|
|
418
446
|
/** The only place the physics module is created — called by the registry
|
|
419
447
|
* descriptor. The module itself takes rapier from `ctx.modules.addons` on awake. */
|
|
420
448
|
async function createRapierPhysicsModule(config) {
|
|
421
449
|
return new (await (lazyRapierPhysicsModule.get()))({ debug: config.debug });
|
|
422
450
|
}
|
|
423
451
|
/**
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
|
|
428
|
-
|
|
452
|
+
* What a physics behaviour needs on top of its class: the rapier lib (loaded
|
|
453
|
+
* with the other addons, before the world starts) and the physics ctx module.
|
|
454
|
+
* A collider on the scene is enough — no line in `settings.addons.json`.
|
|
455
|
+
*/
|
|
456
|
+
var rapierPhysicsPatch = {
|
|
457
|
+
requiredLibs: [LibId.Rapier],
|
|
458
|
+
apply: (starter) => applyRapierPhysicsPatch(starter)
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* Registers the physics module. Idempotent, and strictly before `start()` —
|
|
462
|
+
* three-start forbids `addModules` afterwards.
|
|
429
463
|
*/
|
|
430
464
|
async function applyRapierPhysicsPatch(starter) {
|
|
431
465
|
if (starter.ctx.modules.rapier) return;
|
|
@@ -456,6 +490,7 @@ var physicsModuleDescriptor = {
|
|
|
456
490
|
debug: false
|
|
457
491
|
}),
|
|
458
492
|
create: (config) => createRapierPhysicsModule(config),
|
|
493
|
+
requiredLibs: [LibId.Rapier],
|
|
459
494
|
schema: {
|
|
460
495
|
description: "Physics engine module. false | \"rapier\" | { engine, debug }.",
|
|
461
496
|
oneOf: [
|
|
@@ -531,19 +566,25 @@ var EnvModule = class extends ContextModule {
|
|
|
531
566
|
//#endregion
|
|
532
567
|
//#region packages/world-runtime/src/modules/HtmlHudModule.ts
|
|
533
568
|
var { div } = van.tags;
|
|
569
|
+
var LAYER_STYLE = "position:fixed;inset:0;pointer-events:none;";
|
|
534
570
|
var HtmlHudModule = class extends ContextModule {
|
|
535
|
-
|
|
571
|
+
overlay;
|
|
572
|
+
underlay;
|
|
536
573
|
crosshair;
|
|
537
574
|
constructor() {
|
|
538
575
|
super();
|
|
539
576
|
this.crosshair = div({
|
|
540
|
-
id: "
|
|
577
|
+
id: "kvy-crosshair",
|
|
541
578
|
style: "position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);"
|
|
542
579
|
});
|
|
543
|
-
this.
|
|
544
|
-
id: "
|
|
545
|
-
style:
|
|
580
|
+
this.overlay = div({
|
|
581
|
+
id: "kvy-overlay",
|
|
582
|
+
style: LAYER_STYLE
|
|
546
583
|
}, this.crosshair);
|
|
584
|
+
this.underlay = div({
|
|
585
|
+
id: "kvy-underlay",
|
|
586
|
+
style: LAYER_STYLE
|
|
587
|
+
});
|
|
547
588
|
}
|
|
548
589
|
onAwake() {
|
|
549
590
|
const ctx = this.ctx;
|
|
@@ -551,8 +592,13 @@ var HtmlHudModule = class extends ContextModule {
|
|
|
551
592
|
ctx.on(ThreeContextEvents.Mount, this.onMount);
|
|
552
593
|
ctx.on(ThreeContextEvents.Unmount, this.onUnmount);
|
|
553
594
|
}
|
|
595
|
+
/** Appends nodes over the canvas (the HUD). */
|
|
554
596
|
append(...nodes) {
|
|
555
|
-
this.
|
|
597
|
+
this.overlay.append(...nodes);
|
|
598
|
+
}
|
|
599
|
+
/** Appends nodes under the canvas — visible only through a transparent canvas. */
|
|
600
|
+
appendUnderlay(...nodes) {
|
|
601
|
+
this.underlay.append(...nodes);
|
|
556
602
|
}
|
|
557
603
|
setCrosshair(innerHtml) {
|
|
558
604
|
this.crosshair.innerHTML = innerHtml;
|
|
@@ -563,10 +609,14 @@ var HtmlHudModule = class extends ContextModule {
|
|
|
563
609
|
this.ctx.off(ThreeContextEvents.Unmount, this.onUnmount);
|
|
564
610
|
}
|
|
565
611
|
onMount = (container) => {
|
|
566
|
-
container.
|
|
612
|
+
container.prepend(this.underlay);
|
|
613
|
+
container.append(this.overlay);
|
|
614
|
+
const canvas = this.ctx.renderer.domElement;
|
|
615
|
+
if (!canvas.style.position) canvas.style.position = "relative";
|
|
567
616
|
};
|
|
568
617
|
onUnmount = () => {
|
|
569
|
-
this.
|
|
618
|
+
this.overlay.remove();
|
|
619
|
+
this.underlay.remove();
|
|
570
620
|
};
|
|
571
621
|
};
|
|
572
622
|
//#endregion
|
|
@@ -921,55 +971,55 @@ var entries = [
|
|
|
921
971
|
uuid: "rrb",
|
|
922
972
|
name: "Rigidbody",
|
|
923
973
|
load: () => loadDefault(import("./Rigidbody-DLhN8ddv.js").then((n) => n.n)),
|
|
924
|
-
requiredPatch:
|
|
974
|
+
requiredPatch: rapierPhysicsPatch
|
|
925
975
|
},
|
|
926
976
|
{
|
|
927
977
|
uuid: "rapierballcol",
|
|
928
978
|
name: "BallCollider",
|
|
929
979
|
load: () => loadDefault(import("./BallCollider-3L5vOrcL.js")),
|
|
930
|
-
requiredPatch:
|
|
980
|
+
requiredPatch: rapierPhysicsPatch
|
|
931
981
|
},
|
|
932
982
|
{
|
|
933
983
|
uuid: "rapiercapscol",
|
|
934
984
|
name: "CapsuleCollider",
|
|
935
985
|
load: () => loadDefault(import("./CapsuleCollider-CBTsR-Ys.js")),
|
|
936
|
-
requiredPatch:
|
|
986
|
+
requiredPatch: rapierPhysicsPatch
|
|
937
987
|
},
|
|
938
988
|
{
|
|
939
989
|
uuid: "rapierconecol",
|
|
940
990
|
name: "ConeCollider",
|
|
941
991
|
load: () => loadDefault(import("./ConeCollider-C3XU3Y9X.js")),
|
|
942
|
-
requiredPatch:
|
|
992
|
+
requiredPatch: rapierPhysicsPatch
|
|
943
993
|
},
|
|
944
994
|
{
|
|
945
995
|
uuid: "rapierconvxcol",
|
|
946
996
|
name: "ConvexMeshCollider",
|
|
947
997
|
load: () => loadDefault(import("./ConvexMeshCollider-DzNV4HTO.js")),
|
|
948
|
-
requiredPatch:
|
|
998
|
+
requiredPatch: rapierPhysicsPatch
|
|
949
999
|
},
|
|
950
1000
|
{
|
|
951
1001
|
uuid: "rapiercubcol",
|
|
952
1002
|
name: "CuboidCollider",
|
|
953
1003
|
load: () => loadDefault(import("./CuboidCollider-Dp1DZoFM.js")),
|
|
954
|
-
requiredPatch:
|
|
1004
|
+
requiredPatch: rapierPhysicsPatch
|
|
955
1005
|
},
|
|
956
1006
|
{
|
|
957
1007
|
uuid: "rapiercylcol",
|
|
958
1008
|
name: "CylinderCollider",
|
|
959
1009
|
load: () => loadDefault(import("./CylinderCollider-C0h_nhB5.js")),
|
|
960
|
-
requiredPatch:
|
|
1010
|
+
requiredPatch: rapierPhysicsPatch
|
|
961
1011
|
},
|
|
962
1012
|
{
|
|
963
1013
|
uuid: "rapiertricol",
|
|
964
1014
|
name: "TrimeshCollider",
|
|
965
1015
|
load: () => loadDefault(import("./TrimeshCollider-7dIs_xtZ.js")),
|
|
966
|
-
requiredPatch:
|
|
1016
|
+
requiredPatch: rapierPhysicsPatch
|
|
967
1017
|
},
|
|
968
1018
|
{
|
|
969
1019
|
uuid: "rapierfpsplayercontroller",
|
|
970
1020
|
name: "RapierFpsPlayerController",
|
|
971
1021
|
load: () => loadDefault(import("./RapierFpsPlayerController-DthD8dw9.js")),
|
|
972
|
-
requiredPatch:
|
|
1022
|
+
requiredPatch: rapierPhysicsPatch
|
|
973
1023
|
}
|
|
974
1024
|
];
|
|
975
1025
|
/** Registry of built-in behaviours by uuid — source of truth for both game and export. */
|
|
@@ -1044,122 +1094,123 @@ function createFont(url, family, mime, name) {
|
|
|
1044
1094
|
return source;
|
|
1045
1095
|
}
|
|
1046
1096
|
//#endregion
|
|
1047
|
-
//#region packages/world-runtime/src/addons/
|
|
1097
|
+
//#region packages/world-runtime/src/addons/builtin-libs.ts
|
|
1048
1098
|
/**
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1053
|
-
* importers are built from). Single source for:
|
|
1054
|
-
* - game: the app's importer map (`core/game/settings/optional-imports`),
|
|
1055
|
-
* - export: the `imports.libs` block the build emits into the world module,
|
|
1056
|
-
* - editor: JSON schema (`libs` props) + require typings (`AddonsRequireTypings`).
|
|
1057
|
-
*
|
|
1058
|
-
* Adding a lib = add an entry here (+ importer in the app, + d.ts loader in editor).
|
|
1059
|
-
* The package itself never imports these — see `./optional-imports.ts`.
|
|
1099
|
+
* Libraries the runtime bundles itself: `require("<key>")` hands these back in
|
|
1100
|
+
* every world, no config and no importer involved. Metadata (keys, labels) is
|
|
1101
|
+
* in `lib-registry`; the modules live here so the registry stays importable
|
|
1102
|
+
* from the editor without dragging three & co along.
|
|
1060
1103
|
*/
|
|
1061
|
-
var
|
|
1062
|
-
LibId
|
|
1063
|
-
LibId
|
|
1064
|
-
LibId
|
|
1065
|
-
LibId
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
},
|
|
1080
|
-
["nipplejs"]: {
|
|
1081
|
-
id: "nipplejs",
|
|
1082
|
-
requireKey: "nipplejs",
|
|
1083
|
-
label: "nipplejs (on-screen joystick)",
|
|
1084
|
-
defaultExport: true
|
|
1085
|
-
},
|
|
1086
|
-
["stats"]: {
|
|
1087
|
-
id: "stats",
|
|
1088
|
-
requireKey: "stats.js",
|
|
1089
|
-
label: "Stats.js (fps overlay)",
|
|
1090
|
-
defaultExport: true
|
|
1091
|
-
}
|
|
1104
|
+
var BUILTIN_LIB_MODULES = {
|
|
1105
|
+
[LibId.Three]: THREE,
|
|
1106
|
+
[LibId.Tsl]: TSL,
|
|
1107
|
+
[LibId.ThreeStart]: ThreeStartNS,
|
|
1108
|
+
[LibId.Van]: van,
|
|
1109
|
+
[LibId.Nanostores]: nanostores,
|
|
1110
|
+
[LibId.EventEmitter]: EventEmitter,
|
|
1111
|
+
[LibId.Howler]: howler
|
|
1112
|
+
};
|
|
1113
|
+
/**
|
|
1114
|
+
* The subset of built-ins that is also a bare global in script scope — same
|
|
1115
|
+
* objects as above, so `THREE === require("three")`. Typed literally: the
|
|
1116
|
+
* script context contract depends on these exact shapes.
|
|
1117
|
+
*/
|
|
1118
|
+
var BUILTIN_GLOBALS = {
|
|
1119
|
+
THREE,
|
|
1120
|
+
TSL,
|
|
1121
|
+
van
|
|
1092
1122
|
};
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1123
|
+
//#endregion
|
|
1124
|
+
//#region packages/world-runtime/src/addons/required-libs.ts
|
|
1125
|
+
/**
|
|
1126
|
+
* Libs the world needs beyond what `libs` lists: an enabled module or a built-in
|
|
1127
|
+
* behaviour on the scene declares them itself (`requiredLibs`), so physics keeps
|
|
1128
|
+
* working without a line in `settings.addons.json`. Same answer for the game and
|
|
1129
|
+
* for the export — the build emits importers from it.
|
|
1130
|
+
*/
|
|
1131
|
+
function collectRequiredLibs(addons, usedBuiltinUuids = []) {
|
|
1132
|
+
const required = /* @__PURE__ */ new Set();
|
|
1133
|
+
const configured = addons?.libs ?? {};
|
|
1134
|
+
for (const id of Object.keys(configured)) if (configured[id] && LIB_IDS.includes(id) && !LIBS[id].builtin) required.add(id);
|
|
1135
|
+
for (const descriptor of OPTIONAL_MODULES) {
|
|
1136
|
+
if (!descriptor.requiredLibs?.length) continue;
|
|
1137
|
+
if (descriptor.normalize(addons?.modules?.[descriptor.id]) === null) continue;
|
|
1138
|
+
for (const id of descriptor.requiredLibs) required.add(id);
|
|
1139
|
+
}
|
|
1140
|
+
for (const uuid of usedBuiltinUuids) {
|
|
1141
|
+
const libs = getBuiltinBehaviour(uuid)?.requiredPatch?.requiredLibs;
|
|
1142
|
+
if (libs) for (const id of libs) required.add(id);
|
|
1143
|
+
}
|
|
1144
|
+
return [...required];
|
|
1097
1145
|
}
|
|
1098
1146
|
//#endregion
|
|
1099
1147
|
//#region packages/world-runtime/src/addons/AddonsRuntime.ts
|
|
1100
1148
|
/**
|
|
1101
|
-
* External dependencies of the world:
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1149
|
+
* External dependencies of the world: libs and external scripts. Built-in libs
|
|
1150
|
+
* are registered right away (the runtime bundles them), optional ones are loaded
|
|
1151
|
+
* before the world starts — enabled in `settings.addons.json → libs` or declared
|
|
1152
|
+
* by a module/behaviour through `requiredLibs`. Scripts reach all of them by
|
|
1153
|
+
* `require("<key>")`.
|
|
1104
1154
|
*
|
|
1105
1155
|
* This is the `addons` ctx module, but it also works without a ctx: launcher
|
|
1106
1156
|
* worlds have no 3d context while `require` still has to work, so there the
|
|
1107
|
-
* instance is used directly. How to import packages is up to the
|
|
1108
|
-
* (see `WorldImports`).
|
|
1157
|
+
* instance is used directly. How to import optional packages is up to the
|
|
1158
|
+
* runtime owner (see `WorldImports`).
|
|
1109
1159
|
*/
|
|
1110
1160
|
var AddonsRuntime = class extends ContextModule {
|
|
1111
1161
|
_imports;
|
|
1112
1162
|
/** libs — by requireKey (`@tweenjs/tween.js`), externals — by accessor (`Tone`). */
|
|
1113
1163
|
_byKey = /* @__PURE__ */ new Map();
|
|
1164
|
+
_byLibId = /* @__PURE__ */ new Map();
|
|
1114
1165
|
_scriptTags = [];
|
|
1115
|
-
_rapier = null;
|
|
1116
1166
|
constructor(_imports = {}) {
|
|
1117
1167
|
super();
|
|
1118
1168
|
this._imports = _imports;
|
|
1169
|
+
this._registerBuiltins();
|
|
1119
1170
|
}
|
|
1120
|
-
/** `
|
|
1121
|
-
async load(addons,
|
|
1122
|
-
await this._loadLibs(addons
|
|
1171
|
+
/** `usedBuiltinUuids` — built-in behaviours on the scene; they may require libs. */
|
|
1172
|
+
async load(addons, usedBuiltinUuids = []) {
|
|
1173
|
+
await this._loadLibs(collectRequiredLibs(addons, usedBuiltinUuids));
|
|
1123
1174
|
await this._loadExternals(addons.externals);
|
|
1124
|
-
if (options.physics) await this._loadRapier();
|
|
1125
1175
|
}
|
|
1126
|
-
/** Resolves a require key:
|
|
1176
|
+
/** Resolves a require key: a lib or an external accessor; undefined otherwise. */
|
|
1127
1177
|
resolve(key) {
|
|
1128
1178
|
return this._byKey.get(key);
|
|
1129
1179
|
}
|
|
1130
|
-
/** The
|
|
1131
|
-
|
|
1132
|
-
return this.
|
|
1180
|
+
/** The lib itself, for runtime code that knows what it needs (physics → rapier). */
|
|
1181
|
+
lib(id) {
|
|
1182
|
+
return this._byLibId.get(id);
|
|
1183
|
+
}
|
|
1184
|
+
/** Libs bundled with the runtime: no importer, no config, always available. */
|
|
1185
|
+
_registerBuiltins() {
|
|
1186
|
+
for (const descriptor of BUILTIN_LIB_DESCRIPTORS) {
|
|
1187
|
+
const module = BUILTIN_LIB_MODULES[descriptor.id];
|
|
1188
|
+
if (module === void 0) {
|
|
1189
|
+
console.error(`[addons] built-in lib "${descriptor.id}" has no module`);
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
this._register(descriptor.id, module);
|
|
1193
|
+
}
|
|
1133
1194
|
}
|
|
1134
|
-
async _loadLibs(
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
if (!libs[id]) continue;
|
|
1195
|
+
async _loadLibs(ids) {
|
|
1196
|
+
for (const id of ids) {
|
|
1197
|
+
const descriptor = LIBS[id];
|
|
1138
1198
|
const load = this._imports.libs?.[id];
|
|
1139
1199
|
if (!load) {
|
|
1140
|
-
console.error(`[addons] lib "${id}" is
|
|
1200
|
+
console.error(`[addons] lib "${id}" is needed but has no importer`);
|
|
1141
1201
|
continue;
|
|
1142
1202
|
}
|
|
1143
1203
|
try {
|
|
1144
|
-
|
|
1204
|
+
const module = await load();
|
|
1205
|
+
this._register(id, unwrapLib(id, await (descriptor.init?.(module) ?? module)));
|
|
1145
1206
|
} catch (e) {
|
|
1146
1207
|
console.error(`[addons] failed to load lib "${id}"`, e);
|
|
1147
1208
|
}
|
|
1148
1209
|
}
|
|
1149
1210
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
console.error("[addons] the world needs physics but has no importer for \"@dimforge/rapier3d-compat\"");
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
try {
|
|
1157
|
-
const rapier = await load();
|
|
1158
|
-
await rapier.init();
|
|
1159
|
-
this._rapier = rapier;
|
|
1160
|
-
} catch (e) {
|
|
1161
|
-
console.error("[addons] failed to load rapier", e);
|
|
1162
|
-
}
|
|
1211
|
+
_register(id, value) {
|
|
1212
|
+
this._byLibId.set(id, value);
|
|
1213
|
+
this._byKey.set(LIBS[id].requireKey, value);
|
|
1163
1214
|
}
|
|
1164
1215
|
async _loadExternals(externals) {
|
|
1165
1216
|
if (!externals) return;
|
|
@@ -1190,7 +1241,7 @@ var AddonsRuntime = class extends ContextModule {
|
|
|
1190
1241
|
for (const tag of this._scriptTags) tag.remove();
|
|
1191
1242
|
this._scriptTags.length = 0;
|
|
1192
1243
|
this._byKey.clear();
|
|
1193
|
-
this.
|
|
1244
|
+
this._byLibId.clear();
|
|
1194
1245
|
}
|
|
1195
1246
|
};
|
|
1196
1247
|
/** A package like nipplejs exposes its API as the default export — that is what
|
|
@@ -1200,6 +1251,67 @@ function unwrapLib(id, module) {
|
|
|
1200
1251
|
return module.default ?? module;
|
|
1201
1252
|
}
|
|
1202
1253
|
//#endregion
|
|
1254
|
+
//#region packages/world-runtime/src/world-stores.ts
|
|
1255
|
+
/**
|
|
1256
|
+
* Shared reactive key-value state of a world (`world.stores` in scripts) — the
|
|
1257
|
+
* state counterpart of the `world.events` bus. Stores are created lazily on
|
|
1258
|
+
* first touch; `undefined` means "no value", so `delete`/`clear` only reset
|
|
1259
|
+
* values and never invalidate a reference taken from `of()`.
|
|
1260
|
+
*/
|
|
1261
|
+
var WorldStores = class {
|
|
1262
|
+
_stores = /* @__PURE__ */ new Map();
|
|
1263
|
+
/** The store behind a key, created on first touch. */
|
|
1264
|
+
of(key) {
|
|
1265
|
+
let store = this._stores.get(key);
|
|
1266
|
+
if (!store) {
|
|
1267
|
+
store = atom(void 0);
|
|
1268
|
+
this._stores.set(key, store);
|
|
1269
|
+
}
|
|
1270
|
+
return store;
|
|
1271
|
+
}
|
|
1272
|
+
get(key) {
|
|
1273
|
+
return this.of(key).get();
|
|
1274
|
+
}
|
|
1275
|
+
set(key, value) {
|
|
1276
|
+
this.of(key).set(value);
|
|
1277
|
+
}
|
|
1278
|
+
/** Subscribe to a key; returns unsubscribe. Not called with the current value. */
|
|
1279
|
+
listen(key, listener) {
|
|
1280
|
+
return this.of(key).listen(listener);
|
|
1281
|
+
}
|
|
1282
|
+
/** Same as `listen`, but also fires immediately with the current value. */
|
|
1283
|
+
subscribe(key, listener) {
|
|
1284
|
+
return this.of(key).subscribe(listener);
|
|
1285
|
+
}
|
|
1286
|
+
/** A value has been written to the key and not deleted since. */
|
|
1287
|
+
has(key) {
|
|
1288
|
+
return this._stores.get(key)?.get() !== void 0;
|
|
1289
|
+
}
|
|
1290
|
+
/** Keys that currently hold a value. */
|
|
1291
|
+
keys() {
|
|
1292
|
+
const keys = [];
|
|
1293
|
+
for (const [key, store] of this._stores) if (store.get() !== void 0) keys.push(key);
|
|
1294
|
+
return keys;
|
|
1295
|
+
}
|
|
1296
|
+
/** Plain object with all current values — for debugging and saves. */
|
|
1297
|
+
snapshot() {
|
|
1298
|
+
const result = {};
|
|
1299
|
+
for (const [key, store] of this._stores) {
|
|
1300
|
+
const value = store.get();
|
|
1301
|
+
if (value !== void 0) result[key] = value;
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
/** Resets the key to `undefined`; listeners are notified. */
|
|
1306
|
+
delete(key) {
|
|
1307
|
+
this._stores.get(key)?.set(void 0);
|
|
1308
|
+
}
|
|
1309
|
+
/** Resets every key; listeners are notified. */
|
|
1310
|
+
clear() {
|
|
1311
|
+
for (const store of this._stores.values()) store.set(void 0);
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
//#endregion
|
|
1203
1315
|
//#region packages/world-runtime/src/WorldRuntime.ts
|
|
1204
1316
|
/**
|
|
1205
1317
|
* Owner of the world's runtime state: assets, script execution, ctx access.
|
|
@@ -1210,6 +1322,8 @@ var WorldRuntime = class {
|
|
|
1210
1322
|
options;
|
|
1211
1323
|
/** World lifecycle event bus (`world.events` in scripts). */
|
|
1212
1324
|
emitter = new EventEmitter();
|
|
1325
|
+
/** Shared reactive state of the world (`world.stores` in scripts). */
|
|
1326
|
+
stores = new WorldStores();
|
|
1213
1327
|
_addons;
|
|
1214
1328
|
_executedScripts = /* @__PURE__ */ new Map();
|
|
1215
1329
|
_builtins = /* @__PURE__ */ new Map();
|
|
@@ -1218,6 +1332,7 @@ var WorldRuntime = class {
|
|
|
1218
1332
|
/** Reverse index for `getUrl(resource)` — play mode resolves objects too. */
|
|
1219
1333
|
_pathByResource = /* @__PURE__ */ new Map();
|
|
1220
1334
|
_ctx = null;
|
|
1335
|
+
_starter = null;
|
|
1221
1336
|
_bloom = null;
|
|
1222
1337
|
_scriptContext = null;
|
|
1223
1338
|
constructor(definition, options) {
|
|
@@ -1238,18 +1353,17 @@ var WorldRuntime = class {
|
|
|
1238
1353
|
setContext(ctx) {
|
|
1239
1354
|
this._ctx = ctx;
|
|
1240
1355
|
}
|
|
1356
|
+
/** @internal Set by bootstrap — backs `world.addModules` (register before start). */
|
|
1357
|
+
setStarter(starter) {
|
|
1358
|
+
this._starter = starter;
|
|
1359
|
+
}
|
|
1241
1360
|
/** @internal World bloom, if enabled in the renderer config. */
|
|
1242
1361
|
setBloom(bloom) {
|
|
1243
1362
|
this._bloom = bloom;
|
|
1244
1363
|
}
|
|
1245
|
-
/** @internal
|
|
1364
|
+
/** @internal Libs (config + required by modules/behaviours) and externals. */
|
|
1246
1365
|
async loadAddons() {
|
|
1247
|
-
await this._addons.load(this.definition.addons ?? {},
|
|
1248
|
-
}
|
|
1249
|
-
/** Physics is on in the config, or a built-in behaviour on the scene needs it. */
|
|
1250
|
-
_usesPhysics() {
|
|
1251
|
-
if (normalizePhysics(this.definition.addons?.modules?.physics)) return true;
|
|
1252
|
-
return (this.definition.usedBuiltins ?? []).some((uuid) => getBuiltinBehaviour(uuid)?.requiredPatch);
|
|
1366
|
+
await this._addons.load(this.definition.addons ?? {}, this.definition.usedBuiltins ?? []);
|
|
1253
1367
|
}
|
|
1254
1368
|
/** @internal World resources by asset path — result of `loadAssets`. */
|
|
1255
1369
|
setResources(resources) {
|
|
@@ -1306,7 +1420,7 @@ var WorldRuntime = class {
|
|
|
1306
1420
|
const patch = getBuiltinBehaviour(uuid)?.requiredPatch;
|
|
1307
1421
|
if (!patch) continue;
|
|
1308
1422
|
try {
|
|
1309
|
-
await patch(starter);
|
|
1423
|
+
await patch.apply(starter);
|
|
1310
1424
|
} catch (error) {
|
|
1311
1425
|
console.error(`Required patch of "${uuid}" failed`, error);
|
|
1312
1426
|
}
|
|
@@ -1380,6 +1494,7 @@ var WorldRuntime = class {
|
|
|
1380
1494
|
this._styleElements.length = 0;
|
|
1381
1495
|
this._executedScripts.clear();
|
|
1382
1496
|
this.emitter.removeAllListeners();
|
|
1497
|
+
this.stores.clear();
|
|
1383
1498
|
}
|
|
1384
1499
|
/** Narrow bloom facade for scripts: mode is read-only, params are live
|
|
1385
1500
|
* (uniforms, can be animated per frame). `null` — bloom is disabled. */
|
|
@@ -1425,20 +1540,20 @@ var WorldRuntime = class {
|
|
|
1425
1540
|
},
|
|
1426
1541
|
events: this.emitter,
|
|
1427
1542
|
emitter: this.emitter,
|
|
1543
|
+
stores: this.stores,
|
|
1428
1544
|
rendering: { get bloom() {
|
|
1429
1545
|
return runtime.bloomApi;
|
|
1430
|
-
} }
|
|
1546
|
+
} },
|
|
1547
|
+
addModules: (modules) => {
|
|
1548
|
+
if (!runtime._starter) throw new Error("world.addModules is only available in the entry script (main.ts), before the world starts.");
|
|
1549
|
+
runtime._starter.addModules(modules);
|
|
1550
|
+
}
|
|
1431
1551
|
},
|
|
1552
|
+
modules: lazyProxy(() => runtime.ctx.modules),
|
|
1432
1553
|
require: this.require,
|
|
1433
1554
|
instantiate: this.instantiate,
|
|
1434
1555
|
getUrl: this.getUrl,
|
|
1435
|
-
|
|
1436
|
-
TSL,
|
|
1437
|
-
van,
|
|
1438
|
-
/** Physics engine global; null in worlds without physics. */
|
|
1439
|
-
get RAPIER() {
|
|
1440
|
-
return runtime.addons.rapier;
|
|
1441
|
-
}
|
|
1556
|
+
...BUILTIN_GLOBALS
|
|
1442
1557
|
};
|
|
1443
1558
|
return this._scriptContext;
|
|
1444
1559
|
}
|
|
@@ -1460,7 +1575,7 @@ function isAbsoluteUrl(url) {
|
|
|
1460
1575
|
}
|
|
1461
1576
|
//#endregion
|
|
1462
1577
|
//#region packages/world-runtime/src/version.gen.ts
|
|
1463
|
-
var RUNTIME_VERSION = "0.
|
|
1578
|
+
var RUNTIME_VERSION = "0.5.0";
|
|
1464
1579
|
//#endregion
|
|
1465
1580
|
//#region packages/world-runtime/src/version.ts
|
|
1466
1581
|
/**
|
|
@@ -1498,6 +1613,7 @@ async function bootstrapWorld(definition, options) {
|
|
|
1498
1613
|
runtime.reportProgress("Initializing 3D context");
|
|
1499
1614
|
const world = await createWorld(definition, runtime);
|
|
1500
1615
|
runtime.setContext(world.ctx);
|
|
1616
|
+
runtime.setStarter(world);
|
|
1501
1617
|
await runtime.applyRequiredPatches(world);
|
|
1502
1618
|
runtime.reportProgress("Applying scene settings");
|
|
1503
1619
|
definition.applySceneSettings?.(runtime, world.ctx.scene);
|
|
@@ -1990,4 +2106,4 @@ var TONE_MAPPING_NAMES = [
|
|
|
1990
2106
|
"Neutral"
|
|
1991
2107
|
];
|
|
1992
2108
|
//#endregion
|
|
1993
|
-
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, CHAT_INPUT_MODES, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId, OPTIONAL_MODULES, OPTIONAL_MODULE_IDS, OptionalModuleId, PHYSICS_ENGINES, RUNTIME_VERSION, SHADOW_MAP_TYPES, SceneBloom, SoundsModule, TONE_MAPPING_NAMES, TslNode, UNIFORM_DEFAULTS, UNIFORM_TYPES, UtilsModule, VideoSource, WORLD_FORMAT_VERSION, World, WorldEvent,
|
|
2109
|
+
export { AddonsRuntime, AssetLoaders, AudioSource, BLOOM_MODES, BUILTIN_BEHAVIOURS, BUILTIN_BEHAVIOUR_LIST, BUILTIN_LIB_DESCRIPTORS, CHAT_INPUT_MODES, DEFAULT_RESOURCE_IDS, DefaultResourceId, EXTERNAL_TYPES, EnvModule, FontSource, HtmlHudModule, InputSystemModule, LIBS, LIB_DESCRIPTORS, LIB_IDS, LOADING_MANAGER, LibId, OPTIONAL_LIB_DESCRIPTORS, OPTIONAL_LIB_IDS, OPTIONAL_MODULES, OPTIONAL_MODULE_IDS, OptionalModuleId, PHYSICS_ENGINES, RUNTIME_VERSION, SHADOW_MAP_TYPES, SceneBloom, SoundsModule, TONE_MAPPING_NAMES, TslNode, UNIFORM_DEFAULTS, UNIFORM_TYPES, UtilsModule, VideoSource, WORLD_FORMAT_VERSION, World, WorldEvent, WorldStores, applyRendererConfig, applyTextureDefaults, collectModelSubResources, collectRequiredLibs, configureAssetLoaders, createAudio, createFont, createTslNode, createUniformNode, createVideo, createWorldModules, defineWorld, enableSceneEmissiveMrt, fontFormatFromMime, getBuiltinBehaviour, getDefaultResource, getHalfheightRadiusScale, getOptionalModuleDescriptor, getRadiusScale, isDefaultResourceId, isMobileDevice, isUniformType, lazyProxy, libByRequireKey, loadGeometry, loadHdr, loadModel, loadTexture, normalizeChat, normalizePhysics, parseGeometryJson, parseHdr, parseModel, rapierPhysicsPatch, setColor, uniformType };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
//#region packages/world-runtime/src/addons/lib-registry.ts
|
|
2
|
+
/**
|
|
3
|
+
* Registry of the libraries scripts can `require`. One entry describes one
|
|
4
|
+
* library; the flags say how it gets into the world:
|
|
5
|
+
*
|
|
6
|
+
* - **built-in** (`builtin: true`) — the runtime already bundles it, so it is
|
|
7
|
+
* available in every world with no config and no importer. Some of them are
|
|
8
|
+
* additionally exposed as a bare global (`globalName`) — that is the only
|
|
9
|
+
* difference between `THREE` and, say, `nanostores`.
|
|
10
|
+
* - **optional** — enabled in `settings.addons.json → libs`, or pulled in by
|
|
11
|
+
* something that declares it in `requiredLibs` (physics → rapier). The
|
|
12
|
+
* importer comes from the runtime owner (`WorldImports`), never from the
|
|
13
|
+
* package itself — see `./optional-imports.ts`.
|
|
14
|
+
*
|
|
15
|
+
* `id` is the config key under `libs`, `requireKey` is what the user writes in
|
|
16
|
+
* `require(...)` (and the import specifier importers are built from). Single
|
|
17
|
+
* source for: the app's importer map, the `imports.libs` block of an exported
|
|
18
|
+
* world, the JSON schema and the editor's require typings.
|
|
19
|
+
*/
|
|
20
|
+
var LibId = /* @__PURE__ */ function(LibId) {
|
|
21
|
+
LibId["Three"] = "three";
|
|
22
|
+
LibId["Tsl"] = "tsl";
|
|
23
|
+
LibId["ThreeStart"] = "threeStart";
|
|
24
|
+
LibId["Van"] = "van";
|
|
25
|
+
LibId["Nanostores"] = "nanostores";
|
|
26
|
+
LibId["EventEmitter"] = "eventemitter3";
|
|
27
|
+
LibId["Howler"] = "howler";
|
|
28
|
+
LibId["Tween"] = "tween";
|
|
29
|
+
LibId["Anime"] = "anime";
|
|
30
|
+
LibId["Nipplejs"] = "nipplejs";
|
|
31
|
+
LibId["Stats"] = "stats";
|
|
32
|
+
LibId["Rapier"] = "rapier";
|
|
33
|
+
LibId["Css2d"] = "css2d";
|
|
34
|
+
LibId["Css3d"] = "css3d";
|
|
35
|
+
return LibId;
|
|
36
|
+
}({});
|
|
37
|
+
var LIBS = {
|
|
38
|
+
["three"]: {
|
|
39
|
+
id: "three",
|
|
40
|
+
requireKey: "three",
|
|
41
|
+
label: "three.js (WebGPU build)",
|
|
42
|
+
builtin: true,
|
|
43
|
+
globalName: "THREE"
|
|
44
|
+
},
|
|
45
|
+
["tsl"]: {
|
|
46
|
+
id: "tsl",
|
|
47
|
+
requireKey: "three/tsl",
|
|
48
|
+
packageName: "three",
|
|
49
|
+
label: "Three Shading Language",
|
|
50
|
+
builtin: true,
|
|
51
|
+
globalName: "TSL"
|
|
52
|
+
},
|
|
53
|
+
["threeStart"]: {
|
|
54
|
+
id: "threeStart",
|
|
55
|
+
requireKey: "three-start",
|
|
56
|
+
label: "three-start (engine core)",
|
|
57
|
+
builtin: true
|
|
58
|
+
},
|
|
59
|
+
["van"]: {
|
|
60
|
+
id: "van",
|
|
61
|
+
requireKey: "vanjs-core",
|
|
62
|
+
label: "VanJS (HTML UI)",
|
|
63
|
+
builtin: true,
|
|
64
|
+
globalName: "van",
|
|
65
|
+
defaultExport: true
|
|
66
|
+
},
|
|
67
|
+
["nanostores"]: {
|
|
68
|
+
id: "nanostores",
|
|
69
|
+
requireKey: "nanostores",
|
|
70
|
+
label: "nanostores (atoms behind world.stores)",
|
|
71
|
+
builtin: true
|
|
72
|
+
},
|
|
73
|
+
["eventemitter3"]: {
|
|
74
|
+
id: "eventemitter3",
|
|
75
|
+
requireKey: "eventemitter3",
|
|
76
|
+
label: "EventEmitter3",
|
|
77
|
+
builtin: true,
|
|
78
|
+
defaultExport: true
|
|
79
|
+
},
|
|
80
|
+
["howler"]: {
|
|
81
|
+
id: "howler",
|
|
82
|
+
requireKey: "howler",
|
|
83
|
+
label: "howler.js (audio)",
|
|
84
|
+
builtin: true
|
|
85
|
+
},
|
|
86
|
+
["tween"]: {
|
|
87
|
+
id: "tween",
|
|
88
|
+
requireKey: "@tweenjs/tween.js",
|
|
89
|
+
label: "Tween.js"
|
|
90
|
+
},
|
|
91
|
+
["anime"]: {
|
|
92
|
+
id: "anime",
|
|
93
|
+
requireKey: "animejs/animation",
|
|
94
|
+
packageName: "animejs",
|
|
95
|
+
label: "Anime.js (animate)"
|
|
96
|
+
},
|
|
97
|
+
["nipplejs"]: {
|
|
98
|
+
id: "nipplejs",
|
|
99
|
+
requireKey: "nipplejs",
|
|
100
|
+
label: "nipplejs (on-screen joystick)",
|
|
101
|
+
defaultExport: true
|
|
102
|
+
},
|
|
103
|
+
["stats"]: {
|
|
104
|
+
id: "stats",
|
|
105
|
+
requireKey: "stats.js",
|
|
106
|
+
label: "Stats.js (fps overlay)",
|
|
107
|
+
defaultExport: true
|
|
108
|
+
},
|
|
109
|
+
["rapier"]: {
|
|
110
|
+
id: "rapier",
|
|
111
|
+
requireKey: "@dimforge/rapier3d-compat",
|
|
112
|
+
label: "Rapier (physics engine)",
|
|
113
|
+
init: async (module) => {
|
|
114
|
+
await module.init();
|
|
115
|
+
return module;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
["css2d"]: {
|
|
119
|
+
id: "css2d",
|
|
120
|
+
requireKey: "three/addons/renderers/CSS2DRenderer.js",
|
|
121
|
+
packageName: "three",
|
|
122
|
+
label: "CSS2DRenderer (DOM labels at 3d positions)"
|
|
123
|
+
},
|
|
124
|
+
["css3d"]: {
|
|
125
|
+
id: "css3d",
|
|
126
|
+
requireKey: "three/addons/renderers/CSS3DRenderer.js",
|
|
127
|
+
packageName: "three",
|
|
128
|
+
label: "CSS3DRenderer (DOM elements in 3d)"
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var LIB_IDS = Object.values(LibId);
|
|
132
|
+
var LIB_DESCRIPTORS = LIB_IDS.map((id) => LIBS[id]);
|
|
133
|
+
/** Bundled with the runtime — available in every world. */
|
|
134
|
+
var BUILTIN_LIB_DESCRIPTORS = LIB_DESCRIPTORS.filter((lib) => lib.builtin);
|
|
135
|
+
/** Enabled in `settings.addons.json → libs` or required by a module/behaviour. */
|
|
136
|
+
var OPTIONAL_LIB_DESCRIPTORS = LIB_DESCRIPTORS.filter((lib) => !lib.builtin);
|
|
137
|
+
var OPTIONAL_LIB_IDS = OPTIONAL_LIB_DESCRIPTORS.map((lib) => lib.id);
|
|
138
|
+
function libByRequireKey(requireKey) {
|
|
139
|
+
return LIB_DESCRIPTORS.find((l) => l.requireKey === requireKey);
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { LibId as a, libByRequireKey as c, LIB_IDS as i, LIBS as n, OPTIONAL_LIB_DESCRIPTORS as o, LIB_DESCRIPTORS as r, OPTIONAL_LIB_IDS as s, BUILTIN_LIB_DESCRIPTORS as t };
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { ContextModule } from "three-start";
|
|
2
2
|
import type { HtmlModuleApi } from "./contracts/html.module-api";
|
|
3
3
|
export declare class HtmlHudModule extends ContextModule implements HtmlModuleApi {
|
|
4
|
-
readonly
|
|
4
|
+
readonly overlay: HTMLDivElement;
|
|
5
|
+
readonly underlay: HTMLDivElement;
|
|
5
6
|
readonly crosshair: HTMLDivElement;
|
|
6
7
|
constructor();
|
|
7
8
|
onAwake(): void;
|
|
9
|
+
/** Appends nodes over the canvas (the HUD). */
|
|
8
10
|
append(...nodes: Array<Node | string>): void;
|
|
11
|
+
/** Appends nodes under the canvas — visible only through a transparent canvas. */
|
|
12
|
+
appendUnderlay(...nodes: Array<Node | string>): void;
|
|
9
13
|
setCrosshair(innerHtml: string): void;
|
|
10
14
|
/** three-start ContextModule has no onDestroy; teardown is explicit. */
|
|
11
15
|
dispose(): void;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { LibId } from "../../addons/lib-registry";
|
|
2
2
|
/**
|
|
3
|
-
* Public API of the `addons` module —
|
|
4
|
-
* built-in
|
|
5
|
-
*
|
|
3
|
+
* Public API of the `addons` module — the world's libraries and external
|
|
4
|
+
* scripts: built-in ones, those enabled in `settings.addons.json` and those
|
|
5
|
+
* required by an enabled module or a behaviour on the scene.
|
|
6
6
|
*
|
|
7
7
|
* Scripts normally reach a lib by `require("<key>")`; this module is the same
|
|
8
|
-
* bag behind that call
|
|
8
|
+
* bag behind that call.
|
|
9
9
|
*/
|
|
10
10
|
export interface AddonsModuleApi {
|
|
11
|
-
/** Value by require key (a lib) or by accessor (an external);
|
|
11
|
+
/** Value by require key (a lib) or by accessor (an external); undefined if absent. */
|
|
12
12
|
resolve: (key: string) => unknown;
|
|
13
|
-
/** The
|
|
14
|
-
|
|
13
|
+
/** The lib by its registry id — for runtime code that knows what it needs. */
|
|
14
|
+
lib: <T>(id: LibId) => T | undefined;
|
|
15
15
|
}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public API of the `html` module — fullscreen
|
|
2
|
+
* Public API of the `html` module — two fullscreen DOM layers around the canvas:
|
|
3
|
+
* the HUD above it (`overlay`) and the underlay below it (`underlay`).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Both are `pointer-events: none` on purpose: they cover the whole screen and
|
|
6
|
+
* must not swallow clicks meant for the world. Anything interactive appended to
|
|
7
|
+
* the HUD has to set `pointer-events: auto` on itself; the underlay can never
|
|
8
|
+
* be clicked at all — the canvas is on top of it.
|
|
9
|
+
*
|
|
10
|
+
* The underlay is only visible through a transparent canvas, which needs all
|
|
11
|
+
* three: `renderer.alpha` in `settings.renderer.json`, an empty scene
|
|
12
|
+
* background (a background colour paints over everything), and bloom off —
|
|
13
|
+
* the bloom pass currently loses the scene alpha, see
|
|
14
|
+
* `/docs/potential-issues.md`.
|
|
7
15
|
*/
|
|
8
16
|
export interface HtmlModuleApi {
|
|
9
17
|
/** HUD container over the canvas; click-through (`pointer-events: none`). */
|
|
10
|
-
readonly
|
|
18
|
+
readonly overlay: HTMLDivElement;
|
|
19
|
+
/** Container under the canvas — for DOM that the world should render on top of. */
|
|
20
|
+
readonly underlay: HTMLDivElement;
|
|
11
21
|
readonly crosshair: HTMLDivElement;
|
|
12
|
-
/** Appends nodes
|
|
22
|
+
/** Appends nodes over the canvas — see the note above. */
|
|
13
23
|
append: (...nodes: Array<Node | string>) => void;
|
|
24
|
+
/** Appends nodes under the canvas — see the note above. */
|
|
25
|
+
appendUnderlay: (...nodes: Array<Node | string>) => void;
|
|
14
26
|
setCrosshair: (innerHtml: string) => void;
|
|
15
27
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextModule } from "three-start";
|
|
2
|
+
import type { LibId } from "../addons/lib-registry";
|
|
2
3
|
/**
|
|
3
4
|
* Keys of the pluggable modules — the same keys as in
|
|
4
5
|
* `settings.addons.json → modules`. Core modules (`addons`/`env`/`html`/`input`/
|
|
@@ -27,6 +28,8 @@ export interface OptionalModuleDescriptor<TConfig = unknown> {
|
|
|
27
28
|
defaultConfig: () => TConfig;
|
|
28
29
|
/** The module class is pulled here — a disabled module never reaches the bundle. */
|
|
29
30
|
create: (config: TConfig) => Promise<ContextModule>;
|
|
31
|
+
/** Libs the module cannot work without: enabling it loads them (physics → rapier). */
|
|
32
|
+
requiredLibs?: LibId[];
|
|
30
33
|
/**
|
|
31
34
|
* JSON Schema fragment for `modules.<id>`: the platform assembles the
|
|
32
35
|
* `settings.addons.json` schema from these, so module keys never drift from the
|
package/dist/utils/lazy.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object that forwards every access to `resolve()`. For script globals that are
|
|
3
|
+
* shortcuts into something created later (`modules` → `ctx.modules`): the script
|
|
4
|
+
* bag is destructured eagerly, so a plain reference or a getter would be read
|
|
5
|
+
* before the target exists.
|
|
6
|
+
*/
|
|
7
|
+
export declare function lazyProxy<T extends object>(resolve: () => T): T;
|
|
1
8
|
/** Lazy cached value: the factory is called once on the first `get()`. */
|
|
2
9
|
export declare function lazy<T>(factory: () => T): {
|
|
3
10
|
get: () => T;
|
package/dist/version.gen.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_VERSION = "0.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.5.0";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single reactive value of `world.stores`. Narrow facade over a nanostores
|
|
3
|
+
* atom: scripts see exactly these four methods.
|
|
4
|
+
*/
|
|
5
|
+
export interface WorldStore<T> {
|
|
6
|
+
get: () => T | undefined;
|
|
7
|
+
set: (value: T | undefined) => void;
|
|
8
|
+
/** Fires on every change; returns unsubscribe. */
|
|
9
|
+
listen: (listener: (value: T | undefined) => void) => () => void;
|
|
10
|
+
/** Same as `listen`, but also fires immediately with the current value. */
|
|
11
|
+
subscribe: (listener: (value: T | undefined) => void) => () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Shared reactive key-value state of a world (`world.stores` in scripts) — the
|
|
15
|
+
* state counterpart of the `world.events` bus. Stores are created lazily on
|
|
16
|
+
* first touch; `undefined` means "no value", so `delete`/`clear` only reset
|
|
17
|
+
* values and never invalidate a reference taken from `of()`.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WorldStores {
|
|
20
|
+
private readonly _stores;
|
|
21
|
+
/** The store behind a key, created on first touch. */
|
|
22
|
+
of<T = unknown>(key: string): WorldStore<T>;
|
|
23
|
+
get<T = unknown>(key: string): T | undefined;
|
|
24
|
+
set<T = unknown>(key: string, value: T | undefined): void;
|
|
25
|
+
/** Subscribe to a key; returns unsubscribe. Not called with the current value. */
|
|
26
|
+
listen<T = unknown>(key: string, listener: (value: T | undefined) => void): () => void;
|
|
27
|
+
/** Same as `listen`, but also fires immediately with the current value. */
|
|
28
|
+
subscribe<T = unknown>(key: string, listener: (value: T | undefined) => void): () => void;
|
|
29
|
+
/** A value has been written to the key and not deleted since. */
|
|
30
|
+
has(key: string): boolean;
|
|
31
|
+
/** Keys that currently hold a value. */
|
|
32
|
+
keys(): string[];
|
|
33
|
+
/** Plain object with all current values — for debugging and saves. */
|
|
34
|
+
snapshot(): Record<string, unknown>;
|
|
35
|
+
/** Resets the key to `undefined`; listeners are notified. */
|
|
36
|
+
delete(key: string): void;
|
|
37
|
+
/** Resets every key; listeners are notified. */
|
|
38
|
+
clear(): void;
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kvyverse/world-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Runtime for worlds exported from the Kvyverse platform for self-hosting",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "Vladislav Kruteniuk",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"animejs": "^4.5.0",
|
|
36
36
|
"eventemitter3": "^5.0.1",
|
|
37
37
|
"howler": "^2.2.4",
|
|
38
|
+
"nanostores": "^1.3.0",
|
|
38
39
|
"nipplejs": "^0.10.2",
|
|
39
40
|
"stats.js": "^0.17.0",
|
|
40
41
|
"three": "^0.184.0",
|