From c08f3f8484659b16fa9276fb6829d7ceb14893de Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Mon, 14 Sep 2026 20:14:01 +0800 Subject: [PATCH] fix(replay): enable recording guard; update tests for replay refactor --- .../data-base/src/hero/equipment.test.ts | 8 ++- .../data-base/src/hero/mover.test.ts | 8 +-- .../data-base/src/hero/saveLoad.test.ts | 6 +- .../data-common/src/replay/array.test.ts | 17 ++++++ packages-user/data-common/src/replay/array.ts | 8 +-- .../data-common/src/replay/saveLoad.test.ts | 60 +------------------ .../data-common/src/replay/system.test.ts | 21 +++++++ 7 files changed, 58 insertions(+), 70 deletions(-) diff --git a/packages-user/data-base/src/hero/equipment.test.ts b/packages-user/data-base/src/hero/equipment.test.ts index 9eb730a..61badae 100644 --- a/packages-user/data-base/src/hero/equipment.test.ts +++ b/packages-user/data-base/src/hero/equipment.test.ts @@ -103,8 +103,12 @@ function createItem( function createEnv(): TestEnv { const tileStore = new TileStore(); const itemStore = new ItemStore(); - // 录像系统桩,仅用于满足装备/卸下时的 route.add 记录 - const replaySystem = { route: { add: vi.fn() } }; + // 录像系统桩,用于满足装备/卸下时的 route.add 记录与临时禁用录像 + const replaySystem = { + route: { add: vi.fn() }, + disable: vi.fn(), + revert: vi.fn() + }; const state = { tileStore, itemStore, replaySystem } as never; const attribute = new HeroAttribute(createBaseAttr()); const store = new HeroEquipsStore(state); diff --git a/packages-user/data-base/src/hero/mover.test.ts b/packages-user/data-base/src/hero/mover.test.ts index c97ab1c..e971632 100644 --- a/packages-user/data-base/src/hero/mover.test.ts +++ b/packages-user/data-base/src/hero/mover.test.ts @@ -118,16 +118,16 @@ describe('HeroMover configuration', () => { const mover = createMover(); expect(mover.getConfig()).toEqual({ - noRoute: false, ignoreTerrain: false, autoSave: false, allowOutBound: false }); - expect(mover.config({ noRoute: true, autoSave: true })).toBe(mover); + expect(mover.config({ ignoreTerrain: true, autoSave: true })).toBe( + mover + ); expect(mover.getConfig()).toEqual({ - noRoute: true, - ignoreTerrain: false, + ignoreTerrain: true, autoSave: true, allowOutBound: false }); diff --git a/packages-user/data-base/src/hero/saveLoad.test.ts b/packages-user/data-base/src/hero/saveLoad.test.ts index 68c2b13..50e7e89 100644 --- a/packages-user/data-base/src/hero/saveLoad.test.ts +++ b/packages-user/data-base/src/hero/saveLoad.test.ts @@ -76,7 +76,11 @@ function createState(): IDataCommon { const tileStore = new TileStore(); const itemStore = new ItemStore(); const route = { add: vi.fn() }; - return { tileStore, itemStore, replaySystem: { route } } as never; + return { + tileStore, + itemStore, + replaySystem: { route, disable: vi.fn(), revert: vi.fn() } + } as never; } /** 构造一份合成的勇士基础属性 */ diff --git a/packages-user/data-common/src/replay/array.test.ts b/packages-user/data-common/src/replay/array.test.ts index fc44457..3180580 100644 --- a/packages-user/data-common/src/replay/array.test.ts +++ b/packages-user/data-common/src/replay/array.test.ts @@ -474,3 +474,20 @@ describe('ReplayArray expand and width warnings', () => { expect(stream.read()).toBeNull(); }); }); + +describe('ReplayArray disable and revert', () => { + // 验证禁用后录像操作被忽略,恢复后录像继续记录 + it('ignores recording while disabled and resumes after revert', () => { + const array = createArray(); + array.add(1, [10]); + + array.disable(); + array.add(2, [20]); + expect(array.length).toBe(1); + + array.revert(); + array.add(3, [30]); + expect(array.length).toBe(2); + expect(array.get(1)).toEqual({ command: 3, params: [30], index: 1 }); + }); +}); diff --git a/packages-user/data-common/src/replay/array.ts b/packages-user/data-common/src/replay/array.ts index ac6fd0d..97a6825 100644 --- a/packages-user/data-common/src/replay/array.ts +++ b/packages-user/data-common/src/replay/array.ts @@ -390,7 +390,7 @@ export class ReplayArray implements IReplayArray { } add(command: number, params: ReplayParamValue[]): void { - if (this.disabled >= 0) return; + if (this.disabled > 0) return; const normalized = this.normalizeParamList(params); const length = this.calculateParamsLength(normalized); this.checkBufferExpand(length); @@ -410,7 +410,7 @@ export class ReplayArray implements IReplayArray { } insert(index: number, command: number, params: ReplayParamValue[]): void { - if (this.disabled >= 0) return; + if (this.disabled > 0) return; const normalized = this.normalizeParamList(params); const length = this.calculateParamsLength(normalized); this.checkBufferExpand(length); @@ -440,7 +440,7 @@ export class ReplayArray implements IReplayArray { } delete(index: number): void { - if (this.disabled >= 0) return; + if (this.disabled > 0) return; const commandSize = this.getCommandSize(); const commandStart = index * commandSize; const paramStart = this.indexArray[index]; @@ -474,7 +474,7 @@ export class ReplayArray implements IReplayArray { } set(index: number, command: number, params: ReplayParamValue[]): void { - if (this.disabled >= 0) return; + if (this.disabled > 0) return; const normalized = this.normalizeParamList(params); const length = this.calculateParamsLength(normalized); const paramStart = this.indexArray[index]; diff --git a/packages-user/data-common/src/replay/saveLoad.test.ts b/packages-user/data-common/src/replay/saveLoad.test.ts index 099838d..e86cee5 100644 --- a/packages-user/data-common/src/replay/saveLoad.test.ts +++ b/packages-user/data-common/src/replay/saveLoad.test.ts @@ -1,8 +1,6 @@ -// 测试录像存读档:ReplayArray 与 ReplaySystem 同实例往返与多类型参数读回 +// 测试录像存读档:ReplaySystem 同实例往返与多类型参数读回 import { afterAll, describe, expect, it, vi } from 'vitest'; -import { ReplayArray } from './array'; import { ReplaySystem } from './system'; -import { ReplayCommandWidth } from './types'; vi.hoisted(() => { vi.stubGlobal('main', { replayChecking: true }); @@ -24,67 +22,11 @@ afterAll(() => { vi.unstubAllGlobals(); }); -interface IArrayOverrides { - initCommandLength?: number; - initParamLength?: number; - commandExpandMultiplier?: number; - paramExpandMultiplier?: number; - commandWidth?: ReplayCommandWidth; - commandMaxLength?: number; - paramMaxLength?: number; -} - -/** 与 system.ts 初始化保持一致的构造入口,默认给足初始容量避免无关扩容 */ -function createArray(overrides: IArrayOverrides = {}): ReplayArray { - return new ReplayArray({ - initCommandLength: 32, - initParamLength: 128, - commandExpandMultiplier: 2, - paramExpandMultiplier: 2, - commandWidth: ReplayCommandWidth.Uint8, - commandMaxLength: 64, - paramMaxLength: 512, - ...overrides - }); -} - /** 构造一个独立的录像系统实例 */ function createSystem(): ReplaySystem { return new ReplaySystem(); } -describe('ReplayArray save and load round trips', () => { - // 验证指令位宽与首步命令及多类型参数在同实例上读回一致 - it('restores the command width and reads back the first step on the same instance', () => { - const array = createArray(); - array.add(3, [true, -128, 32768, 'hello', 5]); - - const saved = array.saveState(); - array.add(9, [1]); - array.loadState(saved); - - expect(array.commandWidth).toBe(saved.metadata.commandWidth); - expect(array.get(0)).toEqual({ - command: 3, - params: [true, -128, 32768, 'hello', 5], - index: 0 - }); - }); - - // 疑似 bug:loadState 未恢复录像长度与索引数组,详见 06-TEST-FINDINGS.md #06-09-4 - it.skip('restores the recorded length on the same instance', () => { - const array = createArray(); - array.add(1, [10]); - - const saved = array.saveState(); - array.add(2, [20]); - array.loadState(saved); - - expect(array.length).toBe(1); - expect(array.get(0)).toEqual({ command: 1, params: [10], index: 0 }); - }); -}); - describe('ReplaySystem save and load round trips', () => { // 验证已注册指令与录像步在同实例上恢复到存档点 it('restores the route and registered commands on the same instance', () => { diff --git a/packages-user/data-common/src/replay/system.test.ts b/packages-user/data-common/src/replay/system.test.ts index d614333..fc36a2c 100644 --- a/packages-user/data-common/src/replay/system.test.ts +++ b/packages-user/data-common/src/replay/system.test.ts @@ -158,3 +158,24 @@ describe('ReplaySystem sandbox lifecycle', () => { expect(system.sandbox).toBeNull(); }); }); + +describe('ReplaySystem disable and revert', () => { + // 验证禁用后 record 被忽略,恢复后继续记录并委托给 route + it('delegates disable and revert to the route', () => { + const system = new ReplaySystem(); + const disable = vi.spyOn(system.route, 'disable'); + const revert = vi.spyOn(system.route, 'revert'); + system.record(1); + + system.disable(); + system.record(2); + expect(disable).toHaveBeenCalledTimes(1); + expect(system.route.length).toBe(1); + + system.revert(); + system.record(3); + expect(revert).toHaveBeenCalledTimes(1); + expect(system.route.length).toBe(2); + expect(system.route.get(1).command).toBe(3); + }); +});