@kvyverse/world-runtime 0.1.0 → 0.4.2
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 +84 -59
- package/dist/{BallCollider-8ZrxReEd.js → BallCollider-3L5vOrcL.js} +2 -2
- package/dist/{CapsuleCollider-bynyf6Mc.js → CapsuleCollider-CBTsR-Ys.js} +2 -2
- package/dist/ChatModule-2tiWS_Yr.js +411 -0
- package/dist/{Collider-4_rCTHVA.js → Collider-CPUCG3RA.js} +2 -2
- package/dist/{ConeCollider-DtRZmgK8.js → ConeCollider-C3XU3Y9X.js} +2 -2
- package/dist/{ConvexMeshCollider-BSmDM4o3.js → ConvexMeshCollider-DzNV4HTO.js} +1 -1
- package/dist/{CuboidCollider-CJvq7--s.js → CuboidCollider-Dp1DZoFM.js} +1 -1
- package/dist/{CylinderCollider-CQPSh4tu.js → CylinderCollider-C0h_nhB5.js} +2 -2
- package/dist/{FpsCameraController-BE2EirRj.js → FpsCameraController-2GNWJM5D.js} +1 -1
- package/dist/RapierDebugRenderer-BQy_m-3C.js +73 -0
- package/dist/{RapierFpsPlayerController-VsNfQl6v.js → RapierFpsPlayerController-DthD8dw9.js} +5 -22
- package/dist/{RapierPhysics-C_3b7QOn.js → RapierPhysics-CU6X--c7.js} +34 -15
- package/dist/{Rigidbody-b7QNF4ev.js → Rigidbody-DLhN8ddv.js} +12 -1
- package/dist/World.d.ts +4 -2
- package/dist/WorldRuntime.d.ts +15 -5
- package/dist/addons/AddonsRuntime.d.ts +37 -0
- package/dist/addons/builtin-libs.d.ts +20 -0
- package/dist/addons/lib-registry.d.ts +60 -0
- package/dist/addons/required-libs.d.ts +9 -0
- package/dist/addons/types.d.ts +28 -0
- package/dist/behaviours/physics/BallCollider.d.ts +1 -1
- package/dist/behaviours/physics/CapsuleCollider.d.ts +1 -1
- package/dist/behaviours/physics/ConeCollider.d.ts +1 -1
- package/dist/behaviours/physics/ConvexMeshCollider.d.ts +1 -1
- package/dist/behaviours/physics/CuboidCollider.d.ts +1 -1
- package/dist/behaviours/physics/CylinderCollider.d.ts +1 -1
- package/dist/behaviours/physics/HalfspaceCollider.d.ts +1 -1
- package/dist/behaviours/physics/_required-patch.d.ts +10 -8
- package/dist/behaviours/registry.d.ts +9 -2
- package/dist/chat.module-descriptor-BTsep-Kq.js +107 -0
- package/dist/{get-scale-from-obj-QKv3Fig5.js → get-scale-from-obj-B7JlVPni.js} +1 -1
- package/dist/index.d.ts +26 -14
- package/dist/index.js +594 -202
- package/dist/lib-registry-D1VyVPBg.js +142 -0
- package/dist/modules/HtmlHudModule.d.ts +5 -1
- package/dist/modules/InputSystemModule.d.ts +14 -32
- package/dist/modules/chat/ChatDom.d.ts +100 -0
- package/dist/modules/chat/ChatModule.d.ts +36 -0
- package/dist/modules/chat/chat.module-descriptor.d.ts +43 -0
- package/dist/modules/contracts/addons.module-api.d.ts +15 -0
- package/dist/modules/contracts/chat.module-api.d.ts +52 -0
- package/dist/modules/contracts/controls.module-api.d.ts +58 -0
- package/dist/modules/contracts/html.module-api.d.ts +22 -2
- package/dist/modules/contracts/input.module-api.d.ts +11 -11
- package/dist/modules/contracts/module-events.d.ts +17 -0
- package/dist/modules/contracts/rapier.module-api.d.ts +13 -1
- package/dist/modules/controls/ControlsModule.d.ts +45 -0
- package/dist/modules/controls/KeyboardControlsDriver.d.ts +12 -0
- package/dist/modules/index.d.ts +30 -3
- package/dist/modules/optional-module-descriptor.d.ts +39 -0
- package/dist/modules/optional-modules.d.ts +11 -0
- package/dist/modules/physics/RapierDebugRenderer.d.ts +19 -0
- package/dist/{behaviours/physics/rapier-addon-core → modules/physics}/RapierPhysics.d.ts +13 -3
- package/dist/modules/physics/physics.module-descriptor.d.ts +16 -0
- package/dist/modules/registry.d.ts +7 -3
- package/dist/rendering/SceneBloom.d.ts +1 -1
- package/dist/rendering/renderer-apply.d.ts +1 -1
- package/dist/{settings → rendering}/types.d.ts +1 -12
- package/dist/types.d.ts +10 -3
- 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 +10 -1
- package/dist/RapierDebugRenderer-sURZDLP4.js +0 -55
- package/dist/behaviours/physics/rapier-addon-core/RapierDebugRenderer.d.ts +0 -17
- package/dist/modules/contracts/rapierDebug.module-api.d.ts +0 -5
- package/dist/rolldown-runtime-D7D4PA-g.js +0 -13
- package/dist/settings/AddonsRuntime.d.ts +0 -19
- package/dist/settings/lib-loaders.d.ts +0 -7
- package/dist/settings/lib-registry.d.ts +0 -24
- package/dist/settings/module-registry.d.ts +0 -25
- /package/dist/{settings → addons}/externals.d.ts +0 -0
- /package/dist/behaviours/physics/{rapier-addon-core/Collider.d.ts → Collider.d.ts} +0 -0
- /package/dist/behaviours/physics/{rapier-addon-core/get-scale-from-obj.d.ts → get-scale-from-obj.d.ts} +0 -0
|
@@ -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,38 +1,10 @@
|
|
|
1
1
|
import { ContextModule } from "three-start";
|
|
2
|
-
import EventEmitter from "eventemitter3";
|
|
3
2
|
import * as THREE from "three/webgpu";
|
|
4
3
|
import type { InputModuleApi } from "./contracts/input.module-api";
|
|
5
|
-
declare class PlayerInput extends EventEmitter<{
|
|
6
|
-
dirchanged: [forward: number, lateral: number];
|
|
7
|
-
forcechanged: [force: number];
|
|
8
|
-
jump: [];
|
|
9
|
-
}> {
|
|
10
|
-
private _forward;
|
|
11
|
-
private _lateral;
|
|
12
|
-
private _force;
|
|
13
|
-
/**
|
|
14
|
-
* @default 0
|
|
15
|
-
*/
|
|
16
|
-
get forward(): number;
|
|
17
|
-
/**
|
|
18
|
-
* @default 0
|
|
19
|
-
*/
|
|
20
|
-
get lateral(): number;
|
|
21
|
-
/**
|
|
22
|
-
* @default 1
|
|
23
|
-
*/
|
|
24
|
-
get force(): number;
|
|
25
|
-
setDir(forward: number, lateral: number): void;
|
|
26
|
-
setForce(force: number): void;
|
|
27
|
-
jump(): void;
|
|
28
|
-
/**
|
|
29
|
-
* 0 is keyboard, 1 is joystick
|
|
30
|
-
* @default 0
|
|
31
|
-
*/
|
|
32
|
-
mode: 0 | 1;
|
|
33
|
-
}
|
|
34
4
|
export declare class InputSystemModule extends ContextModule<{
|
|
35
5
|
keyschanged: [];
|
|
6
|
+
keydown: [event: KeyboardEvent];
|
|
7
|
+
keyup: [event: KeyboardEvent];
|
|
36
8
|
}> implements InputModuleApi {
|
|
37
9
|
private _dom;
|
|
38
10
|
private readonly _keys;
|
|
@@ -57,7 +29,15 @@ export declare class InputSystemModule extends ContextModule<{
|
|
|
57
29
|
get ctrlKey(): boolean;
|
|
58
30
|
get metaKey(): boolean;
|
|
59
31
|
isKeyDown: (key: string) => boolean;
|
|
60
|
-
|
|
32
|
+
private _captureKeyboard;
|
|
33
|
+
/**
|
|
34
|
+
* The game listens to the keyboard on the whole document, not just the canvas
|
|
35
|
+
* (otherwise any HTML over the world would swallow input). `false` hands the
|
|
36
|
+
* keyboard entirely to the UI: the game sees no keys and held ones are reset.
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
get captureKeyboard(): boolean;
|
|
40
|
+
set captureKeyboard(value: boolean);
|
|
61
41
|
onAwake(): void;
|
|
62
42
|
/** three-start `ContextModule` has no `onDestroy`; teardown is explicit. */
|
|
63
43
|
dispose(): void;
|
|
@@ -66,6 +46,9 @@ export declare class InputSystemModule extends ContextModule<{
|
|
|
66
46
|
private onDomResize;
|
|
67
47
|
private onFocus;
|
|
68
48
|
private onBlur;
|
|
49
|
+
private onDocumentFocusIn;
|
|
50
|
+
/** The key belongs to the game, not to the UI. */
|
|
51
|
+
private acceptsKey;
|
|
69
52
|
private onKeyDown;
|
|
70
53
|
private onKeyUp;
|
|
71
54
|
private onPointerEnter;
|
|
@@ -84,4 +67,3 @@ export declare class InputSystemModule extends ContextModule<{
|
|
|
84
67
|
private recalculatePointerNdc;
|
|
85
68
|
private updateDomBoundingClientRect;
|
|
86
69
|
}
|
|
87
|
-
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ChatMessage } from "../contracts/chat.module-api";
|
|
2
|
+
export interface ChatDomOptions {
|
|
3
|
+
/** The sent text (already non-empty and trimmed). */
|
|
4
|
+
onSubmit?: (text: string) => void;
|
|
5
|
+
onFocusChange?: (focused: boolean) => void;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
maxLength?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Keep focus in the input after sending. On mobile — yes (otherwise the
|
|
10
|
+
* keyboard closes after every message), on desktop usually not: control goes
|
|
11
|
+
* back to the game.
|
|
12
|
+
*/
|
|
13
|
+
keepFocusAfterSubmit?: boolean;
|
|
14
|
+
/** Where focus goes after sending or Escape (usually the canvas). */
|
|
15
|
+
returnFocusTo?: HTMLElement | null;
|
|
16
|
+
/**
|
|
17
|
+
* Hide the input while it is not focused (`toggle` mode): the chat stays out of
|
|
18
|
+
* the way and opens on Enter. `false` keeps the input always visible.
|
|
19
|
+
*/
|
|
20
|
+
hideWhenIdle?: boolean;
|
|
21
|
+
/** How many recent lines stay visible while the chat is closed. */
|
|
22
|
+
visibleLines?: number;
|
|
23
|
+
/** How many lines are visible while the input is focused. */
|
|
24
|
+
openedLines?: number;
|
|
25
|
+
/** Ms before a line fades out; `0` keeps lines lit. */
|
|
26
|
+
fadeAfter?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Chat DOM: the message feed plus the input field.
|
|
30
|
+
*
|
|
31
|
+
* Built to survive real devices: submit through a `<form>` (Enter/Go/Send on any
|
|
32
|
+
* mobile keyboard), an IME composition guard, and lifting above the on-screen
|
|
33
|
+
* keyboard via `visualViewport`. Details —
|
|
34
|
+
* `./README.md`.
|
|
35
|
+
*/
|
|
36
|
+
export declare class ChatDom {
|
|
37
|
+
readonly root: HTMLDivElement;
|
|
38
|
+
readonly feed: HTMLDivElement;
|
|
39
|
+
readonly form: HTMLFormElement;
|
|
40
|
+
readonly input: HTMLInputElement;
|
|
41
|
+
private readonly _options;
|
|
42
|
+
private _lines;
|
|
43
|
+
private _isComposing;
|
|
44
|
+
private _viewportRaf;
|
|
45
|
+
private _mounted;
|
|
46
|
+
constructor(options?: ChatDomOptions);
|
|
47
|
+
/** The focus policy is only known at runtime (mobile/desktop, canvas). */
|
|
48
|
+
setFocusPolicy(policy: Pick<ChatDomOptions, "keepFocusAfterSubmit" | "returnFocusTo">): void;
|
|
49
|
+
get isMounted(): boolean;
|
|
50
|
+
/** Whether to hide the input while unfocused (`toggle` mode). */
|
|
51
|
+
setHideWhenIdle(hide: boolean): void;
|
|
52
|
+
get isFocused(): boolean;
|
|
53
|
+
get value(): string;
|
|
54
|
+
set value(next: string);
|
|
55
|
+
/**
|
|
56
|
+
* Syncs the feed with the history: new lines are appended at the bottom, lines
|
|
57
|
+
* gone from the history are removed, extra ones are cut from the top. There is
|
|
58
|
+
* no scrolling — what does not fit is not shown.
|
|
59
|
+
*/
|
|
60
|
+
renderMessages(messages: readonly ChatMessage[]): void;
|
|
61
|
+
mount(container: HTMLElement): void;
|
|
62
|
+
unmount(): void;
|
|
63
|
+
focus(): void;
|
|
64
|
+
blur(): void;
|
|
65
|
+
clear(): void;
|
|
66
|
+
dispose(): void;
|
|
67
|
+
/** Keep in the DOM as much as an opened chat could need. */
|
|
68
|
+
private get _maxLines();
|
|
69
|
+
private _createLine;
|
|
70
|
+
private _removeLine;
|
|
71
|
+
/**
|
|
72
|
+
* While the input is focused more lines are shown, including expired ones (an
|
|
73
|
+
* opened chat is meant to be read). Once closed, every line lives by its own
|
|
74
|
+
* deadline again: expired ones fade at once, the rest sit out their remainder.
|
|
75
|
+
*/
|
|
76
|
+
private _syncLines;
|
|
77
|
+
private _setFaded;
|
|
78
|
+
private _bind;
|
|
79
|
+
private _unbind;
|
|
80
|
+
private readonly _onSubmit;
|
|
81
|
+
private readonly _onCompositionStart;
|
|
82
|
+
private readonly _onCompositionEnd;
|
|
83
|
+
/** Some Android keyboards send no Enter keydown, only `insertLineBreak`. */
|
|
84
|
+
private readonly _onBeforeInput;
|
|
85
|
+
private readonly _onKeyDown;
|
|
86
|
+
private readonly _stopPropagation;
|
|
87
|
+
private readonly _onFocus;
|
|
88
|
+
private readonly _onBlur;
|
|
89
|
+
/** In `toggle` mode a hidden input must catch neither clicks nor tab focus. */
|
|
90
|
+
private _syncVisibility;
|
|
91
|
+
private readonly _onViewportChange;
|
|
92
|
+
/**
|
|
93
|
+
* The keyboard changes the layout viewport neither on iOS nor (by default) on
|
|
94
|
+
* Android — anything fixed to the bottom ends up underneath it. We measure the
|
|
95
|
+
* overlap through `visualViewport` and lift the chat by that much.
|
|
96
|
+
*/
|
|
97
|
+
private _syncKeyboardInset;
|
|
98
|
+
/** Styles are inlined once per document: the package ships no separate css file. */
|
|
99
|
+
private static _ensureStyles;
|
|
100
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ContextModule } from "three-start";
|
|
2
|
+
import { ChatDom } from "./ChatDom";
|
|
3
|
+
import type { NormalizedChat } from "./chat.module-descriptor";
|
|
4
|
+
import type { ChatEvents, ChatMessage, ChatModuleApi } from "../contracts/chat.module-api";
|
|
5
|
+
/**
|
|
6
|
+
* The world chat as a pluggable ctx module: message history plus its own DOM
|
|
7
|
+
* input (`dom`), no React. Enabled from `settings.addons.json → modules`; it
|
|
8
|
+
* mounts its DOM itself. Details and mobile quirks — `./README.md`.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ChatModule extends ContextModule<ChatEvents> implements ChatModuleApi {
|
|
11
|
+
readonly dom: ChatDom;
|
|
12
|
+
private readonly _maxMessages;
|
|
13
|
+
private readonly _openKeys;
|
|
14
|
+
private _messages;
|
|
15
|
+
private _seq;
|
|
16
|
+
constructor(config: NormalizedChat);
|
|
17
|
+
get messages(): readonly ChatMessage[];
|
|
18
|
+
onAwake(): void;
|
|
19
|
+
/** Print a line into the feed as is. */
|
|
20
|
+
print(text: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Send a line the way a player does: commands are handled here, everything else
|
|
23
|
+
* goes out as `sent`. It prints nothing by itself — what to show is up to the world.
|
|
24
|
+
*/
|
|
25
|
+
send(text: string): void;
|
|
26
|
+
/** Redraw the whole feed — animation frames, tables, ASCII art. */
|
|
27
|
+
setMessages(texts: readonly string[]): void;
|
|
28
|
+
clear(): void;
|
|
29
|
+
private _emitChanged;
|
|
30
|
+
private _createMessage;
|
|
31
|
+
/** three-start ContextModule has no onDestroy; teardown is explicit. */
|
|
32
|
+
dispose(): void;
|
|
33
|
+
private readonly _onKeyDown;
|
|
34
|
+
private readonly _onMount;
|
|
35
|
+
private readonly _onUnmount;
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { OptionalModuleDescriptor } from "../optional-module-descriptor";
|
|
2
|
+
/**
|
|
3
|
+
* How the input behaves:
|
|
4
|
+
* - `always` — always visible (the classic game chat at the bottom);
|
|
5
|
+
* - `toggle` — hidden, opens on Enter and hides again on Escape or after sending.
|
|
6
|
+
*/
|
|
7
|
+
export type ChatInputMode = "always" | "toggle";
|
|
8
|
+
/** `modules.chat`: off | input mode | an object with options. */
|
|
9
|
+
export type ChatConfig = boolean | ChatInputMode | {
|
|
10
|
+
input?: ChatInputMode;
|
|
11
|
+
/** Max message length. */
|
|
12
|
+
maxLength?: number;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
/** How many messages the history keeps. */
|
|
15
|
+
maxMessages?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Keys that open the chat — `KeyboardEvent.code`, i.e. physical keys:
|
|
18
|
+
* the player's layout does not matter.
|
|
19
|
+
*/
|
|
20
|
+
openKeys?: string[];
|
|
21
|
+
/** How many recent lines stay visible while the chat is closed. */
|
|
22
|
+
visibleLines?: number;
|
|
23
|
+
/** How many lines are visible while the input is focused. */
|
|
24
|
+
openedLines?: number;
|
|
25
|
+
/** Ms before a line fades out; `0` keeps lines lit. */
|
|
26
|
+
fadeAfter?: number;
|
|
27
|
+
};
|
|
28
|
+
export interface NormalizedChat {
|
|
29
|
+
input: ChatInputMode;
|
|
30
|
+
maxLength: number;
|
|
31
|
+
placeholder: string;
|
|
32
|
+
maxMessages: number;
|
|
33
|
+
openKeys: readonly string[];
|
|
34
|
+
visibleLines: number;
|
|
35
|
+
openedLines: number;
|
|
36
|
+
fadeAfter: number;
|
|
37
|
+
}
|
|
38
|
+
/** `Slash` opens the chat with a "/" already typed — the Minecraft command gesture. */
|
|
39
|
+
export declare const CHAT_COMMAND_KEY = "Slash";
|
|
40
|
+
export declare const CHAT_INPUT_MODES: readonly ChatInputMode[];
|
|
41
|
+
/** Chat defaults live here: `true` = an always visible input with a limit of 80. */
|
|
42
|
+
export declare function normalizeChat(value: ChatConfig | undefined): NormalizedChat | null;
|
|
43
|
+
export declare const chatModuleDescriptor: OptionalModuleDescriptor<NormalizedChat>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LibId } from "../../addons/lib-registry";
|
|
2
|
+
/**
|
|
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
|
+
*
|
|
7
|
+
* Scripts normally reach a lib by `require("<key>")`; this module is the same
|
|
8
|
+
* bag behind that call.
|
|
9
|
+
*/
|
|
10
|
+
export interface AddonsModuleApi {
|
|
11
|
+
/** Value by require key (a lib) or by accessor (an external); undefined if absent. */
|
|
12
|
+
resolve: (key: string) => unknown;
|
|
13
|
+
/** The lib by its registry id — for runtime code that knows what it needs. */
|
|
14
|
+
lib: <T>(id: LibId) => T | undefined;
|
|
15
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ModuleEvents } from "./module-events";
|
|
2
|
+
/** A message in the chat history. */
|
|
3
|
+
export interface ChatMessage {
|
|
4
|
+
id: string;
|
|
5
|
+
text: string;
|
|
6
|
+
/** `Date.now()` when the message was added. */
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
export type ChatEvents = {
|
|
10
|
+
/** The feed changed — printed, replaced or cleared. Render your own view here. */
|
|
11
|
+
changed: [messages: ChatMessage[]];
|
|
12
|
+
/** A line was sent (by the player or `send()`); commands never reach here. */
|
|
13
|
+
sent: [text: string];
|
|
14
|
+
/** The input gained or lost focus. */
|
|
15
|
+
inputfocuschanged: [focused: boolean];
|
|
16
|
+
};
|
|
17
|
+
/** Chat DOM — for advanced cases: place, focus or read the input yourself. */
|
|
18
|
+
export interface ChatDomApi {
|
|
19
|
+
readonly root: HTMLDivElement;
|
|
20
|
+
/** Feed element — style it or place your own nodes next to the lines. */
|
|
21
|
+
readonly feed: HTMLDivElement;
|
|
22
|
+
readonly input: HTMLInputElement;
|
|
23
|
+
readonly isMounted: boolean;
|
|
24
|
+
readonly isFocused: boolean;
|
|
25
|
+
value: string;
|
|
26
|
+
mount: (container: HTMLElement) => void;
|
|
27
|
+
unmount: () => void;
|
|
28
|
+
focus: () => void;
|
|
29
|
+
blur: () => void;
|
|
30
|
+
clear: () => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Public API of the `chat` module — world chat: the feed + its own input.
|
|
34
|
+
*
|
|
35
|
+
* Sending and printing are separate on purpose: a sent line only fires `sent`,
|
|
36
|
+
* and nothing shows up until the world prints it.
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* chat.on("sent", (text) => chat.print(`Player: ${text}`));
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export interface ChatModuleApi extends ModuleEvents<ChatEvents> {
|
|
43
|
+
readonly messages: readonly ChatMessage[];
|
|
44
|
+
/** Print a line into the feed as is. */
|
|
45
|
+
print: (text: string) => void;
|
|
46
|
+
/** Send a line as the player would: runs commands, otherwise fires `sent`. */
|
|
47
|
+
send: (text: string) => void;
|
|
48
|
+
/** Replace the whole feed at once — for redraws (frames, tables, ASCII art). */
|
|
49
|
+
setMessages: (texts: readonly string[]) => void;
|
|
50
|
+
clear: () => void;
|
|
51
|
+
readonly dom: ChatDomApi;
|
|
52
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ModuleEvents } from "./module-events";
|
|
2
|
+
import type * as THREE from "three/webgpu";
|
|
3
|
+
/** Device kind a driver reports; any string works, these are the built-in ones. */
|
|
4
|
+
export type ControlsSource = "keyboard" | "touch" | "gamepad" | (string & {});
|
|
5
|
+
/** Frame state a driver fills in. Reused across frames — never store it. */
|
|
6
|
+
export interface ControlsState {
|
|
7
|
+
/** x — strafe, y — forward, -1..1. Write in place: `move.set(x, y)`. */
|
|
8
|
+
readonly move: THREE.Vector2;
|
|
9
|
+
/** Deflection: 1 is a normal walk, higher means a stronger stick tilt. */
|
|
10
|
+
force: number;
|
|
11
|
+
/** Run modifier — shift, a stick pushed past its ring, a UI button. */
|
|
12
|
+
run: boolean;
|
|
13
|
+
/** Held this frame; `controls` fires `jump` on the rising edge. */
|
|
14
|
+
jump: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* An input source for `controls`: keyboard, an on-screen stick, a gamepad, the
|
|
18
|
+
* network, a cutscene. Register with `controls.addDriver(...)` — `read` is then
|
|
19
|
+
* called once per frame, before behaviours update.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* controls.addDriver({ kind: "touch", read: (s) => { s.move.set(x, y); s.force = f; } });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface ControlsDriver {
|
|
26
|
+
/** Reported as `controls.source` while this driver is the one driving. */
|
|
27
|
+
readonly kind: ControlsSource;
|
|
28
|
+
read: (state: ControlsState) => void;
|
|
29
|
+
}
|
|
30
|
+
export type ControlsEvents = {
|
|
31
|
+
/** Movement vector changed (the same reused `move` instance). */
|
|
32
|
+
movechanged: [move: THREE.Vector2];
|
|
33
|
+
forcechanged: [force: number];
|
|
34
|
+
/** The driving device changed — the player switched keys ↔ stick. */
|
|
35
|
+
sourcechanged: [source: ControlsSource];
|
|
36
|
+
jump: [];
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Public API of the `controls` module — the player's movement intent, whatever
|
|
40
|
+
* the device is. Input sources register as drivers, character controllers read
|
|
41
|
+
* `move`/`force`/`run`; raw device state lives in the `input` module.
|
|
42
|
+
*/
|
|
43
|
+
export interface ControlsModuleApi extends ModuleEvents<ControlsEvents> {
|
|
44
|
+
/** x — strafe, y — forward. Reused vector: copy it before storing. */
|
|
45
|
+
readonly move: THREE.Vector2;
|
|
46
|
+
readonly force: number;
|
|
47
|
+
readonly run: boolean;
|
|
48
|
+
/** Kind of the driver currently driving. */
|
|
49
|
+
readonly source: ControlsSource;
|
|
50
|
+
readonly drivers: readonly ControlsDriver[];
|
|
51
|
+
/** Built-in WASD/arrows driver, registered on start; remove it to take over. */
|
|
52
|
+
readonly keyboard: ControlsDriver;
|
|
53
|
+
/** Registers a driver and returns the unsubscribe. */
|
|
54
|
+
addDriver: (driver: ControlsDriver) => () => void;
|
|
55
|
+
removeDriver: (driver: ControlsDriver) => void;
|
|
56
|
+
/** Fire a jump by hand — a UI button, a script. */
|
|
57
|
+
jump: () => void;
|
|
58
|
+
}
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
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`).
|
|
4
|
+
*
|
|
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`.
|
|
15
|
+
*/
|
|
2
16
|
export interface HtmlModuleApi {
|
|
3
|
-
|
|
17
|
+
/** HUD container over the canvas; click-through (`pointer-events: none`). */
|
|
18
|
+
readonly overlay: HTMLDivElement;
|
|
19
|
+
/** Container under the canvas — for DOM that the world should render on top of. */
|
|
20
|
+
readonly underlay: HTMLDivElement;
|
|
4
21
|
readonly crosshair: HTMLDivElement;
|
|
22
|
+
/** Appends nodes over the canvas — see the note above. */
|
|
5
23
|
append: (...nodes: Array<Node | string>) => void;
|
|
24
|
+
/** Appends nodes under the canvas — see the note above. */
|
|
25
|
+
appendUnderlay: (...nodes: Array<Node | string>) => void;
|
|
6
26
|
setCrosshair: (innerHtml: string) => void;
|
|
7
27
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ModuleEvents } from "./module-events";
|
|
1
2
|
import type * as THREE from "three/webgpu";
|
|
2
3
|
/**
|
|
3
4
|
* Public API of the input module, available in user scripts
|
|
@@ -7,7 +8,13 @@ import type * as THREE from "three/webgpu";
|
|
|
7
8
|
* if the class stops matching the contract, the project won't compile.
|
|
8
9
|
* This same file is fed into Monaco (see `monaco/monaco-lib-types.ts`).
|
|
9
10
|
*/
|
|
10
|
-
export
|
|
11
|
+
export type InputEvents = {
|
|
12
|
+
/** Set of pressed keys changed. */
|
|
13
|
+
keyschanged: [];
|
|
14
|
+
keydown: [event: KeyboardEvent];
|
|
15
|
+
keyup: [event: KeyboardEvent];
|
|
16
|
+
};
|
|
17
|
+
export interface InputModuleApi extends ModuleEvents<InputEvents> {
|
|
11
18
|
readonly pointerDeltaX: number;
|
|
12
19
|
readonly pointerDeltaY: number;
|
|
13
20
|
readonly isDragging: boolean;
|
|
@@ -18,14 +25,7 @@ export interface InputModuleApi {
|
|
|
18
25
|
readonly ctrlKey: boolean;
|
|
19
26
|
readonly metaKey: boolean;
|
|
20
27
|
isKeyDown: (key: string) => boolean;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export interface PlayerInputApi {
|
|
25
|
-
readonly forward: number;
|
|
26
|
-
readonly lateral: number;
|
|
27
|
-
readonly force: number;
|
|
28
|
-
setDir: (forward: number, lateral: number) => void;
|
|
29
|
-
setForce: (force: number) => void;
|
|
30
|
-
jump: () => void;
|
|
28
|
+
/** Game reads the keyboard; `false` hands it over to the world's own UI.
|
|
29
|
+
* @default true */
|
|
30
|
+
captureKeyboard: boolean;
|
|
31
31
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed events every module inherits from the engine.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* world.ctx.modules.chat.on("sent", (text) => { ... });
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* Not a module contract — a shared piece the `*.module-api.ts` files extend.
|
|
9
|
+
*/
|
|
10
|
+
export interface ModuleEvents<TEvents extends Record<string, unknown[]>> {
|
|
11
|
+
/** Subscribe. */
|
|
12
|
+
on: <TKey extends keyof TEvents & string>(event: TKey, fn: (...args: TEvents[TKey]) => void) => this;
|
|
13
|
+
/** Subscribe until the first call. */
|
|
14
|
+
once: <TKey extends keyof TEvents & string>(event: TKey, fn: (...args: TEvents[TKey]) => void) => this;
|
|
15
|
+
/** Unsubscribe; without `fn` removes every listener of the event. */
|
|
16
|
+
off: <TKey extends keyof TEvents & string>(event: TKey, fn?: (...args: TEvents[TKey]) => void) => this;
|
|
17
|
+
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
import type { ModuleEvents } from "./module-events";
|
|
1
2
|
import type RAPIER from "@dimforge/rapier3d-compat";
|
|
2
3
|
export type RapierJsModule = typeof RAPIER;
|
|
3
4
|
/** Public API of the `rapier` module — Rapier physics world, event queue, simulation step. */
|
|
4
|
-
export
|
|
5
|
+
export type RapierEvents = {
|
|
6
|
+
/** Right before the physics world steps. */
|
|
7
|
+
stepbefore: [];
|
|
8
|
+
/** Right after the step — transforms are already updated. */
|
|
9
|
+
stepafter: [];
|
|
10
|
+
};
|
|
11
|
+
export interface RapierModuleApi extends ModuleEvents<RapierEvents> {
|
|
5
12
|
readonly isRapierPhysics: true;
|
|
6
13
|
readonly api: RapierJsModule;
|
|
7
14
|
readonly world: RAPIER.World;
|
|
8
15
|
readonly eventQueue: RAPIER.EventQueue;
|
|
9
16
|
/** @default "vary" */
|
|
10
17
|
timeStep: number | "vary";
|
|
18
|
+
/** Collider debug rendering is on right now. */
|
|
19
|
+
readonly isDebugEnabled: boolean;
|
|
20
|
+
/** Turn collider debug rendering on; the renderer is fetched on demand. */
|
|
21
|
+
enableDebug: () => Promise<void>;
|
|
22
|
+
disableDebug: () => void;
|
|
11
23
|
}
|