lili 1981a6be01
Some checks failed
contract-gates / contract-gates (push) Has been cancelled
docs-gate / docs-gate (push) Has been cancelled
feat(storage): 闭合 Agent 到 Runtime 的 OSS 游戏链路
增加不可变源码检出、两阶段 finalize/activate、Runtime 同源对象读取与 iframe 安全边界,并用 fail-closed CI 门固定契约。Git 仅提交平台实现、契约、迁移和 SoT,不包含具体游戏源码、素材、bundle 或验收证据。
2026-07-30 05:21:44 -07:00

92 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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