v0.1.35b: 兼容层铲除——迁移链/版本戳/死表死代码/normalize 兼容批/死字段
【A+I】migrate.ts 迁移链(CURRENT_SCHEMA/MIGRATIONS/migrateIfNeeded/MigrationError)整链铲除→validateLoadedState 结构校验(seed/family/members 断言);信封统一 payload 单格式(去双格式兜底);APP_ID 保留 【F】schemaVersion 字段全删(domain/creation/save 8 处);SaveMeta.version 记游戏版本字符串 【C】chronicle 死表+saveChronicle 死方法(首行 return+unreachable legacy 僵尸)全删 【D/E】GameEngine __state globalThis 泄漏口+seed 三元(恒不可达)删除;engineFromSnapshot/noAutoCreate 保留(测试便利——非兼容机器) 【B/H】normalizeGameState 兼容批删:老档字段补(28行)/worldGen 重放(18行)/P0-2 占位 def(9行)/worldSim 字段批删;保留 npcs 重注册/headId 修复/d distress 防护;initSim 补 0.1.34 四键(唯一初始源) 【G】domain 收紧 5 兼容 optional(techniqueRank/Progress/tribBoost/worldGen 必填)+pcgen 必写 【测试】删 9 个兼容验证体(audit 0.1.0-era/全残缺矩阵/空 worldSim 兜底);5989 全绿——金钟罩零漂(新档结构全字段,删除不涉世界数值)
This commit is contained in:
@@ -10,18 +10,6 @@ import { GameState } from '../src/renderer/game/types/domain'
|
||||
import { resetSaveBus, attachLogSink } from './world.helpers'
|
||||
|
||||
describe('0.1.3 audit regression', () => {
|
||||
it('loads a 0.1.0-era save (missing finance/yearStats fields)', () => {
|
||||
const w = World.create({ seed: 'legacy', surname: '崔', familyName: '崔家', motto: 'm', difficulty: 'normal' })
|
||||
const legacy = JSON.parse(JSON.stringify(w.state)) as GameState
|
||||
delete (legacy as Record<string, unknown>)['finance']
|
||||
delete (legacy as Record<string, unknown>)['yearStats']
|
||||
delete (legacy as Record<string, unknown>)['yearlyReports']
|
||||
const w2 = new World(legacy)
|
||||
for (let i = 0; i < 40; i++) w2.advanceMonth()
|
||||
expect(w2.state.yearlyReports.length).toBeGreaterThan(0)
|
||||
expect(Number.isNaN(w2.state.family.stones)).toBe(false)
|
||||
})
|
||||
|
||||
it('marries a npc family once only (allied lock)', () => {
|
||||
const w = World.create({ seed: 'hy', surname: '卫', familyName: '卫家', motto: 'm', difficulty: 'normal' })
|
||||
const npcId = Object.keys(w.state.npcFamilies)[0]!
|
||||
|
||||
@@ -73,36 +73,6 @@ describe('志向矩阵', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeGameState 全残缺矩阵', () => {
|
||||
it.each([
|
||||
['finance', 'yearStats', 'yearlyReports', 'stats'].map((k) => [k] as const)
|
||||
].flat())('%s 缺失补齐', (key) => {
|
||||
const w = baseWorld('norm-' + key)
|
||||
const state = JSON.parse(JSON.stringify(w.state))
|
||||
delete (state as Record<string, unknown>)[key]
|
||||
const fixed = normalizeGameState(state)
|
||||
expect((fixed as Record<string, unknown>)[key]).toBeTruthy()
|
||||
})
|
||||
|
||||
it('无 headId 老档补齐首个存活着', () => {
|
||||
const w = baseWorld('norm-head')
|
||||
const state = JSON.parse(JSON.stringify(w.state))
|
||||
state.family.headId = 'x-not-exist'
|
||||
const fixed = normalizeGameState(state)
|
||||
expect(fixed.family.headId).toBe('x1')
|
||||
})
|
||||
|
||||
it('缺 npcFamilies 补齐空表(不崩外交)', () => {
|
||||
const w = baseWorld('norm-npc')
|
||||
const state = JSON.parse(JSON.stringify(w.state))
|
||||
delete state.npcFamilies
|
||||
const fixed = normalizeGameState(state)
|
||||
const w2 = new World(fixed)
|
||||
for (let i = 0; i < 3; i++) w2.advanceMonth() // 不抛
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('大比寄读渡劫动态事件矩阵', () => {
|
||||
it.each([
|
||||
['ev-tournament-10', '称病不出'],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { World } from '../src/renderer/game/engine/runtime/World'
|
||||
import { buildReport, verdictLine } from '../src/renderer/game/engine/narrative/legacyreport'
|
||||
import { migrateIfNeeded, exportEnvelope, parseImportEnvelope, CURRENT_SCHEMA, APP_ID } from '../src/renderer/game/storage/migrate'
|
||||
import { exportEnvelope, parseImportEnvelope, validateLoadedState } from '../src/renderer/game/storage/migrate'
|
||||
import { FORMATIONS, formationById, FormationId } from '../src/renderer/game/data/formations'
|
||||
import { resolveEncounter } from '../src/renderer/game/engine/runtime/Systems/combat'
|
||||
import { ENEMIES } from '../src/renderer/game/data/secrets'
|
||||
@@ -56,53 +56,6 @@ describe('legacyreport 百年报告', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('存档迁移管线', () => {
|
||||
it('CURRENT_SCHEMA=2 且 v1→v2 迁移补字段', () => {
|
||||
expect(CURRENT_SCHEMA).toBe(2)
|
||||
const w = baseWorld('mig-a')
|
||||
const asV1 = JSON.parse(JSON.stringify(w.state))
|
||||
asV1.schemaVersion = 1
|
||||
delete asV1.stats
|
||||
const res = migrateIfNeeded(asV1)
|
||||
expect(res.ok).toBe(true)
|
||||
const s = (res as never as { state: { stats: unknown; schemaVersion: number } }).state
|
||||
expect(s.stats).toBeTruthy()
|
||||
expect(s.schemaVersion).toBe(2)
|
||||
})
|
||||
|
||||
it('未来版本拒绝加载(防误用)', () => {
|
||||
const w = baseWorld('mig-b')
|
||||
const s = JSON.parse(JSON.stringify(w.state))
|
||||
s.schemaVersion = 99
|
||||
const res = migrateIfNeeded(s)
|
||||
expect(res.ok).toBe(false)
|
||||
expect((res as never as { error: { code: string } }).error.code).toBe('TOO_NEW')
|
||||
})
|
||||
|
||||
it('导出信封带 appId 与版本;导入旧版可迁移', () => {
|
||||
const w = baseWorld('mig-c')
|
||||
w.state.schemaVersion = CURRENT_SCHEMA
|
||||
const env = JSON.parse(exportEnvelope(w.state))
|
||||
expect(env.app).toBe(APP_ID)
|
||||
expect(env.schemaVersion).toBe(2)
|
||||
const parsed = parseImportEnvelope(JSON.stringify(env))
|
||||
expect(parsed.ok).toBe(true)
|
||||
})
|
||||
|
||||
it('异 app 拒绝导入', () => {
|
||||
const w = baseWorld('mig-d')
|
||||
const env = JSON.parse(exportEnvelope(w.state))
|
||||
env.app = 'other-game'
|
||||
const parsed = parseImportEnvelope(JSON.stringify(env))
|
||||
expect(parsed.ok).toBe(false)
|
||||
})
|
||||
|
||||
it('损坏 JSON 拒绝导入', () => {
|
||||
const res = parseImportEnvelope('not-json-wrapper')
|
||||
expect(res.ok).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('战斗阵型', () => {
|
||||
it.each(Object.keys(FORMATIONS).map((k) => [k as FormationId] as const))('%s 阵型定义合法', (id) => {
|
||||
const f = FORMATIONS[id]
|
||||
|
||||
@@ -67,7 +67,7 @@ describe('0.1.30 矩阵:月末大圈(600 例级)', () => {
|
||||
expect(w.state.seq).toBeGreaterThan(0)
|
||||
expect(Array.isArray(w.state.completedEvents)).toBe(true)
|
||||
expect(Array.isArray(w.state.missions)).toBe(true)
|
||||
expect(typeof w.state.schemaVersion).toBe('number')
|
||||
expect(typeof w.state.seed).toBe('string') // 0.1.35 版本戳移除——seed 为世界锚
|
||||
expect(typeof w.state.seed).toBe('string')
|
||||
expect(Array.isArray(w.state.battles)).toBe(true)
|
||||
})
|
||||
|
||||
@@ -4,18 +4,6 @@ import { normalizeGameState } from '../src/renderer/game/engine/runtime/World'
|
||||
import { WorldSimState } from '../src/renderer/game/engine/sim/worldsim-data'
|
||||
|
||||
describe('0.1.19 八方风雨', () => {
|
||||
it('兜底:空 worldSim 的旧档 normalize 后 tideTicks 有值(P0 修复)', () => {
|
||||
const w = World.create({ seed: 'p0-1', surname: '甯', familyName: '甯家', motto: 'm', difficulty: 'normal' })
|
||||
const state = w.state as never
|
||||
const fixed = normalizeGameState({
|
||||
...(state as Record<string, unknown>),
|
||||
worldSim: { marketPool: {}, npcDyn: {}, secretQi: {}, newsFeed: [] }
|
||||
} as never) as { worldSim: { tideTicks: number; era: string; calamityLeft: number } }
|
||||
expect(fixed.worldSim.tideTicks).toBe(0)
|
||||
expect(fixed.worldSim.era).toBe('pingshi')
|
||||
expect(fixed.worldSim.calamityLeft).toBe(0)
|
||||
})
|
||||
|
||||
it('stance:四姿态值域 + 年首(约8年)可再评估', () => {
|
||||
const w = World.create({ seed: 'st-1', surname: '冀', familyName: '冀家', motto: 'm', difficulty: 'normal' })
|
||||
const seen = new Set<string>()
|
||||
|
||||
Reference in New Issue
Block a user