/** * 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} 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 轨接)。 }); }