refactor: ReplaySystem.route -> ReplaySystem.array,修复 disabled 标记在 setReplayArray 中清零可能导致意外记录的问题

This commit is contained in:
AncTe committed 2026-09-17 19:18:30 +08:00
1 parent 3a3a6ac82d
commit a2a8e6ec35
19 files changed
+96 -87

No files matched your search

@@ -853,7 +853,6 @@ export class ReplayArray implements IReplayArray {
paramBuffer: ArrayBuffer,
length: number
): void {
this.disabled = 0;
this.commandWidth = commandWidth;
this.commandBuffer = commandBuffer;
this.commandArray = new Uint8Array(commandBuffer);
@@ -896,4 +895,8 @@ export class ReplayArray implements IReplayArray {
this.disabled--;
}
}
clearDisableFlag(): void {
this.disabled = 0;
}
}
+2 -2
View File
@@ -66,7 +66,7 @@ export function beginReplaySafetyCollection(system: IReplaySystem): void {
}
replaySystem = system;
collecting = true;
beforeLength = system.route.length;
beforeLength = system.array.length;
shouldIgnore = false;
collection.messages.length = 0;
currentCollection = collection;
@@ -82,7 +82,7 @@ export function endReplaySafetyCollection(): void {
}
try {
if (shouldIgnore) return;
if (replaySystem.route.length > beforeLength) return;
if (replaySystem.array.length > beforeLength) return;
if (collection.messages.length === 0) return;
// 需要把收集内容输出,这里只输出一层,完整输出需要在控制台手动调用
@@ -33,7 +33,7 @@ function createCommand(
// 用录像系统构造一个可手动驱动的录像沙箱
function createSandbox(system: ReplaySystem): IManualReplaySandbox {
return system.createReplaySandbox({
route: system.route,
route: system.array,
reseter: { reset: () => {} }
}) as IManualReplaySandbox;
}
@@ -204,7 +204,7 @@ describe('ReplaySandbox stepping', () => {
system.record(1);
const sandbox = createSandbox(system);
start(sandbox);
system.route.add(1, []);
system.array.add(1, []);
await expect(sandbox.step()).resolves.toBe(false);
@@ -226,7 +226,7 @@ describe('ReplaySandbox stepping', () => {
system.record(1);
system.record(1);
const sandbox = system.createReplaySandbox({
route: system.route,
route: system.array,
reseter: { reset: () => {} },
startIndex: 2
}) as IManualReplaySandbox;
@@ -386,7 +386,7 @@ describe('ReplaySandbox playback control', () => {
);
system.record(1);
const sandbox = createSandbox(system);
system.route.add(1, []);
system.array.add(1, []);
sandbox.play();
await new Promise<void>(resolve => setTimeout(resolve, 0));
@@ -40,9 +40,9 @@ describe('ReplaySystem save and load round trips', () => {
system.replaying = true;
system.loadState(snapshot);
expect(system.route.length).toBe(1);
expect(system.route.commandWidth).toBe(snapshot.commandWidth);
expect(system.route.get(0)).toEqual({
expect(system.array.length).toBe(1);
expect(system.array.commandWidth).toBe(snapshot.commandWidth);
expect(system.array.get(0)).toEqual({
command: 3,
params: [1, true, 'param'],
index: 0
@@ -60,9 +60,9 @@ describe('ReplaySystem save and load round trips', () => {
system.record(5, 9);
system.loadState(snapshot);
expect(system.route.getCommandArray()).toBe(snapshot.commandArray);
expect(system.route.getParamArray()).toBe(snapshot.paramArray);
expect(system.route.get(0).command).toBe(2);
expect(system.array.getCommandArray()).toBe(snapshot.commandArray);
expect(system.array.getParamArray()).toBe(snapshot.paramArray);
expect(system.array.get(0).command).toBe(2);
});
// 验证读档整体替换缓冲区后,此前创建的活跃沙箱读流被标记过期并触发告警码 156(#06-17-3)
@@ -75,7 +75,7 @@ describe('ReplaySystem save and load round trips', () => {
system.record(1, 9);
const sandbox = system.createReplaySandbox({
route: system.route,
route: system.array,
reseter: { reset: () => {} }
});
system.loadState(snapshot);
@@ -41,8 +41,8 @@ describe('ReplaySystem registration and recording', () => {
it('builds an empty uint8 route', () => {
const system = new ReplaySystem();
expect(system.route.commandWidth).toBe(ReplayCommandWidth.Uint8);
expect(system.route.length).toBe(0);
expect(system.array.commandWidth).toBe(ReplayCommandWidth.Uint8);
expect(system.array.length).toBe(0);
});
// 验证注册后可取回命令且未知码返回 null
@@ -85,8 +85,8 @@ describe('ReplaySystem registration and recording', () => {
system.record(5, 1, true);
expect(system.route.length).toBe(1);
expect(system.route.get(0)).toEqual({
expect(system.array.length).toBe(1);
expect(system.array.get(0)).toEqual({
command: 5,
params: [1, true],
index: 0
@@ -102,8 +102,8 @@ describe('ReplaySystem registration and recording', () => {
first.record(1);
expect(second.getCommand(1)).toBeNull();
expect(second.route.length).toBe(0);
expect(first.route.length).toBe(1);
expect(second.array.length).toBe(0);
expect(first.array.length).toBe(1);
});
});
@@ -123,7 +123,7 @@ describe('ReplaySystem sandbox lifecycle', () => {
const save = new Map<string, unknown>([['hp', 10]]);
const sandbox = system.createReplaySandbox({
route: system.route,
route: system.array,
reseter,
save
});
@@ -138,7 +138,7 @@ describe('ReplaySystem sandbox lifecycle', () => {
const system = new ReplaySystem();
const { reseter } = createReseter();
const sandbox = system.createReplaySandbox({
route: system.route,
route: system.array,
reseter
});
const stop = vi.spyOn(sandbox, 'stop').mockResolvedValue(undefined);
@@ -163,19 +163,19 @@ 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');
const disable = vi.spyOn(system.array, 'disable');
const revert = vi.spyOn(system.array, 'revert');
system.record(1);
system.disable();
system.record(2);
expect(disable).toHaveBeenCalledTimes(1);
expect(system.route.length).toBe(1);
expect(system.array.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);
expect(system.array.length).toBe(2);
expect(system.array.get(1).command).toBe(3);
});
});
+11 -11
View File
@@ -24,14 +24,14 @@ export class ReplaySystem
{
replaying: boolean = false;
sandbox: IReplaySandbox | null = null;
readonly route: IReplayArray;
readonly array: IReplayArray;
/** 所有注册的指令 */
private readonly commands: Map<number, IReplayCommand> = new Map();
constructor() {
super();
this.route = new ReplayArray({
this.array = new ReplayArray({
initCommandLength: 1000,
initParamLength: 10000,
commandExpandMultiplier: 1.2,
@@ -61,9 +61,9 @@ export class ReplaySystem
}
record(code: number, ...params: ReplayParamValue[]): void {
this.route.add(code, params);
this.array.add(code, params);
this.forEachHook(hook =>
hook.onRecordCommand?.(code, this.route.length - 1, params)
hook.onRecordCommand?.(code, this.array.length - 1, params)
);
}
@@ -87,24 +87,24 @@ export class ReplaySystem
}
disable(): void {
this.route.disable();
this.array.disable();
}
revert(): void {
this.route.revert();
this.array.revert();
}
saveState(): IReplaySystemSave {
return {
length: this.route.length,
commandWidth: this.route.commandWidth,
commandArray: this.route.getCommandArray(),
paramArray: this.route.getParamArray()
length: this.array.length,
commandWidth: this.array.commandWidth,
commandArray: this.array.getCommandArray(),
paramArray: this.array.getParamArray()
};
}
loadState(state: IReplaySystemSave): void {
this.route.setReplayArray(
this.array.setReplayArray(
state.commandWidth,
state.commandArray,
state.paramArray,
@@ -292,9 +292,16 @@ export interface IReplayArray {
disable(): void;
/**
* 将录像记录功能从禁用状态恢复为启用状态
* 将录像记录功能从禁用状态恢复为上一个禁用状态。
* 具体来说,每次调用 `disable` 时都会使得录像禁用层数加一,此方法可以使其减一,直到减为 0。
* 这么做的目的是为了防止嵌套禁用调用时出现下层启用后上层意外记录录像的问题。
*/
revert(): void;
/**
* 清空录像记录的 `disable` 记录,直接跳出所有的 `disable` 层
*/
clearDisableFlag(): void;
}
export interface IReplaySystemHooks extends IHookBase {
@@ -369,7 +376,7 @@ export interface IReplaySystem
/** 当前正在播放的录像沙箱实例 */
readonly sandbox: IReplaySandbox | null;
/** 当前的录像操作器,用于直接操作或读取录像数据 */
readonly route: IReplayArray;
readonly array: IReplayArray;
/**
* 注册一个录像命令