U1 前置另一半(plan 002):声明式 gameDefinition → 适配器 → entry-bundle → build.mjs → 真九门绿。
- _adapter-probe/:generated-factory(adaptGameDefinition+内联 source)+ entry-bundle(拷模板)+ index(拷模板)
+ play-spec(tap-targets driver, assert score increased, expectLatch) + bundle.iife.js(211KB, LittleJS 真入包)。
- 真九门(本机快走查):A装载/B未捕获0/C掌帧Δ30/D真渲染/E活性5/5/F真接线(particles+audio)/
G输入有效/H 机制+latch(score 0→3 + 终态驻留)/I(无 controlCheck);首局门 可玩51ms/首反馈/60s闭环7s 全过。
- 适配器跟进:① _forensicsView 改契约形态 { seed, state:()=>snapshot }(对齐 boot-game-host:283 + tictactoe);
② 命中加引擎粒子+音效反馈(game-feel + 满足 F 门),经 ctx.getEngine 容错。单测仍 6/6 绿。
证成:E1(repo 标"真空白"最高风险件)声明式路端到端可行;build-from-sourceProject 管线(E2)可行;
回答 adversarial MVP-latch 质疑——单 behavior 声明式游戏确过 H 门 latch。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
5.7 KiB
HTML
96 lines
5.7 KiB
HTML
<!DOCTYPE html>
|
||
<!--
|
||
index.template.html —— W-G1 通用 host 页模板(逐字拷进每个生成 game 目录为 index.html)
|
||
owner:W-G1 lane | 用法:
|
||
· 同目录 esbuild 产物 bundle.iife.js(globalName=__GameBundle)由本页 <script src> 加载;
|
||
· serve game-runtime 根,CDP 导航到 /games/_wg1-gen/<game>/index.html;
|
||
· 就绪标记 __genBooted(bootGameHost().then 置);real 引擎接管标记 __gameHostEngineInitFired(boot-game-host 内置);
|
||
· 假绿守卫钩子:window.__engineCalls(recHook 记每次引擎能力调用 call-ID)、window.__genInternals(onReady 收 uncaught[])。
|
||
布局收编(对齐 ref 教训):canvas 顶在 (0,0) 满幅 390×844;状态行 absolute overlay + pointer-events:none,
|
||
绝不下推/截触摸(否则 CDP 真输入坐标整体偏移 + 底部不可点)。引擎自建 mainCanvas#game-engine 叠加渲染。
|
||
-->
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<!-- 固定取证视口:390×844 移动竖屏基准;user-scalable=no 防缩放扰动像素哈希。 -->
|
||
<meta name="viewport" content="width=390, height=844, initial-scale=1, user-scalable=no" />
|
||
<title>W-G1 generated game</title>
|
||
<style>
|
||
html, body { margin: 0; padding: 0; background: #111; color: #ccc;
|
||
font-family: -apple-system, "PingFang SC", monospace; overflow: hidden; }
|
||
#wrap { position: relative; width: 390px; margin: 0 auto; }
|
||
#game { width: 390px; height: 844px; background: #000; display: block; touch-action: none; }
|
||
/* 状态行默认隐藏:boot-game-host 的「engine RAF running…」调试浮层会被截图带进画面、污染 player 视觉判分;
|
||
harness 真玩门读 JS 变量(__genBooted/__genBootError/console)判定,不依赖此可视行,故隐藏不影响七门。
|
||
仅 BOOT ERROR 时由下方脚本显式显示,便于人工排查。 */
|
||
#status { display: none; position: absolute; top: 0; left: 0; right: 0; z-index: 2; pointer-events: none;
|
||
font-size: 12px; line-height: 1.4; min-height: 15px; text-align: center; word-break: break-all;
|
||
opacity: 0.85; text-shadow: 0 1px 2px rgba(0,0,0,0.8); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="wrap">
|
||
<div id="status">booting…</div>
|
||
<!-- 受控画布(opts.canvas 占位/输入兜底);real 引擎自建 #game-engine 叠加。 -->
|
||
<canvas id="game"></canvas>
|
||
</div>
|
||
|
||
<!-- esbuild 产物:iife 全局 __GameBundle(含 GameHostFactory + bootGameHost)。 -->
|
||
<script src="./bundle.iife.js"></script>
|
||
<script>
|
||
// 引导:取 __GameBundle.bootGameHost 启动;注入取证钩子;失败留痕 + 重抛(假绿守卫要看 console 零未捕获)。
|
||
(function () {
|
||
'use strict';
|
||
var statusEl = document.getElementById('status');
|
||
var canvas = document.getElementById('game');
|
||
// 假绿守卫C·真接线探针:每次引擎能力被调,recHook 记一条 call-ID 进此数组(证「真调引擎/插件库」非自绘伪装)。
|
||
window.__engineCalls = [];
|
||
function recHook(id) { try { window.__engineCalls.push(id); } catch (_) {} }
|
||
// 就绪/错误标记初值。
|
||
window.__genBooted = false;
|
||
window.__genBootError = null;
|
||
// W-G1 组C·首局门断言①「可玩≤2s」精确计时:页面侧 performance.now() 在「可交互首帧」(__genBooted=true) 打点,
|
||
// 写 window.__playableAtMs(= 自 navigationStart/timeOrigin 起的毫秒)。harness 直接读它,
|
||
// 规避 ① CDP 轮询 ~300ms 粒度抖动 ② waitBoot 固定 settle floor 对计时的污染——二者都会让 harness 侧计时失真。
|
||
window.__playableAtMs = null;
|
||
try {
|
||
var G = window.__GameBundle;
|
||
if (!G || typeof G.bootGameHost !== 'function') {
|
||
throw new Error('__GameBundle.bootGameHost 未找到(bundle.iife.js 未正确加载或 globalName 不符)');
|
||
}
|
||
G.bootGameHost({
|
||
canvas: canvas,
|
||
statusEl: statusEl,
|
||
recHook: recHook,
|
||
// onReady:收集 internals(uncaught[] 等)供假绿守卫B 读。
|
||
onReady: function (host, internals) {
|
||
window.__genHost = host;
|
||
window.__genInternals = internals || null;
|
||
}
|
||
})
|
||
.then(function (host) {
|
||
window.__genHost = window.__genHost || host;
|
||
// 可测性别名:守卫H 经此读游戏可观测状态(host.state() 透传 game._forensicsView().state;游戏未实现则返回 {phase:'booting'})。
|
||
// 宿主侧挂载,工厂仍零 window;与 ref 在 onReady 挂 __refForensics 同范式。
|
||
window.__gameState = function () { try { return host && typeof host.state === 'function' ? host.state() : null; } catch (_) { return null; } };
|
||
// 首局门断言①打点:可交互首帧达成的墙钟(performance.now() 以 navigationStart 为 0 点),供 harness 读取判 ≤2s。
|
||
try { window.__playableAtMs = (typeof performance !== 'undefined' && performance.now) ? performance.now() : null; } catch (_) {}
|
||
window.__genBooted = true; // CDP 探活标记(区分「脚本没加载」与「boot 抛错」)
|
||
})
|
||
.catch(function (e) {
|
||
window.__genBooted = false;
|
||
window.__genBootError = String((e && e.message) || e);
|
||
if (statusEl) { statusEl.style.display = 'block'; statusEl.textContent = 'BOOT ERROR: ' + window.__genBootError; }
|
||
throw e; // 重抛 → 'unhandledrejection' 被假绿守卫逮住
|
||
});
|
||
} catch (e) {
|
||
window.__genBooted = false;
|
||
window.__genBootError = String((e && e.message) || e);
|
||
if (statusEl) statusEl.textContent = 'BOOT ERROR: ' + window.__genBootError;
|
||
throw e;
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|