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:
zizi 2026-06-13 05:12:35 +00:00
parent b300cd0daa
commit 28a57b8e00
4 changed files with 174 additions and 5 deletions

View File

@ -62,7 +62,16 @@
"enum": ["eager", "lazy"]
},
"bundleSize": { "description": "包总字节(编译期门禁校验上限)", "type": "integer", "minimum": 0 },
"checksum": { "description": "整包 sha256hexManifest 完整性校验", "type": "string", "pattern": "^[a-f0-9]{64}$" }
"checksum": { "description": "整包 sha256hexManifest 完整性校验。T1b-β 门4 防 demo 兜底假阳性=Network 捕获本 checksum 比对发布版本", "type": "string", "pattern": "^[a-f0-9]{64}$" },
"packageUrl": {
"description": "运行包可访问 URLT1b-β 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/304false/缺省=普通缓存。additive 可选。",
"type": "boolean"
}
}
},
"meta": {

View File

@ -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 消息 payloadidle 离线产出存档HJ-MC-TPL-EXEC-002 §6.1.1T1b-β 上提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.5runtime
* loadState resolve runtime payloadStorageSetPayload.value JSON
* **T1b-β R4-P1**宿game-studio inject.ts !Array.isArray
* value TS Record<string,unknown>|nullarray TS object
* ****//resolve null
* storage / game-studio host/contract.ts / inject.ts arraynull
*/
value: Record<string, unknown> | null;
}

View File

@ -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`(依赖 A0input 改「对引擎轮询态(`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.jsagent 生成域,非插件〕切引擎 `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.jsagent 生成域〕切相机,不切则保偏移补层 |
| 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/mathYAGNI 核 impl引擎 import 只活集成段 host)②GamePackage `manifest.packageUrl`+`immutable`(复用后端 packageUrl 降级 DB-manifest)③SDK `StoragePlugin` 根契约上提(含 R4-P1 array 排除)。

View File

@ -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 nullgetEngine() null
* getAudioContext() null PluginContext
* 4) YAGNI impl particles-juice /
* audio-music / gamefeel 3 **-3 hitStop/**
* impl +(render.js)=
* render.jsagent 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
* littlejsengineParticleEmitter/zzfxG/zzfxM/lerp/smoothStep
* ** import host**host-dev null host
*/
engineFactory?: (() => EngineCapabilities) | null;
}
/** createHostDevContext 返回:受控上下文 + host 私有 tick 驱动器 + 帧数读取 + 输入桥 + 每插件派生器。 */