refactor(0.1.14-P2): Kernel 三合一 + GameEngine 引擎门面

- Kernel(engine/kernel/Kernel.ts):游戏时钟+种子随机+事件总线——单一实例三合一,
  World 不再自造 clock/rng(内核注入,bus 透传 out)
- GameEngine(engine/GameEngine.ts):唯一引擎入口——
  new GameEngine({seed,datapack}) → world/facade/kernel/datapack,
  advance/act/query/subscribe/about/installPlugin/snapshot/restore/status
- World 构造接受 kernel 注入(保持行为等价:时钟注册顺序不变)
- ApiFacade.subscribe 简化纯实现
- 验证:967 测试全绿、金钟罩三档零漂移、typecheck 0 error
This commit is contained in:
2026-08-23 13:25:42 +08:00
parent ff6df8054c
commit 8dfdaf843a
4 changed files with 206 additions and 3 deletions
+134
View File
@@ -0,0 +1,134 @@
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 {
readonly 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()}-${Math.random().toString(36).slice(2, 8)}`
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
const kernelClock = this.kernel.clock
const worldClock = this.world.clock
// 内核统一驱动(执行内核时钟注册,world.clock 仅作镜像)
void worldClock
this.tickOn(kernelClock)
this.tickSinceSave++
this.world.syncRng()
}
private tickOn(clock: GameClock): void {
const w = this.world
const s = w.state
s.month++
if (s.month > 12) {
s.month = 1
s.year++
clock.fireYearStart(w as never)
}
s.totalTicks++
clock.stepMonthly(w as never)
}
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
}
/** 从快照恢复(可回放) */
restore(snap: GameState): void {
normalizeGameState(snap)
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({ noAutoCreate: true })
eng.restore(state)
return eng
}