v0.1.15: 深度审计加固(7 P0 + 20+ P1 歼灭)

【引擎层 P0】
- GameEngine.restore 双重时钟注册(回档后整月双跑)→ restore 重建内核+回置 rng 状态
- engineFromSnapshot 必崩(globalThis.__state 从未设)→ 修正带 seed 重建
- DEV 性能分支二次执行整月管线 → 只读告警(绝不重跑)
- 引擎 advance 遇待决事件死锁 → resolvePending(idx) 通道(自动化/测试推进)

【WorldSim 大修】
- NPC 换代文案垃圾(n-danxin行情降50%)→ 换代快讯专属文案
- secretQi 假闭环(纯写无读)→ 全秘境初始化+missions 统一 consumeSecretQi
- 快讯节流自锁(lastNewsMonth 恒 1 月)→ totalTicks%24
- power 月度收敛坍塌(1400±3)→ 换代/年首化+年度成长
- relation 双重拉零(4 家全 0)→ 交还外交年度漂移
- 市场基准 5 处重复 → POOL_BASE 单一权威;灾年 stale 清理;drift 各池独立噪声
- worldsim 能力卡补全 + normalize 兜底(worldSim/stats 子字段)

【UI/动效/存储】
- ModalShell 三条 CSS 规则真正落地(close 定位/body 滚动/footer 独立底排)——0.1.12 声明未实施项
- fx-canvas:resize 监听移除/mistTimer 清零/reduced 联动/soft 档无雾 + drift 走 gate(过滤器)
- 档位 density 脱钩修复;顶栏“止”钮补 onClick;版本号三处统一 0.1.15
- importAll 清 chronicle 表;自动推进季度落盘(手动逐次)
- 性能:LogFeed memo / Genealogy computeGenealogy memo / Chronicle deps 修正;死码清除(lastPhaseStats/clockFromKernel/哨兵函数等 12 处)
- 删除误入库的“p”游离文件

【测试】979 全绿(36 套件);金钟罩三档 0.1.15 基线固化;
typecheck/build 通过
This commit is contained in:
2026-08-23 13:52:51 +08:00
parent 26bd61fba0
commit c5fa2a3357
26 changed files with 220 additions and 208 deletions
+16 -5
View File
@@ -22,7 +22,7 @@ export type EngineStatus = 'idle' | 'running' | 'gameover'
* UI/测试/未来工具仅需认识 GameEngine。
*/
export class GameEngine {
readonly kernel: Kernel
kernel: Kernel
readonly datapack: typeof PACK
world: World
facade: ApiFacade
@@ -31,7 +31,7 @@ export class GameEngine {
private tickSinceSave = 0
constructor(opts: GameEngineOptions = {}) {
this._seed = opts.seed ?? `cotyc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
this._seed = opts.seed ?? `cotyc-${Date.now().toString(36)}-${Math.floor(Math.random() * 1296).toString(36)}`
this.kernel = makeKernel(this._seed)
this.datapack = PACK
if (opts.datapack) this.datapack.override(opts.datapack)
@@ -55,8 +55,16 @@ export class GameEngine {
advance(): void {
if (this.world.state.gameOver) return
if (this.world.state.pendingEvent) return // 事件待决:停下(用户/自动化应 resolvePending
this.world.advanceMonth()
this.tickSinceSave++
}
/** 处理当前待决事件(默认选第一项);无待决则 no-op */
resolvePending(idx = 0): boolean {
const pid = this.world.state.pendingEvent
if (!pid) return false
// 直接走门面 apply 通路的引擎包装(不依赖 UI)
return this.world.applyEventChoice(pid, idx)
}
syncRng(): void {
@@ -90,9 +98,12 @@ export class GameEngine {
return JSON.parse(JSON.stringify(this.world.state)) as GameState
}
/** 从快照恢复(可回放) */
/** 从快照恢复(可回放)——重建内核以避免时钟双注册,并回置 rng 保真 */
restore(snap: GameState): void {
normalizeGameState(snap)
// 新内核(同一 seed 派生)+ 回写存档 rng 快照 —— 回放保真
this.kernel = makeKernel(snap.seed)
this.kernel.rng.state = { ...snap.rng }
const newWorld = new World(snap, [], this.kernel)
newWorld.out = this.world.out
this.world = newWorld
@@ -110,7 +121,7 @@ export class GameEngine {
/** 已用旧内核时钟注册的世界快照,反序列化时公用(便捷入口) */
export function engineFromSnapshot(state: GameState): GameEngine {
const eng = new GameEngine({ noAutoCreate: true })
const eng = new GameEngine({ seed: state.seed, noAutoCreate: true })
eng.restore(state)
return eng
}