@threenative/core 0.1.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/dist/audio-3vkjtiuo.d.ts +35 -0
- package/dist/game-DRPs3M7r.d.ts +269 -0
- package/dist/hot.d.ts +17 -0
- package/dist/hot.js +109 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +1514 -0
- package/dist/playtest.d.ts +11 -0
- package/dist/playtest.js +614 -0
- package/package.json +47 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AudioListener, Object3D, Audio, Vector3, PositionalAudio } from 'three';
|
|
2
|
+
|
|
3
|
+
interface AudioBusOptions {
|
|
4
|
+
readonly camera: Object3D;
|
|
5
|
+
readonly gestureTarget?: EventTarget;
|
|
6
|
+
readonly listener?: AudioListener;
|
|
7
|
+
readonly source?: () => EventTarget | undefined;
|
|
8
|
+
}
|
|
9
|
+
interface AudioPlayOptions {
|
|
10
|
+
readonly fade?: number;
|
|
11
|
+
readonly loop?: boolean;
|
|
12
|
+
readonly volume?: number;
|
|
13
|
+
}
|
|
14
|
+
interface AudioRuntimeSnapshot {
|
|
15
|
+
readonly queued: number;
|
|
16
|
+
readonly voices: number;
|
|
17
|
+
}
|
|
18
|
+
declare class AudioBus {
|
|
19
|
+
#private;
|
|
20
|
+
readonly listener: AudioListener;
|
|
21
|
+
constructor(options: AudioBusOptions);
|
|
22
|
+
get queued(): number;
|
|
23
|
+
get voices(): number;
|
|
24
|
+
setCamera(camera: Object3D): void;
|
|
25
|
+
reparent(camera: Object3D): void;
|
|
26
|
+
unlock(): Promise<void>;
|
|
27
|
+
play(buffer: AudioBuffer, options?: AudioPlayOptions): Audio;
|
|
28
|
+
playAt(buffer: AudioBuffer, source: Object3D | Vector3, options?: AudioPlayOptions): PositionalAudio;
|
|
29
|
+
music(buffer: AudioBuffer, options?: AudioPlayOptions): Audio;
|
|
30
|
+
stop(): void;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|
|
33
|
+
declare function audioRuntimeSnapshot(): AudioRuntimeSnapshot;
|
|
34
|
+
|
|
35
|
+
export { AudioBus as A, audioRuntimeSnapshot as a, type AudioBusOptions as b, type AudioPlayOptions as c };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Texture, Vector2, Object3D, Camera, Vector3, Scene as Scene$1 } from 'three';
|
|
2
|
+
import { StoreApi } from 'zustand/vanilla';
|
|
3
|
+
|
|
4
|
+
interface AssetLoaderOptions {
|
|
5
|
+
readonly basePath?: string;
|
|
6
|
+
readonly model?: (url: string) => Promise<unknown>;
|
|
7
|
+
readonly texture?: (url: string) => Promise<Texture>;
|
|
8
|
+
readonly audio?: (url: string) => Promise<AudioBuffer>;
|
|
9
|
+
}
|
|
10
|
+
interface AssetLoader {
|
|
11
|
+
model<T = unknown>(path: string): Promise<T>;
|
|
12
|
+
texture(path: string): Promise<Texture>;
|
|
13
|
+
audio(path: string): Promise<AudioBuffer>;
|
|
14
|
+
release(kind: "audio" | "model" | "texture", path: string): boolean;
|
|
15
|
+
clear(): void;
|
|
16
|
+
}
|
|
17
|
+
declare function createAssetLoader(options?: AssetLoaderOptions): AssetLoader;
|
|
18
|
+
|
|
19
|
+
interface InputAction {
|
|
20
|
+
readonly buttons?: readonly number[];
|
|
21
|
+
readonly down?: readonly string[];
|
|
22
|
+
readonly left?: readonly string[];
|
|
23
|
+
readonly pointer?: boolean;
|
|
24
|
+
readonly right?: readonly string[];
|
|
25
|
+
readonly up?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
type InputBindings = Record<string, InputAction>;
|
|
28
|
+
interface InputGamepad {
|
|
29
|
+
readonly axes: ArrayLike<number>;
|
|
30
|
+
readonly buttons: readonly {
|
|
31
|
+
readonly pressed: boolean;
|
|
32
|
+
}[];
|
|
33
|
+
}
|
|
34
|
+
type InputPlatformSource = () => readonly (InputGamepad | null)[];
|
|
35
|
+
interface RawInputState {
|
|
36
|
+
readonly keys: ReadonlySet<string>;
|
|
37
|
+
readonly pointer: {
|
|
38
|
+
buttons: number;
|
|
39
|
+
down: boolean;
|
|
40
|
+
readonly position: Vector2;
|
|
41
|
+
};
|
|
42
|
+
readonly gamepad: {
|
|
43
|
+
axes: readonly number[];
|
|
44
|
+
buttons: readonly boolean[];
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
declare class InputMap {
|
|
48
|
+
#private;
|
|
49
|
+
readonly raw: RawInputState;
|
|
50
|
+
constructor(bindings?: InputBindings, target?: EventTarget, pointerTarget?: EventTarget, source?: InputPlatformSource);
|
|
51
|
+
vector(name: string): Vector2;
|
|
52
|
+
pressed(name: string): boolean;
|
|
53
|
+
justPressed(name: string): boolean;
|
|
54
|
+
justReleased(name: string): boolean;
|
|
55
|
+
tick(): void;
|
|
56
|
+
clear(): void;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}
|
|
59
|
+
declare function input(bindings?: InputBindings, target?: EventTarget, pointerTarget?: EventTarget, source?: InputPlatformSource): InputMap;
|
|
60
|
+
|
|
61
|
+
interface Random {
|
|
62
|
+
(): number;
|
|
63
|
+
pick<T>(items: readonly T[]): T;
|
|
64
|
+
range(min: number, max: number): number;
|
|
65
|
+
state: number;
|
|
66
|
+
}
|
|
67
|
+
declare function createRandom(seed?: number): Random;
|
|
68
|
+
|
|
69
|
+
type RendererKind = "webgpu" | "webgl2";
|
|
70
|
+
interface RendererLike {
|
|
71
|
+
readonly domElement: HTMLCanvasElement;
|
|
72
|
+
readonly kind: RendererKind;
|
|
73
|
+
readonly raw: unknown;
|
|
74
|
+
compute(node: unknown): void;
|
|
75
|
+
render(scene: Object3D, camera: Camera): void;
|
|
76
|
+
setOutputNode(node: unknown): void;
|
|
77
|
+
setSize(width: number, height: number, updateStyle?: boolean): void;
|
|
78
|
+
dispose(): void;
|
|
79
|
+
}
|
|
80
|
+
interface RendererPlatformSource {
|
|
81
|
+
createCanvas(): HTMLCanvasElement;
|
|
82
|
+
hasWebGPU(): boolean;
|
|
83
|
+
observeResize(canvas: HTMLCanvasElement, resize: () => void): () => void;
|
|
84
|
+
readSize(canvas: HTMLCanvasElement): readonly [width: number, height: number];
|
|
85
|
+
}
|
|
86
|
+
interface RendererOptions {
|
|
87
|
+
canvas?: HTMLCanvasElement;
|
|
88
|
+
preferWebGPU?: boolean;
|
|
89
|
+
source?: RendererPlatformSource;
|
|
90
|
+
webgpuFactory?: (canvas: HTMLCanvasElement) => Promise<unknown> | unknown;
|
|
91
|
+
webgl2Factory?: (canvas: HTMLCanvasElement) => unknown;
|
|
92
|
+
}
|
|
93
|
+
declare function createRenderer(options?: RendererOptions): Promise<RendererLike>;
|
|
94
|
+
|
|
95
|
+
type EntitySnapshot = Record<string, Record<string, unknown> & {
|
|
96
|
+
tags?: string[];
|
|
97
|
+
}>;
|
|
98
|
+
interface Debuggable {
|
|
99
|
+
debug(): Record<string, unknown>;
|
|
100
|
+
}
|
|
101
|
+
declare function autoFields(entity: object): Record<string, unknown>;
|
|
102
|
+
declare class Registry {
|
|
103
|
+
#private;
|
|
104
|
+
add<T extends object>(name: string, entity: T): T;
|
|
105
|
+
get<T extends object = object>(name: string): T | undefined;
|
|
106
|
+
remove(name: string): void;
|
|
107
|
+
clear(): void;
|
|
108
|
+
snapshot(): EntitySnapshot;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type ScheduleHandle = (() => void) & {
|
|
112
|
+
cancel(): void;
|
|
113
|
+
readonly active: boolean;
|
|
114
|
+
};
|
|
115
|
+
type TweenProperties<T extends object> = {
|
|
116
|
+
[K in keyof T]?: number;
|
|
117
|
+
};
|
|
118
|
+
declare class Scheduler {
|
|
119
|
+
#private;
|
|
120
|
+
get size(): number;
|
|
121
|
+
after(delay: number, callback: () => void): ScheduleHandle;
|
|
122
|
+
every(callback: (dt: number) => void): ScheduleHandle;
|
|
123
|
+
tween<T extends object>(target: T, properties: TweenProperties<T>, duration: number): Promise<void>;
|
|
124
|
+
tick(dt: number): void;
|
|
125
|
+
clear(): void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type StatePatch<T extends Record<string, unknown>> = Partial<T> | ((state: T) => Partial<T>);
|
|
129
|
+
type GameStore<T extends Record<string, unknown>> = StoreApi<T> & {
|
|
130
|
+
set(patch: StatePatch<T>): void;
|
|
131
|
+
flush(): void;
|
|
132
|
+
start(): void;
|
|
133
|
+
stop(): void;
|
|
134
|
+
};
|
|
135
|
+
declare function createGameStore<T extends Record<string, unknown>>(initial: T, intervalMs?: number): GameStore<T>;
|
|
136
|
+
|
|
137
|
+
interface ViewportSize {
|
|
138
|
+
readonly aspect: number;
|
|
139
|
+
readonly height: number;
|
|
140
|
+
readonly width: number;
|
|
141
|
+
}
|
|
142
|
+
interface ViewportOptions {
|
|
143
|
+
readonly camera: Camera;
|
|
144
|
+
readonly renderer: RendererLike;
|
|
145
|
+
readonly source?: ViewportPlatformSource;
|
|
146
|
+
}
|
|
147
|
+
type ViewportResizeHandler = (size: ViewportSize) => void;
|
|
148
|
+
interface ViewportPlatformSource {
|
|
149
|
+
observeResize(canvas: HTMLCanvasElement, resize: () => void): () => void;
|
|
150
|
+
readSize(canvas: HTMLCanvasElement): ViewportSize;
|
|
151
|
+
}
|
|
152
|
+
declare class Viewport {
|
|
153
|
+
#private;
|
|
154
|
+
readonly camera: Camera;
|
|
155
|
+
readonly renderer: RendererLike;
|
|
156
|
+
constructor(options: ViewportOptions);
|
|
157
|
+
get size(): ViewportSize;
|
|
158
|
+
projectPosition(screen: Vector2, z?: number): Vector3;
|
|
159
|
+
unprojectPosition(world: Vector3): Vector2;
|
|
160
|
+
onResize(handler: ViewportResizeHandler): () => void;
|
|
161
|
+
resize(): void;
|
|
162
|
+
dispose(): void;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
declare abstract class Scene<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> {
|
|
166
|
+
static readonly initialState: Record<string, unknown> | undefined;
|
|
167
|
+
load(_ctx: Ctx<TState, TPhysics>): void | Promise<void>;
|
|
168
|
+
enter(_ctx: Ctx<TState, TPhysics>): SceneEnterResult<TState, TPhysics>;
|
|
169
|
+
exit(_ctx: Ctx<TState, TPhysics>): void;
|
|
170
|
+
update(_ctx: Ctx<TState, TPhysics>, _dt: number): void;
|
|
171
|
+
render(_ctx: Ctx<TState, TPhysics>): void;
|
|
172
|
+
}
|
|
173
|
+
type SceneConstructor<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> = new () => Scene<TState, TPhysics>;
|
|
174
|
+
type SceneFrame<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> = (ctx: Ctx<TState, TPhysics>, dt: number) => void;
|
|
175
|
+
type SceneEnterResult<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> = // biome-ignore lint/suspicious/noConfusingVoidType: void preserves existing Scene.enter overrides.
|
|
176
|
+
void | SceneFrame<TState, TPhysics>;
|
|
177
|
+
interface Ctx<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> {
|
|
178
|
+
readonly fps: number;
|
|
179
|
+
readonly renderer: RendererLike;
|
|
180
|
+
readonly viewport: Viewport;
|
|
181
|
+
readonly scene: Scene$1;
|
|
182
|
+
readonly camera: Camera;
|
|
183
|
+
readonly entities: Registry;
|
|
184
|
+
readonly add: (object: Object3D) => Object3D;
|
|
185
|
+
readonly input: InputMap;
|
|
186
|
+
readonly assets: AssetLoader;
|
|
187
|
+
readonly after: (delay: number, callback: () => void) => ScheduleHandle;
|
|
188
|
+
readonly every: (callback: (dt: number) => void) => ScheduleHandle;
|
|
189
|
+
readonly state: GameStore<TState>;
|
|
190
|
+
readonly tween: <T extends object>(target: T, properties: {
|
|
191
|
+
[K in keyof T]?: number;
|
|
192
|
+
}, duration: number) => Promise<void>;
|
|
193
|
+
readonly random: Random;
|
|
194
|
+
readonly goto: (name: string) => Promise<void>;
|
|
195
|
+
physics: TPhysics;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
type PluginCleanup = () => void;
|
|
199
|
+
interface GamePluginRuntime {
|
|
200
|
+
fixedStep(ticks: number): number;
|
|
201
|
+
readonly random?: Pick<Random, "state">;
|
|
202
|
+
rapier?: string | null;
|
|
203
|
+
readonly seed: number | null;
|
|
204
|
+
readonly step: number;
|
|
205
|
+
}
|
|
206
|
+
interface GamePlatformSource {
|
|
207
|
+
readonly devToolsHost?: Record<string, unknown>;
|
|
208
|
+
readonly input: NonNullable<ConstructorParameters<typeof InputMap>[3]>;
|
|
209
|
+
readonly inputTarget?: EventTarget;
|
|
210
|
+
readonly renderer: NonNullable<RendererOptions["source"]>;
|
|
211
|
+
readonly viewport: NonNullable<ViewportOptions["source"]>;
|
|
212
|
+
mountCanvas(canvas: HTMLCanvasElement, container?: HTMLElement): void;
|
|
213
|
+
unmountCanvas(canvas: HTMLCanvasElement): void;
|
|
214
|
+
}
|
|
215
|
+
type GamePluginFunction<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> = (ctx: Ctx<TState, TPhysics>) => undefined | PluginCleanup;
|
|
216
|
+
interface GamePluginHooks<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> {
|
|
217
|
+
setup?(ctx: Ctx<TState, TPhysics>, runtime?: GamePluginRuntime): undefined | PluginCleanup | Promise<undefined | PluginCleanup>;
|
|
218
|
+
beforeUpdate?(ctx: Ctx<TState, TPhysics>, dt: number): void;
|
|
219
|
+
update?(ctx: Ctx<TState, TPhysics>, dt: number): void;
|
|
220
|
+
sceneExit?(ctx: Ctx<TState, TPhysics>): void;
|
|
221
|
+
dispose?(ctx: Ctx<TState, TPhysics>): void;
|
|
222
|
+
}
|
|
223
|
+
type GamePlugin<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> = GamePluginFunction<TState, TPhysics> | GamePluginHooks<TState, TPhysics>;
|
|
224
|
+
interface GameConfig<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> {
|
|
225
|
+
readonly assets?: AssetLoaderOptions;
|
|
226
|
+
readonly camera?: CameraConfig;
|
|
227
|
+
readonly canvas?: HTMLCanvasElement;
|
|
228
|
+
readonly container?: HTMLElement;
|
|
229
|
+
readonly input?: InputBindings;
|
|
230
|
+
readonly initialState?: TState;
|
|
231
|
+
readonly inputTarget?: EventTarget;
|
|
232
|
+
readonly maxSteps?: number;
|
|
233
|
+
readonly platform?: GamePlatformSource;
|
|
234
|
+
readonly plugins?: readonly GamePlugin<TState, TPhysics>[];
|
|
235
|
+
readonly render?: Pick<RendererOptions, "preferWebGPU">;
|
|
236
|
+
readonly renderer?: RendererOptions;
|
|
237
|
+
readonly seed?: number;
|
|
238
|
+
readonly scenes: Record<string, SceneConstructor<TState, TPhysics>>;
|
|
239
|
+
readonly step?: number;
|
|
240
|
+
readonly start: string;
|
|
241
|
+
readonly stateFlushMs?: number;
|
|
242
|
+
}
|
|
243
|
+
interface PerspectiveCameraConfig {
|
|
244
|
+
readonly projection: "perspective";
|
|
245
|
+
readonly fov?: number;
|
|
246
|
+
readonly near?: number;
|
|
247
|
+
readonly far?: number;
|
|
248
|
+
}
|
|
249
|
+
interface OrthogonalCameraConfig {
|
|
250
|
+
readonly projection: "orthogonal";
|
|
251
|
+
readonly size: number;
|
|
252
|
+
readonly near?: number;
|
|
253
|
+
readonly far?: number;
|
|
254
|
+
}
|
|
255
|
+
type CameraConfig = PerspectiveCameraConfig | OrthogonalCameraConfig;
|
|
256
|
+
interface Game<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined> {
|
|
257
|
+
readonly ctx: Ctx<TState, TPhysics> | undefined;
|
|
258
|
+
readonly scene: Scene<TState, TPhysics> | undefined;
|
|
259
|
+
readonly state: GameStore<TState>;
|
|
260
|
+
/** Rebuilds the requested scene from the game's declared initial state. */
|
|
261
|
+
goto(name: string): Promise<void>;
|
|
262
|
+
start(): Promise<void>;
|
|
263
|
+
pause(): void;
|
|
264
|
+
resume(): void;
|
|
265
|
+
stop(): void;
|
|
266
|
+
}
|
|
267
|
+
declare function defineGame<TState extends Record<string, unknown>, TPhysics = undefined>(config: GameConfig<TState, TPhysics>): Game<TState, TPhysics>;
|
|
268
|
+
|
|
269
|
+
export { type AssetLoader as A, autoFields as B, type CameraConfig as C, type Debuggable as D, type EntitySnapshot as E, createAssetLoader as F, type Game as G, createGameStore as H, type InputAction as I, createRandom as J, createRenderer as K, defineGame as L, input as M, type OrthogonalCameraConfig as O, type PerspectiveCameraConfig as P, type RendererLike as R, Scene as S, Viewport as V, type GamePluginRuntime as a, type GamePluginHooks as b, type AssetLoaderOptions as c, type Ctx as d, type GameConfig as e, type GamePlatformSource as f, type GamePlugin as g, type GamePluginFunction as h, type GameStore as i, type InputBindings as j, InputMap as k, type PluginCleanup as l, type Random as m, type RawInputState as n, Registry as o, type RendererKind as p, type RendererOptions as q, type SceneConstructor as r, type SceneEnterResult as s, type SceneFrame as t, type ScheduleHandle as u, Scheduler as v, type StatePatch as w, type ViewportOptions as x, type ViewportResizeHandler as y, type ViewportSize as z };
|
package/dist/hot.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { a as audioRuntimeSnapshot } from './audio-3vkjtiuo.js';
|
|
2
|
+
import { G as Game } from './game-DRPs3M7r.js';
|
|
3
|
+
import 'three';
|
|
4
|
+
import 'zustand/vanilla';
|
|
5
|
+
|
|
6
|
+
interface HotDiagnostics {
|
|
7
|
+
readonly reloads: number;
|
|
8
|
+
readonly entities: number;
|
|
9
|
+
readonly sceneObjects: number;
|
|
10
|
+
readonly canvases: number;
|
|
11
|
+
readonly audio: ReturnType<typeof audioRuntimeSnapshot>;
|
|
12
|
+
readonly physics: number | null;
|
|
13
|
+
}
|
|
14
|
+
declare function assertPortableState(state: unknown): void;
|
|
15
|
+
declare function acceptHotUpdate<TState extends Record<string, unknown>, TPhysics>(game: Game<TState, TPhysics>, hot: ImportMeta["hot"]): void;
|
|
16
|
+
|
|
17
|
+
export { type HotDiagnostics, acceptHotUpdate, assertPortableState };
|
package/dist/hot.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import 'three';
|
|
2
|
+
|
|
3
|
+
// src/audio.ts
|
|
4
|
+
var buses = /* @__PURE__ */ new Set();
|
|
5
|
+
function audioRuntimeSnapshot() {
|
|
6
|
+
let queued = 0;
|
|
7
|
+
let voices = 0;
|
|
8
|
+
for (const bus of buses) {
|
|
9
|
+
queued += bus.queued;
|
|
10
|
+
voices += bus.voices;
|
|
11
|
+
}
|
|
12
|
+
return { queued, voices };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/hot.ts
|
|
16
|
+
var isDev = import.meta.env?.DEV === true;
|
|
17
|
+
function assertPortableState(state) {
|
|
18
|
+
visitPortable(state, "state", /* @__PURE__ */ new Set());
|
|
19
|
+
}
|
|
20
|
+
function acceptHotUpdate(game, hot) {
|
|
21
|
+
if (hot === void 0) return;
|
|
22
|
+
hot.accept();
|
|
23
|
+
const carried = hot.data.threenative;
|
|
24
|
+
const reloads = carried?.reloads ?? 0;
|
|
25
|
+
if (carried !== void 0) restoreState(game, carried.state);
|
|
26
|
+
if (isDev && typeof window !== "undefined") {
|
|
27
|
+
const host = window;
|
|
28
|
+
host.__THREENATIVE__ = {
|
|
29
|
+
...host.__THREENATIVE__,
|
|
30
|
+
hot: () => diagnostics(game, reloads),
|
|
31
|
+
snapshot: host.__THREENATIVE__?.snapshot ?? (() => ({}))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
let disposed = false;
|
|
35
|
+
hot.dispose((data) => {
|
|
36
|
+
if (disposed) return;
|
|
37
|
+
disposed = true;
|
|
38
|
+
try {
|
|
39
|
+
game.state.flush();
|
|
40
|
+
const state = game.state.getState();
|
|
41
|
+
assertPortableState(state);
|
|
42
|
+
data.threenative = { reloads: reloads + 1, state };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
data.threenative = void 0;
|
|
45
|
+
hot.invalidate(error instanceof Error ? error.message : String(error));
|
|
46
|
+
throw error;
|
|
47
|
+
} finally {
|
|
48
|
+
game.stop();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function restoreState(game, carried) {
|
|
53
|
+
assertPortableState(carried);
|
|
54
|
+
const defaults = game.state.getState();
|
|
55
|
+
const state = { ...defaults };
|
|
56
|
+
const dropped = Object.keys(carried).filter((key) => !(key in defaults));
|
|
57
|
+
const added = Object.keys(defaults).filter((key) => !(key in carried));
|
|
58
|
+
for (const key of Object.keys(defaults)) {
|
|
59
|
+
if (key in carried) state[key] = carried[key];
|
|
60
|
+
}
|
|
61
|
+
game.state.setState(state);
|
|
62
|
+
if (dropped.length > 0 || added.length > 0)
|
|
63
|
+
console.info(
|
|
64
|
+
`ThreeNative hot reload state shape: dropped [${dropped.join(", ")}]; added [${added.join(", ")}]`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
function diagnostics(game, reloads) {
|
|
68
|
+
const ctx = game.ctx;
|
|
69
|
+
let sceneObjects = 0;
|
|
70
|
+
ctx?.scene.traverse(() => {
|
|
71
|
+
sceneObjects += 1;
|
|
72
|
+
});
|
|
73
|
+
const physics = ctx?.physics;
|
|
74
|
+
return {
|
|
75
|
+
reloads,
|
|
76
|
+
entities: ctx === void 0 ? 0 : Object.keys(ctx.entities.snapshot()).length,
|
|
77
|
+
sceneObjects,
|
|
78
|
+
canvases: typeof document === "undefined" ? 0 : document.querySelectorAll("canvas").length,
|
|
79
|
+
audio: audioRuntimeSnapshot(),
|
|
80
|
+
physics: typeof physics?.numBodies === "function" ? physics.numBodies() : null
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function visitPortable(value, path, ancestors) {
|
|
84
|
+
if (value === null) return;
|
|
85
|
+
if (typeof value === "string" || typeof value === "boolean") return;
|
|
86
|
+
if (typeof value !== "object") {
|
|
87
|
+
if (typeof value === "number" && Number.isFinite(value)) return;
|
|
88
|
+
if (typeof value === "number")
|
|
89
|
+
throw new Error(`Hot reload state ${path} must contain only finite numbers.`);
|
|
90
|
+
throw new Error(`Hot reload state ${path} must contain only JSON-shaped values.`);
|
|
91
|
+
}
|
|
92
|
+
if (ancestors.has(value)) throw new Error(`Hot reload state ${path} contains a cycle.`);
|
|
93
|
+
if (Array.isArray(value)) {
|
|
94
|
+
ancestors.add(value);
|
|
95
|
+
for (let index = 0; index < value.length; index += 1)
|
|
96
|
+
visitPortable(value[index], `${path}[${index}]`, ancestors);
|
|
97
|
+
ancestors.delete(value);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const prototype = Object.getPrototypeOf(value);
|
|
101
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
102
|
+
throw new Error(`Hot reload state ${path} must contain only plain objects.`);
|
|
103
|
+
ancestors.add(value);
|
|
104
|
+
for (const key of Object.keys(value))
|
|
105
|
+
visitPortable(value[key], `${path}.${key}`, ancestors);
|
|
106
|
+
ancestors.delete(value);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { acceptHotUpdate, assertPortableState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export { A as AudioBus, b as AudioBusOptions, c as AudioPlayOptions } from './audio-3vkjtiuo.js';
|
|
2
|
+
import { R as RendererLike, a as GamePluginRuntime, b as GamePluginHooks } from './game-DRPs3M7r.js';
|
|
3
|
+
export { A as AssetLoader, c as AssetLoaderOptions, C as CameraConfig, d as Ctx, D as Debuggable, E as EntitySnapshot, G as Game, e as GameConfig, f as GamePlatformSource, g as GamePlugin, h as GamePluginFunction, i as GameStore, I as InputAction, j as InputBindings, k as InputMap, O as OrthogonalCameraConfig, P as PerspectiveCameraConfig, l as PluginCleanup, m as Random, n as RawInputState, o as Registry, p as RendererKind, q as RendererOptions, S as Scene, r as SceneConstructor, s as SceneEnterResult, t as SceneFrame, u as ScheduleHandle, v as Scheduler, w as StatePatch, V as Viewport, x as ViewportOptions, y as ViewportResizeHandler, z as ViewportSize, B as autoFields, F as createAssetLoader, H as createGameStore, J as createRandom, K as createRenderer, L as defineGame, M as input } from './game-DRPs3M7r.js';
|
|
4
|
+
import { AnimationMixer, AnimationClip, Object3D, Sprite } from 'three';
|
|
5
|
+
import { StorageBufferNode, SpriteNodeMaterial, ComputeNode } from 'three/webgpu';
|
|
6
|
+
import { IReplayRecording } from '@threenative/playtest';
|
|
7
|
+
import 'zustand/vanilla';
|
|
8
|
+
|
|
9
|
+
interface AnimationPlayerOptions {
|
|
10
|
+
readonly clips: readonly AnimationClip[];
|
|
11
|
+
readonly root: Object3D;
|
|
12
|
+
}
|
|
13
|
+
interface AnimationPlayOptions {
|
|
14
|
+
readonly fade?: number;
|
|
15
|
+
}
|
|
16
|
+
declare class AnimationPlayer {
|
|
17
|
+
#private;
|
|
18
|
+
readonly mixer: AnimationMixer;
|
|
19
|
+
constructor(options: AnimationPlayerOptions);
|
|
20
|
+
get current(): string | undefined;
|
|
21
|
+
get advancedFrames(): number;
|
|
22
|
+
play(name: string, options?: AnimationPlayOptions): void;
|
|
23
|
+
update(dt: number): void;
|
|
24
|
+
stop(): void;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface FixedStepLoopOptions {
|
|
29
|
+
readonly step?: number;
|
|
30
|
+
readonly maxSteps?: number;
|
|
31
|
+
readonly onUpdate: (dt: number) => void;
|
|
32
|
+
readonly onRender?: () => void;
|
|
33
|
+
readonly requestFrame?: (callback: (time: number) => void) => number;
|
|
34
|
+
readonly cancelFrame?: (handle: number) => void;
|
|
35
|
+
}
|
|
36
|
+
declare class FixedStepLoop {
|
|
37
|
+
#private;
|
|
38
|
+
readonly step: number;
|
|
39
|
+
readonly maxSteps: number;
|
|
40
|
+
constructor(options: FixedStepLoopOptions);
|
|
41
|
+
get running(): boolean;
|
|
42
|
+
get fps(): number;
|
|
43
|
+
start(now?: number): void;
|
|
44
|
+
stop(): void;
|
|
45
|
+
stepFrame(now: number): number;
|
|
46
|
+
advance(ticks: number): number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface GPUParticles3DBuffers {
|
|
50
|
+
readonly positions: StorageBufferNode<"vec3">;
|
|
51
|
+
readonly velocities: StorageBufferNode<"vec3">;
|
|
52
|
+
}
|
|
53
|
+
interface GPUParticles3DOptions {
|
|
54
|
+
readonly amount: number;
|
|
55
|
+
readonly material: SpriteNodeMaterial;
|
|
56
|
+
readonly start: (buffers: GPUParticles3DBuffers) => ComputeNode;
|
|
57
|
+
readonly process: (buffers: GPUParticles3DBuffers) => ComputeNode;
|
|
58
|
+
}
|
|
59
|
+
declare class GPUParticles3D extends Sprite {
|
|
60
|
+
#private;
|
|
61
|
+
readonly amount: number;
|
|
62
|
+
readonly buffers: GPUParticles3DBuffers;
|
|
63
|
+
emitting: boolean;
|
|
64
|
+
constructor(options: GPUParticles3DOptions);
|
|
65
|
+
get released(): boolean;
|
|
66
|
+
attachRenderer(renderer: RendererLike): void;
|
|
67
|
+
process(renderer?: RendererLike | undefined): void;
|
|
68
|
+
restart(): void;
|
|
69
|
+
detach(): void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type Recording = IReplayRecording;
|
|
73
|
+
type ReplayPublic = {
|
|
74
|
+
readonly recording: Recording | undefined;
|
|
75
|
+
readonly runId: symbol;
|
|
76
|
+
};
|
|
77
|
+
declare function replay<TState extends Record<string, unknown> = Record<string, unknown>, TPhysics = undefined>(): GamePluginHooks<TState, TPhysics> & ReplayPublic;
|
|
78
|
+
declare function createReplayDriver(recording: Recording, target: EventTarget, pointerTarget?: EventTarget): ((runtime: GamePluginRuntime) => number) & {
|
|
79
|
+
prepare: (runtime: GamePluginRuntime) => void;
|
|
80
|
+
runId: symbol;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
declare const version = "0.1.0";
|
|
84
|
+
|
|
85
|
+
export { type AnimationPlayOptions, AnimationPlayer, type AnimationPlayerOptions, FixedStepLoop, type FixedStepLoopOptions, GPUParticles3D, type GPUParticles3DBuffers, type GPUParticles3DOptions, GamePluginHooks, GamePluginRuntime, type Recording, RendererLike, createReplayDriver, replay, version };
|