@ttmg/cli 0.3.9 → 0.4.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 +6 -1
- package/dist/index.js +280 -61
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/public/assets/{Card-DvUkoCA3.js → Card-BMss5cuV.js} +1 -1
- package/dist/public/assets/{Detail-Cbq9dDUe.js → Detail-Lyas-t6F.js} +1 -1
- package/dist/public/assets/Detail-Lyas-t6F.js.br +0 -0
- package/dist/public/assets/{MonetizationMode-0taoFj2g.js → MonetizationMode-DI4fy3U7.js} +1 -1
- package/dist/public/assets/{MonetizationModeSummary--L4WcmYS.js → MonetizationModeSummary-B6yEB26H.js} +1 -1
- package/dist/public/assets/{SectionHeader-DfBBWmpa.js → SectionHeader-3b39hv3n.js} +1 -1
- package/dist/public/assets/{Tag-DuP9fCS0.js → Tag-B5gYnANu.js} +1 -1
- package/dist/public/assets/{arrow-left-1V5G0Kw7.js → arrow-left-B3swWB64.js} +1 -1
- package/dist/public/assets/{baseForm-2J62W6xr.js → baseForm-Dsi8Ia0k.js} +1 -1
- package/dist/public/assets/baseForm-Dsi8Ia0k.js.br +0 -0
- package/dist/public/assets/{chevron-right-C5K8xBff.js → chevron-right-WZeuFw11.js} +1 -1
- package/dist/public/assets/{compass-DZKQ36UU.js → compass-CCsIev11.js} +1 -1
- package/dist/public/assets/{index-CZ7VVYfd.js → index-B3-gsm5-.js} +1 -1
- package/dist/public/assets/index-B3L1GnMP.js +1 -0
- package/dist/public/assets/index-B3L1GnMP.js.br +0 -0
- package/dist/public/assets/{index-BaZPp3HP.js → index-BJV8-tz8.js} +1 -1
- package/dist/public/assets/{index-cPr70sDS.js → index-BqSAtEgq.js} +1 -1
- package/dist/public/assets/{index-CmVUh50W.css → index-ByX5RuFA.css} +1 -1
- package/dist/public/assets/{index-WZnCtUAc.js → index-C2NWNiPX.js} +1 -1
- package/dist/public/assets/{index-BdDJLxXD.js → index-C9HXjiVu.js} +1 -1
- package/dist/public/assets/{index-DqoKSlOn.js → index-C9Un1hFP.js} +1 -1
- package/dist/public/assets/{index-Bmw61rl1.css → index-CFd8iglC.css} +1 -1
- package/dist/public/assets/index-CFd8iglC.css.br +0 -0
- package/dist/public/assets/{index-fS7DhM40.js → index-CV2u_S0E.js} +1 -1
- package/dist/public/assets/index-CV2u_S0E.js.br +0 -0
- package/dist/public/assets/index-Ci-lqt1V.js +1 -0
- package/dist/public/assets/index-Ci-lqt1V.js.br +0 -0
- package/dist/public/assets/{index-DYxQAjNc.js → index-Cigxnpav.js} +1 -1
- package/dist/public/assets/index-Cigxnpav.js.br +0 -0
- package/dist/public/assets/{index-BWWe1lsP.js → index-D0xEiy7C.js} +1 -1
- package/dist/public/assets/{index-DNktXE2W.js → index-D1oGAPRa.js} +1 -1
- package/dist/public/assets/{index-Br2fR8kJ.js → index-DeL2bgxo.js} +1 -1
- package/dist/public/assets/{index-B13yZ-GH.js → index-DjY3Igd6.js} +1 -1
- package/dist/public/assets/{index-CcxOfQrE.js → index-DpWpqPKC.js} +1 -1
- package/dist/public/assets/index-DpWpqPKC.js.br +0 -0
- package/dist/public/assets/{index-CnReCFYA.js → index-DxISN0Xc.js} +5 -5
- package/dist/public/assets/index-DxISN0Xc.js.br +0 -0
- package/dist/public/assets/{index-CgTtqphq.js → index-HYSh4-Ri.js} +1 -1
- package/dist/public/assets/{index-B-k2MmlV.js → index-JNUqDBWt.js} +1 -1
- package/dist/public/assets/{index-CwYCdibK.js → index-aqIfJUqW.js} +1 -1
- package/dist/public/assets/{index-BttcxiCg.js → index-uquhwGkB.js} +1 -1
- package/dist/public/assets/{sparkles-tr6K1Npg.js → sparkles-D2QPhLAu.js} +1 -1
- package/dist/public/assets/{zap-CuMRVa_B.js → zap-Cv4X8yRx.js} +1 -1
- package/dist/public/index.html +1 -1
- package/package.json +1 -1
- package/dist/public/assets/Detail-Cbq9dDUe.js.br +0 -0
- package/dist/public/assets/baseForm-2J62W6xr.js.br +0 -0
- package/dist/public/assets/index-Bmw61rl1.css.br +0 -0
- package/dist/public/assets/index-CcxOfQrE.js.br +0 -0
- package/dist/public/assets/index-CnReCFYA.js.br +0 -0
- package/dist/public/assets/index-DYxQAjNc.js.br +0 -0
- package/dist/public/assets/index-YkO-JjYA.js +0 -1
- package/dist/public/assets/index-fS7DhM40.js.br +0 -0
- package/dist/public/assets/index-qHht7NSU.js +0 -1
- package/dist/public/assets/index-qHht7NSU.js.br +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -238,4 +238,9 @@ API 预检建议文案统一收敛,避免重复提示,阅读更清晰
|
|
|
238
238
|
6. 新版本提示由红点改为「有更新」文字标签,含义更直观。
|
|
239
239
|
7. 上传游戏包按钮配色与产品主题色统一。
|
|
240
240
|
8. 修复多个页面中标题与正文内容未对齐的问题,整体排版更整齐。
|
|
241
|
-
9. 文案细节调整:「身份与授权」更名为「登录与授权」;移除侧边栏冗余的「查看文档」入口。
|
|
241
|
+
9. 文案细节调整:「身份与授权」更名为「登录与授权」;移除侧边栏冗余的「查看文档」入口。
|
|
242
|
+
|
|
243
|
+
## 0.3.9-beta.1
|
|
244
|
+
本次测试版本主要补充启动模式场景能力,并优化场景命名可理解性:
|
|
245
|
+
|
|
246
|
+
1. 启动模式新增 `inbox` 场景,支持在调试链路中模拟消息入口启动小游戏。
|
package/dist/index.js
CHANGED
|
@@ -8372,8 +8372,81 @@ async function compile(context) {
|
|
|
8372
8372
|
}
|
|
8373
8373
|
|
|
8374
8374
|
// import { uploadGame } from './uploadGame';
|
|
8375
|
+
/* ---------------------------------------------------------------------------
|
|
8376
|
+
* Watch suspension control
|
|
8377
|
+
*
|
|
8378
|
+
* The wasm-split pipeline (prepare / split download / rollback) intentionally
|
|
8379
|
+
* rewrites a burst of project files — the instrumented wasm, the `wasmcode*`
|
|
8380
|
+
* subpackage dirs, `game.json`, `webgl-wasm-split.js`. Without gating, every
|
|
8381
|
+
* one of those writes trips the watcher below, and each trip runs a full
|
|
8382
|
+
* `compile` + device `resourceChange` reload *in the middle of the pipeline*.
|
|
8383
|
+
* That both wastes cycles and can reload the device onto a half-written /
|
|
8384
|
+
* transient build. We suspend the watcher around those operations and fire
|
|
8385
|
+
* exactly one update after they complete ("完成之后再触发更新").
|
|
8386
|
+
*
|
|
8387
|
+
* `suspendDepth` is a refcount so overlapping / nested operations are safe.
|
|
8388
|
+
* `muteUntil` keeps swallowing events for a short cooldown AFTER resume:
|
|
8389
|
+
* chokidar's `awaitWriteFinish` delays events ~1s past the actual write, so
|
|
8390
|
+
* those late events from our own writes would otherwise land after we've
|
|
8391
|
+
* resumed and re-trigger the very reload we just suppressed.
|
|
8392
|
+
* ------------------------------------------------------------------------- */
|
|
8393
|
+
let suspendDepth = 0;
|
|
8394
|
+
let muteUntil = 0;
|
|
8395
|
+
let pendingWhileSuspended = false;
|
|
8396
|
+
// chokidar awaitWriteFinish stabilityThreshold (1000ms) + headroom, so the
|
|
8397
|
+
// delayed FS events caused by the pipeline's own writes are swallowed.
|
|
8398
|
+
const RESUME_COOLDOWN_MS = 2500;
|
|
8399
|
+
// Wired up inside `watch()` so suspend/resume can reuse the exact same
|
|
8400
|
+
// (debounced) compile + resourceChange action the watcher fires normally.
|
|
8401
|
+
let triggerUpdate = null;
|
|
8402
|
+
function suspendWatch() {
|
|
8403
|
+
suspendDepth += 1;
|
|
8404
|
+
}
|
|
8405
|
+
function resumeWatch(options = {}) {
|
|
8406
|
+
if (suspendDepth > 0) {
|
|
8407
|
+
suspendDepth -= 1;
|
|
8408
|
+
}
|
|
8409
|
+
if (suspendDepth > 0) {
|
|
8410
|
+
return;
|
|
8411
|
+
}
|
|
8412
|
+
// Back to depth 0 — start the cooldown that swallows our own delayed FS
|
|
8413
|
+
// events, then emit a single update if anything changed while suspended
|
|
8414
|
+
// (or the caller forces it).
|
|
8415
|
+
muteUntil = Date.now() + RESUME_COOLDOWN_MS;
|
|
8416
|
+
const shouldEmit = options.emit ?? pendingWhileSuspended;
|
|
8417
|
+
pendingWhileSuspended = false;
|
|
8418
|
+
if (shouldEmit) {
|
|
8419
|
+
triggerUpdate?.();
|
|
8420
|
+
}
|
|
8421
|
+
}
|
|
8422
|
+
/**
|
|
8423
|
+
* Run a project-file-mutating operation with the watcher muted, then fire a
|
|
8424
|
+
* single update once it finishes. Use this around the wasm-split pipeline
|
|
8425
|
+
* steps so their intermediate writes don't each reload the device.
|
|
8426
|
+
*/
|
|
8427
|
+
async function withWatchSuspended(fn, options = {}) {
|
|
8428
|
+
suspendWatch();
|
|
8429
|
+
try {
|
|
8430
|
+
return await fn();
|
|
8431
|
+
}
|
|
8432
|
+
finally {
|
|
8433
|
+
resumeWatch({ emit: options.emitOnFinish ?? true });
|
|
8434
|
+
}
|
|
8435
|
+
}
|
|
8375
8436
|
async function watch() {
|
|
8376
8437
|
let debounceTimer = null;
|
|
8438
|
+
const runUpdate = async () => {
|
|
8439
|
+
await compile({ mode: 'watch' });
|
|
8440
|
+
wsServer.sendResourceChange();
|
|
8441
|
+
};
|
|
8442
|
+
triggerUpdate = () => {
|
|
8443
|
+
if (debounceTimer)
|
|
8444
|
+
clearTimeout(debounceTimer);
|
|
8445
|
+
debounceTimer = setTimeout(() => {
|
|
8446
|
+
runUpdate();
|
|
8447
|
+
debounceTimer = null;
|
|
8448
|
+
}, 2000);
|
|
8449
|
+
};
|
|
8377
8450
|
// 监听当前工作目录,排除 node_modules 和 .git
|
|
8378
8451
|
const watcher = chokidar.watch(process.cwd(), {
|
|
8379
8452
|
/**
|
|
@@ -8388,17 +8461,20 @@ async function watch() {
|
|
|
8388
8461
|
});
|
|
8389
8462
|
// 任意文件变化都触发
|
|
8390
8463
|
watcher.on('all', (event, path) => {
|
|
8391
|
-
//
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8464
|
+
// Pipeline is actively rewriting project files. Remember that something
|
|
8465
|
+
// changed so the resume flush can fire one update, and skip the per-write
|
|
8466
|
+
// compile/reload.
|
|
8467
|
+
if (suspendDepth > 0) {
|
|
8468
|
+
pendingWhileSuspended = true;
|
|
8469
|
+
return;
|
|
8470
|
+
}
|
|
8471
|
+
// Cooldown window right after a pipeline finished: these are the delayed
|
|
8472
|
+
// `awaitWriteFinish` events from the pipeline's own writes. We already
|
|
8473
|
+
// emitted one update on resume, so swallow them to avoid a double reload.
|
|
8474
|
+
if (Date.now() < muteUntil) {
|
|
8475
|
+
return;
|
|
8476
|
+
}
|
|
8477
|
+
triggerUpdate?.();
|
|
8402
8478
|
});
|
|
8403
8479
|
watcher.on('error', error => {
|
|
8404
8480
|
// console.error(chalk.red('[watch] 监听发生错误:'), error);
|
|
@@ -10490,42 +10566,83 @@ function restoreSplitConfigFromCache(entryDir = process.cwd()) {
|
|
|
10490
10566
|
* to change.
|
|
10491
10567
|
*/
|
|
10492
10568
|
function restoreFromCache(entryDir = process.cwd()) {
|
|
10569
|
+
// Rollback rewrites a burst of project files (restored wasm, game.json,
|
|
10570
|
+
// split config, removed split-output dirs). Mute the dev-server watcher for
|
|
10571
|
+
// the duration so it doesn't fire a compile + device reload per file, then
|
|
10572
|
+
// emit a single update once the project is back to its restored state.
|
|
10573
|
+
suspendWatch();
|
|
10574
|
+
try {
|
|
10575
|
+
restoreFromCacheInner(entryDir);
|
|
10576
|
+
}
|
|
10577
|
+
finally {
|
|
10578
|
+
resumeWatch({ emit: true });
|
|
10579
|
+
}
|
|
10580
|
+
}
|
|
10581
|
+
function restoreFromCacheInner(entryDir) {
|
|
10493
10582
|
const cacheDir = path.join(entryDir, WASM_SPLIT_CACHE_DIR);
|
|
10494
|
-
// 1) Wipe stale split residue inside wasmcode/ first, THEN restore the
|
|
10495
|
-
// original. Order matters: if we restore first then wipe, we'd delete
|
|
10496
|
-
// the very file we just brought back.
|
|
10497
10583
|
const originDir = path.join(entryDir, WASM_SPLIT_SUBPACKAGE_CONFIG.origin.root);
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10584
|
+
// Resolve the cached original wasm BEFORE touching anything on disk. The
|
|
10585
|
+
// previous order (wipe `wasmcode/*.br` first, then look for a cached
|
|
10586
|
+
// original to restore) was destructive when the cache had no original to
|
|
10587
|
+
// give back — e.g. the project was duplicated without `__TTMG_TEMP__`, the
|
|
10588
|
+
// temp dir was cleared, or split completed in a session whose cache is gone.
|
|
10589
|
+
// In that case it deleted the project's only wasm and restored nothing,
|
|
10590
|
+
// leaving `wasmcode/` empty while `game.json` still pointed at the now-missing
|
|
10591
|
+
// `<md5>.wasm.br`. The next prepare then crashed the dev server on an ENOENT
|
|
10592
|
+
// decompress. We only clean + restore when we have a confirmed replacement.
|
|
10593
|
+
const cachedWasmBr = fs.existsSync(cacheDir)
|
|
10594
|
+
? fs.readdirSync(cacheDir).find(item => item.endsWith('.br'))
|
|
10595
|
+
: undefined;
|
|
10596
|
+
if (cachedWasmBr) {
|
|
10597
|
+
// Safe to wipe split residue first: we have a confirmed original to put
|
|
10598
|
+
// back. Order matters — restoring first then wiping would delete the file
|
|
10599
|
+
// we just brought back.
|
|
10600
|
+
if (fs.existsSync(originDir)) {
|
|
10601
|
+
for (const entry of fs.readdirSync(originDir)) {
|
|
10602
|
+
// Only clean files split is known to write — `.br` (main wasm) and
|
|
10603
|
+
// the empty `game.js` placeholder. Touching anything else risks
|
|
10604
|
+
// nuking developer-authored content that happens to live in
|
|
10605
|
+
// wasmcode/ for unrelated reasons.
|
|
10606
|
+
if (entry.endsWith('.br') || entry === 'game.js') {
|
|
10607
|
+
fs.rmSync(path.join(originDir, entry), { force: true });
|
|
10608
|
+
}
|
|
10506
10609
|
}
|
|
10507
10610
|
}
|
|
10611
|
+
const destWasmBrPath = path.join(originDir, path.basename(cachedWasmBr));
|
|
10612
|
+
ensureDirSync(path.dirname(destWasmBrPath));
|
|
10613
|
+
fs.copyFileSync(path.join(cacheDir, cachedWasmBr), destWasmBrPath);
|
|
10508
10614
|
}
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10615
|
+
else {
|
|
10616
|
+
// No cached original. Do NOT delete whatever wasm is currently on disk —
|
|
10617
|
+
// it is all the project has left. Preserve it so the project can still
|
|
10618
|
+
// boot and a fresh prepare can re-seed the cache, rather than wiping it and
|
|
10619
|
+
// crashing the next prepare. The split-output dirs below are still cleaned.
|
|
10620
|
+
console.warn('[wasmtool] restoreFromCache: no cached original wasm found in ' +
|
|
10621
|
+
`${WASM_SPLIT_CACHE_DIR}; skipping wasmcode/*.br cleanup to avoid ` +
|
|
10622
|
+
'leaving the project without a wasm binary.');
|
|
10623
|
+
}
|
|
10624
|
+
// Only roll back the split config + game.json when we actually restored the
|
|
10625
|
+
// original wasm above. Restoring game.json (which points at the original
|
|
10626
|
+
// `<md5>.wasm.br`) while the matching wasm is NOT on disk recreates exactly
|
|
10627
|
+
// the broken state this guard exists to prevent — a game.json referencing a
|
|
10628
|
+
// file that isn't there. keepCacheSync writes the wasm, config and game.json
|
|
10629
|
+
// together, so a cache that has game.json but no `.br` is partial/corrupt and
|
|
10630
|
+
// must not be applied.
|
|
10631
|
+
if (cachedWasmBr) {
|
|
10632
|
+
const splitConfigCachePath = path.join(cacheDir, WASM_SPLIT_CONFIG_FILE_NAME);
|
|
10633
|
+
if (fs.existsSync(splitConfigCachePath)) {
|
|
10634
|
+
fs.copyFileSync(splitConfigCachePath, path.join(entryDir, WASM_SPLIT_CONFIG_FILE_NAME));
|
|
10635
|
+
}
|
|
10636
|
+
const gameJsonCachePath = path.join(cacheDir, 'game.json');
|
|
10637
|
+
if (fs.existsSync(gameJsonCachePath)) {
|
|
10638
|
+
fs.copyFileSync(gameJsonCachePath, path.join(entryDir, 'game.json'));
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
// Restore wasmcode/game.js. We just deleted whatever was there in the
|
|
10642
|
+
// cleanup loop above, so we always need to put something back when wasmcode
|
|
10643
|
+
// is a subpackage. Scoped to the same `cachedWasmBr` guard: in the degraded
|
|
10644
|
+
// (lost-cache) path we left the existing wasmcode/ untouched, so we must not
|
|
10645
|
+
// rewrite its game.js either.
|
|
10529
10646
|
// Strategy:
|
|
10530
10647
|
// - Prefer the cache (keepCacheSync stashes pre-split contents to
|
|
10531
10648
|
// `__unity_cache__/wasmcode-game.js` when the original existed)
|
|
@@ -10536,14 +10653,16 @@ function restoreFromCache(entryDir = process.cwd()) {
|
|
|
10536
10653
|
// downloadSplited.ts also writes; it satisfies the platform requirement
|
|
10537
10654
|
// without changing semantics for projects that don't use wasmcode as
|
|
10538
10655
|
// a subpackage (the file is harmless empty).
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
if (fs.existsSync(
|
|
10543
|
-
fs.
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10656
|
+
if (cachedWasmBr) {
|
|
10657
|
+
const originGameJsCachePath = path.join(cacheDir, 'wasmcode-game.js');
|
|
10658
|
+
const originGameJsDestPath = path.join(originDir, 'game.js');
|
|
10659
|
+
if (fs.existsSync(originDir)) {
|
|
10660
|
+
if (fs.existsSync(originGameJsCachePath)) {
|
|
10661
|
+
fs.copyFileSync(originGameJsCachePath, originGameJsDestPath);
|
|
10662
|
+
}
|
|
10663
|
+
else {
|
|
10664
|
+
fs.writeFileSync(originGameJsDestPath, '', 'utf-8');
|
|
10665
|
+
}
|
|
10547
10666
|
}
|
|
10548
10667
|
}
|
|
10549
10668
|
for (const subDir of SPLIT_OUTPUT_DIRS) {
|
|
@@ -10661,19 +10780,51 @@ function setLocalState(partial) {
|
|
|
10661
10780
|
Object.assign(state, partial);
|
|
10662
10781
|
}
|
|
10663
10782
|
|
|
10783
|
+
/**
|
|
10784
|
+
* Prepare overwrites the project wasm in place with the instrumented build.
|
|
10785
|
+
* Mute the dev-server file watcher for the duration so the in-place rewrite
|
|
10786
|
+
* doesn't fire a compile + device reload while the file is still being
|
|
10787
|
+
* written, then emit a single update once the instrumented wasm is on disk.
|
|
10788
|
+
*/
|
|
10664
10789
|
async function startPrepare$1(params) {
|
|
10790
|
+
return withWatchSuspended(() => startPrepareImpl(params), {
|
|
10791
|
+
emitOnFinish: true,
|
|
10792
|
+
});
|
|
10793
|
+
}
|
|
10794
|
+
async function startPrepareImpl(params) {
|
|
10665
10795
|
const tempDir = path$1.join(process.cwd(), TTMG_TEMP_DIR);
|
|
10666
10796
|
ensureDirSync(tempDir);
|
|
10667
10797
|
const inputPath = path$1.join(process.cwd(), params.wasm_file_path);
|
|
10668
|
-
|
|
10669
|
-
if (inputPath.endsWith('.br')) {
|
|
10670
|
-
await decompressWasmFile(inputPath, rawWasmPath);
|
|
10671
|
-
}
|
|
10672
|
-
else {
|
|
10673
|
-
fs$1.copyFileSync(inputPath, rawWasmPath);
|
|
10674
|
-
}
|
|
10798
|
+
const rawWasmPath = path$1.join(tempDir, 'original.wasm');
|
|
10675
10799
|
const preparedWasmPath = path$1.join(tempDir, 'prepared.wasm');
|
|
10676
10800
|
try {
|
|
10801
|
+
// The project wasm referenced by game.json can legitimately be missing on
|
|
10802
|
+
// disk: a previous rollback may have wiped `wasmcode/*.br` without a cached
|
|
10803
|
+
// original to restore, the project may have been duplicated without
|
|
10804
|
+
// `__TTMG_TEMP__`, or Unity was re-exported. Detect it up front and return
|
|
10805
|
+
// a structured error. Previously the `decompressWasmFile` / `copyFileSync`
|
|
10806
|
+
// below sat OUTSIDE this try block, so a missing input threw ENOENT out of
|
|
10807
|
+
// the route handler as an unhandled rejection and crashed the whole
|
|
10808
|
+
// dev-server process instead of surfacing a recoverable error to the IDE.
|
|
10809
|
+
if (!fs$1.existsSync(inputPath)) {
|
|
10810
|
+
return {
|
|
10811
|
+
data: null,
|
|
10812
|
+
error: {
|
|
10813
|
+
code: 404,
|
|
10814
|
+
message: `WASM file not found: ${params.wasm_file_path}. ` +
|
|
10815
|
+
'The original wasm is missing — a previous "放弃分包" may have removed it ' +
|
|
10816
|
+
'without a cached original to restore. Re-export the Unity build (or restore ' +
|
|
10817
|
+
'the original wasm) and try preparing again.',
|
|
10818
|
+
},
|
|
10819
|
+
ctx: { logid: 'local', httpStatusCode: 404 },
|
|
10820
|
+
};
|
|
10821
|
+
}
|
|
10822
|
+
if (inputPath.endsWith('.br')) {
|
|
10823
|
+
await decompressWasmFile(inputPath, rawWasmPath);
|
|
10824
|
+
}
|
|
10825
|
+
else {
|
|
10826
|
+
fs$1.copyFileSync(inputPath, rawWasmPath);
|
|
10827
|
+
}
|
|
10677
10828
|
const result = ttmgWasmtool.prepare(rawWasmPath, preparedWasmPath);
|
|
10678
10829
|
verboseLog(`[wasmtool] prepare done: ${result.outputSize} bytes, ${result.timeCost}s`);
|
|
10679
10830
|
const gameJson = getGameJson();
|
|
@@ -11244,7 +11395,18 @@ function updateSubpackageConfigSync(archive = false) {
|
|
|
11244
11395
|
fs__namespace.writeFileSync(gameJsonPath, JSON.stringify(gameJson, null, JSON_INDENT) + JSON_EOL);
|
|
11245
11396
|
}
|
|
11246
11397
|
|
|
11247
|
-
|
|
11398
|
+
/**
|
|
11399
|
+
* Lays down the split outputs (`wasmcode*` dirs, game.json, webgl-wasm-split.js)
|
|
11400
|
+
* into the project root — a burst of file writes. Mute the watcher across the
|
|
11401
|
+
* whole operation so it doesn't reload the device per file mid-write, then fire
|
|
11402
|
+
* one update once the split layout is fully on disk.
|
|
11403
|
+
*/
|
|
11404
|
+
async function downloadSplited$1(context) {
|
|
11405
|
+
return withWatchSuspended(() => downloadSplitedImpl(context), {
|
|
11406
|
+
emitOnFinish: true,
|
|
11407
|
+
});
|
|
11408
|
+
}
|
|
11409
|
+
async function downloadSplitedImpl(_context) {
|
|
11248
11410
|
const cwd = process.cwd();
|
|
11249
11411
|
const { splitMeta } = getLocalState();
|
|
11250
11412
|
if (!splitMeta) {
|
|
@@ -11567,7 +11729,17 @@ async function downloadOne(opts) {
|
|
|
11567
11729
|
// so the subpackage loader doesn't complain about missing js entries.
|
|
11568
11730
|
fs.writeFileSync(path.join(path.dirname(out), 'game.js'), '', 'utf-8');
|
|
11569
11731
|
}
|
|
11732
|
+
/**
|
|
11733
|
+
* Remote-mode counterpart of `downloadSplited`: downloads the server-built
|
|
11734
|
+
* split artifacts and copies them into the project root. Same watcher gating
|
|
11735
|
+
* rationale — mute across the burst of writes, emit one update at the end.
|
|
11736
|
+
*/
|
|
11570
11737
|
async function downloadSplitedRemote(context) {
|
|
11738
|
+
return withWatchSuspended(() => downloadSplitedRemoteImpl(context), {
|
|
11739
|
+
emitOnFinish: true,
|
|
11740
|
+
});
|
|
11741
|
+
}
|
|
11742
|
+
async function downloadSplitedRemoteImpl(context) {
|
|
11571
11743
|
const cwd = process.cwd();
|
|
11572
11744
|
const splitTempDir = path.join(cwd, WASM_SPLIT_CACHE_DIR, DIR_SPLIT);
|
|
11573
11745
|
ensureDirSync(splitTempDir);
|
|
@@ -12687,14 +12859,61 @@ const routes = [
|
|
|
12687
12859
|
gamePipelineModeGetRoute,
|
|
12688
12860
|
gameLanguageRoute,
|
|
12689
12861
|
];
|
|
12862
|
+
/**
|
|
12863
|
+
* Express 4 does not catch rejections from async route handlers — an
|
|
12864
|
+
* unhandled rejection in any handler propagates to the process and, under
|
|
12865
|
+
* Node's default `unhandledRejection` behavior, kills the whole dev server.
|
|
12866
|
+
* That's how a single missing-file ENOENT in `wasm-prepare` could take the
|
|
12867
|
+
* CLI down. Wrap every handler so a failing request returns a 500 instead of
|
|
12868
|
+
* crashing the process. Individual routes can still return structured errors;
|
|
12869
|
+
* this is only the last-resort net for unexpected throws.
|
|
12870
|
+
*/
|
|
12871
|
+
function withErrorBoundary(handler) {
|
|
12872
|
+
return (req, res, next) => {
|
|
12873
|
+
try {
|
|
12874
|
+
const result = handler(req, res, next);
|
|
12875
|
+
if (result && typeof result.then === 'function') {
|
|
12876
|
+
result.catch((err) => {
|
|
12877
|
+
console.error(`[dev-server] unhandled error in ${req.method} ${req.path}:`, err);
|
|
12878
|
+
if (!res.headersSent) {
|
|
12879
|
+
res
|
|
12880
|
+
.status(500)
|
|
12881
|
+
.send({
|
|
12882
|
+
code: -1,
|
|
12883
|
+
error: {
|
|
12884
|
+
code: 500,
|
|
12885
|
+
message: err instanceof Error ? err.message : String(err),
|
|
12886
|
+
},
|
|
12887
|
+
});
|
|
12888
|
+
}
|
|
12889
|
+
});
|
|
12890
|
+
}
|
|
12891
|
+
}
|
|
12892
|
+
catch (err) {
|
|
12893
|
+
console.error(`[dev-server] unhandled error in ${req.method} ${req.path}:`, err);
|
|
12894
|
+
if (!res.headersSent) {
|
|
12895
|
+
res
|
|
12896
|
+
.status(500)
|
|
12897
|
+
.send({
|
|
12898
|
+
code: -1,
|
|
12899
|
+
error: {
|
|
12900
|
+
code: 500,
|
|
12901
|
+
message: err instanceof Error ? err.message : String(err),
|
|
12902
|
+
},
|
|
12903
|
+
});
|
|
12904
|
+
}
|
|
12905
|
+
}
|
|
12906
|
+
};
|
|
12907
|
+
}
|
|
12690
12908
|
function registerRoutes(app, options) {
|
|
12691
12909
|
const allRoutes = [...routes, getGameFallbackRoute(options.publicPath)];
|
|
12692
12910
|
for (const route of allRoutes) {
|
|
12911
|
+
const handler = withErrorBoundary(route.handler);
|
|
12693
12912
|
if (route.method === 'get') {
|
|
12694
|
-
app.get(route.path,
|
|
12913
|
+
app.get(route.path, handler);
|
|
12695
12914
|
}
|
|
12696
12915
|
else if (route.method === 'post') {
|
|
12697
|
-
app.post(route.path,
|
|
12916
|
+
app.post(route.path, handler);
|
|
12698
12917
|
}
|
|
12699
12918
|
}
|
|
12700
12919
|
}
|
|
@@ -13139,7 +13358,7 @@ async function upload({ clientKey, note = '--', dir, }) {
|
|
|
13139
13358
|
}
|
|
13140
13359
|
}
|
|
13141
13360
|
|
|
13142
|
-
var version = "0.
|
|
13361
|
+
var version = "0.4.0";
|
|
13143
13362
|
var pkg = {
|
|
13144
13363
|
version: version};
|
|
13145
13364
|
|