@datamoc/mw_games 0.40.0 → 0.42.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/CHANGELOG.md +115 -0
- package/REFERENCE.md +2128 -0
- package/api-surface.json +72 -0
- package/data/microgm-opt.PROVENANCE.md +3 -0
- package/data/soundfont-catalog.json +183 -0
- package/dist/ai/index.d.ts +10 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/lua.d.ts +6 -0
- package/dist/ai/lua.d.ts.map +1 -1
- package/dist/ai/lua.js.map +1 -1
- package/dist/audio/MidiRender.d.ts +3 -1
- package/dist/audio/MidiRender.d.ts.map +1 -1
- package/dist/audio/MidiRender.js +3 -1
- package/dist/audio/MidiRender.js.map +1 -1
- package/dist/audio/SampleDecoder.d.ts +41 -0
- package/dist/audio/SampleDecoder.d.ts.map +1 -0
- package/dist/audio/SampleDecoder.js +70 -0
- package/dist/audio/SampleDecoder.js.map +1 -0
- package/dist/audio/SoundFont.d.ts +93 -1
- package/dist/audio/SoundFont.d.ts.map +1 -1
- package/dist/audio/SoundFont.js +290 -8
- package/dist/audio/SoundFont.js.map +1 -1
- package/dist/audio/index.d.ts +4 -2
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +2 -1
- package/dist/audio/index.js.map +1 -1
- package/dist/core/DialogueText.d.ts +25 -0
- package/dist/core/DialogueText.d.ts.map +1 -1
- package/dist/core/DialogueText.js +25 -0
- package/dist/core/DialogueText.js.map +1 -1
- package/dist/core/Motion.d.ts +20 -0
- package/dist/core/Motion.d.ts.map +1 -1
- package/dist/core/Motion.js +20 -0
- package/dist/core/Motion.js.map +1 -1
- package/dist/core/Save.d.ts +31 -0
- package/dist/core/Save.d.ts.map +1 -1
- package/dist/core/Save.js +31 -0
- package/dist/core/Save.js.map +1 -1
- package/dist/core/Twee.d.ts +34 -0
- package/dist/core/Twee.d.ts.map +1 -1
- package/dist/core/Twee.js +34 -0
- package/dist/core/Twee.js.map +1 -1
- package/dist/mw_games.global.js +11 -11
- package/dist/mwl/presentation.d.ts +16 -0
- package/dist/mwl/presentation.d.ts.map +1 -1
- package/dist/mwl/presentation.js.map +1 -1
- package/dist/mwl/pyroeis-runtime.d.ts +7 -0
- package/dist/mwl/pyroeis-runtime.d.ts.map +1 -1
- package/dist/mwl/pyroeis-runtime.js.map +1 -1
- package/dist/mwl/pyroeis.d.ts +3 -0
- package/dist/mwl/pyroeis.d.ts.map +1 -1
- package/dist/mwl/pyroeis.js.map +1 -1
- package/dist/physics/PhysicsWorld.d.ts +3 -0
- package/dist/physics/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/XpbdWorld.d.ts +56 -0
- package/dist/physics/XpbdWorld.d.ts.map +1 -1
- package/dist/physics/XpbdWorld.js +45 -0
- package/dist/physics/XpbdWorld.js.map +1 -1
- package/dist/physics/cloth/index.d.ts +20 -19
- package/dist/physics/cloth/index.d.ts.map +1 -1
- package/dist/physics/cloth/index.js +19 -0
- package/dist/physics/cloth/index.js.map +1 -1
- package/dist/physics/spring/index.d.ts +19 -18
- package/dist/physics/spring/index.d.ts.map +1 -1
- package/dist/physics/spring/index.js +23 -0
- package/dist/physics/spring/index.js.map +1 -1
- package/dist/three-d/Character3D.d.ts +8 -7
- package/dist/three-d/Character3D.d.ts.map +1 -1
- package/dist/three-d/Character3D.js +7 -0
- package/dist/three-d/Character3D.js.map +1 -1
- package/dist/three-d/Collision3D.d.ts +4 -0
- package/dist/three-d/Collision3D.d.ts.map +1 -1
- package/dist/three-d/Collision3D.js.map +1 -1
- package/dist/three-d/Engine3D.d.ts +2 -0
- package/dist/three-d/Engine3D.d.ts.map +1 -1
- package/dist/three-d/Engine3D.js.map +1 -1
- package/dist/three-d/Grid.d.ts +5 -0
- package/dist/three-d/Grid.d.ts.map +1 -1
- package/dist/three-d/Grid.js.map +1 -1
- package/dist/three-d/Heightmap.d.ts +3 -0
- package/dist/three-d/Heightmap.d.ts.map +1 -1
- package/dist/three-d/Heightmap.js.map +1 -1
- package/dist/three-d/TileGrid3D.d.ts +6 -0
- package/dist/three-d/TileGrid3D.d.ts.map +1 -1
- package/dist/three-d/TileGrid3D.js.map +1 -1
- package/dist/three-d/Vox.d.ts +7 -0
- package/dist/three-d/Vox.d.ts.map +1 -1
- package/dist/three-d/Vox.js.map +1 -1
- package/dist/two-d/presentation/MwlPresentation.d.ts +7 -0
- package/dist/two-d/presentation/MwlPresentation.d.ts.map +1 -1
- package/dist/two-d/presentation/MwlPresentation.js.map +1 -1
- package/dist/two-d/ui/theme.d.ts +18 -0
- package/dist/two-d/ui/theme.d.ts.map +1 -1
- package/dist/two-d/ui/theme.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +23 -1
- package/sbom.cdx.json +599 -139
- package/tools/benchmark-browser.mjs +6 -0
- package/tools/bundle-size.mjs +4 -0
- package/tools/compile-resources.d.mts +6 -0
- package/tools/compile-resources.mjs +53 -16
- package/tools/emit-page.d.mts +3 -0
- package/tools/emit-page.mjs +15 -2
- package/tools/init.mjs +3 -1
- package/tools/make-example-assets.mjs +4 -0
- package/tools/multiplayer-server.mjs +4 -0
- package/tools/mwl.mjs +7 -0
- package/tools/play-uci.mjs +4 -0
- package/tools/sbom.mjs +4 -0
- package/tools/soundfonts.d.mts +79 -0
- package/tools/soundfonts.mjs +301 -0
- package/tools/svg-optimise.d.mts +8 -0
- package/tools/svg-optimise.mjs +45 -0
- package/tools/verify-scenarios.mjs +6 -2
- package/tools/vite.mjs +2 -1
- package/tools.md +218 -0
|
@@ -136,6 +136,12 @@ export async function measurePage({
|
|
|
136
136
|
* `benchmark-results/<page>.json`, named by the page so two pages keep separate histories.
|
|
137
137
|
*/
|
|
138
138
|
async function main(argv) {
|
|
139
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
140
|
+
console.log(
|
|
141
|
+
'usage: mwg-bench <page.html | dist folder> [--frames=180] [--min-fps=45] [--max-p95=40] [--history=<file>]',
|
|
142
|
+
);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
139
145
|
const value = (name, env, fallback) =>
|
|
140
146
|
argv.find((a) => a.startsWith(`--${name}=`))?.split('=')[1] ?? process.env[env] ?? fallback;
|
|
141
147
|
const target = argv.find((arg) => !arg.startsWith('--')) ?? 'examples/dungeon/dist/index.html';
|
package/tools/bundle-size.mjs
CHANGED
|
@@ -99,6 +99,10 @@ function measureFramework(root) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function main(argv) {
|
|
102
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
103
|
+
console.log('usage: mwg-size <dist folder> [--baseline=<file>] [--tolerance=0.02] [--update]');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
102
106
|
const tolerance = Number(
|
|
103
107
|
argv.find((a) => a.startsWith('--tolerance='))?.slice(12) ?? process.env.MWG_SIZE_TOLERANCE ?? 0.02,
|
|
104
108
|
);
|
|
@@ -12,6 +12,8 @@ export interface CompileResourcesOptions {
|
|
|
12
12
|
toWebp?: boolean;
|
|
13
13
|
webpLossless?: boolean;
|
|
14
14
|
webpQuality?: number;
|
|
15
|
+
/** minify embedded `.svg` assets through the optional `svgo` package (version 4 or newer); false by default */
|
|
16
|
+
svgo?: boolean;
|
|
15
17
|
/** Subset an embedded `.sf2` to the programs the embedded `.mid`/`.midi` files use. Default true; `false` embeds every SoundFont whole. */
|
|
16
18
|
soundFont?: boolean;
|
|
17
19
|
/** Explicit `{ bank, program }` list to subset a SoundFont to, overriding the MIDI-derived list. */
|
|
@@ -29,6 +31,8 @@ export interface CompileResourcesResult {
|
|
|
29
31
|
rawBytes: number;
|
|
30
32
|
embeddedBytes: number;
|
|
31
33
|
webpConverted: number;
|
|
34
|
+
/** SVG assets embedded smaller than their source because of `svgo` */
|
|
35
|
+
svgOptimised: number;
|
|
32
36
|
skipped: number;
|
|
33
37
|
/** the `CREDITS.txt` text when the asset folder holds a `credits.json`, else null */
|
|
34
38
|
credits: string | null;
|
|
@@ -50,6 +54,8 @@ export interface SoundFontEmbed {
|
|
|
50
54
|
programs: number | null;
|
|
51
55
|
rawBytes: number;
|
|
52
56
|
embeddedBytes: number;
|
|
57
|
+
/** the programs the game's MIDI files ask for that this font does not carry; empty when none */
|
|
58
|
+
missing: SoundFontUsage[];
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
/** parses a `--soundfont-usage=bank:program,...` CLI value; undefined when the flag is absent */
|
|
@@ -41,9 +41,10 @@ export const DEFAULT_MIME = {
|
|
|
41
41
|
'.mid': 'audio/midi',
|
|
42
42
|
'.midi': 'audio/midi',
|
|
43
43
|
'.sf2': 'application/octet-stream',
|
|
44
|
+
'.sf3': 'application/octet-stream',
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
const isSoundFontKey = (key) =>
|
|
47
|
+
const isSoundFontKey = (key) => /\.sf[23]$/i.test(key);
|
|
47
48
|
const isMidiKey = (key) => /\.(mid|midi)$/i.test(key);
|
|
48
49
|
|
|
49
50
|
/**
|
|
@@ -120,7 +121,11 @@ export async function* walk(dir) {
|
|
|
120
121
|
* pixel-art tileset's exact colours survive; set false only with `webpQuality` to opt into
|
|
121
122
|
* lossy re-encoding for photographic source art)
|
|
122
123
|
* @param options.webpQuality 0-100, used only when `webpLossless` is false (default 90)
|
|
123
|
-
* @param options.
|
|
124
|
+
* @param options.svgo minify embedded `.svg` assets through the optional `svgo` package (default
|
|
125
|
+
* false - see `tools/svg-optimise.mjs`). The key never changes, only the embedded bytes, and
|
|
126
|
+
* only when the optimised file is actually smaller. A file svgo cannot parse fails the build by
|
|
127
|
+
* name rather than shipping unoptimised in silence.
|
|
128
|
+
* @param options.soundFont subset an embedded `.sf2` or `.sf3` to the programs the embedded
|
|
124
129
|
* `.mid`/`.midi` files use (default true); `false` embeds every SoundFont whole
|
|
125
130
|
* @param options.soundFontUsage explicit `{ bank, program }` list to subset to, for a
|
|
126
131
|
* game that voices notes no shipped MIDI file requests; overrides the MIDI-derived list
|
|
@@ -140,6 +145,7 @@ export async function compileResources({
|
|
|
140
145
|
toWebp: convertToWebp = false,
|
|
141
146
|
webpLossless = true,
|
|
142
147
|
webpQuality = 90,
|
|
148
|
+
svgo: optimiseSvgFiles = false,
|
|
143
149
|
soundFont = true,
|
|
144
150
|
soundFontUsage,
|
|
145
151
|
} = {}) {
|
|
@@ -204,10 +210,12 @@ export async function compileResources({
|
|
|
204
210
|
let rawBytes = 0;
|
|
205
211
|
let embeddedBytes = 0;
|
|
206
212
|
let webpConverted = 0;
|
|
213
|
+
let svgOptimised = 0;
|
|
207
214
|
|
|
208
215
|
// only paid when toWebp is actually used, so a build that never asks for it never imports
|
|
209
216
|
// (or requires installing) sharp
|
|
210
217
|
const toWebp = convertToWebp ? (await import('./webp-convert.mjs')).toWebp : null;
|
|
218
|
+
const optimiseSvg = optimiseSvgFiles ? (await import('./svg-optimise.mjs')).optimiseSvg : null;
|
|
211
219
|
|
|
212
220
|
const creditsFile = join(from, 'credits.json');
|
|
213
221
|
let credits = null;
|
|
@@ -239,6 +247,19 @@ export async function compileResources({
|
|
|
239
247
|
}
|
|
240
248
|
}
|
|
241
249
|
|
|
250
|
+
if (optimiseSvg && ext === '.svg') {
|
|
251
|
+
let optimised;
|
|
252
|
+
try {
|
|
253
|
+
optimised = await optimiseSvg(data);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
throw new Error(`cannot optimise SVG asset ${key}: ${error.message ?? error}`);
|
|
256
|
+
}
|
|
257
|
+
if (optimised.length < data.length) {
|
|
258
|
+
bytes = optimised;
|
|
259
|
+
svgOptimised++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
242
263
|
if (isSoundFontKey(key)) {
|
|
243
264
|
if (usage !== undefined && soundFontTools) {
|
|
244
265
|
try {
|
|
@@ -246,7 +267,14 @@ export async function compileResources({
|
|
|
246
267
|
} catch (error) {
|
|
247
268
|
throw new Error(`cannot subset SoundFont asset ${key}: ${error.message ?? error}`);
|
|
248
269
|
}
|
|
249
|
-
soundFonts.push({
|
|
270
|
+
soundFonts.push({
|
|
271
|
+
key,
|
|
272
|
+
programs: usage.length,
|
|
273
|
+
rawBytes: data.length,
|
|
274
|
+
embeddedBytes: bytes.length,
|
|
275
|
+
//the programs the MIDI files ask for that this font does not carry
|
|
276
|
+
missing: soundFontTools.missingSoundFontPrograms(data, usage),
|
|
277
|
+
});
|
|
250
278
|
} else {
|
|
251
279
|
soundFonts.push({ key, programs: null, rawBytes: data.length, embeddedBytes: data.length });
|
|
252
280
|
}
|
|
@@ -272,7 +300,7 @@ export async function compileResources({
|
|
|
272
300
|
|
|
273
301
|
if (credits) await writeFile(join(to, 'CREDITS.txt'), credits, 'utf8');
|
|
274
302
|
|
|
275
|
-
return { groups: written, rawBytes, embeddedBytes, webpConverted, skipped, credits, soundFonts };
|
|
303
|
+
return { groups: written, rawBytes, embeddedBytes, webpConverted, svgOptimised, skipped, credits, soundFonts };
|
|
276
304
|
}
|
|
277
305
|
|
|
278
306
|
/** the plain-text credits for a manifest, one block per entry; throws on an entry that cannot be attributed */
|
|
@@ -301,7 +329,7 @@ async function runCli() {
|
|
|
301
329
|
const [from, to] = args.filter((a) => !a.startsWith('--'));
|
|
302
330
|
if (!from || !to) {
|
|
303
331
|
console.error(
|
|
304
|
-
'usage: node compile-resources.mjs <asset folder> <output folder> [--to-webp] [--webp-lossy[=quality]] [--no-soundfont-subset] [--soundfont-usage=bank:program,...]',
|
|
332
|
+
'usage: node compile-resources.mjs <asset folder> <output folder> [--to-webp] [--webp-lossy[=quality]] [--svgo] [--no-soundfont-subset] [--soundfont-usage=bank:program,...]',
|
|
305
333
|
);
|
|
306
334
|
process.exit(1);
|
|
307
335
|
}
|
|
@@ -314,17 +342,19 @@ async function runCli() {
|
|
|
314
342
|
args.find((a) => a.startsWith('--soundfont-usage='))?.slice('--soundfont-usage='.length),
|
|
315
343
|
);
|
|
316
344
|
|
|
317
|
-
const { groups, rawBytes, embeddedBytes, webpConverted, skipped, soundFonts } =
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
345
|
+
const { groups, rawBytes, embeddedBytes, webpConverted, svgOptimised, skipped, soundFonts } =
|
|
346
|
+
await compileResources({
|
|
347
|
+
from,
|
|
348
|
+
to,
|
|
349
|
+
//a flat folder has no subdirectories to group by, so everything lands in one file
|
|
350
|
+
groupBy: (key) => (key.includes('/') ? key.slice(0, key.indexOf('/')) : 'assets'),
|
|
351
|
+
toWebp,
|
|
352
|
+
webpLossless,
|
|
353
|
+
webpQuality,
|
|
354
|
+
svgo: args.includes('--svgo'),
|
|
355
|
+
soundFont: !args.includes('--no-soundfont-subset'),
|
|
356
|
+
soundFontUsage,
|
|
357
|
+
});
|
|
328
358
|
|
|
329
359
|
const kb = (n) => (n / 1024).toFixed(1).padStart(8) + ' KB';
|
|
330
360
|
for (const { name, count, size } of groups) {
|
|
@@ -337,7 +367,14 @@ async function runCli() {
|
|
|
337
367
|
? ` ${font.key}: SoundFont embedded whole (${kb(font.rawBytes)}), no shipped MIDI to subset against`
|
|
338
368
|
: ` ${font.key}: SoundFont subset to ${font.programs} program(s), ${kb(font.rawBytes)} -> ${kb(font.embeddedBytes)}`,
|
|
339
369
|
);
|
|
370
|
+
if (font.missing?.length > 0) {
|
|
371
|
+
console.log(
|
|
372
|
+
` warning: ${font.key} does not carry ${font.missing.map(({ bank, program }) => `${bank}:${program}`).join(', ')}: ` +
|
|
373
|
+
"they play the font's piano, or the built-in synthesizer with `missingProgram: 'synth'`",
|
|
374
|
+
);
|
|
375
|
+
}
|
|
340
376
|
}
|
|
377
|
+
if (args.includes('--svgo')) console.log(` ${svgOptimised} SVG(s) optimised`);
|
|
341
378
|
if (toWebp) {
|
|
342
379
|
console.log(
|
|
343
380
|
` ${webpConverted} image(s) converted to WebP (${webpLossless ? 'lossless' : `lossy, quality ${webpQuality}`}), ` +
|
package/tools/emit-page.d.mts
CHANGED
|
@@ -20,6 +20,8 @@ export interface EmitPageOptions {
|
|
|
20
20
|
toWebp?: boolean;
|
|
21
21
|
webpLossless?: boolean;
|
|
22
22
|
webpQuality?: number;
|
|
23
|
+
/** minify embedded `.svg` assets through the optional `svgo` package; false by default */
|
|
24
|
+
svgo?: boolean;
|
|
23
25
|
/** Subset an embedded `.sf2` to the programs the embedded `.mid`/`.midi` files use. Default true; `false` embeds every SoundFont whole. */
|
|
24
26
|
soundFont?: boolean;
|
|
25
27
|
/** Explicit `{ bank, program }` list to subset a SoundFont to, overriding the MIDI-derived list. */
|
|
@@ -42,6 +44,7 @@ export interface EmitPageResult {
|
|
|
42
44
|
rawBytes: number;
|
|
43
45
|
embeddedBytes: number;
|
|
44
46
|
webpConverted: number;
|
|
47
|
+
svgOptimised: number;
|
|
45
48
|
/** one entry per embedded `.sf2`: the programs kept (null when embedded whole) and the byte saving */
|
|
46
49
|
soundFonts: SoundFontEmbed[];
|
|
47
50
|
compressed: CompressRow[] | null;
|
package/tools/emit-page.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import { withContentSecurityPolicy } from './csp.mjs';
|
|
|
29
29
|
* usage: mwg-emit <project folder> [--dist=<dir>] [--assets=<dir>] [--no-csp] [--connect=<origin>]...
|
|
30
30
|
* [--no-compress] [--xz]
|
|
31
31
|
* [--single-file] [--single-file-compress[=level]]
|
|
32
|
-
* [--single-file-brotli[=quality]] [--to-webp] [--webp-lossy[=quality]]
|
|
32
|
+
* [--single-file-brotli[=quality]] [--to-webp] [--webp-lossy[=quality]] [--svgo]
|
|
33
33
|
* [--no-soundfont-subset] [--soundfont-usage=bank:program,...]
|
|
34
34
|
*
|
|
35
35
|
* `--dist` defaults to `<project folder>/dist` and `--assets` to `<project folder>/assets`; with
|
|
@@ -53,6 +53,9 @@ import { withContentSecurityPolicy } from './csp.mjs';
|
|
|
53
53
|
* `--to-webp` converts `.png`/`.jpg`/`.jpeg` example assets to WebP before embedding (see
|
|
54
54
|
* `tools/webp-convert.mjs`), lossless by default; `--webp-lossy[=quality]` opts into lossy
|
|
55
55
|
* re-encoding instead. Needs the optional `sharp` devDependency installed.
|
|
56
|
+
*
|
|
57
|
+
* `--svgo` minifies embedded `.svg` assets (see `tools/svg-optimise.mjs`), keeping the `viewBox` and
|
|
58
|
+
* every id. Needs the optional `svgo` package installed.
|
|
56
59
|
*/
|
|
57
60
|
|
|
58
61
|
/**
|
|
@@ -74,6 +77,7 @@ export async function emitPage({
|
|
|
74
77
|
toWebp = false,
|
|
75
78
|
webpLossless = true,
|
|
76
79
|
webpQuality = 90,
|
|
80
|
+
svgo = false,
|
|
77
81
|
soundFont = true,
|
|
78
82
|
soundFontUsage,
|
|
79
83
|
csp = true,
|
|
@@ -89,10 +93,11 @@ export async function emitPage({
|
|
|
89
93
|
toWebp,
|
|
90
94
|
webpLossless,
|
|
91
95
|
webpQuality,
|
|
96
|
+
svgo,
|
|
92
97
|
soundFont,
|
|
93
98
|
soundFontUsage,
|
|
94
99
|
})
|
|
95
|
-
: { groups: [], rawBytes: 0, embeddedBytes: 0, webpConverted: 0, soundFonts: [] };
|
|
100
|
+
: { groups: [], rawBytes: 0, embeddedBytes: 0, webpConverted: 0, svgOptimised: 0, soundFonts: [] };
|
|
96
101
|
|
|
97
102
|
const html = await readFile(join(dist, 'index.html'), 'utf8');
|
|
98
103
|
const classic = toClassicScript(html);
|
|
@@ -162,6 +167,7 @@ async function main(argv) {
|
|
|
162
167
|
toWebp: argv.includes('--to-webp'),
|
|
163
168
|
webpLossless: !webpLossyFlag,
|
|
164
169
|
webpQuality: webpLossyFlag?.includes('=') ? Number(webpLossyFlag.split('=')[1]) : 90,
|
|
170
|
+
svgo: argv.includes('--svgo'),
|
|
165
171
|
soundFont: !argv.includes('--no-soundfont-subset'),
|
|
166
172
|
soundFontUsage,
|
|
167
173
|
csp: argv.includes('--no-csp')
|
|
@@ -179,12 +185,19 @@ async function main(argv) {
|
|
|
179
185
|
`${kb(result.rawBytes)} -> ${kb(result.embeddedBytes)} embedded`,
|
|
180
186
|
);
|
|
181
187
|
}
|
|
188
|
+
if (argv.includes('--svgo')) console.log(` ${result.svgOptimised} SVG(s) optimised`);
|
|
182
189
|
for (const font of result.soundFonts ?? []) {
|
|
183
190
|
console.log(
|
|
184
191
|
font.programs === null
|
|
185
192
|
? ` ${font.key}: SoundFont embedded whole (${kb(font.rawBytes)}), no shipped MIDI to subset against`
|
|
186
193
|
: ` ${font.key}: SoundFont subset to ${font.programs} program(s), ${kb(font.rawBytes)} -> ${kb(font.embeddedBytes)}`,
|
|
187
194
|
);
|
|
195
|
+
if (font.missing?.length > 0) {
|
|
196
|
+
console.log(
|
|
197
|
+
` warning: ${font.key} does not carry ${font.missing.map(({ bank, program }) => `${bank}:${program}`).join(', ')}: ` +
|
|
198
|
+
"they play the font's piano, or the built-in synthesizer with `missingProgram: 'synth'`",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
188
201
|
}
|
|
189
202
|
console.log(' open index.html directly - no server needed');
|
|
190
203
|
if (result.compressed) {
|
package/tools/init.mjs
CHANGED
|
@@ -130,7 +130,9 @@ export function initProject(folder) {
|
|
|
130
130
|
//no top-level await: this module is also imported, and the real path covers an npm bin symlink
|
|
131
131
|
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
132
132
|
const folder = process.argv[2];
|
|
133
|
-
if (
|
|
133
|
+
if (folder === '--help' || folder === '-h') {
|
|
134
|
+
console.log('usage: mwg-init <new folder>');
|
|
135
|
+
} else if (!folder) {
|
|
134
136
|
console.error('usage: mwg-init <new folder>');
|
|
135
137
|
process.exitCode = 2;
|
|
136
138
|
} else {
|
|
@@ -717,6 +717,10 @@ if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.me
|
|
|
717
717
|
const args = process.argv.slice(2);
|
|
718
718
|
const folder = args.find((arg) => !arg.startsWith('--'));
|
|
719
719
|
const inRepository = existsSync(join(here, '..', 'examples'));
|
|
720
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
721
|
+
console.log('usage: mwg-placeholder-assets <folder> [--force]');
|
|
722
|
+
process.exit(0);
|
|
723
|
+
}
|
|
720
724
|
if (!folder && !inRepository) {
|
|
721
725
|
console.error('usage: mwg-placeholder-assets <folder> [--force]');
|
|
722
726
|
process.exit(1);
|
|
@@ -187,6 +187,10 @@ export function createLockstepServer(options = {}) {
|
|
|
187
187
|
//realpath, since an npm `bin` shim reaches this file through a symlink
|
|
188
188
|
if (process.argv[1] && realpathSync(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
189
189
|
const args = process.argv.slice(2);
|
|
190
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
191
|
+
console.log('usage: mwg-lockstep-server [port] [--host=<address>]');
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
190
194
|
const port = Number(args.find((arg) => !arg.startsWith('--')) ?? 8787);
|
|
191
195
|
const host = args.find((arg) => arg.startsWith('--host='))?.slice(7) ?? '127.0.0.1';
|
|
192
196
|
const server = createLockstepServer({ port, host });
|
package/tools/mwl.mjs
CHANGED
|
@@ -65,6 +65,13 @@ const assetRoot = assetRootFlag === -1 ? undefined : args[assetRootFlag + 1];
|
|
|
65
65
|
const targetFlag = args.indexOf('--to');
|
|
66
66
|
const target = targetFlag === -1 ? undefined : args[targetFlag + 1];
|
|
67
67
|
|
|
68
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
69
|
+
console.log(
|
|
70
|
+
'usage: mwg-mwl <validate|compile|format|migrate|extract-i18n|assets|report|diagnostics|ci|hooks|build> input.mwl [-o output] [--to 1.0] [--manifest hooks.json] [--asset-root directory]',
|
|
71
|
+
);
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
|
|
68
75
|
if (!command || !input || !commands.includes(command)) {
|
|
69
76
|
console.error(
|
|
70
77
|
'Usage: mwl <validate|compile|format|migrate|extract-i18n|assets|report|diagnostics|ci|hooks|build> input.mwl [-o output] [--to 1.0] [--manifest hooks.json] [--asset-root directory]',
|
package/tools/play-uci.mjs
CHANGED
|
@@ -171,6 +171,10 @@ const isMain = process.argv[1] && realpathSync(process.argv[1]) === fileURLToPat
|
|
|
171
171
|
|
|
172
172
|
if (isMain) {
|
|
173
173
|
const args = process.argv.slice(2);
|
|
174
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
175
|
+
console.log('usage: mwg-uci [--book <book.json>] [--tables <dir>] [--depth N]');
|
|
176
|
+
process.exit(0);
|
|
177
|
+
}
|
|
174
178
|
const flag = (name) => {
|
|
175
179
|
const at = args.indexOf(name);
|
|
176
180
|
return at === -1 ? undefined : args[at + 1];
|
package/tools/sbom.mjs
CHANGED
|
@@ -301,6 +301,10 @@ async function readJson(path) {
|
|
|
301
301
|
* `application`, anything else as a `library`. `--check` compares instead of writing, for CI.
|
|
302
302
|
*/
|
|
303
303
|
async function main(argv) {
|
|
304
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
305
|
+
console.log('usage: mwg-sbom [project folder] [--out=<file>] [--check]');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
304
308
|
const root = resolve(argv.find((arg) => !arg.startsWith('--')) ?? '.');
|
|
305
309
|
const out = resolve(root, argv.find((arg) => arg.startsWith('--out='))?.slice(6) ?? OUTPUT);
|
|
306
310
|
const packageJson = await readJson(join(root, 'package.json'));
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** how freely a catalogue font may be shipped inside a game */
|
|
2
|
+
export type SoundFontLicenceClass =
|
|
3
|
+
'public-domain' | 'permissive' | 'attribution' | 'share-alike' | 'copyleft' | 'check';
|
|
4
|
+
|
|
5
|
+
export interface SoundFontCatalogEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
licence: string;
|
|
8
|
+
licenceClass: SoundFontLicenceClass;
|
|
9
|
+
web?: string;
|
|
10
|
+
/** instrument files the collection carries for this font (programs for a bundled bank) */
|
|
11
|
+
programs: number;
|
|
12
|
+
/** megabytes of a full General MIDI bank, SoundFont 3 compressed; absent for the bundled bank */
|
|
13
|
+
gmMB?: number;
|
|
14
|
+
/** megabytes of every instrument file, SoundFont 3 compressed (the bundled bank's own size) */
|
|
15
|
+
fullMB: number;
|
|
16
|
+
/** the file of a bundled bank, relative to the package root */
|
|
17
|
+
file?: string;
|
|
18
|
+
note?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SoundFontCatalog {
|
|
22
|
+
description: string;
|
|
23
|
+
/** where the per-instrument files are fetched from by default */
|
|
24
|
+
source: string;
|
|
25
|
+
/** the name of the bank the package ships and games use unless they choose another */
|
|
26
|
+
default: string;
|
|
27
|
+
bundled: SoundFontCatalogEntry[];
|
|
28
|
+
fonts: SoundFontCatalogEntry[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SoundFontProgramRef {
|
|
32
|
+
bank: number;
|
|
33
|
+
program: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface FetchSoundFontOptions {
|
|
37
|
+
/** a catalogue font name, from `mwg-soundfonts list` */
|
|
38
|
+
name: string;
|
|
39
|
+
programs: SoundFontProgramRef[];
|
|
40
|
+
catalog?: SoundFontCatalog;
|
|
41
|
+
/** a folder or `http(s)` URL laid out as `<font>/<bank>/<program>.sf3`; the catalogue's source by default */
|
|
42
|
+
source?: string;
|
|
43
|
+
/** required for a licence class that is not public-domain, permissive or attribution */
|
|
44
|
+
acceptLicence?: boolean;
|
|
45
|
+
/** the decoded font is a SoundFont 2 (`ffmpeg` needed) instead of a SoundFont 3; `sf3` by default */
|
|
46
|
+
format?: 'sf2' | 'sf3';
|
|
47
|
+
/** Ogg Vorbis bytes to mono 16-bit frames, for `format: 'sf2'`; `ffmpeg` by default */
|
|
48
|
+
decode?: (ogg: Uint8Array, rate: number) => Int16Array | Promise<Int16Array>;
|
|
49
|
+
/** replaces the file reader, mainly for tests */
|
|
50
|
+
read?: (path: string) => Promise<Uint8Array | null>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FetchedSoundFont {
|
|
54
|
+
/** one font holding the found programs, in `format` */
|
|
55
|
+
bytes: Uint8Array;
|
|
56
|
+
format: 'sf2' | 'sf3';
|
|
57
|
+
found: SoundFontProgramRef[];
|
|
58
|
+
/** requested programs the collection does not carry */
|
|
59
|
+
missing: SoundFontProgramRef[];
|
|
60
|
+
font: SoundFontCatalogEntry;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** the licence-classified catalogue of fetchable fonts */
|
|
64
|
+
export declare const CATALOG_PATH: string;
|
|
65
|
+
export declare function readCatalog(path?: string): SoundFontCatalog;
|
|
66
|
+
export declare function findFont(catalog: SoundFontCatalog, name: string): SoundFontCatalogEntry | undefined;
|
|
67
|
+
/** `"0,24,128:0"`: a bare number is a program of bank 0, `bank:program` names both */
|
|
68
|
+
export declare function parseProgramList(text: string): SoundFontProgramRef[];
|
|
69
|
+
/** mono 16-bit frames from Ogg Vorbis bytes, through `ffmpeg` */
|
|
70
|
+
export declare function ffmpegDecode(ogg: Uint8Array): Int16Array;
|
|
71
|
+
/** a SoundFont 3 decoded into SoundFont 2 bytes (`ffmpeg` unless `decode` is given) */
|
|
72
|
+
export declare function sf3ToSf2(
|
|
73
|
+
bytes: Uint8Array,
|
|
74
|
+
decode?: (ogg: Uint8Array, rate: number) => Int16Array | Promise<Int16Array>,
|
|
75
|
+
): Promise<Uint8Array>;
|
|
76
|
+
export declare function readCollectionFile(source: string, path: string): Promise<Uint8Array | null>;
|
|
77
|
+
export declare function fetchSoundFont(options: FetchSoundFontOptions): Promise<FetchedSoundFont>;
|
|
78
|
+
/** the credits note that travels beside a fetched font */
|
|
79
|
+
export declare function creditsFor(font: SoundFontCatalogEntry, programs: SoundFontProgramRef[]): string;
|