From 9e66ef0e0525b93e35657ec4f2c491e9df54d975 Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Sun, 18 Oct 2026 17:31:03 +0200 Subject: [PATCH] fix(ui): map VIRTUAL/LOGIC per-frame sides in v2 decoder Backport parity with the v1 decoder: the v2 streaming path writes the raw side string into SoldierState, but ProtobufDecoderV2.mapSideString did not handle "VIRTUAL"/"LOGIC", so virtual/zeus units in v2 recordings were misclassified as CIV (wrong color, filters, and side-derived stats). Add regression test covering both VIRTUAL and LOGIC per-frame sides. --- .../__tests__/protobufDecoderV2.test.ts | 30 +++++++++++++++++++ ui/src/data/decoders/protobufDecoderV2.ts | 2 ++ 2 files changed, 32 insertions(+) diff --git a/ui/src/data/decoders/__tests__/protobufDecoderV2.test.ts b/ui/src/data/decoders/__tests__/protobufDecoderV2.test.ts index c9f36caa..d1472e84 100644 --- a/ui/src/data/decoders/__tests__/protobufDecoderV2.test.ts +++ b/ui/src/data/decoders/__tests__/protobufDecoderV2.test.ts @@ -351,6 +351,36 @@ describe("ProtobufDecoderV2.decodeChunk", () => { expect(vehicleStates![1]).toEqual(vehicleStates![0]); // unchanged state carries forward }); + it("maps VIRTUAL/LOGIC per-frame sides to the VIRTUAL faction (backport parity with v1)", () => { + const buffer = encodePb(PbChunk, { + index: 0, + startFrame: 0, + frameCount: 2, + frames: [ + { + frameNum: 0, + soldiers: [{ + id: 1, + position: { x: 0, y: 0, z: 0 }, + bearing: 0, + lifestate: 1, + side: "VIRTUAL", + }, { + id: 2, + position: { x: 0, y: 0, z: 0 }, + bearing: 0, + lifestate: 1, + side: "LOGIC", + }], + }, + ], + }); + + const chunk = decoder.decodeChunk(buffer); + expect(chunk.entities.get(1)![0].side).toBe("VIRTUAL"); + expect(chunk.entities.get(2)![0].side).toBe("VIRTUAL"); + }); + it("decodes v2 soldier extensions", () => { const buffer = encodePb(PbChunk, { index: 0, diff --git a/ui/src/data/decoders/protobufDecoderV2.ts b/ui/src/data/decoders/protobufDecoderV2.ts index 2fb6374b..e0a6b78c 100644 --- a/ui/src/data/decoders/protobufDecoderV2.ts +++ b/ui/src/data/decoders/protobufDecoderV2.ts @@ -69,6 +69,8 @@ function mapSideString(raw: string): Side { case "INDEPENDENT": return "GUER"; case "CIV": case "CIVILIAN": return "CIV"; + case "VIRTUAL": + case "LOGIC": return "VIRTUAL"; default: return "CIV"; } }