@datamoc/mw_games 0.2.1 → 0.3.1
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 +395 -352
- package/dist/assets/Streaming.d.ts +10 -4
- package/dist/assets/Streaming.d.ts.map +1 -1
- package/dist/assets/Streaming.js +17 -6
- package/dist/assets/Streaming.js.map +1 -1
- package/dist/assets/index.d.ts +10 -2
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +8 -4
- package/dist/assets/index.js.map +1 -1
- package/dist/audio/Music.d.ts +10 -0
- package/dist/audio/Music.d.ts.map +1 -1
- package/dist/audio/Music.js +36 -1
- package/dist/audio/Music.js.map +1 -1
- package/dist/audio/Playable.d.ts +3 -0
- package/dist/audio/Playable.d.ts.map +1 -1
- package/dist/audio/Playable.js.map +1 -1
- package/dist/core/Feedback.d.ts +3 -9
- package/dist/core/Feedback.d.ts.map +1 -1
- package/dist/core/Feedback.js +6 -18
- package/dist/core/Feedback.js.map +1 -1
- package/dist/core/HttpTransport.d.ts +22 -0
- package/dist/core/HttpTransport.d.ts.map +1 -0
- package/dist/core/HttpTransport.js +33 -0
- package/dist/core/HttpTransport.js.map +1 -0
- package/dist/core/Multiplayer.d.ts +63 -0
- package/dist/core/Multiplayer.d.ts.map +1 -0
- package/dist/core/Multiplayer.js +73 -0
- package/dist/core/Multiplayer.js.map +1 -0
- package/dist/core/SaveSync.d.ts +17 -14
- package/dist/core/SaveSync.d.ts.map +1 -1
- package/dist/core/SaveSync.js +35 -31
- package/dist/core/SaveSync.js.map +1 -1
- package/dist/core/Stats.d.ts +33 -0
- package/dist/core/Stats.d.ts.map +1 -0
- package/dist/core/Stats.js +34 -0
- package/dist/core/Stats.js.map +1 -0
- package/dist/core/Telemetry.d.ts +34 -0
- package/dist/core/Telemetry.d.ts.map +1 -0
- package/dist/core/Telemetry.js +47 -0
- package/dist/core/Telemetry.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mw_games.global.js +10 -9
- package/dist/render/StatusVisuals.d.ts +46 -0
- package/dist/render/StatusVisuals.d.ts.map +1 -0
- package/dist/render/StatusVisuals.js +48 -0
- package/dist/render/StatusVisuals.js.map +1 -0
- package/dist/render/index.d.ts +2 -0
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/index.js +1 -0
- package/dist/render/index.js.map +1 -1
- package/dist/simulation/Scenario.d.ts +29 -0
- package/dist/simulation/Scenario.d.ts.map +1 -0
- package/dist/simulation/Scenario.js +25 -0
- package/dist/simulation/Scenario.js.map +1 -0
- package/dist/simulation/Turns.d.ts +29 -0
- package/dist/simulation/Turns.d.ts.map +1 -0
- package/dist/simulation/Turns.js +30 -0
- package/dist/simulation/Turns.js.map +1 -0
- package/dist/simulation/index.d.ts +5 -0
- package/dist/simulation/index.d.ts.map +1 -0
- package/dist/simulation/index.js +3 -0
- package/dist/simulation/index.js.map +1 -0
- package/dist/ui/Button.d.ts +18 -1
- package/dist/ui/Button.d.ts.map +1 -1
- package/dist/ui/Button.js +15 -5
- package/dist/ui/Button.js.map +1 -1
- package/dist/ui/Label.d.ts +8 -0
- package/dist/ui/Label.d.ts.map +1 -1
- package/dist/ui/Label.js +3 -0
- package/dist/ui/Label.js.map +1 -1
- package/dist/ui/StatsScreen.d.ts +25 -0
- package/dist/ui/StatsScreen.d.ts.map +1 -0
- package/dist/ui/StatsScreen.js +27 -0
- package/dist/ui/StatsScreen.js.map +1 -0
- package/dist/ui/index.d.ts +3 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +147 -112
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AssetProgress } from './index.ts';
|
|
1
2
|
export interface AssetBundle {
|
|
2
3
|
id: string;
|
|
3
4
|
paths: readonly string[];
|
|
@@ -7,7 +8,7 @@ export interface AssetBundle {
|
|
|
7
8
|
}
|
|
8
9
|
export interface AssetStreamOptions {
|
|
9
10
|
budgetBytes?: number;
|
|
10
|
-
load?: (paths: string[]) => Promise<void>;
|
|
11
|
+
load?: (paths: string[], onProgress?: AssetProgress) => Promise<void>;
|
|
11
12
|
release?: (paths: string[]) => Promise<void>;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
@@ -25,9 +26,14 @@ export declare class AssetStream {
|
|
|
25
26
|
private clock;
|
|
26
27
|
constructor(options?: AssetStreamOptions);
|
|
27
28
|
/** loads one bundle now, then evicts older ready bundles if its budget requires it */
|
|
28
|
-
preload(bundle: AssetBundle): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
preload(bundle: AssetBundle, onProgress?: AssetProgress): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Preloads bundles in highest-priority-first order; callers may intentionally not await it.
|
|
32
|
+
* `onProgress`, when given, reports overall fraction across every bundle in `bundles` (each
|
|
33
|
+
* one weighted equally), for a caller that wants one progress figure across the whole list
|
|
34
|
+
* rather than per-bundle callbacks of its own.
|
|
35
|
+
*/
|
|
36
|
+
preloadLikely(bundles: readonly AssetBundle[], onProgress?: AssetProgress): Promise<void>;
|
|
31
37
|
/** marks a ready bundle as recently used at the moment a scene adopts it */
|
|
32
38
|
use(id: string): boolean;
|
|
33
39
|
isReady(id: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../src/assets/Streaming.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../src/assets/Streaming.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAOD;;;;;;GAMG;AACH,qBAAa,WAAW;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiE;IAC5F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqC;IACnE,OAAO,CAAC,KAAK,CAAK;IAElB,YAAY,OAAO,GAAE,kBAAuB,EAK3C;IAED,sFAAsF;IAChF,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5E;IAED;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,UAAU,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9F;IAED,4EAA4E;IAC5E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAKvB;IAED,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE3B;IAED,8EAA8E;IACxE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMtC;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;YAEa,aAAa;IAU3B,OAAO,CAAC,UAAU;CAGlB"}
|
package/dist/assets/Streaming.js
CHANGED
|
@@ -17,7 +17,7 @@ export class AssetStream {
|
|
|
17
17
|
this.releaseAssets = options.release ?? release;
|
|
18
18
|
}
|
|
19
19
|
/** loads one bundle now, then evicts older ready bundles if its budget requires it */
|
|
20
|
-
async preload(bundle) {
|
|
20
|
+
async preload(bundle, onProgress) {
|
|
21
21
|
if (!bundle.id)
|
|
22
22
|
throw new Error('asset bundle needs an id');
|
|
23
23
|
if (bundle.estimatedBytes !== undefined && bundle.estimatedBytes < 0)
|
|
@@ -25,18 +25,29 @@ export class AssetStream {
|
|
|
25
25
|
const existing = this.entries.get(bundle.id);
|
|
26
26
|
if (existing?.ready) {
|
|
27
27
|
existing.lastUsed = ++this.clock;
|
|
28
|
+
onProgress?.(1);
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
const paths = bundle.paths.filter((path) => !this.isRetained(path));
|
|
31
32
|
if (paths.length > 0)
|
|
32
|
-
await this.loadAssets([...paths]);
|
|
33
|
+
await this.loadAssets([...paths], onProgress);
|
|
34
|
+
else
|
|
35
|
+
onProgress?.(1);
|
|
33
36
|
this.entries.set(bundle.id, { ...bundle, lastUsed: ++this.clock, ready: true });
|
|
34
37
|
await this.enforceBudget(bundle.id);
|
|
35
38
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Preloads bundles in highest-priority-first order; callers may intentionally not await it.
|
|
41
|
+
* `onProgress`, when given, reports overall fraction across every bundle in `bundles` (each
|
|
42
|
+
* one weighted equally), for a caller that wants one progress figure across the whole list
|
|
43
|
+
* rather than per-bundle callbacks of its own.
|
|
44
|
+
*/
|
|
45
|
+
async preloadLikely(bundles, onProgress) {
|
|
46
|
+
const ordered = [...bundles].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
47
|
+
for (const [index, bundle] of ordered.entries()) {
|
|
48
|
+
await this.preload(bundle, onProgress && ((fraction) => onProgress((index + fraction) / ordered.length)));
|
|
49
|
+
}
|
|
50
|
+
onProgress?.(1);
|
|
40
51
|
}
|
|
41
52
|
/** marks a ready bundle as recently used at the moment a scene adopts it */
|
|
42
53
|
use(id) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Streaming.js","sourceRoot":"","sources":["../../src/assets/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Streaming.js","sourceRoot":"","sources":["../../src/assets/Streaming.ts"],"names":[],"mappings":"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"}
|
package/dist/assets/index.d.ts
CHANGED
|
@@ -23,13 +23,21 @@ export declare function paths(): string[];
|
|
|
23
23
|
export declare function has(path: string): boolean;
|
|
24
24
|
/** an asset path resolved to something the browser can actually load */
|
|
25
25
|
export declare function resolve(path: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* 0 (started) to 1 (complete) - a count of assets finished, not a byte count. Pixi's own
|
|
28
|
+
* loaders do not expose bytes transferred across every asset type uniformly, so this reports
|
|
29
|
+
* what is actually available rather than a byte figure it cannot back with a real number.
|
|
30
|
+
*/
|
|
31
|
+
export type AssetProgress = (fraction: number) => void;
|
|
26
32
|
/**
|
|
27
33
|
* Loads assets by path, giving each one an alias equal to its path.
|
|
28
34
|
*
|
|
29
35
|
* Load everything a scene needs before creating it; afterwards `texture()` is synchronous,
|
|
30
|
-
* so game code never has to await in the middle of building a scene.
|
|
36
|
+
* so game code never has to await in the middle of building a scene. `onProgress`, when
|
|
37
|
+
* given, is item 135's `LoadQueue`/`LoadingScreen` seam for a task that would otherwise only
|
|
38
|
+
* ever report 0 then 1 around one opaque `await`.
|
|
31
39
|
*/
|
|
32
|
-
export declare function load(paths: string[]): Promise<void>;
|
|
40
|
+
export declare function load(paths: string[], onProgress?: AssetProgress): Promise<void>;
|
|
33
41
|
/** a loaded texture, by the same path it was loaded with */
|
|
34
42
|
export declare function texture(path: string): Texture;
|
|
35
43
|
/** a loaded asset of any other kind, such as parsed JSON */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,OAAO,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;GAUG;AAEH,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM;QACf,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC;CACD;AAKD,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1C;AAMD,2EAA2E;AAC3E,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,KAAK,IAAI,MAAM,EAAE,CAEhC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGzC;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5C;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,OAAO,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;;;;;;GAUG;AAEH,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,MAAM;QACf,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC;CACD;AAKD,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1C;AAMD,2EAA2E;AAC3E,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,KAAK,IAAI,MAAM,EAAE,CAEhC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGzC;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5C;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrF;AAED,4DAA4D;AAC5D,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAK7C;AAED,4DAA4D;AAC5D,wBAAgB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAKtC;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAI5D;AAED,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/assets/index.js
CHANGED
|
@@ -34,16 +34,20 @@ export function resolve(path) {
|
|
|
34
34
|
* Loads assets by path, giving each one an alias equal to its path.
|
|
35
35
|
*
|
|
36
36
|
* Load everything a scene needs before creating it; afterwards `texture()` is synchronous,
|
|
37
|
-
* so game code never has to await in the middle of building a scene.
|
|
37
|
+
* so game code never has to await in the middle of building a scene. `onProgress`, when
|
|
38
|
+
* given, is item 135's `LoadQueue`/`LoadingScreen` seam for a task that would otherwise only
|
|
39
|
+
* ever report 0 then 1 around one opaque `await`.
|
|
38
40
|
*/
|
|
39
|
-
export async function load(paths) {
|
|
41
|
+
export async function load(paths, onProgress) {
|
|
40
42
|
const pending = paths.filter((path) => !Assets.cache.has(path));
|
|
41
|
-
if (pending.length === 0)
|
|
43
|
+
if (pending.length === 0) {
|
|
44
|
+
onProgress?.(1);
|
|
42
45
|
return;
|
|
46
|
+
}
|
|
43
47
|
for (const path of pending) {
|
|
44
48
|
Assets.add({ alias: path, src: resolve(path) });
|
|
45
49
|
}
|
|
46
|
-
await Assets.load(pending);
|
|
50
|
+
await Assets.load(pending, onProgress);
|
|
47
51
|
}
|
|
48
52
|
/** a loaded texture, by the same path it was loaded with */
|
|
49
53
|
export function texture(path) {
|
package/dist/assets/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAoB1C,kFAAkF;AAClF,IAAI,IAAI,GAAG,EAAE,CAAC;AAEd,mEAAmE;AACnE,MAAM,UAAU,OAAO,CAAC,IAAY;IACnC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;AAC9D,CAAC;AAED,SAAS,QAAQ;IAChB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC;AAC1E,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU;IACzB,OAAO,QAAQ,EAAE,KAAK,SAAS,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,KAAK;IACpB,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY;IAC/B,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,IAAY;IACnC,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,GAAG,IAAI,CAAC;IAE7B,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACd,UAAU,IAAI,mFAAmF,CACjG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/assets/index.ts"],"names":[],"mappings":"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"}
|
package/dist/audio/Music.d.ts
CHANGED
|
@@ -12,10 +12,20 @@ export declare class Music {
|
|
|
12
12
|
private current;
|
|
13
13
|
private fades;
|
|
14
14
|
private create;
|
|
15
|
+
private trackQueue;
|
|
16
|
+
private playlistFade;
|
|
15
17
|
volume: number;
|
|
16
18
|
constructor(options?: MusicOptions);
|
|
17
19
|
/** @param fadeDuration seconds; 0 switches immediately with no crossfade */
|
|
18
20
|
play(path: string, fadeDuration?: number): void;
|
|
21
|
+
/**
|
|
22
|
+
* Plays the supplied sequence repeatedly. This is deliberately small and backend-agnostic:
|
|
23
|
+
* callers can repeat entries to weight a track, while HTMLAudioElement's optional `onended`
|
|
24
|
+
* advances the queue when available.
|
|
25
|
+
*/
|
|
26
|
+
playTracks(paths: readonly string[], fadeDuration?: number): void;
|
|
27
|
+
private startNextTrack;
|
|
28
|
+
private start;
|
|
19
29
|
stop(fadeDuration?: number): void;
|
|
20
30
|
update(dt: number): void;
|
|
21
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Music.d.ts","sourceRoot":"","sources":["../../src/audio/Music.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAW3D,MAAM,WAAW,YAAY;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC;CACpC;AAED;;;;GAIG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAA6B;IAC3C,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,OAAO,GAAE,YAAiB,EAGrC;IAED,4EAA4E;IAC5E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"Music.d.ts","sourceRoot":"","sources":["../../src/audio/Music.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAW3D,MAAM,WAAW,YAAY;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC;CACpC;AAED;;;;GAIG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,OAAO,GAAE,YAAiB,EAGrC;IAED,4EAA4E;IAC5E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI,CAGzC;IAED;;;;OAIG;IACH,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,YAAY,SAAI,GAAG,IAAI,CAQ3D;IAED,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,KAAK;IAuCb,IAAI,CAAC,YAAY,SAAI,GAAG,IAAI,CAW3B;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAevB;CACD"}
|
package/dist/audio/Music.js
CHANGED
|
@@ -8,13 +8,47 @@ export class Music {
|
|
|
8
8
|
constructor(options = {}) {
|
|
9
9
|
this.current = null;
|
|
10
10
|
this.fades = [];
|
|
11
|
+
this.trackQueue = null;
|
|
12
|
+
this.playlistFade = 1;
|
|
11
13
|
this.volume = options.volume ?? 1;
|
|
12
14
|
this.create = options.create ?? createAudio;
|
|
13
15
|
}
|
|
14
16
|
/** @param fadeDuration seconds; 0 switches immediately with no crossfade */
|
|
15
17
|
play(path, fadeDuration = 1) {
|
|
18
|
+
this.trackQueue = null;
|
|
19
|
+
this.start(path, fadeDuration, true);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Plays the supplied sequence repeatedly. This is deliberately small and backend-agnostic:
|
|
23
|
+
* callers can repeat entries to weight a track, while HTMLAudioElement's optional `onended`
|
|
24
|
+
* advances the queue when available.
|
|
25
|
+
*/
|
|
26
|
+
playTracks(paths, fadeDuration = 1) {
|
|
27
|
+
if (paths.length === 0) {
|
|
28
|
+
this.stop(fadeDuration);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.trackQueue = [...paths];
|
|
32
|
+
this.playlistFade = fadeDuration;
|
|
33
|
+
this.startNextTrack();
|
|
34
|
+
}
|
|
35
|
+
startNextTrack() {
|
|
36
|
+
if (!this.trackQueue?.length)
|
|
37
|
+
return;
|
|
38
|
+
const path = this.trackQueue.shift();
|
|
39
|
+
this.trackQueue.push(path);
|
|
40
|
+
this.start(path, this.playlistFade, false);
|
|
41
|
+
}
|
|
42
|
+
start(path, fadeDuration, loop) {
|
|
16
43
|
const incoming = this.create(path);
|
|
17
|
-
incoming.loop =
|
|
44
|
+
incoming.loop = loop;
|
|
45
|
+
if (!loop && incoming.onended !== undefined) {
|
|
46
|
+
// A delayed event from a fading-out track must not advance a newer playlist.
|
|
47
|
+
incoming.onended = () => {
|
|
48
|
+
if (this.current === incoming)
|
|
49
|
+
this.startNextTrack();
|
|
50
|
+
};
|
|
51
|
+
}
|
|
18
52
|
const previous = this.current;
|
|
19
53
|
this.current = incoming;
|
|
20
54
|
if (fadeDuration <= 0) {
|
|
@@ -41,6 +75,7 @@ export class Music {
|
|
|
41
75
|
}
|
|
42
76
|
}
|
|
43
77
|
stop(fadeDuration = 1) {
|
|
78
|
+
this.trackQueue = null;
|
|
44
79
|
const audio = this.current;
|
|
45
80
|
if (!audio)
|
|
46
81
|
return;
|
package/dist/audio/Music.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Music.js","sourceRoot":"","sources":["../../src/audio/Music.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAgB3D;;;;GAIG;AACH,MAAM,OAAO,KAAK;
|
|
1
|
+
{"version":3,"file":"Music.js","sourceRoot":"","sources":["../../src/audio/Music.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAgB3D;;;;GAIG;AACH,MAAM,OAAO,KAAK;IAQjB,YAAY,OAAO,GAAiB,EAAE;QAP9B,YAAO,GAAoB,IAAI,CAAC;QAChC,UAAK,GAAW,EAAE,CAAC;QAEnB,eAAU,GAAoB,IAAI,CAAC;QACnC,iBAAY,GAAG,CAAC,CAAC;QAIxB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC;IAC7C,CAAC;IAED,4EAA4E;IAC5E,IAAI,CAAC,IAAY,EAAE,YAAY,GAAG,CAAC;QAClC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,KAAwB,EAAE,YAAY,GAAG,CAAC;QACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,cAAc,EAAE,CAAC;IACvB,CAAC;IAEO,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM;YAAE,OAAO;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAG,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,IAAY,EAAE,YAAoB,EAAE,IAAa;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC7C,6EAA6E;YAC7E,QAAQ,CAAC,OAAO,GAAG,GAAG,EAAE;gBACvB,IAAI,IAAI,CAAC,OAAO,KAAK,QAAQ;oBAAE,IAAI,CAAC,cAAc,EAAE,CAAC;YACtD,CAAC,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC;QAExB,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACvB,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC9B,iFAAiF;YACjF,iFAAiF;YACjF,0EAA0E;YAC1E,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtD,QAAQ,EAAE,KAAK,EAAE,CAAC;YAClB,OAAO;QACR,CAAC;QAED,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACpB,KAAK,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAErH,IAAI,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBACf,KAAK,EAAE,QAAQ;gBACf,OAAO,EAAE,CAAC;gBACV,QAAQ,EAAE,YAAY;gBACtB,IAAI,EAAE,QAAQ,CAAC,MAAM;gBACrB,EAAE,EAAE,CAAC;gBACL,SAAS,EAAE,IAAI;aACf,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,IAAI,CAAC,YAAY,GAAG,CAAC;QACpB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO;QACR,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5G,CAAC;IAED,MAAM,CAAC,EAAU;QAChB,iGAAiG;QACjG,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE1D,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/C,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACpB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;oBACnB,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
|
package/dist/audio/Playable.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export interface Playable {
|
|
|
10
10
|
currentTime: number;
|
|
11
11
|
volume: number;
|
|
12
12
|
loop: boolean;
|
|
13
|
+
/** Optional completion hook, matching HTMLAudioElement.onended. Backends without an
|
|
14
|
+
* end event can omit it; single-track Music remains fully usable there. */
|
|
15
|
+
onended?: ((event: Event) => unknown) | null;
|
|
13
16
|
}
|
|
14
17
|
/** the real thing: an `HTMLAudioElement` pointed at a resolved asset path */
|
|
15
18
|
export declare function createAudio(path: string): Playable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Playable.d.ts","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACxB,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Playable.d.ts","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACxB,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAElD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Playable.js","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Playable.js","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAmBhD,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,OAAO,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
package/dist/core/Feedback.d.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
|
+
import { HttpTransport, type HttpTransportOptions } from './HttpTransport.ts';
|
|
1
2
|
export interface FeedbackRequest {
|
|
2
3
|
message: string;
|
|
3
4
|
contact?: string;
|
|
4
5
|
context?: Record<string, string | number | boolean>;
|
|
5
6
|
}
|
|
6
|
-
export
|
|
7
|
-
endpoint: string;
|
|
8
|
-
timeoutMs?: number;
|
|
9
|
-
fetch?: typeof globalThis.fetch;
|
|
10
|
-
}
|
|
7
|
+
export type FeedbackOptions = HttpTransportOptions;
|
|
11
8
|
export interface FeedbackResponse {
|
|
12
9
|
ok: boolean;
|
|
13
10
|
status: number;
|
|
14
11
|
}
|
|
15
12
|
/** Small, injectable transport for a game's own feedback endpoint. */
|
|
16
|
-
export declare class FeedbackClient {
|
|
17
|
-
private readonly endpoint;
|
|
18
|
-
private readonly timeoutMs;
|
|
19
|
-
private readonly fetchFn;
|
|
13
|
+
export declare class FeedbackClient extends HttpTransport {
|
|
20
14
|
constructor(options: FeedbackOptions);
|
|
21
15
|
submit(request: FeedbackRequest): Promise<FeedbackResponse>;
|
|
22
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Feedback.d.ts","sourceRoot":"","sources":["../../src/core/Feedback.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"Feedback.d.ts","sourceRoot":"","sources":["../../src/core/Feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,qBAAa,cAAe,SAAQ,aAAa;IAChD,YAAY,OAAO,EAAE,eAAe,EAEnC;IAEK,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAYhE;CACD"}
|
package/dist/core/Feedback.js
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
|
+
import { HttpTransport } from './HttpTransport.js';
|
|
1
2
|
/** Small, injectable transport for a game's own feedback endpoint. */
|
|
2
|
-
export class FeedbackClient {
|
|
3
|
+
export class FeedbackClient extends HttpTransport {
|
|
3
4
|
constructor(options) {
|
|
4
|
-
|
|
5
|
-
throw new Error('feedback endpoint is required');
|
|
6
|
-
this.endpoint = options.endpoint;
|
|
7
|
-
this.timeoutMs = options.timeoutMs ?? 10000;
|
|
8
|
-
if (!(this.timeoutMs > 0))
|
|
9
|
-
throw new Error('feedback timeout must be positive');
|
|
10
|
-
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
11
|
-
if (!this.fetchFn)
|
|
12
|
-
throw new Error('fetch is unavailable; provide FeedbackOptions.fetch');
|
|
5
|
+
super(options, 'feedback');
|
|
13
6
|
}
|
|
14
7
|
async submit(request) {
|
|
15
8
|
if (!request.message?.trim())
|
|
16
9
|
throw new Error('feedback message cannot be empty');
|
|
17
|
-
|
|
18
|
-
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
19
|
-
try {
|
|
10
|
+
return this.withTimeout(async (signal) => {
|
|
20
11
|
const response = await this.fetchFn(this.endpoint, {
|
|
21
12
|
method: 'POST',
|
|
22
13
|
headers: { 'content-type': 'application/json' },
|
|
23
14
|
body: JSON.stringify(request),
|
|
24
|
-
signal
|
|
15
|
+
signal,
|
|
25
16
|
});
|
|
26
17
|
if (!response.ok)
|
|
27
18
|
throw new Error(`feedback request failed with HTTP ${response.status}`);
|
|
28
19
|
return { ok: true, status: response.status };
|
|
29
|
-
}
|
|
30
|
-
finally {
|
|
31
|
-
clearTimeout(timer);
|
|
32
|
-
}
|
|
20
|
+
});
|
|
33
21
|
}
|
|
34
22
|
}
|
|
35
23
|
//# sourceMappingURL=Feedback.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Feedback.js","sourceRoot":"","sources":["../../src/core/Feedback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Feedback.js","sourceRoot":"","sources":["../../src/core/Feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,oBAAoB,CAAC;AAe9E,sEAAsE;AACtE,MAAM,OAAO,cAAe,SAAQ,aAAa;IAChD,YAAY,OAAwB;QACnC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAwB;QACpC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAClD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC7B,MAAM;aACN,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1F,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;IACJ,CAAC;CACD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface HttpTransportOptions {
|
|
2
|
+
endpoint: string;
|
|
3
|
+
timeoutMs?: number;
|
|
4
|
+
fetch?: typeof globalThis.fetch;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The constructor validation and timeout-wrapped fetch shared by every injectable HTTPS
|
|
8
|
+
* transport here (`FeedbackClient`, `SaveSyncClient`, `TelemetryClient`) - each one is a
|
|
9
|
+
* plain request/response against a game's own endpoint, differing only in what they send and
|
|
10
|
+
* expect back, not in how the endpoint/timeout/fetch options are validated or how a request
|
|
11
|
+
* is aborted after `timeoutMs`. `label` names the concrete client in its own error messages
|
|
12
|
+
* ("feedback endpoint is required", not a generic one that would leave a game guessing which
|
|
13
|
+
* of several clients threw).
|
|
14
|
+
*/
|
|
15
|
+
export declare abstract class HttpTransport {
|
|
16
|
+
protected readonly endpoint: string;
|
|
17
|
+
protected readonly timeoutMs: number;
|
|
18
|
+
protected readonly fetchFn: typeof globalThis.fetch;
|
|
19
|
+
constructor(options: HttpTransportOptions, label: string);
|
|
20
|
+
protected withTimeout<T>(run: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=HttpTransport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpTransport.d.ts","sourceRoot":"","sources":["../../src/core/HttpTransport.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,8BAAsB,aAAa;IAClC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IACpC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IACrC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAEpD,YAAY,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAOvD;IAED,UAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQnF;CACD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The constructor validation and timeout-wrapped fetch shared by every injectable HTTPS
|
|
3
|
+
* transport here (`FeedbackClient`, `SaveSyncClient`, `TelemetryClient`) - each one is a
|
|
4
|
+
* plain request/response against a game's own endpoint, differing only in what they send and
|
|
5
|
+
* expect back, not in how the endpoint/timeout/fetch options are validated or how a request
|
|
6
|
+
* is aborted after `timeoutMs`. `label` names the concrete client in its own error messages
|
|
7
|
+
* ("feedback endpoint is required", not a generic one that would leave a game guessing which
|
|
8
|
+
* of several clients threw).
|
|
9
|
+
*/
|
|
10
|
+
export class HttpTransport {
|
|
11
|
+
constructor(options, label) {
|
|
12
|
+
if (!options.endpoint)
|
|
13
|
+
throw new Error(`${label} endpoint is required`);
|
|
14
|
+
this.endpoint = options.endpoint;
|
|
15
|
+
this.timeoutMs = options.timeoutMs ?? 10000;
|
|
16
|
+
if (!(this.timeoutMs > 0))
|
|
17
|
+
throw new Error(`${label} timeout must be positive`);
|
|
18
|
+
this.fetchFn = options.fetch ?? globalThis.fetch;
|
|
19
|
+
if (!this.fetchFn)
|
|
20
|
+
throw new Error(`fetch is unavailable; provide ${label}Options.fetch`);
|
|
21
|
+
}
|
|
22
|
+
async withTimeout(run) {
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
25
|
+
try {
|
|
26
|
+
return await run(controller.signal);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=HttpTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpTransport.js","sourceRoot":"","sources":["../../src/core/HttpTransport.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,MAAM,OAAgB,aAAa;IAKlC,YAAY,OAA6B,EAAE,KAAa;QACvD,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,uBAAuB,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;QAC5C,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,2BAA2B,CAAC,CAAC;QAChF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,eAAe,CAAC,CAAC;IAC3F,CAAC;IAES,KAAK,CAAC,WAAW,CAAI,GAAwC;QACtE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC;YACJ,OAAO,MAAM,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Signal } from './Signal.ts';
|
|
2
|
+
/** the subset of the DOM/Node `WebSocket` this needs - injectable so a test never opens a real socket */
|
|
3
|
+
export interface WebSocketLike {
|
|
4
|
+
readyState: number;
|
|
5
|
+
send(data: string): void;
|
|
6
|
+
close(): void;
|
|
7
|
+
onopen: ((event: unknown) => void) | null;
|
|
8
|
+
onclose: ((event: unknown) => void) | null;
|
|
9
|
+
onerror: ((event: unknown) => void) | null;
|
|
10
|
+
onmessage: ((event: {
|
|
11
|
+
data: string;
|
|
12
|
+
}) => void) | null;
|
|
13
|
+
}
|
|
14
|
+
export interface LockstepClientOptions {
|
|
15
|
+
url: string;
|
|
16
|
+
/** defaults to the global `WebSocket`; inject a fake in tests, or `ws`'s own client in Node */
|
|
17
|
+
create?: (url: string) => WebSocketLike;
|
|
18
|
+
}
|
|
19
|
+
export interface TickEvent {
|
|
20
|
+
tick: number;
|
|
21
|
+
/** this room's every client id mapped to its input for `tick`, or `null` if it missed the deadline */
|
|
22
|
+
inputs: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A client for this project's own reference lockstep server (`tools/multiplayer-server.mjs`):
|
|
26
|
+
* the transport, identity, and tick-authority piece item 129 asked for, without `mwg`
|
|
27
|
+
* inventing a full prediction/rollback netcode a real game would likely want to replace
|
|
28
|
+
* anyway. Every connected client submits at most one input per tick; the server broadcasts a
|
|
29
|
+
* `tick` message once every client in its room has submitted for that tick, or once a
|
|
30
|
+
* configured timeout elapses, substituting `null` for whoever is still missing - lockstep,
|
|
31
|
+
* the model turn-driven and RTS-shaped games have used for this for decades, chosen because
|
|
32
|
+
* it needs no client-side prediction to already be correct. A game wanting prediction or
|
|
33
|
+
* interpolation on top of this still can: `onTick` hands over the same ordered, authoritative
|
|
34
|
+
* input stream a prediction layer would reconcile against.
|
|
35
|
+
*
|
|
36
|
+
* This is deliberately the only network surface in `mwg` that assumes a server exists rather
|
|
37
|
+
* than a game's own endpoint - `FeedbackClient`/`NewsClient`/`SaveSyncClient` all assume the
|
|
38
|
+
* opposite, a one-shot request/response a game points at its own backend. Real-time
|
|
39
|
+
* multiplayer has no one-shot shape to hide a server behind, which is why `mwg` ships a
|
|
40
|
+
* reference one (`tools/multiplayer-server.mjs`) rather than assuming a game brings its own,
|
|
41
|
+
* the same reasoning that decided this the moment the item was sized.
|
|
42
|
+
*/
|
|
43
|
+
export declare class LockstepClient {
|
|
44
|
+
readonly onWelcome: Signal<{
|
|
45
|
+
id: string;
|
|
46
|
+
}>;
|
|
47
|
+
readonly onTick: Signal<TickEvent>;
|
|
48
|
+
readonly onClose: Signal<void>;
|
|
49
|
+
private socket;
|
|
50
|
+
private readonly url;
|
|
51
|
+
private readonly createSocket;
|
|
52
|
+
private _id;
|
|
53
|
+
constructor(options: LockstepClientOptions);
|
|
54
|
+
/** this client's own id, assigned by the server's `welcome` message; `null` before it arrives */
|
|
55
|
+
get id(): string | null;
|
|
56
|
+
get connected(): boolean;
|
|
57
|
+
connect(): void;
|
|
58
|
+
/** submits this client's input for the current tick; a no-op before the connection is open */
|
|
59
|
+
submitInput(payload: unknown): void;
|
|
60
|
+
close(): void;
|
|
61
|
+
private handleMessage;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=Multiplayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Multiplayer.d.ts","sourceRoot":"","sources":["../../src/core/Multiplayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACtD;AAID,MAAM,WAAW,qBAAqB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,+FAA+F;IAC/F,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,CAAC;CACxC;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,sGAAsG;IACtG,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAc;IAC1B,QAAQ,CAAC,SAAS;YAAoB,MAAM;OAAM;IAClD,QAAQ,CAAC,MAAM,oBAA2B;IAC1C,QAAQ,CAAC,OAAO,eAAsB;IAEtC,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiC;IAC9D,OAAO,CAAC,GAAG,CAAuB;IAElC,YAAY,OAAO,EAAE,qBAAqB,EAIzC;IAED,iGAAiG;IACjG,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,CAEtB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,OAAO,IAAI,IAAI,CAUd;IAED,8FAA8F;IAC9F,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAGlC;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,OAAO,CAAC,aAAa;CASrB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Signal } from './Signal.js';
|
|
2
|
+
const OPEN = 1; //WebSocket.OPEN, kept as a literal so this file needs no DOM/Node WebSocket type import
|
|
3
|
+
/**
|
|
4
|
+
* A client for this project's own reference lockstep server (`tools/multiplayer-server.mjs`):
|
|
5
|
+
* the transport, identity, and tick-authority piece item 129 asked for, without `mwg`
|
|
6
|
+
* inventing a full prediction/rollback netcode a real game would likely want to replace
|
|
7
|
+
* anyway. Every connected client submits at most one input per tick; the server broadcasts a
|
|
8
|
+
* `tick` message once every client in its room has submitted for that tick, or once a
|
|
9
|
+
* configured timeout elapses, substituting `null` for whoever is still missing - lockstep,
|
|
10
|
+
* the model turn-driven and RTS-shaped games have used for this for decades, chosen because
|
|
11
|
+
* it needs no client-side prediction to already be correct. A game wanting prediction or
|
|
12
|
+
* interpolation on top of this still can: `onTick` hands over the same ordered, authoritative
|
|
13
|
+
* input stream a prediction layer would reconcile against.
|
|
14
|
+
*
|
|
15
|
+
* This is deliberately the only network surface in `mwg` that assumes a server exists rather
|
|
16
|
+
* than a game's own endpoint - `FeedbackClient`/`NewsClient`/`SaveSyncClient` all assume the
|
|
17
|
+
* opposite, a one-shot request/response a game points at its own backend. Real-time
|
|
18
|
+
* multiplayer has no one-shot shape to hide a server behind, which is why `mwg` ships a
|
|
19
|
+
* reference one (`tools/multiplayer-server.mjs`) rather than assuming a game brings its own,
|
|
20
|
+
* the same reasoning that decided this the moment the item was sized.
|
|
21
|
+
*/
|
|
22
|
+
export class LockstepClient {
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.onWelcome = new Signal();
|
|
25
|
+
this.onTick = new Signal();
|
|
26
|
+
this.onClose = new Signal();
|
|
27
|
+
this.socket = null;
|
|
28
|
+
this._id = null;
|
|
29
|
+
if (!options.url)
|
|
30
|
+
throw new Error('lockstep client url is required');
|
|
31
|
+
this.url = options.url;
|
|
32
|
+
this.createSocket = options.create ?? ((url) => new WebSocket(url));
|
|
33
|
+
}
|
|
34
|
+
/** this client's own id, assigned by the server's `welcome` message; `null` before it arrives */
|
|
35
|
+
get id() {
|
|
36
|
+
return this._id;
|
|
37
|
+
}
|
|
38
|
+
get connected() {
|
|
39
|
+
return this.socket !== null && this.socket.readyState === OPEN;
|
|
40
|
+
}
|
|
41
|
+
connect() {
|
|
42
|
+
if (this.socket)
|
|
43
|
+
throw new Error('lockstep client is already connected');
|
|
44
|
+
const socket = this.createSocket(this.url);
|
|
45
|
+
socket.onmessage = (event) => this.handleMessage(event.data);
|
|
46
|
+
socket.onclose = () => {
|
|
47
|
+
this.socket = null;
|
|
48
|
+
this._id = null;
|
|
49
|
+
this.onClose.dispatch();
|
|
50
|
+
};
|
|
51
|
+
this.socket = socket;
|
|
52
|
+
}
|
|
53
|
+
/** submits this client's input for the current tick; a no-op before the connection is open */
|
|
54
|
+
submitInput(payload) {
|
|
55
|
+
if (!this.connected)
|
|
56
|
+
return;
|
|
57
|
+
this.socket?.send(JSON.stringify({ type: 'input', payload }));
|
|
58
|
+
}
|
|
59
|
+
close() {
|
|
60
|
+
this.socket?.close();
|
|
61
|
+
}
|
|
62
|
+
handleMessage(raw) {
|
|
63
|
+
const message = JSON.parse(raw);
|
|
64
|
+
if (message.type === 'welcome' && message.id !== undefined) {
|
|
65
|
+
this._id = message.id;
|
|
66
|
+
this.onWelcome.dispatch({ id: message.id });
|
|
67
|
+
}
|
|
68
|
+
else if (message.type === 'tick' && message.tick !== undefined && message.inputs !== undefined) {
|
|
69
|
+
this.onTick.dispatch({ tick: message.tick, inputs: message.inputs });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=Multiplayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Multiplayer.js","sourceRoot":"","sources":["../../src/core/Multiplayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAarC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,wFAAwF;AAcxG;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,cAAc;IAU1B,YAAY,OAA8B;QATjC,cAAS,GAAG,IAAI,MAAM,EAAkB,CAAC;QACzC,WAAM,GAAG,IAAI,MAAM,EAAa,CAAC;QACjC,YAAO,GAAG,IAAI,MAAM,EAAQ,CAAC;QAE9B,WAAM,GAAyB,IAAI,CAAC;QAGpC,QAAG,GAAkB,IAAI,CAAC;QAGjC,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrE,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,GAAG,CAA6B,CAAC,CAAC;IACjG,CAAC;IAED,iGAAiG;IACjG,IAAI,EAAE;QACL,OAAO,IAAI,CAAC,GAAG,CAAC;IACjB,CAAC;IAED,IAAI,SAAS;QACZ,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI,CAAC;IAChE,CAAC;IAED,OAAO;QACN,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,8FAA8F;IAC9F,WAAW,CAAC,OAAgB;QAC3B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACtB,CAAC;IAEO,aAAa,CAAC,GAAW;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmF,CAAC;QAClH,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC5D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;IACF,CAAC;CACD"}
|