From 4caaea8aab478f35d8d203c4c9cff1cc0af003bd Mon Sep 17 00:00:00 2001 From: zizi Date: Sat, 13 Jun 2026 19:14:06 +0000 Subject: [PATCH] =?UTF-8?q?docs(runner-v2):=20P2=20=E5=8F=8C=20spec=20?= =?UTF-8?q?=E5=AE=9A=E7=A8=BF=E2=80=94=E2=80=94=E9=80=9A=E7=94=A8=E5=AE=BF?= =?UTF-8?q?=E4=B8=BB=E6=B3=9B=E5=8C=96(=E5=AF=B9=E6=8A=97=E5=A4=8D?= =?UTF-8?q?=E5=AE=A14=20mustFix=E5=85=A8=E7=BA=B3+4=E9=98=BB=E6=96=AD?= =?UTF-8?q?=E9=A1=B9=E6=8B=8D=E5=AE=9A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P2 = 把 P1 装载契约从「装载王蓝莓一款」泛化为「装载任意 GamePackage」入 feed 真玩。 4 路只读侦察→草拟→opus 文档级对抗复审→综合落 review(411行)+execution(303行)。 对抗门逮 4 mustFix 全纳入(主 agent grep 独立复核属实): - 门②跨层命名:telemetry game_play_end→lifecycle game_end(completed===true)(母spec L66 早警告) - 体积口径:540KB(开工前 inferred 误值)→实测 259KB raw/88KB gz(SIZES.md:33) - O2 范围:Phase C 纳入标为扩张请求(待创始人追认) - ref bundle 产线不在仓:固化补法落实 4 阻断项全拍定→P2 解锁:O2(b)=追认纳入 Phase C(创始人)/O-P2-2=① DB-manifest 内嵌 (创始人·安全红线,bundle 文本进 engineBundle 字段+iframe 内联 script,CSP 不放外域)/ O-P2-1=ref 脚本灌库过渡桩(真产线留 P3)/O-P2-3=globalName __GameBundle。 架构形状:manifest 端点是 application/json 吐不了 .js→bundle 内嵌 GamePackage JSON 的 engineBundle 字段(落既有 package_json LONGTEXT,sha256 照覆盖),后端唯一改动=buildGamePackage 加一行+契约#4 加一可选字段;掏空壳线上零可玩=替换零兼容包袱;受控面6项+game-host.d.ts 零改。 Co-Authored-By: Claude Opus 4.8 (1M context) --- ...26-06-13-P2-runner-v2泛化宿主-execution.md | 307 +++++++++++++ .../2026-06-13-P2-runner-v2泛化宿主-review.md | 412 ++++++++++++++++++ 2 files changed, 719 insertions(+) create mode 100644 docs/agent-specs/2026-06-13-P2-runner-v2泛化宿主-execution.md create mode 100644 docs/agent-specs/2026-06-13-P2-runner-v2泛化宿主-review.md diff --git a/docs/agent-specs/2026-06-13-P2-runner-v2泛化宿主-execution.md b/docs/agent-specs/2026-06-13-P2-runner-v2泛化宿主-execution.md new file mode 100644 index 00000000..6f2e0d02 --- /dev/null +++ b/docs/agent-specs/2026-06-13-P2-runner-v2泛化宿主-execution.md @@ -0,0 +1,307 @@ +# P2 · 通用宿主泛化(Runner v2 装载任意 GamePackage)· 执行版 + +> **状态**:execution 版 · 2026-06-13 · 给执行 agent 实现。配套 review 版 [`2026-06-13-P2-runner-v2泛化宿主-review.md`](./2026-06-13-P2-runner-v2泛化宿主-review.md);母梁 [`2026-06-13-runner-v2-arc-review.md`](./2026-06-13-runner-v2-arc-review.md) §5.2;母 spec [`2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md`](./2026-06-13-T1b-β引擎真接线与Runner-v2-execution.md)(Phase B/C/D + 门4/门6)。 +> **修订**:2026-06-13 对抗复审 → 纳入 4 mustFix(门② lifecycle game_end·非 telemetry / 体积 259KB·非 540KB / O2 范围 Phase C 标扩张 / ref bundle 产线落实)+ 3 shouldFix(门③后端 status=1 门禁叠加 / CSP img-src 补全 / bridge.ts 行号校准)。逐条裁决见 §13。 +> **模型分流**:一律 opus(创始人拍,本环境 fable 子代理不可用)。 +> **铁律**:下层受控面 6 项契约(`getContext2d/getInput/getAudioContext/getEngine`,`api.d.ts:269/281/286/303`)+ 上层装载契约(`game-host.d.ts:25-59`)**零改**;引擎 import 只活 entry/host(Q4);区分 verified/inferred/assumption,凡引代码事实带 file:line。 +> **前置裁决(4 阻断项全拍定 · 2026-06-13 创始人 + 主 agent)→ P2 全面解锁可直接落地**: +> - **O2(b)=追认纳入 Phase C**(创始人拍):掏空壳 `runtime/index.ts:56` 直接改「引擎包加载分支」、**跳过复活 1.x**;母 spec Phase C 条目由 P2 supersede,改动记账归 P2。 +> - **O-P2-2=① DB-manifest 内嵌**(创始人拍·安全红线):bundle 文本进 GamePackage JSON `engineBundle` 字段、iframe 内**内联 ` → window. + IJ->>BH: bootGameHost({factory:取自全局名, pkg, canvas}) + BH->>LJS: setGLEnable(false)+engineInit 五回调 + LJS-->>BH: .then → mainContext 就绪 + BH->>BH: game.init({ctx,mainContext,canvas,seed}) + loop 引擎内部 RAF + LJS->>BH: gameUpdate→game.update / gameRender→game.render(mainContext) + end + BH->>GP: sdk.__emit('game_loaded')→bridge→GamePlayer:292 + BH->>GP: 结算 sdk.__emit('game_end',{completed:true})→bridge→GamePlayer:300 + else 无 engineBundle(向后兼容/demo 兜底) + IJ->>IJ: startRuntime:184 掏空壳 → game_error「重构中」 + end +``` + +**关键数据流不变量**: +- bundle 载体经 `package_json` LONGTEXT 随 manifest 一跳下发,sha256(整包) 覆盖 bundle 完整性(无第二校验面)。 +- 取包一律 **play 场景**(不拼 `?scene=preview`),后端 manifest 端点强制 status=1(shouldFix#1 第一道门禁)。 +- 就绪闭环唯一信号=lifecycle `game_end`(`GamePlayer.vue:300`),**非** telemetry `game_play_end`(mustFix#1)。 +- 引擎掌帧/绘制面/能力面三不变量沿 P1(掌帧唯一源=引擎 / 绘制面=mainContext / 能力面=ctx.getEngine())。 + +--- + +## 5. 接口 / 契约(复用 + additive 增量逐字写清) + +### 5.1 复用(零改) +- **上层装载契约**(`game-runtime/src/core/game-host.d.ts:25-59`,verified):`GameHostBootContext{ctx,mainContext,canvas,seed}` / `GameInstance{init,update,render,onInput?,destroy}` / `GameHostFactory=(opts?)=>GameInstance`。P2 通用宿主 import 它、取 factory、按五生命周期驱动。**零改。** +- **下层交付锚**(`contracts/game-package.schema.json:66-74`,verified):`packageUrl`(additive 可选,MVP 降级=DB-manifest 端点)+ `immutable`。**零改。** +- **下层受控面 6 项**(`api.d.ts:269/281/286/303`):`getContext2d/getInput/getAudioContext/getEngine`。**零改。** +- **宿主↔runtime 稳定边界**(保不破):`RuntimeSDKView.__emit/track/reportError/saveState/loadState` + `RuntimeHandle.destroy` + GamePlayer lifecycle emit 映射(`game_loaded:292`/`game_end:300`)。 + +### 5.2 additive 增量(唯一契约面新增,逐字) + +**`contracts/game-package.schema.json` `properties` 增**(与 `packageUrl`/`immutable` 同级,不进 `required`): + +```jsonc +"engineBundle": { + "description": "引擎+包装层+游戏代码的可执行 iife bundle 文本(T1b-β P2 通用宿主载体;additive 可选,存量消费者无此字段时走 startRuntime 旧路/demo 兜底)。MVP 默认=随 manifest 内嵌经 DB-manifest 端点下发(落 game_runtime_package.package_json LONGTEXT,sha256 整包 checksum 覆盖其完整性,不接 OSS、不增第二校验面);切 OSS 后改为 packageUrl 外链。iife 顶层全局名固定为 (宿主据此从 window 取 GameHostFactory)。后端写权=DifyCallbackTxService.buildGamePackage put。", + "type": "string" +} +``` + +> **[verified]** 现 schema 无 `engineBundle`/`engineCode`/`engineUrl`(grep contracts/+game-studio/+game-cloud/+game-runtime/ 零命中)。本字段是 P2 唯一契约面新增。`game-studio/src/host/contract.ts` GamePackage 前端副本同步加 `engineBundle?: string`。 +> **[verified]** bundle 随 JSON 落 `package_json` 后,`buildGamePackage` 末段 `bundleSize = T_base.getBytes(UTF_8).length` 自然涵盖 bundle 字节(门禁语义成立);checksum=sha256(整包文本) 覆盖 bundle 完整性。 + +--- + +## 6. 关键实施步骤(分三轨·有序) + +> 三轨可部分并行(后端轨/宿主轨弱耦合),但**前端引擎包分支依赖宿主轨 `bootGameHost` 签名 + 后端轨 `engineBundle` 字段**,故前端轨末做。开工前 P-8 待拍项(O2(b)/O-P2-1/2/3)必须先拍。 + +### 6.1 后端轨(game-cloud + 产线) + +- **B0(mustFix#4·产线先固化)**: + - `game-runtime/package.json` scripts 加 `build:ref-bundle`,内部 `node scripts/build.mjs games/wanglanmei-ref/entry.js games/wanglanmei-ref/dist/ref-bundle.js --global-name=`(钉死路径+globalName)。 + - 落实 game-cloud 后端取 ref bundle 文本的路径(O-P2-1 三选一):打包期拷贝 / 构建产物入库 / CI 产物传递。**P2 仅跑 ref 一款时,可显式降级为「脚本一次性把 ref-bundle.js 文本灌入 DB 某 versionId 的 package_json.engineBundle」过渡桩**(须在代码注释+ §11 标过渡桩,P3 接生成主线改随回调入参)。 +- **B1(唯一必要后端代码改动)**:`DifyCallbackTxService.buildGamePackage`(`:268-329`)在 checksum 占位回填前增 `pkg.put("engineBundle", )`(来源=B0 落实的取得路径)。`storeForVersion`(`:198`)/`DbPackageStore.putManifest`(`:27`)/表/端点零改。 +- **B2(契约)**:`game-package.schema.json` 加 `engineBundle`(§5.2 逐字);前端 `contract.ts` 副本同步。 +- **B3(验证·后端只读)**:发布主链三服务不改,被动验证(门①)。 + +### 6.2 宿主轨(bootGameHost·P1 bootRef 泛化) + +- **H1**:把 P1 `bootRef` setupReal 内引擎骨架(`setGLEnable(false)`→`engineInit` 五回调→`.then(setupReal)`→`createHostDevContext(context2d=mainContext)`→`createEngineCaps`→`game.init`→`__refBootedReal`)抽成 `bootGameHost(opts)`,其中: + - **游戏工厂来源参数化(D1/D2)**:替 P1 `main.js:48` 静态 `import createShopGame`——`bootGameHost` 从 `opts.factory`(宿主已从 bundle 全局名取出的 `GameHostFactory`)建实例。 + - **插件清单参数化(D3·O-P2-4)**:P2 阶段先用王蓝莓 7 件作 `opts.plugins` 默认标准集。 + - **视口参数化(D4)**:`opts.viewport`(P2 阶段先用王蓝莓 390×844;后续从 GamePackage 取)。 +- **H2**:`bootRef` 退化为薄包装:`bootGameHost({factory:createShopGame, plugins:王蓝莓7件, viewport:390×844, recHook})`,保 ref 取证全局名(`__refForensics/__refHost/__refBootedReal`)。**三者共用同一引擎骨架与 engine-caps 模块,不产生第三套。** +- **H3**:SIZES.md 确认游戏产物口径=259KB raw / 88KB gz(`SIZES.md:33` 已坐实,校对无误即可,mustFix#2)。 + +### 6.3 前端轨(studio host 引擎包分支) + +- **F1(分流·依赖宿主轨 H1 + 后端轨 B1)**:`inject.ts boot()`(`:174`)加 `pkg.engineBundle?` 分叉: + - **有 `engineBundle`**:iframe 内注入内联 ``(DB-manifest 内嵌路·非外域 `