Files
ChronicleOfTheImmortalClan/src/renderer/game/engine/GameEngine.ts
T
thzxx c5fa2a3357 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 通过
2026-08-23 13:52:51 +08:00

128 lines
4.1 KiB
TypeScript
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.
import { Kernel, makeKernel } from './kernel/Kernel'
import { World } from './runtime/World'
import { GameFacade as ApiFacade, ActName, ActPayload, FacadeEvent } from './runtime/ApiFacade'
import { GameState } from '../types/domain'
import { GameClock } from './kernel/clock'
import { CotycPlugin } from './kernel/plugin'
import { DataPack, PACK } from '../data/registry'
import { createWorldState } from './runtime/creation'
import { normalizeGameState } from './runtime/World'
export interface GameEngineOptions {
seed?: string
datapack?: Partial<DataPack>
noAutoCreate?: boolean
}
export type EngineStatus = 'idle' | 'running' | 'gameover'
/**
* GameEngine —— 仙途家族志统一游戏引擎(0.1.14 架构核心)。
* 一个引擎对象 = 内核(Kernel: 时钟+随机+总线) + 世界(World) + 门面(ApiFacade) + 存储接口。
* UI/测试/未来工具仅需认识 GameEngine。
*/
export class GameEngine {
kernel: Kernel
readonly datapack: typeof PACK
world: World
facade: ApiFacade
private _seed: string
autosaveEvery = 12
private tickSinceSave = 0
constructor(opts: GameEngineOptions = {}) {
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)
// 组装World(内核注入→时钟/随机/总线单源)
const state = opts.seed ? createWorldState({
seed: this._seed,
surname: '林',
familyName: '林氏',
motto: '耕读传家,术法继世',
difficulty: 'normal'
}) : (globalThis as never as { __state?: GameState }).__state
this.world = new World(state!, [], this.kernel)
this.facade = new ApiFacade(this.world, 1)
// Kernel 总线 → World.out 桥接(attached 后同步 feed
this.world.out = this.world.out.concat([])
}
view(): GameState {
return this.world.state
}
advance(): void {
if (this.world.state.gameOver) return
if (this.world.state.pendingEvent) return // 事件待决:停下(用户/自动化应 resolvePending
this.world.advanceMonth()
}
/** 处理当前待决事件(默认选第一项);无待决则 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 {
this.world.syncRng()
}
act(name: ActName, payload: ActPayload): boolean {
return this.facade.act(name, payload)
}
query(ref: string): Record<string, unknown> {
return this.facade.query(ref)
}
subscribe(on: (e: FacadeEvent) => void): () => void {
return this.facade.subscribe(on)
}
about(): { title: string; version: string; modules: number; systems: number; plugins: number; packFingerprint: string } {
const a = this.facade.about()
return { ...a, version: '0.1.14' }
}
installPlugin(p: CotycPlugin): { ok: boolean; reason?: string } {
return this.world.installPlugin(p)
}
/** 档位快照(存档 JSON */
snapshot(): GameState {
this.world.syncRng()
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
this.facade = new ApiFacade(this.world, 1)
}
status(): EngineStatus {
return this.world.state.gameOver ? 'gameover' : 'running'
}
seed(): string {
return this._seed
}
}
/** 已用旧内核时钟注册的世界快照,反序列化时公用(便捷入口) */
export function engineFromSnapshot(state: GameState): GameEngine {
const eng = new GameEngine({ seed: state.seed, noAutoCreate: true })
eng.restore(state)
return eng
}