v0.1.21: 大势流转(线程定论 + era景气双向闭环 + 世界史表 + 渲染管线)
【线程架构定论(实测)】 - 引擎与 UI 同在 renderer 主线程(单线程);2160 月实测 advanceMonth 平均 0.05ms/tick - 结论:Worker 化无收益——0.05ms 不需要换线程;此前 60ms 告警来自 UI 渲染+存档(已修复) 【世界自演进闭环(双向因果,自演进最高形态)】 - era↔景气双向闭环:世界温度(marketPool 景气均值)调制 era 转移权重 (tempEraK=0.8)——大世托盛世、市道衰微催乱世/末法;时代由天下兴衰孕育 - 天下史书独立表 worldAnnals[]:十年一鉴独立留档(免 newsFeed 120 条裁剪丢失) - region 灵潮异质:REGION_TIDE(4 家×4 era)接入 NPC 贸易速率(乱世北岳凶、盛世东丘旺) - 灾年全环:灾年群雄相噬(互攻概率×1.5) 【渲染管线(性能)】 - LogFeed 精订阅(去掉 revision 级联重渲)+ 滑动窗 120 - WorldPanel newsFeed useMemo 预聚合(不再每 render 全量重扫) 【测试】1018 全绿(43 套件):tide-0.1.21 7 例(延续档/温度/史表/区域/灾年/指纹敏感/滑动窗); 双金钟罩 0.1.21 基线固化;build 通过
This commit is contained in:
@@ -82,7 +82,7 @@ describe('审计回归:P0 修复固化', () => {
|
||||
})
|
||||
|
||||
it('防御性修补后金钟罩不变(行为等价确认)', () => {
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('3b784f49')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('0bf7ec62')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('879a909d')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('e6936427')
|
||||
})
|
||||
})
|
||||
|
||||
+8
-8
@@ -7,20 +7,20 @@ import { World } from '../src/renderer/game/engine/runtime/World'
|
||||
* 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。
|
||||
* 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。
|
||||
*/
|
||||
// 0.1.20 万世无谬基线(指纹含全世界轴:era/stance/prosperity/relationsWithOthers/pendingEvent):
|
||||
// 事件闸优先级/era 延续档/潮汐正号/rebalance 弱锚定/断供单常量后固化。
|
||||
// 0.1.21 大势流转基线(指纹含全世界轴+worldAnnals):
|
||||
// era↔景气双向闭环(世界温度调制转移)/region 灵潮异质/灾年群雄相噬/史表独立留档后固化。
|
||||
const GOLDEN: Record<string, Record<number, string>> = {
|
||||
'bell-seed-1': { 560: '3b784f49', 1200: 'b62fada2', 2160: '999782cc' },
|
||||
'bell-seed-2': { 560: '0f73bffb', 1200: '454eedc9', 2160: '426c967f' },
|
||||
'bell-seed-3': { 560: '0bf7ec62', 1200: '06cf0213', 2160: '51feb3ee' }
|
||||
'bell-seed-1': { 560: '879a909d', 1200: 'fa07c9e7', 2160: '5a64b3e9' },
|
||||
'bell-seed-2': { 560: '33d511bc', 1200: '71930809', 2160: '4e67314c' },
|
||||
'bell-seed-3': { 560: 'e6936427', 1200: '56961a5e', 2160: 'bb972ccd' }
|
||||
}
|
||||
|
||||
/** 第二金钟罩:自动 resolve 长跑("现实"世界——每 tick 处理待决事件;
|
||||
* 锁事件闸/事件流全程,防"冻结世界"指纹漏锁)。 */
|
||||
const GOLDEN_RESOLVED: Record<string, Record<number, string>> = {
|
||||
'bell-seed-1': { 560: 'b28b99f9', 1200: 'ffd53c37', 2160: 'd7eadca0' },
|
||||
'bell-seed-2': { 560: 'c712ad0f', 1200: '2304c1c4', 2160: '76519353' },
|
||||
'bell-seed-3': { 560: '6878460d', 1200: '6633cdde', 2160: 'f285188f' }
|
||||
'bell-seed-1': { 560: 'cbc13720', 1200: '0b4c6d57', 2160: 'c9de8ab5' },
|
||||
'bell-seed-2': { 560: 'e0706d1e', 1200: 'd3899339', 2160: '9c7ef509' },
|
||||
'bell-seed-3': { 560: 'fe429293', 1200: '79cbbca9', 2160: 'ada0a493' }
|
||||
}
|
||||
|
||||
const TIERS = [
|
||||
|
||||
@@ -170,7 +170,7 @@ describe('GameFacade 门面', () => {
|
||||
|
||||
it('默认配置金钟罩不受门面化影响', () => {
|
||||
PACK.reset()
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('3b784f49')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('0bf7ec62')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('879a909d')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('e6936427')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -80,9 +80,9 @@ describe('PluginCore 插件协议', () => {
|
||||
})
|
||||
|
||||
it('默认管线金钟罩不受插件层影响', () => {
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('3b784f49')
|
||||
expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('0f73bffb')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('0bf7ec62')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('879a909d')
|
||||
expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('33d511bc')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('e6936427')
|
||||
})
|
||||
|
||||
it('facade 插件查询与 about.plugins', () => {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
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 { WorldSimState, ERA_CONF, REGION_TIDE } from '../src/renderer/game/engine/sim/worldsim-data'
|
||||
import { stateFingerprint } from './fingerprint.helper'
|
||||
|
||||
function worldMid(seed: string): World {
|
||||
const w = World.create({ seed, surname: '澹', familyName: '澹台家', motto: 'm', difficulty: 'normal' })
|
||||
for (let i = 0; i < 360; i++) w.advanceMonth()
|
||||
return w
|
||||
}
|
||||
|
||||
describe('0.1.21 大势流转', () => {
|
||||
it('era 转移有延续档(窗口内可续任:flow 权重和 < 1)', () => {
|
||||
for (const k of Object.keys(ERA_CONF) as Array<keyof typeof ERA_CONF>) {
|
||||
const sum = ERA_CONF[k].flow.reduce((a, [, wgt]) => a + wgt, 0)
|
||||
expect(sum).toBeLessThan(1)
|
||||
}
|
||||
})
|
||||
|
||||
it('世界温度:景气高涨 → 向盛世转移权重提升(双向闭环)', () => {
|
||||
const w = World.create({ seed: 't1', surname: '贺', familyName: '贺家', motto: 'm', difficulty: 'normal' })
|
||||
w.advanceMonth()
|
||||
const sim = new WorldSim(w)
|
||||
expect(sim.worldTemperature()).toBeGreaterThan(0.4)
|
||||
// 池压到高景气 → 温度高
|
||||
const ws = w.state.worldSim as WorldSimState
|
||||
ws.marketPool = { lingcao: 1200, lingkuang: 600, beastcore: 160, 'pill-qiyuan': 180, 'pill-ningyuan': 80 }
|
||||
expect(sim.worldTemperature()).toBeGreaterThan(1.5)
|
||||
})
|
||||
|
||||
it('十年鉴入独立史表(worldAnnals),newsFeed 裁剪后仍留存', () => {
|
||||
const w = World.create({ seed: 't2', surname: '燕', familyName: '燕家', motto: 'm', difficulty: 'normal' })
|
||||
for (let i = 0; i < 480; i++) w.advanceMonth()
|
||||
const ws = w.state.worldSim as WorldSimState
|
||||
expect((ws.worldAnnals ?? []).length).toBeGreaterThanOrEqual(4)
|
||||
const feedEntries = ws.newsFeed.filter((r) => r.kind === 'annal').length
|
||||
expect(feedEntries).toBeLessThanOrEqual((ws.worldAnnals ?? []).length + 2)
|
||||
// 史表内容锚定
|
||||
const last = (ws.worldAnnals ?? []).pop()!
|
||||
expect(last.text).toContain('史官')
|
||||
})
|
||||
|
||||
it('区域灵势表完整(4 家 × 4 era)', () => {
|
||||
for (const id of ['n-xuanying', 'n-danxin', 'n-sihai', 'n-nulei']) {
|
||||
const r = REGION_TIDE[id]
|
||||
expect(r).toBeTruthy()
|
||||
for (const e of ['shengshi', 'pingshi', 'luanshi', 'mofa'] as const) {
|
||||
expect(r[e]).toBeTypeOf('number')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('灾年 NPC 互攻概率提高(W4 全环)', () => {
|
||||
const w = World.create({ seed: 't4', surname: '蓟', familyName: '蓟家', motto: 'm', difficulty: 'normal' })
|
||||
w.advanceMonth()
|
||||
const ws = w.state.worldSim as WorldSimState
|
||||
const before = (ws.npcDyn['n-nulei']?.relationsWithOthers?.['n-xuanying'] ?? 0)
|
||||
ws.calamity = '旱灾'
|
||||
ws.calamityLeft = 6
|
||||
expect(typeof before).toBe('number')
|
||||
// 灾年期间长跑不炸(60 月)
|
||||
for (let i = 0; i < 60; i++) {
|
||||
w.state.worldSim = ws as never
|
||||
w.state.month = 1
|
||||
w.state.year++
|
||||
w.advanceMonth()
|
||||
}
|
||||
expect(w.state).toBeTruthy()
|
||||
})
|
||||
|
||||
it('新指纹敏感度:era 变动仍红(含 worldAnnals 之后)', () => {
|
||||
const w = worldMid('t5')
|
||||
const f1 = stateFingerprint(w.state)
|
||||
const ws = w.state.worldSim as WorldSimState
|
||||
ws.era = ws.era === 'mofa' ? 'shengshi' : 'mofa'
|
||||
const f2 = stateFingerprint(w.state)
|
||||
expect(f1).not.toBe(f2)
|
||||
})
|
||||
|
||||
it('LogFeed 滑动窗语义(120 条上限的函数观点)', () => {
|
||||
const arr = Array.from({ length: 300 }, (_, i) => ({ id: i, kind: 'info' as const, text: String(i), year: 1, month: 1 }))
|
||||
const windowed = arr.slice(-120)
|
||||
expect(windowed.length).toBe(120)
|
||||
expect(windowed[0]!.id).toBe(180)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user