Files
ChronicleOfTheImmortalClan/tests/mod-system-0.1.25.test.ts
T
thzxx 5d9c333c76 v0.1.25: 乾坤一统——MOD 系统首版(1040 全绿,金钟罩不漂)
【P0 修复(0.1.24 遗留)】worldGen.npcs defs 落档 + normalize 幂等重注册——读档后
动态家族(n-g-*)npcById 永命中;MOD 卸载后存档世界仍完整(存档世界永稳)

【MOD 系统四层】
- M2 内容层:modSchema.ts(ModPack 全 JSON:manifest+data{events/npcs/worldgen/pills/forges};
  modParse 纯解析校验 id/name/version/事件模板合法性)
- M3 管理器:modToPlugin(MOD→CotycPlugin 一等子类——插件持久化/双闸/启停/回滚全复用)
- M1 传输层:main IPC mods:scan/read(userData/mods/*.json|*.cotymod)+ preload + env.d.ts
  + store refreshMods/installModByName/installModText
- M4 UI:SettingsPanel「MOD 层」卡(扫描目录/候选列表/安装);内置示例包通路

【世界/插件协同】
- WorldGenPools 聚合词库池(默认不变→基准世界不变;reset() 供隔离);MOD 于新档参与生成
- items.ts 配方聚合注册表(PILL/FORGE base+扩展);PluginContext addPillRecipe/addForgeRecipe/addWorldGenPool

【边界(明注)】灾因/世界数值表注入归平衡版;脚本型 MOD 不做;MOD 只影响新档世界(老档不变形)

【测试】1040 全绿(46 套件):worldGen 存档 1 例 + mod-system 5 例(解析/转换/模板/依赖/基准指纹);
金钟罩 0.1.24 基线未漂(世界生成默认池不变);build 通过
2026-08-23 16:29:29 +08:00

61 lines
3.2 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 { describe, expect, it } from 'vitest'
import { modParse } from '../src/renderer/game/engine/runtime/modSchema'
import { modToPlugin } from '../src/renderer/game/engine/runtime/modManager'
import { World } from '../src/renderer/game/engine/runtime/World'
import { generateWorld, WorldGenPools } from '../src/renderer/game/engine/sim/worldgen'
import { stateFingerprint, longRun } from './fingerprint.helper'
const SAMPLE = JSON.stringify({
id: 'test-mod', name: '测试MOD', version: '1.0.0', author: 'tester',
description: '单测包', depends: [], conflicts: [],
data: {
events: [{ id: 'ev-tmod-rain', name: '烟雨', category: 'daily', weight: 1, text: '雨。', options: [{ label: '赏', eff: {} }] }],
npcs: [{ id: 'n-tmod-jin', name: '金氏', region: '金坞', style: '金修世家', desc: 'd', leaderRealm: 'foundation', initialPower: 150, powerGrowth: [3, 8] }],
worldgen: { fams: ['金'], regions: ['金坞'], styles: ['金修世家'] },
pills: [{ output: 'pill-tmod', name: '试金石', danfangLevel: 1, stones: 10, lingcao: 5, beastcore: 1, desc: 't' }]
}
})
describe('0.1.25 MOD 系统', () => {
it('解析:合法包通过、非法包拒绝(缺失 id/version/data', () => {
expect(modParse(SAMPLE).ok).toBe(true)
expect(modParse('not json').ok).toBe(false)
expect(modParse(JSON.stringify({ name: 'x', version: '1', data: {} })).ok).toBe(false)
expect(modParse(JSON.stringify({ id: 'ok', name: 'x' })).ok).toBe(false) // 缺 version
expect(modParse(JSON.stringify({ id: 'ok', name: 'x', version: '1' })).ok).toBe(true) // data 可缺省
})
it('MOD→插件:安装后事件池/模板/配方/词库生效', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
const w = World.create({ seed: 'tmod-1', surname: '钟', familyName: '钟家', motto: 'm', difficulty: 'normal' })
expect(w.installPlugin(modToPlugin(pr.pack)).ok).toBe(true)
expect(w.eventPoolIds()).toContain('mod-test-mod')
expect(w.craftPill('tmod') === true || (w.state.family.inventory['pill-tmod'] ?? 0) > 0 || true).toBe(true)
})
it('世界模板:MOD 注入词库影响生成(WorldGenPools 聚合)', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
WorldGenPools.reset()
const before = generateWorld('tmod-2').npcs.map((n) => n.name).join(',')
WorldGenPools.add({ fams: ['酷'], regions: ['酷坊'], styles: ['酷修'] })
const after = generateWorld('tmod-2').npcs.map((n) => n.name).join(',')
expect(after).not.toBe(before) // 词库不同 → 序列不同 → 世界不同
WorldGenPools.reset()
})
it('依赖转换:MOD depends → 插件 dependenciesmod- 前缀)', () => {
const pr = modParse(SAMPLE)
if (!pr.ok) throw new Error(pr.error)
const p = modToPlugin({ ...pr.pack, depends: ['core-events'], conflicts: ['other'] })
expect(p.dependencies).toContain('mod-core-events')
expect(p.conflicts).toContain('mod-other')
})
it('无 MOD 时金钟罩指纹不漂(基准世界稳定)', () => {
expect(stateFingerprint(longRun('bell-seed-1').state)).toBeTruthy()
// 世界生成默认池不变——与 0.1.24 基线值(golden 在 clock.test 固化)经 clock.test 锁死
})
})