增加不可变源码检出、两阶段 finalize/activate、Runtime 同源对象读取与 iframe 安全边界,并用 fail-closed CI 门固定契约。Git 仅提交平台实现、契约、迁移和 SoT,不包含具体游戏源码、素材、bundle 或验收证据。
92 lines
7.0 KiB
JavaScript
92 lines
7.0 KiB
JavaScript
/**
|
||
* entry-generic.js — P3 generic bundle 入口壳(模板·esbuild 锁参打成 iife,globalName=__GameBundle)
|
||
* owner:P3/W-G1 生成主线 L0 | 消费方:studio host 引擎包分支(DB-manifest 内嵌路,与 wanglanmei-ref/entry-bundle.js 同消费方)
|
||
*
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
* 【本入口是什么 · 与 wanglanmei-ref/entry-bundle.js 的关系】
|
||
* entry-bundle.js 是 wanglanmei 一款游戏的「studio-consumable」bundle 入口(工厂写死 import game-shop)。
|
||
* 本 entry-generic.js 把它泛化成「任意生成游戏」的通用入口壳:
|
||
* · 游戏工厂来源参数化 → `import createGame from './generated-factory.js'`(L1 生成 worker 写入的产出靶);
|
||
* · 装配走 buildGenericHostConfig(零wanglanmei专属标准能力插件集),不再走 buildRefHostConfig。
|
||
* 这是 L1 生成 worker 的「套壳靶」:worker 只需产出 generated-factory.js(一个 GameHostFactory),
|
||
* 套进本壳 esbuild 打成 __GameBundle bundle,即得一个 studio 可消费的引擎包——避免 20 个 prompt 各造一遍
|
||
* 「7 插件 wiring + boot 入口」(见 execution §3.2 套壳分级框 / §7-S1)。
|
||
*
|
||
* 【契约(冻结·逐字对齐)】
|
||
* - iife 顶层全局名固定 **__GameBundle**(与 game-package.schema.json/contract.ts/inject.ts 逐字一致);
|
||
* studio iframe 注入本 bundle 文本 → 取 window.__GameBundle.bootGameHost 启动。
|
||
* - boot 入口调用形 **{canvas, seed}** 逐字对齐 game-studio/src/host/inject.ts:248
|
||
* (`bundleNS.bootGameHost({ canvas: canvas, seed: seed })`)。
|
||
* - 本 bundle = 引擎(LittleJS) + 包装层(bootGameHost + generic 装配) + 生成游戏代码(generated-factory)。
|
||
*
|
||
* 【Q4 · Option C:引擎 import 只落本入口,且经动态 import 延后求值】
|
||
* littlejsengine ESM 在模块装载期触 window/AudioContext,顶层静态 import 会让 node 一 import 即抛 →
|
||
* 击穿 import-safe。修法(与 entry-bundle 逐字同款):引擎 import 改「bootGameHost 内动态 import」:
|
||
* · node import 本入口时不触引擎(bootGameHost 未被调)→ import-safe;
|
||
* · esbuild(bundle+iife 无 splitting)把动态 import 的引擎内联进单文件 → 引擎真入 bundle;
|
||
* · 浏览器运行期 studio 调 __GameBundle.bootGameHost 时才求值引擎(window/AudioContext 已存在)。
|
||
* ════════════════════════════════════════════════════════════════════════════
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
// 通用宿主(引擎骨架参数化;本身零引擎 import,引擎经 opts.engine 注入)。
|
||
import { bootGameHost as bootGameHostCore } from '../../src/host/boot-game-host.js';
|
||
// generic 装配配置(标准能力插件集 / 偏序 / 视口 / wiring;零wanglanmei专属)。
|
||
import { buildGenericHostConfig } from '../../src/host/generic-host-config.js';
|
||
// 【游戏工厂来源·参数化】L1 生成 worker 写入的生成工厂(默认导出 GameHostFactory,零引擎 import)。
|
||
// 占位期 = games/_generic/generated-factory.js 的占位 stub;L1 出码替换该文件即换游戏,本壳零改。
|
||
import createGame from './generated-factory.js';
|
||
|
||
/**
|
||
* 【契约导出】GameHostFactory:studio host 从 window.__GameBundle.GameHostFactory 取它(契约冻结)。
|
||
* = 生成工厂(createGame)。L1 出码替换 generated-factory.js 的默认导出即换它。
|
||
* @type {import('../../src/core/game-host.d.ts').GameHostFactory}
|
||
*/
|
||
export const GameHostFactory = createGame;
|
||
// 默认导出同为 GameHostFactory(便于 import default 取用;与具名导出同一引用)。
|
||
export default createGame;
|
||
|
||
/**
|
||
* 【boot 入口】供 studio host 在 iframe 内启动本 bundle(studio 仓无引擎,故由本 bundle 注入引擎后驱动)。
|
||
* - 动态 import 引擎(Option C):调用时才求值,esbuild 已把引擎内联进本 bundle(无外部请求)。
|
||
* - engineMode 强制 'real'(studio 引擎包分支一律引擎掌帧)。
|
||
* - 插件/视口/wiring 取自 buildGenericHostConfig(标准能力插件集 + 390×844)。
|
||
* - 工厂取 createGame(本 bundle 内 generated-factory 默认导出;studio 也可经 opts.factory 覆盖,缺省即用它)。
|
||
*
|
||
* @param {Object} opts studio host 传入的最小入参(调用形 {canvas, seed} 逐字对齐 inject.ts:248)。
|
||
* @param {HTMLCanvasElement} opts.canvas iframe 内目标画布(real 通道引擎自建 mainCanvas,本 canvas 作占位/输入兜底)。
|
||
* @param {HTMLElement} [opts.statusEl] 状态行(可空)。
|
||
* @param {number} [opts.seed] 主随机种子(缺省固定值,保确定性;studio 由 versionId 派生后传)。
|
||
* @param {any} [opts.engine] 引擎名字空间覆盖(缺省=动态 import littlejsengine;测试可注入桩引擎)。
|
||
* @param {import('../../src/core/game-host.d.ts').GameHostFactory} [opts.factory] 工厂覆盖(缺省=本 bundle 的 createGame)。
|
||
* @returns {Promise<object>} bootGameHost 的 host 句柄(frameCount/stepFrames/tap/do/state 等)。
|
||
*/
|
||
export async function bootGameHost(opts) {
|
||
const o = opts || {};
|
||
// 动态 import 引擎:求值时机延后到本函数被调用(浏览器运行期)。esbuild 内联,无外部请求。
|
||
// opts.engine 覆盖优先(测试可注桩引擎,免拉真引擎);缺省动态 import 真引擎。
|
||
const engine = o.engine || (await import('littlejsengine'));
|
||
// seed 缺省固定值(与 ref/inject 同口径:非数字回退 0x1234abcd);规整无符号 32 位。
|
||
const seed = (o.seed != null ? o.seed : 0x1234abcd) >>> 0;
|
||
// generic 装配(标准能力插件集 / 偏序 / 视口 / wiring)。
|
||
const cfg = buildGenericHostConfig({ seed });
|
||
// 调通用宿主:real 通道引擎掌帧;canvas/statusEl 由 studio 传;其余取 generic 装配。
|
||
return bootGameHostCore({
|
||
canvas: o.canvas,
|
||
statusEl: o.statusEl || null,
|
||
engine,
|
||
seed,
|
||
mode: 'play', // studio 真玩=play
|
||
engineMode: 'real', // studio 引擎包分支一律引擎掌帧
|
||
viewport: cfg.viewport, // {w:390,h:844}(P3 钉死)
|
||
factory: o.factory || cfg.factory || createGame, // 缺省用本 bundle 内置生成工厂
|
||
plugins: cfg.plugins,
|
||
registerOrder: cfg.registerOrder,
|
||
buildFactoryOpts: cfg.buildFactoryOpts,
|
||
assets: o.assets || [],
|
||
// studio 渠道产物:不注 recHook(引擎 call-ID 取证是 ref harness 专属;渠道产物无副作用)。
|
||
// 无 onReady:studio 不消费 ref 取证全局;就绪信号经 SDK lifecycle game_loaded/game_end(studio 轨接)。
|
||
});
|
||
}
|