diff --git a/src/renderer/game/engine/sim/WorldSim.ts b/src/renderer/game/engine/sim/WorldSim.ts index 70881c6..2eec4dc 100644 --- a/src/renderer/game/engine/sim/WorldSim.ts +++ b/src/renderer/game/engine/sim/WorldSim.ts @@ -25,6 +25,10 @@ export class WorldSim { } tick(): void { + // 0.1.39 根治双闸:clocks.ts 外部守卫 + 此处入口二次守卫。 + // 与 tournament/apprentice 等系统卡同模式(sysEnabled ?? true 兜底, + // 注册后默认开、关停后 false)。防止未来新增直接调 tick() 的路径绕过守卫。 + if (!this.w.sysEnabled('worldsim')) return const s = this.s() const rng = this.w.rng // ---- C. 灵气潮汐(大周期) ---- diff --git a/tests/worldsim-freeze-0.1.39.test.ts b/tests/worldsim-freeze-0.1.39.test.ts new file mode 100644 index 0000000..f69f79c --- /dev/null +++ b/tests/worldsim-freeze-0.1.39.test.ts @@ -0,0 +1,107 @@ +/** + * 0.1.39 P3:worldsim 能力卡关停根治测试 + * 验证关停后世界自主演进完全冻结——潮汐、NPC 动力学、时代弧、快讯不再推进。 + * 注意:玩家行为(修炼耗草→tradeSettle 回写池、买卖交易回写池)不受 worldsim + * 能力卡关停影响——这是玩家与世界的交互而非世界自主演进。 + */ +import { describe, expect, it } from 'vitest' +import { World } from '../src/renderer/game/engine/runtime/World' +import { WorldSim } from '../src/renderer/game/engine/sim/WorldSim' +import { resetSaveBus, attachLogSink } from './world.helpers' +import type { WorldSimState } from '../src/renderer/game/engine/sim/worldsim-data' + +function baseWorld(seed: string): World { + const w = World.create({ seed, surname: '封', familyName: '封家', motto: 'm', difficulty: 'normal' }) + resetSaveBus() + attachLogSink(w) + return w +} + +describe('0.1.39 worldsim 关停根治', () => { + it('sysEnabled("worldsim") 默认开 → toggleSystem 后关', () => { + const w = baseWorld('ws-toggle-1') + expect(w.sysEnabled('worldsim')).toBe(true) + const after = w.toggleSystem('worldsim') + expect(after).toBe(false) + expect(w.sysEnabled('worldsim')).toBe(false) + }) + + it('关停后 tick() 直接 return(入口双闸守卫)', () => { + const w = baseWorld('ws-freeze-1') + // 推进 12 月让 worldSim 有初始状态 + for (let i = 0; i < 12; i++) w.advanceMonth() + const ws = w.state.worldSim as WorldSimState + expect(ws).toBeDefined() + + // 快照关键字段 + const tideBefore = ws.tide + const tideTicksBefore = ws.tideTicks + const poolBefore = { ...ws.marketPool } + + // 关停 worldsim + w.toggleSystem('worldsim') + expect(w.sysEnabled('worldsim')).toBe(false) + + // 直接调 tick() 应被入口守卫拦截 + new WorldSim(w).tick() + + // 状态完全冻结(tick 内所有逻辑未执行) + expect(ws.tide).toBe(tideBefore) + expect(ws.tideTicks).toBe(tideTicksBefore) + expect(ws.marketPool).toEqual(poolBefore) + }) + + it('关停后 advanceMonth 不再自主演进世界(潮汐/NPC/时代弧/快讯冻结)', () => { + const w = baseWorld('ws-freeze-2') + // 推进 24 月让世界有实质演化 + for (let i = 0; i < 24; i++) w.advanceMonth() + const ws = w.state.worldSim as WorldSimState + expect(ws).toBeDefined() + + // 快照世界自主演进指标 + const tideTicksBefore = ws.tideTicks + const npcDynBefore = JSON.stringify(ws.npcDyn) + const secretQiBefore = JSON.stringify(ws.secretQi) + const eraBefore = ws.era + const newsLenBefore = ws.newsFeed.length + const calamityBefore = ws.calamity + const warFatigueBefore = ws.warFatigue + + // 关停 + w.toggleSystem('worldsim') + + // 推进 36 月——世界自主演进应纹丝不动 + for (let i = 0; i < 36; i++) w.advanceMonth() + + // tick 内逻辑全部冻结 + expect(ws.tideTicks).toBe(tideTicksBefore) // 潮汐不推进 + expect(JSON.stringify(ws.npcDyn)).toBe(npcDynBefore) // NPC 动力学不变 + expect(JSON.stringify(ws.secretQi)).toBe(secretQiBefore) // 秘境灵气不自恢复 + expect(ws.era).toBe(eraBefore) // 时代弧不变 + expect(ws.newsFeed.length).toBe(newsLenBefore) // 无新快讯 + expect(ws.calamity).toBe(calamityBefore) // 灾年状态不变 + expect(ws.warFatigue).toBe(warFatigueBefore) // 战争疲劳不变 + }) + + it('重新开启后世界恢复演进(可逆性)', () => { + const w = baseWorld('ws-resume-1') + for (let i = 0; i < 12; i++) w.advanceMonth() + const ws = w.state.worldSim as WorldSimState + + // 关停 12 月 + w.toggleSystem('worldsim') + const tideFrozen = ws.tide + const ticksFrozen = ws.tideTicks + for (let i = 0; i < 12; i++) w.advanceMonth() + expect(ws.tide).toBe(tideFrozen) + expect(ws.tideTicks).toBe(ticksFrozen) + + // 重新开启 + w.toggleSystem('worldsim') + expect(w.sysEnabled('worldsim')).toBe(true) + for (let i = 0; i < 12; i++) w.advanceMonth() + + // 世界恢复演进——tideTicks 必须增长 + expect(ws.tideTicks).toBeGreaterThan(ticksFrozen) + }) +})