@moku-labs/game 0.0.1 → 0.0.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 +424 -37
- package/dist/assets.d.mts +161 -0
- package/dist/assets.mjs +4729 -0
- package/dist/component-DGg5DqKK.mjs +44 -0
- package/dist/control.d.mts +162 -0
- package/dist/control.mjs +758 -0
- package/dist/define-sFoO3y6X.d.mts +263 -0
- package/dist/headless-CvamCUcR.mjs +186 -0
- package/dist/headless-KaSWcd0s.d.mts +181 -0
- package/dist/index.d.mts +1204 -50
- package/dist/index.mjs +20978 -2010
- package/dist/inspect.d.mts +109 -0
- package/dist/inspect.mjs +519 -0
- package/dist/jsx-dev-runtime.d.mts +2 -0
- package/dist/jsx-dev-runtime.mjs +2 -0
- package/dist/jsx-runtime.d.mts +2 -0
- package/dist/jsx-runtime.mjs +2 -0
- package/dist/memory-CvgdnsQO.mjs +259 -0
- package/dist/registry-DlpRCibU.mjs +384 -0
- package/dist/runtime-DRlwxkIv.mjs +182 -0
- package/dist/runtime-DiOTkDZz.d.mts +77 -0
- package/dist/session-DmAxY6Ll.mjs +145 -0
- package/dist/testing.d.mts +23 -111
- package/dist/testing.mjs +12 -289
- package/dist/types-BxkNNYul.d.mts +2840 -0
- package/dist/types-CTPS9GBu.d.mts +742 -0
- package/dist/types-DD-QrG_z.d.mts +588 -0
- package/dist/types-DWILGrPn.d.mts +622 -0
- package/dist/types-DYLnSgMI.d.mts +570 -0
- package/dist/types-JNc_UQBo.d.mts +2896 -0
- package/dist/types-yg_ywtT-.d.mts +1859 -0
- package/dist/visual-BDUHSRvf.mjs +734 -0
- package/package.json +26 -4
- package/dist/registry-DWV5C0Mf.mjs +0 -666
- package/dist/types-BfsmUzLC.d.mts +0 -1908
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
import { T as Descriptor, k as Api$2, lt as Require, t as Api$1, x as NodeInfo } from "./types-JNc_UQBo.mjs";
|
|
2
|
+
import { r as PixiTexture, t as Api$3 } from "./types-BxkNNYul.mjs";
|
|
3
|
+
import { Log } from "@moku-labs/common/browser";
|
|
4
|
+
import { PluginCtx } from "@moku-labs/core";
|
|
5
|
+
|
|
6
|
+
//#region src/plugins/assets/types.d.ts
|
|
7
|
+
declare namespace types_d_exports {
|
|
8
|
+
export { Api, AssetKind, AssetsCtx, AssetsIo, AtlasFrame, BundleMap, BundleRecord, BundleSpec, BundleUsage, Config, CreateTextureOptions, DecodedImage, DefineBundles, Deps, Events, FetchResponse, FlowRest, FontAsset, FontPage, Inflight, KernelSlice, LoadBundles, LoadReason, LoadResult, LoadedAssets, LoadedFont, Manifest, ManifestBundle, ManifestFile, NineBorders, NineSlice, PreloadQueue, State, Texture, Tier, Usage };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* When a bundle is loaded. `boot` and `core` stay for the whole session, `scene` and `feature`
|
|
12
|
+
* come and go with the graph, `lazy` is never preloaded.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const tier: Tier = "scene";
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
type Tier = "boot" | "core" | "scene" | "feature" | "lazy";
|
|
20
|
+
/**
|
|
21
|
+
* What one file of a bundle is. A `.fnt` with its `.png` pages is one `font`, an `.mp3` is
|
|
22
|
+
* `audio`, everything else is a `texture`. A file of an older manifest that names no kind is a
|
|
23
|
+
* texture.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const kind: AssetKind = "font";
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
type AssetKind = "texture" | "font" | "audio";
|
|
31
|
+
/**
|
|
32
|
+
* A GPU texture. `assets` owns its lifetime and never imports Pixi: `renderer` makes and destroys
|
|
33
|
+
* it, this plugin only says when.
|
|
34
|
+
*/
|
|
35
|
+
type Texture = PixiTexture;
|
|
36
|
+
/**
|
|
37
|
+
* A decoded image, ready for the upload. `createImageBitmap` produces the first shape.
|
|
38
|
+
*/
|
|
39
|
+
type DecodedImage = ImageBitmap | HTMLImageElement;
|
|
40
|
+
/**
|
|
41
|
+
* Nine-slice borders in pixels, in the order left, top, right, bottom.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const borders: NineBorders = [48, 48, 48, 48];
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
type NineBorders = readonly [number, number, number, number];
|
|
49
|
+
/**
|
|
50
|
+
* Options of `io.createTexture`. Same shape as `renderer.sync.textures.create`.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const options: CreateTextureOptions = { nine: [48, 48, 48, 48] };
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
type CreateTextureOptions = {
|
|
58
|
+
nine?: NineBorders;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The part of a fetch response the plugin reads: the manifest is `json`, a texture is a `blob`,
|
|
62
|
+
* a font file is `text` and an audio file stays the raw `arrayBuffer`. The global `Response`
|
|
63
|
+
* fits it.
|
|
64
|
+
*/
|
|
65
|
+
type FetchResponse = {
|
|
66
|
+
ok: boolean;
|
|
67
|
+
status: number;
|
|
68
|
+
json(): Promise<unknown>;
|
|
69
|
+
blob(): Promise<Blob>;
|
|
70
|
+
text(): Promise<string>;
|
|
71
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The I/O seam: everything that leaves the plugin. `undefined` in the config means the browser
|
|
75
|
+
* pair plus `renderer.sync.textures`; a test passes a fake and nothing touches the network or a GPU.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* // A unit test serves two files from memory and counts the textures it handed out.
|
|
80
|
+
* const io: AssetsIo = {
|
|
81
|
+
* fetch: async () => ({
|
|
82
|
+
* ok: true,
|
|
83
|
+
* status: 200,
|
|
84
|
+
* json: async () => ({}),
|
|
85
|
+
* blob: async () => blob,
|
|
86
|
+
* text: async () => 'info face="body"',
|
|
87
|
+
* arrayBuffer: async () => new ArrayBuffer(8)
|
|
88
|
+
* }),
|
|
89
|
+
* decode: async () => bitmap,
|
|
90
|
+
* createTexture: () => ({ id: "t1" }) as unknown as Texture,
|
|
91
|
+
* destroyTexture: texture => destroyed.push(texture)
|
|
92
|
+
* };
|
|
93
|
+
*
|
|
94
|
+
* createApp({ plugins: [...screen], pluginConfigs: { assets: { io, manifest } } });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
type AssetsIo = {
|
|
98
|
+
/**
|
|
99
|
+
* Fetches one URL: the manifest, or a file of a bundle.
|
|
100
|
+
*
|
|
101
|
+
* @param url - Absolute or root-relative URL.
|
|
102
|
+
* @param init - Carries the abort signal of the running load.
|
|
103
|
+
* @param init.signal - Aborts the request when the load is cancelled.
|
|
104
|
+
* @returns The response.
|
|
105
|
+
*/
|
|
106
|
+
fetch(url: string, init: {
|
|
107
|
+
signal: AbortSignal;
|
|
108
|
+
}): Promise<FetchResponse>;
|
|
109
|
+
/**
|
|
110
|
+
* Decodes one image file.
|
|
111
|
+
*
|
|
112
|
+
* @param blob - The bytes of the file.
|
|
113
|
+
* @returns The decoded image.
|
|
114
|
+
*/
|
|
115
|
+
decode(blob: Blob): Promise<DecodedImage>;
|
|
116
|
+
/**
|
|
117
|
+
* Uploads one decoded image to the GPU.
|
|
118
|
+
*
|
|
119
|
+
* @param image - The decoded image.
|
|
120
|
+
* @param options - `nine` becomes the default nine-slice borders of the texture.
|
|
121
|
+
* @returns The new texture.
|
|
122
|
+
*/
|
|
123
|
+
createTexture(image: DecodedImage, options?: CreateTextureOptions): Texture;
|
|
124
|
+
/**
|
|
125
|
+
* Frees one texture and its source.
|
|
126
|
+
*
|
|
127
|
+
* @param texture - The texture to free.
|
|
128
|
+
*/
|
|
129
|
+
destroyTexture(texture: Texture): void;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Nine-slice metadata of one file, as the scanner writes it. Always four numbers: the tags
|
|
133
|
+
* `{nine=N}`, `{nine=H,V}` and `{nine=L,T,R,B}` all land here.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* const nine: NineSlice = { left: 48, top: 48, right: 48, bottom: 48 };
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
type NineSlice = {
|
|
141
|
+
left: number;
|
|
142
|
+
top: number;
|
|
143
|
+
right: number;
|
|
144
|
+
bottom: number;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Reserved atlas placement. V2 never writes it and refuses to load a file that carries it.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* const frame: AtlasFrame = { page: "ui-0.png", x: 0, y: 0, width: 128, height: 128 };
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
type AtlasFrame = {
|
|
155
|
+
page: string;
|
|
156
|
+
x: number;
|
|
157
|
+
y: number;
|
|
158
|
+
width: number;
|
|
159
|
+
height: number;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* One page image of a font. A page has no key of its own: it belongs to the `.fnt` file that
|
|
163
|
+
* names it, and is loaded and freed with it.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* const page: FontPage = {
|
|
168
|
+
* path: "features/ui/assets/body_0.png",
|
|
169
|
+
* width: 256,
|
|
170
|
+
* height: 128,
|
|
171
|
+
* mb: 0.125
|
|
172
|
+
* };
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
type FontPage = {
|
|
176
|
+
path: string;
|
|
177
|
+
width: number;
|
|
178
|
+
height: number;
|
|
179
|
+
mb: number;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* One file of a bundle. `mb` is what it costs: `width × height × 4` bytes for a texture, the sum
|
|
183
|
+
* of the pages for a font, the size of the file for audio. `kind` is absent for a texture, which
|
|
184
|
+
* is what every manifest written before fonts and audio carries.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* const file: ManifestFile = {
|
|
189
|
+
* key: "ui.panel",
|
|
190
|
+
* path: "features/ui/assets/panel{nine=48}.png",
|
|
191
|
+
* width: 256,
|
|
192
|
+
* height: 128,
|
|
193
|
+
* mb: 0.125,
|
|
194
|
+
* nine: { left: 48, top: 48, right: 48, bottom: 48 }
|
|
195
|
+
* };
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
type ManifestFile = {
|
|
199
|
+
key: string;
|
|
200
|
+
path: string;
|
|
201
|
+
kind?: AssetKind;
|
|
202
|
+
width: number;
|
|
203
|
+
height: number;
|
|
204
|
+
mb: number;
|
|
205
|
+
pages?: readonly FontPage[];
|
|
206
|
+
nine?: NineSlice;
|
|
207
|
+
atlas?: AtlasFrame;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* One bundle of the manifest: which feature owns it, when it loads and what it costs.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* const bundle: ManifestBundle = { feature: "ui", tier: "core", mb: 0.125, files: [] };
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
type ManifestBundle = {
|
|
218
|
+
feature: string;
|
|
219
|
+
tier: Tier;
|
|
220
|
+
mb: number;
|
|
221
|
+
files: readonly ManifestFile[];
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* The manifest: the contract between the scanner, this plugin and the editor later. Bundles are
|
|
225
|
+
* sorted by name and files by key, so two scans of the same tree give the same bytes.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* const manifest: Manifest = {
|
|
230
|
+
* version: 1,
|
|
231
|
+
* bundles: { ui: { feature: "ui", tier: "core", mb: 0, files: [] } }
|
|
232
|
+
* };
|
|
233
|
+
*
|
|
234
|
+
* createApp({ plugins: [...screen], pluginConfigs: { assets: { manifest } } });
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
type Manifest = {
|
|
238
|
+
version: 1;
|
|
239
|
+
bundles: Readonly<Record<string, ManifestBundle>>;
|
|
240
|
+
};
|
|
241
|
+
/**
|
|
242
|
+
* What a feature declares about one of its bundles. `files` are globs relative to the feature's
|
|
243
|
+
* `assets/`; a bundle with `files` takes those files out of the feature's default bundle.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* const spec: BundleSpec = { tier: "lazy", files: ["chains/*.png"] };
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
type BundleSpec = {
|
|
251
|
+
tier: Tier;
|
|
252
|
+
files?: readonly string[];
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* What `defineBundles` returns: plain data the scanner and the plugin both read.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```ts
|
|
259
|
+
* const bundles: BundleMap = {
|
|
260
|
+
* kind: "bundles",
|
|
261
|
+
* map: { board: { tier: "scene" }, "board.chains": { tier: "lazy", files: ["chains/*.png"] } }
|
|
262
|
+
* };
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
type BundleMap<Key extends string = string> = {
|
|
266
|
+
kind: "bundles";
|
|
267
|
+
map: Partial<Record<Key, BundleSpec>>;
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* `defineBundles` bound to the bundle keys of one game. `defineGame` returns it, so a key that is
|
|
271
|
+
* not in `BundleKey` does not compile.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* const defineGameBundles: DefineBundles<"board" | "board.chains"> = defineBundles;
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
type DefineBundles<Key extends string> = (map: Partial<Record<Key, BundleSpec>>) => BundleMap<Key>;
|
|
279
|
+
/**
|
|
280
|
+
* The `load` descriptor helper bound to the bundle keys of one game.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* const loadGameBundle: LoadBundles<"board" | "board.chains"> = load;
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
type LoadBundles<Key extends string> = (bundle: Key | readonly Key[]) => Descriptor;
|
|
288
|
+
/**
|
|
289
|
+
* What the `load` effect resolves with, so a loading node can write its own progress.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* const result: LoadResult = { loaded: ["board.chains"], mb: 1.25 };
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
type LoadResult = {
|
|
297
|
+
loaded: string[];
|
|
298
|
+
mb: number;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Why a bundle was loaded. It is the reason of the caller that started the load; a later waiter
|
|
302
|
+
* changes nothing.
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* const reason: LoadReason = "enter";
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
type LoadReason = "boot" | "enter" | "request" | "preload";
|
|
310
|
+
/**
|
|
311
|
+
* The running load of one bundle. Every caller is a waiter; the last one to leave aborts it.
|
|
312
|
+
* `promise` never rejects: what broke waits in `error`, so a load nobody joined any more cannot
|
|
313
|
+
* become an unhandled rejection.
|
|
314
|
+
*/
|
|
315
|
+
type Inflight = {
|
|
316
|
+
promise: Promise<void>;
|
|
317
|
+
controller: AbortController;
|
|
318
|
+
waiters: number;
|
|
319
|
+
error: unknown;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* A loaded font: the `.fnt` file as text and the page textures it names. `text` installs the
|
|
323
|
+
* first page in the renderer; every page is destroyed with the bundle.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* const font: FontAsset = { fnt: 'info face="body" size=32', texture };
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
type FontAsset = {
|
|
331
|
+
fnt: string;
|
|
332
|
+
texture: Texture;
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* What the plugin keeps for a loaded font: the public pair plus every page, because a font of
|
|
336
|
+
* two pages owns two textures.
|
|
337
|
+
*/
|
|
338
|
+
type LoadedFont = FontAsset & {
|
|
339
|
+
pages: readonly Texture[];
|
|
340
|
+
};
|
|
341
|
+
/**
|
|
342
|
+
* What one bundle brought, by asset key: textures, fonts with their pages, and the undecoded
|
|
343
|
+
* bytes of the audio files. A running load fills the same three maps before they are published.
|
|
344
|
+
*/
|
|
345
|
+
type LoadedAssets = {
|
|
346
|
+
textures: Map<string, Texture>;
|
|
347
|
+
fonts: Map<string, LoadedFont>;
|
|
348
|
+
audio: Map<string, ArrayBuffer>;
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* What the plugin knows about one bundle. `lastUsed` is the value of `useCounter` at the last
|
|
352
|
+
* touch: a counter, never a clock.
|
|
353
|
+
*/
|
|
354
|
+
type BundleRecord = LoadedAssets & {
|
|
355
|
+
status: "idle" | "loading" | "loaded";
|
|
356
|
+
inflight: Inflight | undefined;
|
|
357
|
+
lastUsed: number;
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* The background preload. A new rest node replaces it and aborts the old controller.
|
|
361
|
+
*/
|
|
362
|
+
type PreloadQueue = {
|
|
363
|
+
bundles: string[];
|
|
364
|
+
controller: AbortController;
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* assets plugin config.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```ts
|
|
371
|
+
* createApp({
|
|
372
|
+
* plugins: [...screen, boardFeature],
|
|
373
|
+
* pluginConfigs: { assets: { manifest: "/assets/manifest.json", textureBudgetMb: 192 } }
|
|
374
|
+
* });
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
type Config = {
|
|
378
|
+
/** URL of `manifest.json`, or the parsed manifest itself (tests, headless). `undefined`: an empty manifest. */manifest: string | Manifest | undefined; /** Texture memory budget in MB. */
|
|
379
|
+
textureBudgetMb: number; /** How many edges from a rest node the background preload looks ahead. `0` turns preload off. */
|
|
380
|
+
preloadDepth: number; /** Prefix of every file URL. The CDN seam. `undefined`: the folder of the manifest URL, or `"/"`. */
|
|
381
|
+
baseUrl: string | undefined; /** I/O seam. `undefined`: the browser pair plus `renderer.sync.textures`, or headless. */
|
|
382
|
+
io: AssetsIo | undefined;
|
|
383
|
+
};
|
|
384
|
+
/**
|
|
385
|
+
* assets plugin state.
|
|
386
|
+
*/
|
|
387
|
+
type State = {
|
|
388
|
+
/** `undefined` means headless: manifest only, no fetches, no textures. */io: AssetsIo | undefined; /** Empty until `onStart` read it. */
|
|
389
|
+
manifest: Manifest; /** Asset key to the bundle that carries it. */
|
|
390
|
+
bundleOfKey: Map<string, string>; /** Scene id to its bundle, read from the `scenes` key of `flow.features`. */
|
|
391
|
+
bundleOfScene: Map<string, string>; /** Flow id to the feature that owns it, read from the `flows` key of `flow.features`. */
|
|
392
|
+
featureOfFlow: Map<string, string>;
|
|
393
|
+
records: Map<string, BundleRecord>; /** Bundles of the node entered last. They are never unloaded. */
|
|
394
|
+
pinned: Set<string>; /** The bundle of the scene entered last. A node without `scene` keeps it. */
|
|
395
|
+
sceneBundle: string | undefined;
|
|
396
|
+
queue: PreloadQueue | undefined;
|
|
397
|
+
current: NodeInfo | undefined;
|
|
398
|
+
useCounter: number; /** Keys already reported missing, so one key warns and starts its background load once. */
|
|
399
|
+
warned: Set<string>; /** `onEnter`, the `load` handler and the texture provider. */
|
|
400
|
+
removers: Array<() => void>;
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* What `usage()` reports about one loaded bundle.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* const entry: BundleUsage = { name: "board", tier: "scene", mb: 3.5, lastUsed: 12 };
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
type BundleUsage = {
|
|
411
|
+
name: string;
|
|
412
|
+
tier: Tier;
|
|
413
|
+
mb: number;
|
|
414
|
+
lastUsed: number;
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* What `usage()` reports: the numbers a dev overlay and the tests read.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```ts
|
|
421
|
+
* const report: Usage = { textureMb: 3.5, budgetMb: 192, bundles: [] };
|
|
422
|
+
* ```
|
|
423
|
+
*/
|
|
424
|
+
type Usage = {
|
|
425
|
+
textureMb: number;
|
|
426
|
+
budgetMb: number;
|
|
427
|
+
bundles: readonly BundleUsage[];
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
* assets plugin events.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* // A game reports every loaded bundle to its analytics.
|
|
435
|
+
* createPlugin("loadReport", {
|
|
436
|
+
* depends: [assetsPlugin],
|
|
437
|
+
* hooks: ctx => ({ "assets:bundle-loaded": ({ bundle, mb }) => ctx.log.info("loaded", { bundle, mb }) })
|
|
438
|
+
* }); // the board bundle logs { bundle: "board", mb: 3.5 }
|
|
439
|
+
*
|
|
440
|
+
* // A splash screen fills its loading bar file by file.
|
|
441
|
+
* createPlugin("loadingBar", {
|
|
442
|
+
* depends: [assetsPlugin],
|
|
443
|
+
* createState: () => ({ share: 0 }),
|
|
444
|
+
* hooks: ctx => ({
|
|
445
|
+
* "assets:bundle-progress": ({ loaded, total }) => {
|
|
446
|
+
* ctx.state.share = loaded / total;
|
|
447
|
+
* }
|
|
448
|
+
* })
|
|
449
|
+
* }); // a board bundle of four files sets share to 0.25, 0.5, 0.75 and 1; bundle-loaded follows
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
type Events = {
|
|
453
|
+
/** Every file of a bundle is a texture now. */"assets:bundle-loaded": {
|
|
454
|
+
bundle: string;
|
|
455
|
+
tier: Tier;
|
|
456
|
+
mb: number;
|
|
457
|
+
reason: LoadReason;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* One more file of a running load settled. `loaded` counts the settled files (a font once, with
|
|
461
|
+
* its pages), `total` is the file count of the bundle; the last one of a load has
|
|
462
|
+
* `loaded === total` and comes before `assets:bundle-loaded`. An aborted load sends none.
|
|
463
|
+
*/
|
|
464
|
+
"assets:bundle-progress": {
|
|
465
|
+
bundle: string;
|
|
466
|
+
loaded: number;
|
|
467
|
+
total: number;
|
|
468
|
+
}; /** The textures of a bundle were destroyed. `keys` names every asset that went with it. */
|
|
469
|
+
"assets:bundle-unloaded": {
|
|
470
|
+
bundle: string;
|
|
471
|
+
tier: Tier;
|
|
472
|
+
mb: number;
|
|
473
|
+
reason: "budget" | "request";
|
|
474
|
+
keys: readonly string[];
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* assets plugin API, `app.assets`. Bundles of textures by key: the graph decides when they arrive,
|
|
479
|
+
* the budget decides when they leave.
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```ts
|
|
483
|
+
* // A loading node asks for a bundle and a game system asks for one texture of it.
|
|
484
|
+
* await app.assets.load("board");
|
|
485
|
+
* app.assets.texture("board.cell"); // the Pixi texture, once the bundle is there
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
type Api = {
|
|
489
|
+
/**
|
|
490
|
+
* Loads every file of a bundle. An already loaded bundle resolves at once, a loading one joins
|
|
491
|
+
* the running load. Headless it resolves at once and touches nothing.
|
|
492
|
+
*
|
|
493
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
494
|
+
* @returns A promise that resolves when every texture of the bundle exists.
|
|
495
|
+
* @throws {Error} When the manifest has no such bundle, and when a file fails to load.
|
|
496
|
+
* @example
|
|
497
|
+
* ```ts
|
|
498
|
+
* // A game plugin warms the bundle of a timed event before its popup can open.
|
|
499
|
+
* await app.assets.load("event.halloween");
|
|
500
|
+
* app.assets.isLoaded("event.halloween"); // true
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
load(bundle: string): Promise<void>;
|
|
504
|
+
/**
|
|
505
|
+
* Destroys the textures of a bundle and tells `renderer` that its keys are gone. A pinned
|
|
506
|
+
* bundle and the tiers `boot` and `core` are refused with a warning. A running load is aborted.
|
|
507
|
+
*
|
|
508
|
+
* @param bundle - Name of a loaded bundle.
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* // The timed event ended: its textures go back to the GPU.
|
|
512
|
+
* app.assets.unload("event.halloween");
|
|
513
|
+
* app.assets.isLoaded("event.halloween"); // false
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
unload(bundle: string): void;
|
|
517
|
+
/**
|
|
518
|
+
* Tells whether every texture of a bundle exists. Headless every bundle of the manifest counts
|
|
519
|
+
* as loaded, so a headless game never waits for a loading screen.
|
|
520
|
+
*
|
|
521
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
522
|
+
* @returns True when the bundle is loaded.
|
|
523
|
+
* @example
|
|
524
|
+
* ```ts
|
|
525
|
+
* // `scenes` skips its loading path when the bundle of the next scene is already there.
|
|
526
|
+
* if (!app.assets.isLoaded("board")) await app.assets.load("board");
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
isLoaded(bundle: string): boolean;
|
|
530
|
+
/**
|
|
531
|
+
* The texture of a loaded asset key. It touches the use counter of the bundle, which is what
|
|
532
|
+
* the LRU reads. A key of a bundle that is not loaded warns once, starts a background load and
|
|
533
|
+
* answers `undefined`; the sprites waiting for it are textured when that load lands.
|
|
534
|
+
*
|
|
535
|
+
* @param key - Asset key, as `generated/assets.ts` types it.
|
|
536
|
+
* @returns The texture, or `undefined` while the bundle is not loaded.
|
|
537
|
+
* @example
|
|
538
|
+
* ```ts
|
|
539
|
+
* // A game system builds one Pixi object by hand instead of using the Sprite component.
|
|
540
|
+
* const texture = app.assets.texture("board.cell"); // undefined until the board bundle lands
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
texture(key: string): Texture | undefined;
|
|
544
|
+
/**
|
|
545
|
+
* The font of a loaded asset key: the `.fnt` file as text and the texture of its first page.
|
|
546
|
+
* It touches the use counter of the bundle. A font of a bundle that is not loaded, a key of
|
|
547
|
+
* another kind and every headless run answer `undefined`.
|
|
548
|
+
*
|
|
549
|
+
* @param key - Asset key of a `.fnt` file, as `generated/assets.ts` types it in `FontKey`.
|
|
550
|
+
* @returns The font, or `undefined` while its bundle is not loaded.
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* // `text` installs the font in the renderer when the bundle that carries it arrived.
|
|
554
|
+
* const font = app.assets.font("ui.body"); // { fnt: 'info face="body" size=32', texture }
|
|
555
|
+
*
|
|
556
|
+
* if (font !== undefined) app.renderer.sync.fonts.install("ui.body", font.fnt, font.texture);
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
font(key: string): FontAsset | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* The bytes of a loaded audio file, exactly as they were fetched: this plugin never decodes
|
|
562
|
+
* them. It touches the use counter of the bundle. A bundle that is not loaded, a key of
|
|
563
|
+
* another kind and every headless run answer `undefined`.
|
|
564
|
+
*
|
|
565
|
+
* @param key - Asset key of an `.mp3` file, as `generated/assets.ts` types it in `AudioKey`.
|
|
566
|
+
* @returns The undecoded bytes, or `undefined` while its bundle is not loaded.
|
|
567
|
+
* @example
|
|
568
|
+
* ```ts
|
|
569
|
+
* // `audio` decodes a sound once and keeps it until the bundle is unloaded.
|
|
570
|
+
* const bytes = app.assets.audio("ui.click"); // the ArrayBuffer of click.mp3
|
|
571
|
+
* const buffer = bytes === undefined ? undefined : await context.decodeAudioData(bytes);
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
audio(key: string): ArrayBuffer | undefined;
|
|
575
|
+
/**
|
|
576
|
+
* What the loaded bundles cost, sorted by name. `lastUsed` is the use counter, not a clock.
|
|
577
|
+
*
|
|
578
|
+
* @returns The used and allowed megabytes and one entry per loaded bundle.
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* // A dev overlay draws the memory bar of the game.
|
|
582
|
+
* const { textureMb, budgetMb, bundles } = app.assets.usage();
|
|
583
|
+
* // textureMb: 3.5, budgetMb: 192, bundles: [{ name: "board", tier: "scene", mb: 3.5, lastUsed: 12 }]
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
usage(): Usage;
|
|
587
|
+
};
|
|
588
|
+
/**
|
|
589
|
+
* Resolved dependency APIs.
|
|
590
|
+
*/
|
|
591
|
+
type Deps = {
|
|
592
|
+
flow: Api$1;
|
|
593
|
+
renderer: Api$3;
|
|
594
|
+
time: Api$2;
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* What the kernel context offers before the deps are attached.
|
|
598
|
+
*
|
|
599
|
+
* `index.ts` writes `events` with an annotated `register` (core spec `14-EVENT-REGISTRATION.md`
|
|
600
|
+
* row 8), so the two own events reach the context the kernel hands the factories and `emit` is the
|
|
601
|
+
* kernel's own, assets-typed one. No member of the context is cast.
|
|
602
|
+
*/
|
|
603
|
+
type KernelSlice = PluginCtx<Config, State, Events> & {
|
|
604
|
+
readonly global: object;
|
|
605
|
+
readonly log: Log.LogApi;
|
|
606
|
+
readonly require: Require;
|
|
607
|
+
};
|
|
608
|
+
/**
|
|
609
|
+
* Domain context shared by the files of the plugin.
|
|
610
|
+
*/
|
|
611
|
+
type AssetsCtx = KernelSlice & {
|
|
612
|
+
readonly deps: Deps;
|
|
613
|
+
};
|
|
614
|
+
/**
|
|
615
|
+
* Payload of the one event assets listens to.
|
|
616
|
+
*/
|
|
617
|
+
type FlowRest = {
|
|
618
|
+
path: string;
|
|
619
|
+
checkpoint: boolean;
|
|
620
|
+
};
|
|
621
|
+
//#endregion
|
|
622
|
+
export { DefineBundles as a, LoadReason as c, Tier as d, Usage as f, Config as i, Manifest as l, BundleMap as n, Events as o, types_d_exports as p, BundleSpec as r, LoadBundles as s, Api as t, State as u };
|