feat(contracts): T1b-β P2 三契约冻结(grounded 据真签名+插件真调用面)——待对抗复审
①受控面引擎能力透传通道 PluginContext.getEngine()→EngineCapabilities(api.d.ts):白名单3项particles.spawnEmitter/audio.synth/math.lerp+smoothStep(YAGNI核6件impl真调用点);铁律=封装禁裸Vector2/引擎import只活集成段host背书engineFactory不散进插件impl(保引擎可换+满足门1真接线);random/time集成段换引擎RandomGenerator/timeReal背书(契约形状不变) ②GamePackage manifest加packageUrl+immutable(additive,复用后端packageUrl镜像走DB-manifest降级,checksum复用旧字段供门4防demo假阳性) ③SDK StoragePlugin根契约上提(sdk-interface.d.ts):前端contract.ts定稿对象形态上提+含R4-P1 array排除契约语义声明 grounded工位核impl逮关键澄清:hitStop/屏震非通道项(impl.js:582-615=受控面输出+游戏层render.js:915应用闭环,插件不调引擎;屏震引擎化=render.js切相机属agent生成域)→通道收敛6件→3项,§7/§6 A2同步澄清;audio.synth留契约A2据SIZES定用否 JSON校验过+.d.ts括号平衡 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b300cd0daa
commit
28a57b8e00
@ -62,7 +62,16 @@
|
||||
"enum": ["eager", "lazy"]
|
||||
},
|
||||
"bundleSize": { "description": "包总字节(编译期门禁校验上限)", "type": "integer", "minimum": 0 },
|
||||
"checksum": { "description": "整包 sha256(hex),Manifest 完整性校验", "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
||||
"checksum": { "description": "整包 sha256(hex),Manifest 完整性校验。T1b-β 门4 防 demo 兜底假阳性=Network 捕获本 checksum 比对发布版本", "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||||
"packageUrl": {
|
||||
"description": "运行包可访问 URL(T1b-β Runner v2 交付锚;additive 可选,存量消费者无此字段时回退后端 resolveManifestUrl 原链路)。MVP 默认降级=复用后端 game_version.package_url 走 DB-manifest 端点 /app-api/runtime/package/{versionId}/manifest(天然含 versionId 版本化,不接 OSS、不改写链);切 OSS 后=OSS 版本化路径。= 后端 RuntimePackageDO.packageUrl 的 GamePackage 契约镜像(同源同物,后端写权)。",
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"immutable": {
|
||||
"description": "本 URL 是否携带 immutable 强缓存语义(Cache-Control: public,max-age=31536000,immutable)。true=URL 版本化+内容寻址、命中即永久缓存(编译缓存靠 versionId 兜,门4 用 CDP Network timing 验缓存命中 fromCache/304);false/缺省=普通缓存。additive 可选。",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
|
||||
48
contracts/sdk-interface.d.ts
vendored
48
contracts/sdk-interface.d.ts
vendored
@ -40,6 +40,8 @@ export interface WanxiangGameSDK {
|
||||
/** 插件(首次调用懒加载) */
|
||||
ad: AdPlugin;
|
||||
pay: PayPlugin;
|
||||
/** 存储插件(首次调用懒加载;idle 离线存档;T1b-β 由前端 host/contract.ts 定稿形态上提至根契约) */
|
||||
storage: StoragePlugin;
|
||||
}
|
||||
|
||||
/** 广告插件(MVP 桩 → 真实穿山甲/优量汇切换;每调用 5s 超时降级) */
|
||||
@ -56,6 +58,22 @@ export interface PayPlugin {
|
||||
pay(order: { orderId: string; amount: number }, cb: (result: { paid: boolean }) => void): void;
|
||||
}
|
||||
|
||||
/** 存储插件(idle 离线产出存档;HJ-MC-TPL-EXEC-002 §6.1.1 定稿对象形态,T1b-β 上提至根契约,semver 只增不改) */
|
||||
export interface StoragePlugin {
|
||||
/**
|
||||
* 写入存档(fire-and-forget,不返回 Promise)。宿主侧白名单前缀校验 + 大小上限校验后落 localStorage。
|
||||
* @param key 存档键(runtime 侧按 'idle:'+gameId+':'+versionId 约定)。
|
||||
* @param value 存档值(JSON 字符串;宿主侧落库前校验大小)。
|
||||
*/
|
||||
save(key: string, value: string): void;
|
||||
/**
|
||||
* 读取存档(宿主受信边界已解析为对象后回调;runtime 侧零 JSON.parse,守 runtime §5.5 零解析红线)。
|
||||
* @param key 存档键。
|
||||
* @param cb 回调;命中=已解析对象,未命中/校验失败/**非纯对象(含数组)**=null(见 StorageResultPayload.value 的 array 排除)。
|
||||
*/
|
||||
load(key: string, cb: (value: Record<string, unknown> | null) => void): void;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* SDK ↔ 宿主 postMessage 协议
|
||||
* 游戏在 iframe 沙箱内,通过 postMessage 与 game-studio 宿主通信。
|
||||
@ -87,3 +105,33 @@ export interface PostMessageEnvelope<T = unknown> {
|
||||
traceId: string;
|
||||
payload: T;
|
||||
}
|
||||
|
||||
/* ----- storage 消息 payload(idle 离线产出存档;HJ-MC-TPL-EXEC-002 §6.1.1,T1b-β 上提,semver 只增不改) ----- */
|
||||
|
||||
/** storage 写入请求 payload(游戏→宿主:保存键值) */
|
||||
export interface StorageSetPayload {
|
||||
/** 存档键(runtime 侧按 'idle:'+gameId+':'+versionId 约定,宿主侧白名单前缀校验) */
|
||||
key: string;
|
||||
/** 存档值(JSON 字符串,宿主侧落 localStorage 前校验大小上限) */
|
||||
value: string;
|
||||
}
|
||||
|
||||
/** storage 读取请求 payload(游戏→宿主:按 key 取值;带 requestId,宿主读分支按同 requestId 回包) */
|
||||
export interface StorageGetPayload {
|
||||
key: string;
|
||||
}
|
||||
|
||||
/** storage 读取结果 payload(宿主→游戏:回包 value,无则 null) */
|
||||
export interface StorageResultPayload {
|
||||
key: string;
|
||||
/**
|
||||
* 命中的存档值 = 宿主受信边界已解析好的对象(非原始 JSON 字符串);未命中/校验失败为 null。
|
||||
* 解析在宿主侧做:runtime 侧零 JSON.parse(守 runtime §5.5「runtime 内不引可抛解析路径」),
|
||||
* loadState resolve 出对象后 runtime 直接读字段。写入 payload(StorageSetPayload.value)仍是 JSON 字符串。
|
||||
* **对象语义不含数组(T1b-β R4-P1)**:宿主回包端(game-studio inject.ts 已落 !Array.isArray 运行时排除)
|
||||
* 与本类型层须一致——value 虽 TS 标注 Record<string,unknown>|null(array 在 TS 是 object 子型不被拒),
|
||||
* 但**契约语义明令排除数组**:数组/字符串/标量一律按未命中处理(resolve null)。
|
||||
* storage 三套形状(本根契约 / game-studio host/contract.ts / inject.ts 应答端)收敛校验须覆盖此「array→null」运行时语义,不止类型签名对齐。
|
||||
*/
|
||||
value: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/
|
||||
**Phase A**:
|
||||
- **A0(地基)引擎主循环接管**:host-dev `host.js`(文件头§注明的「RAF+bundle.tick+普通 2D canvas 桩」)改 **`engineInit(gameInit,gameUpdate,gameUpdatePost,gameRender,gameRenderPost,...)` 五回调接管**帧驱动+渲染循环(引擎掌帧、回调里接桩;**无 `engineUpdate` 导出可手摇**)——**这是 particles/physics/audio/time 一切真接线的前提**,不是可选项。
|
||||
- A1 受控面:random 透传引擎 `RandomGenerator`;time 透传引擎 `time`(依赖 A0);input 改「对引擎轮询态(`mouseWasPressed`/`keyIsDown`)的归一化包装」(订阅形状留给 P4/P10 消费者,底层读引擎态,不另造事件系统);**probe 墙钟独立保留**。
|
||||
- A2 particles-juice:粒子→`ParticleEmitter`(包装);hitStop→`timeScale`(包装);**屏震→引擎 `cameraPos`,须同步改 render.js 渲染基座到引擎相机**(否则全 Canvas2D 的 render 不响应 cameraPos;不改则屏震保留受控面偏移量补层)。
|
||||
- A2 particles-juice:粒子→经 `ctx.getEngine().particles.spawnEmitter` 调引擎 `ParticleEmitter`(通道包装)。**hitStop/屏震不进透传通道**(P2 grounded 核 impl.js:582-615 定):二者在 impl 里是「受控面输出偏移/timeScale + 游戏层自己应用」闭环,插件不调引擎——hitStop 保插件自管(游戏层读 `getTimeScale()`);**屏震引擎化=render.js〔agent 生成域,非插件〕切引擎 `cameraPos` 相机**(render.js:915 现 `g.translate(shake)`,切相机后由引擎驱动;不切则保受控面偏移补层)。
|
||||
- A3 audio-music:合成核 zzfxG/zzfxM→引擎 `zzfx`/`zzfxM`(包装);音乐用 **`ZzFXMusic extends Sound`/`Sound.playMusic`**(**引擎无独立 `Music` 类**);**播放/混音层=补层**(受控面 PCM playChannels 与引擎世界音频 `Sound.play` 不兼容,§1.5)。
|
||||
- A4 collision/gamefeel/physics-lite:按 §7 三分逐函数落地。
|
||||
- A5 SIZES 重建(引擎首次入 bundle,单口径「引擎+插件」,**非「消双份记账」**——双份在 SIZES 从未成立,R3-P1-2)。
|
||||
@ -90,8 +90,8 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/
|
||||
| audio-music | zzfxG/zzfxM 合成核 | `zzfxG→Array:2724`/`zzfxM→Array:3730` | ✅ | n/a | ✅ | **合成包装** |
|
||||
| audio-music | playChannels 播放/混音 | `Sound.play(pos,...):2551` 世界定位+range 衰减 | ✗ | ✗ | ✗ | **缺件补层** |
|
||||
| particles-juice | 粒子发射/池 | `ParticleEmitter(pos,...) extends EngineObject:3232` | ✅ | ✗(世界 vs 像素) | ~ | **包装+世界↔像素阻抗层** |
|
||||
| particles-juice | hitStop | `timeScale:352`/`setTimeScale:638` | ✅ | n/a | ✅ | **包装** |
|
||||
| particles-juice | 屏震 | `cameraPos:334`/`setCameraPos:626` | ✅ | ~ | ⚠跨层 | **包装(须 render 切引擎相机)/否则补层** |
|
||||
| particles-juice | hitStop | `timeScale:352`/`setTimeScale:638` | ✅ | n/a | ✅ | **包装(但不进透传通道)**——插件自管 timeScale,游戏层读 `getTimeScale()` 应用,插件不调引擎(P2 核 impl.js:582-593) |
|
||||
| particles-juice | 屏震 | `cameraPos:334`/`setCameraPos:626` | ✅ | ~ | ⚠跨层 | **render.js 迁移(非插件通道)**——插件出偏移、render.js:915 `g.translate` 应用;引擎化=render.js〔agent 生成域〕切相机,不切则保偏移补层 |
|
||||
| palette-post | postProcess | `PostProcessPlugin(shaderCode):3578` GLSL 可注入 | — | — | ✗(CPU 像素缓冲 vs GPU GLSL) | **整体缺件补层**(引擎仅提供 GLSL 注入位) |
|
||||
| palette-post | RGB↔HSL/Bayer | `Color.setHSLA/HSLA:1431-40`(单色) | ~ | n/a | ✗(单色 vs 缓冲批) | **混合:基元参照,缓冲/索引/抖动补层** |
|
||||
| physics-lite | 抛体/clampSpeed/friction | `EngineObject.updatePhysics:2832`(帧步刚体)/`objectMaxSpeed:469` | ✗ | ✗ | ✗(秒级无状态纯函数 vs 帧步 OOP) | **缺件补层** |
|
||||
@ -160,4 +160,4 @@ A0 引擎主循环接管(host-dev 桩→**`engineInit` 五回调**〔gameInit/
|
||||
|
||||
---
|
||||
|
||||
> R3 复审 5P0/5P1/3P2 全采纳整改 + R4 二轮复审「可冻结」(1P1+3P2 已就地补净) + 待确认 A 默认采纳降级——**✅ 已冻结开工**。**Phase 0 ✅ 完成(`204eaed`:装 littlejsengine@1.18.19+ENGINE-CAPABILITIES.md 50+ 能力真签名+§7 验签填定);§1.5 延迟裁定获验证(4 条初判包装翻缺件补层)。Phase A 解锁**(地基=A0 engineInit 五回调接管);P2 三契约冻结待主会话据签名定(透传白名单/Runner URL 降级/storage 根)。
|
||||
> R3 复审 5P0/5P1/3P2 全采纳整改 + R4 二轮复审「可冻结」(1P1+3P2 已就地补净) + 待确认 A 默认采纳降级——**✅ 已冻结开工**。**Phase 0 ✅ 完成(`204eaed`:装 littlejsengine@1.18.19+ENGINE-CAPABILITIES.md 50+ 能力真签名+§7 验签填定);§1.5 延迟裁定获验证(4 条初判包装翻缺件补层)。Phase A 解锁**(地基=A0 engineInit 五回调接管);**P2 三契约 ✅ 冻结(grounded 据真签名+插件真调用面,待对抗复审)**:①受控面引擎能力透传通道 `PluginContext.getEngine()`(白名单 3 项 particles/audio/math,YAGNI 核 impl;引擎 import 只活集成段 host)②GamePackage `manifest.packageUrl`+`immutable`(复用后端 packageUrl 降级 DB-manifest)③SDK `StoragePlugin` 根契约上提(含 R4-P1 array 排除)。
|
||||
|
||||
112
game-runtime/src/core/api.d.ts
vendored
112
game-runtime/src/core/api.d.ts
vendored
@ -46,6 +46,103 @@ export interface FrameHandle {
|
||||
cancel(): void;
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────────────────
|
||||
* 受控引擎能力透传面(T1b-β 新增;core-protocol-v0.1)
|
||||
* 背景:PluginContext 的 6 项抽象(canvas/帧/输入/音频/时间/随机)是「引擎可换」边界,
|
||||
* 不含粒子发射器 / 合成核 / 引擎数学库。β 真接线需让**特定包装插件**经受控面够到这些
|
||||
* 引擎内建能力,故在 PluginContext 上新开一条「引擎能力透传通道」getEngine()。
|
||||
*
|
||||
* 铁律(与 6 项抽象同源):
|
||||
* 1) 仍封装——绝不暴露 littlejsengine 裸对象 / 裸类型 / 裸 Vector2;几何一律归一化 {x,y}。
|
||||
* 2) 引擎 import 只活在集成段 host(背书本通道),不散进各插件 impl.js;
|
||||
* 插件经 ctx.getEngine().* 够引擎,host-dev 桩与集成段 host 同形(换引擎只换 host)。
|
||||
* 3) host-dev 无引擎时整条通道为 null(getEngine() 返 null),插件须容错降级——
|
||||
* 对齐 getAudioContext() 的「返 null 不抛、告警一次」范式(见下 PluginContext)。
|
||||
* 4) 白名单最小完整(YAGNI,每项指到插件 impl 真有引擎调用点):仅收 particles-juice 发射器 /
|
||||
* audio-music 合成核 / gamefeel 数学库 3 项。**裁决②-3 的 hitStop/屏震不进通道**——它们在
|
||||
* impl 里是「受控面输出偏移+游戏层(render.js)自己应用」的闭环,插件不调引擎;屏震引擎化=
|
||||
* render.js〔agent 生成域〕切引擎相机,非插件经通道(2026-06-13 P2 grounded 核 impl.js:582-615 定)。
|
||||
* ⚠ 通道 = P2 定形状 / Phase A2 接调用点 / 门1② probe 在 A2 后验;audio.synth 是否真用由
|
||||
* audio-music lane 在 A2 据 SIZES 实测定(去重 vendored zzfx 收益 vs 降级回退复杂度),不用则清。
|
||||
* ────────────────────────────────────────────────────────────────────────── */
|
||||
|
||||
/** 归一化二维矢量(POJO,非引擎 Vector2 实例)——通道内一切坐标用它,禁裸 Vector2 泄漏。仅本通道用,不回改 InputEvent。 */
|
||||
export interface Vec2 {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/** 受控粒子发射器句柄(封装引擎 ParticleEmitter;插件只见本面,不见引擎对象)。 */
|
||||
export interface EngineEmitterHandle {
|
||||
/** 是否仍在发射(封装引擎 isActive)。 */
|
||||
isActive(): boolean;
|
||||
/** 停止并从引擎对象列表移除(幂等;封装引擎对象销毁)。 */
|
||||
stop(): void;
|
||||
}
|
||||
|
||||
/** 归一化发射器参数(受控面像素 + 中性形态;host 映射到引擎 ParticleEmitter,禁引擎构造签名泄漏)。 */
|
||||
export interface EngineEmitterSpec {
|
||||
/** 发射原点(受控面像素坐标 {x,y})。 */
|
||||
pos: Vec2;
|
||||
/** 发射锥中心方向(弧度)。 */
|
||||
angle?: number;
|
||||
/** 发射尺寸(像素半径,host 换算引擎 emitSize)。 */
|
||||
emitSize?: number;
|
||||
/** 持续时长(秒,0=一次性 burst)。 */
|
||||
emitTime?: number;
|
||||
/** 每秒发射率(continuous 模式)。 */
|
||||
emitRate?: number;
|
||||
/** 单粒子生命(秒)。 */
|
||||
particleTime?: number;
|
||||
}
|
||||
|
||||
/** 受控粒子能力面(封装引擎 ParticleEmitter;坐标=受控面像素,host 内部做像素↔世界阻抗换算)。 */
|
||||
export interface EngineParticles {
|
||||
/**
|
||||
* 创建一个引擎背书的粒子发射器。坐标/尺寸均为**受控面像素口径**(host 换算到引擎世界坐标)。
|
||||
* @param spec 归一化发射参数(中性运动学,无玩法语义;host 映射到引擎 ParticleEmitter 构造)。
|
||||
* @returns 发射器句柄。
|
||||
*/
|
||||
spawnEmitter(spec: EngineEmitterSpec): EngineEmitterHandle;
|
||||
}
|
||||
|
||||
/** 受控合成核能力面(封装引擎 zzfxG/zzfxM;返回 PCM 样本数组,与 vendored 同口径,不播放)。 */
|
||||
export interface EngineAudioSynth {
|
||||
/**
|
||||
* ZzFX 单音效合成核:参数包 → PCM 单声道样本数组(不播放)。与插件 vendored zzfxG 同口径。
|
||||
* @returns PCM 样本数组;无引擎或合成失败返 null(插件回退 vendored)。
|
||||
*/
|
||||
synthSfx(params: number[]): number[] | null;
|
||||
/**
|
||||
* ZzFXM 曲谱合成核:乐器/模式/序列/BPM → [L,R] 双声道样本(不播放)。
|
||||
* @returns 双声道样本数组;无引擎/失败返 null(插件回退 vendored zzfxM)。
|
||||
*/
|
||||
synthSong(instruments: number[][], patterns: number[][][], sequence: number[], bpm?: number): number[][] | null;
|
||||
}
|
||||
|
||||
/** 受控引擎数学库能力面(封装引擎 lerp/smoothStep;口径对齐引擎,纯函数恒可用)。 */
|
||||
export interface EngineMath {
|
||||
/** 线性插值(封装引擎 lerp,口径一致)。 */
|
||||
lerp(valueA: number, valueB: number, percent: number): number;
|
||||
/** smoothstep 缓动(封装引擎 smoothStep,口径一致)。 */
|
||||
smoothStep(percent: number): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 受控引擎能力透传面(β 新增)。挂在 PluginContext.getEngine() 上。
|
||||
* **封装边界**:所有方法/类型均为归一化封装,零 littlejsengine 裸对象/裸类型泄漏。
|
||||
* **降级语义**:host-dev/node 无引擎时 getEngine() 返 null,插件须整体容错(粒子退受控面自管、
|
||||
* 合成核退 vendored、数学退插件内置)。集成段 host 注入真实引擎背书实现。
|
||||
*/
|
||||
export interface EngineCapabilities {
|
||||
/** 受控粒子能力(封装 ParticleEmitter)。 */
|
||||
particles: EngineParticles;
|
||||
/** 受控合成核(封装 zzfxG/zzfxM)。A2 据 SIZES 定是否真用,不用则清。 */
|
||||
audio: { synth: EngineAudioSynth };
|
||||
/** 受控引擎数学库(封装 lerp/smoothStep)。 */
|
||||
math: EngineMath;
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────────────────
|
||||
* 受控输入(Gate0.1)—— 具名消费者 P4 输入缓冲 / P10 t_input_bound
|
||||
* ────────────────────────────────────────────────────────────────────────── */
|
||||
@ -126,8 +223,17 @@ export interface PluginContext {
|
||||
* 受控随机源(可注种子)。
|
||||
* **Gate0.1 起为 per-plugin 派生流**:注册器 init 时给每插件派生独立子随机源
|
||||
* (主 seed + 插件名 FNV-1a 子种子),插件 A 取随机不再推进插件 B 的序列。
|
||||
* **β 集成段实现换引擎背书**:底层由引擎 `RandomGenerator`(种子 xorshift)背书,
|
||||
* 非 host-dev 的 mulberry32 SeededRandom(裁决②-2;契约形状不变,仅换实现)。
|
||||
*/
|
||||
random: RandomSource;
|
||||
/**
|
||||
* 受控引擎能力透传面(β 新增;具名消费者:particles-juice 发射器 / audio-music 合成核 / gamefeel 数学库)。
|
||||
* **host-dev/node 无引擎时返回 null 并 console 告警一次(不抛错)**——对齐 getAudioContext 范式,
|
||||
* 插件须整体容错降级(粒子退受控面自管 / 合成核退 vendored / 数学退插件内置纯函数)。
|
||||
* 集成段=LittleJS 真实句柄背书;**引擎 import 只在集成段 host,不进插件 impl(保引擎可换 + 满足门1 真接线)**。
|
||||
*/
|
||||
getEngine(): EngineCapabilities | null;
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────────────────────────────────
|
||||
@ -224,6 +330,12 @@ export interface HostDevContextOptions {
|
||||
* 集成段注入真实工厂(如 () => new AudioContext())。
|
||||
*/
|
||||
audioFactory?: (() => AudioContext) | null;
|
||||
/**
|
||||
* 引擎能力背书工厂(β 新增)。缺省 null → getEngine() 返 null 并告警一次(插件容错降级)。
|
||||
* 集成段注入真实工厂(封装 littlejsengine:ParticleEmitter/zzfxG/zzfxM/lerp/smoothStep)。
|
||||
* **本工厂是「引擎 import 只活在集成段 host」的唯一落点**——host-dev 桩传 null,集成段 host 传真实背书。
|
||||
*/
|
||||
engineFactory?: (() => EngineCapabilities) | null;
|
||||
}
|
||||
|
||||
/** createHostDevContext 返回:受控上下文 + host 私有 tick 驱动器 + 帧数读取 + 输入桥 + 每插件派生器。 */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user