t2-demo-parser 2.2.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -7
- package/dist/BitStream.d.ts +14 -3
- package/dist/BitStream.d.ts.map +1 -1
- package/dist/BitStream.js +28 -6
- package/dist/BitStream.js.map +1 -1
- package/dist/BitWriter.d.ts +29 -0
- package/dist/BitWriter.d.ts.map +1 -0
- package/dist/BitWriter.js +76 -0
- package/dist/BitWriter.js.map +1 -0
- package/dist/ClassRegistry.d.ts +23 -28
- package/dist/ClassRegistry.d.ts.map +1 -1
- package/dist/ClassRegistry.js +75 -84
- package/dist/ClassRegistry.js.map +1 -1
- package/dist/DataBlockParsers.d.ts.map +1 -1
- package/dist/DataBlockParsers.js +60 -85
- package/dist/DataBlockParsers.js.map +1 -1
- package/dist/DemoParser.d.ts +23 -22
- package/dist/DemoParser.d.ts.map +1 -1
- package/dist/DemoParser.js +218 -266
- package/dist/DemoParser.js.map +1 -1
- package/dist/EventParsers.d.ts.map +1 -1
- package/dist/EventParsers.js +87 -58
- package/dist/EventParsers.js.map +1 -1
- package/dist/GhostManager.d.ts +7 -1
- package/dist/GhostManager.d.ts.map +1 -1
- package/dist/GhostManager.js +231 -146
- package/dist/GhostManager.js.map +1 -1
- package/dist/HuffmanProcessor.d.ts +15 -0
- package/dist/HuffmanProcessor.d.ts.map +1 -1
- package/dist/HuffmanProcessor.js +45 -0
- package/dist/HuffmanProcessor.js.map +1 -1
- package/dist/LiveParser.d.ts +20 -1
- package/dist/LiveParser.d.ts.map +1 -1
- package/dist/LiveParser.js +31 -12
- package/dist/LiveParser.js.map +1 -1
- package/dist/PacketParser.d.ts +22 -2
- package/dist/PacketParser.d.ts.map +1 -1
- package/dist/PacketParser.js +111 -48
- package/dist/PacketParser.js.map +1 -1
- package/dist/Timeline.d.ts +5 -2
- package/dist/Timeline.d.ts.map +1 -1
- package/dist/Timeline.js +25 -12
- package/dist/Timeline.js.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/dataBlockDataTypes.d.ts +118 -117
- package/dist/dataBlockDataTypes.d.ts.map +1 -1
- package/dist/dataBlockDataTypes.js.map +1 -1
- package/dist/defaultRegistry.d.ts +9 -0
- package/dist/defaultRegistry.d.ts.map +1 -0
- package/dist/defaultRegistry.js +43 -0
- package/dist/defaultRegistry.js.map +1 -0
- package/dist/eventDataTypes.d.ts +74 -72
- package/dist/eventDataTypes.d.ts.map +1 -1
- package/dist/eventDataTypes.js.map +1 -1
- package/dist/ghostDataTypes.d.ts +125 -131
- package/dist/ghostDataTypes.d.ts.map +1 -1
- package/dist/ghostDataTypes.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +51 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -1
- package/package.json +4 -1
- package/dist/BitStream.test.d.ts +0 -2
- package/dist/BitStream.test.d.ts.map +0 -1
- package/dist/BitStream.test.js +0 -419
- package/dist/BitStream.test.js.map +0 -1
- package/dist/ClassRegistry.test.d.ts +0 -2
- package/dist/ClassRegistry.test.d.ts.map +0 -1
- package/dist/ClassRegistry.test.js +0 -83
- package/dist/ClassRegistry.test.js.map +0 -1
- package/dist/DataBlockParsers.test.d.ts +0 -2
- package/dist/DataBlockParsers.test.d.ts.map +0 -1
- package/dist/DataBlockParsers.test.js +0 -294
- package/dist/DataBlockParsers.test.js.map +0 -1
- package/dist/DemoParser.test.d.ts +0 -2
- package/dist/DemoParser.test.d.ts.map +0 -1
- package/dist/DemoParser.test.js +0 -472
- package/dist/DemoParser.test.js.map +0 -1
- package/dist/GhostStateAccumulator.test.d.ts +0 -2
- package/dist/GhostStateAccumulator.test.d.ts.map +0 -1
- package/dist/GhostStateAccumulator.test.js +0 -201
- package/dist/GhostStateAccumulator.test.js.map +0 -1
- package/dist/GhostTracker.test.d.ts +0 -2
- package/dist/GhostTracker.test.d.ts.map +0 -1
- package/dist/GhostTracker.test.js +0 -64
- package/dist/GhostTracker.test.js.map +0 -1
- package/dist/IncrementalParser.test.d.ts +0 -2
- package/dist/IncrementalParser.test.d.ts.map +0 -1
- package/dist/IncrementalParser.test.js +0 -226
- package/dist/IncrementalParser.test.js.map +0 -1
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { DemoParser } from "./DemoParser.js";
|
|
5
|
-
const DEMO_DIR = path.resolve(import.meta.dirname, "..", "data");
|
|
6
|
-
async function loadDataBlocks(file) {
|
|
7
|
-
const buffer = fs.readFileSync(path.join(DEMO_DIR, file));
|
|
8
|
-
const parser = new DemoParser(new Uint8Array(buffer));
|
|
9
|
-
const { initialBlock } = await parser.load();
|
|
10
|
-
return initialBlock.dataBlocks;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Regression tests for projectile datablock field naming, which was
|
|
14
|
-
* binary-verified against Tribes2.exe build 25034 (initPersistFields
|
|
15
|
-
* FUN_0062b3c0 / FUN_0063fcb0 map names to the struct offsets that
|
|
16
|
-
* unpackData reads in order). Prior to that verification several fields
|
|
17
|
-
* were misassigned — most damagingly, the true lifetimeMS landed in a
|
|
18
|
-
* field named `fizzleType`, breaking client-side projectile simulation.
|
|
19
|
-
*/
|
|
20
|
-
describe("projectile datablock field decoding", () => {
|
|
21
|
-
it("decodes LinearProjectileData fields with engine semantics", async () => {
|
|
22
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
23
|
-
const linears = [];
|
|
24
|
-
for (const [, db] of dataBlocks) {
|
|
25
|
-
if (db.className === "LinearProjectileData" ||
|
|
26
|
-
db.className === "TracerProjectileData" ||
|
|
27
|
-
db.className === "LinearFlareProjectileData") {
|
|
28
|
-
linears.push(db.data);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
expect(linears.length).toBeGreaterThan(0);
|
|
32
|
-
for (const block of linears) {
|
|
33
|
-
// lifetimeMS/fizzleTimeMS are real milliseconds, rounded up to a
|
|
34
|
-
// 32ms tick by the engine's onAdd, and clamped to 511 ticks.
|
|
35
|
-
expect(block.lifetimeMS % 32).toBe(0);
|
|
36
|
-
expect(block.lifetimeMS).toBeGreaterThanOrEqual(32);
|
|
37
|
-
expect(block.lifetimeMS).toBeLessThanOrEqual(511 * 32);
|
|
38
|
-
expect(block.fizzleTimeMS % 32).toBe(0);
|
|
39
|
-
// Angles are ranged 0–90 degrees on the wire.
|
|
40
|
-
expect(block.reflectOnWaterImpactAngle).toBeGreaterThanOrEqual(0);
|
|
41
|
-
expect(block.reflectOnWaterImpactAngle).toBeLessThanOrEqual(90);
|
|
42
|
-
expect(typeof block.explodeOnDeath).toBe("boolean");
|
|
43
|
-
expect(typeof block.doDynamicClientHits).toBe("boolean");
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
it("decodes retail DiscProjectile exactly", async () => {
|
|
47
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
48
|
-
let disc;
|
|
49
|
-
for (const [, db] of dataBlocks) {
|
|
50
|
-
const data = db.data;
|
|
51
|
-
if (db.className === "LinearProjectileData" &&
|
|
52
|
-
data.dryVelocity === 90 &&
|
|
53
|
-
data.explodeOnDeath === true) {
|
|
54
|
-
disc = data;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
expect(disc, "retail DiscProjectile datablock").toBeDefined();
|
|
59
|
-
// disc.cs: lifetimeMS = fizzleTimeMS = 5000 → tick-rounded to 5024.
|
|
60
|
-
expect(disc.lifetimeMS).toBe(5024);
|
|
61
|
-
expect(disc.fizzleTimeMS).toBe(5024);
|
|
62
|
-
// disc.cs: reflectOnWaterImpactAngle = 15.0.
|
|
63
|
-
expect(disc.reflectOnWaterImpactAngle).toBe(15);
|
|
64
|
-
});
|
|
65
|
-
it("decodes retail EnergyBolt (blaster) fields exactly", async () => {
|
|
66
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
67
|
-
let bolt;
|
|
68
|
-
for (const [, db] of dataBlocks) {
|
|
69
|
-
if (db.className === "EnergyProjectileData") {
|
|
70
|
-
bolt = db.data;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
expect(bolt, "retail EnergyBolt datablock").toBeDefined();
|
|
75
|
-
// blaster.cs: binary-verified names (initPersistFields FUN_00694b40,
|
|
76
|
-
// unpackData FUN_00694d80) — these were previously mislabeled with
|
|
77
|
-
// ELF-style beam field names.
|
|
78
|
-
expect(bolt.crossViewAng).toBeCloseTo(0.99, 5);
|
|
79
|
-
expect(bolt.crossSize).toBeCloseTo(0.55, 5);
|
|
80
|
-
expect(bolt.blurLifetime).toBeCloseTo(0.2, 5);
|
|
81
|
-
expect(bolt.blurWidth).toBeCloseTo(0.25, 5);
|
|
82
|
-
expect(bolt.blurColor.r).toBeCloseTo(0.4, 5);
|
|
83
|
-
expect(bolt.blurColor.g).toBeCloseTo(0, 5);
|
|
84
|
-
expect(bolt.blurColor.b).toBeCloseTo(0, 5);
|
|
85
|
-
expect(bolt.texture0).toBe("special/blasterBolt");
|
|
86
|
-
expect(bolt.texture1).toBe("special/blasterBoltCross");
|
|
87
|
-
// blaster.cs: scale = "0.25 20.0 1.0" (bolt quad half-width / length).
|
|
88
|
-
expect(bolt.scale.x).toBeCloseTo(0.25, 5);
|
|
89
|
-
expect(bolt.scale.y).toBeCloseTo(20, 5);
|
|
90
|
-
});
|
|
91
|
-
it("decodes retail BasicSniperShot (laser rifle) fields exactly", async () => {
|
|
92
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
93
|
-
let shot;
|
|
94
|
-
for (const [, db] of dataBlocks) {
|
|
95
|
-
if (db.className === "SniperProjectileData") {
|
|
96
|
-
shot = db.data;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
expect(shot, "retail BasicSniperShot datablock").toBeDefined();
|
|
101
|
-
// sniperRifle.cs values.
|
|
102
|
-
expect(shot.maxRifleRange).toBeCloseTo(1000, 5);
|
|
103
|
-
expect(shot.beamColor.r).toBeCloseTo(1, 2);
|
|
104
|
-
expect(shot.beamColor.g).toBeCloseTo(0.1, 2);
|
|
105
|
-
expect(shot.beamColor.b).toBeCloseTo(0.1, 2);
|
|
106
|
-
expect(shot.fadeTime).toBeCloseTo(1.0, 5);
|
|
107
|
-
expect(shot.startBeamWidth).toBeCloseTo(0.145, 5);
|
|
108
|
-
expect(shot.endBeamWidth).toBeCloseTo(0.25, 5);
|
|
109
|
-
expect(shot.pulseSpeed).toBeCloseTo(6.0, 5);
|
|
110
|
-
expect(shot.pulseLength).toBeCloseTo(0.15, 5);
|
|
111
|
-
expect(shot.textures[0]).toBe("special/flare");
|
|
112
|
-
expect(shot.textures[1]).toBe("special/nonlingradient");
|
|
113
|
-
expect(shot.textures[2]).toBe("special/laserrip01");
|
|
114
|
-
expect(shot.textures[11]).toBe("special/sniper00");
|
|
115
|
-
});
|
|
116
|
-
it("decodes both ELF datablocks (gun and turret) exactly", async () => {
|
|
117
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
118
|
-
const elfs = [];
|
|
119
|
-
for (const [, db] of dataBlocks) {
|
|
120
|
-
if (db.className === "ELFProjectileData") {
|
|
121
|
-
elfs.push(db.data);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// Binary-verified names (initPersistFields FUN_0064a860, unpackData
|
|
125
|
-
// FUN_0064ae00) — previously fabricated beam names. Two datablocks
|
|
126
|
-
// exist: the handheld BasicELF at beamRange 38 (the z0dd/ZOD server
|
|
127
|
-
// patch every surviving server lineage runs — "WHAT?? INCREASE ELF
|
|
128
|
-
// RANGE?!!? was 37"; the stock script says 30) and the stock
|
|
129
|
-
// ELFTurretBolt at 75 (ELFBarrelLarge.cs).
|
|
130
|
-
expect(elfs.map((e) => e.beamRange).sort((a, b) => a - b)).toEqual([
|
|
131
|
-
38, 75,
|
|
132
|
-
]);
|
|
133
|
-
for (const elf of elfs) {
|
|
134
|
-
expect(elf.mainBeamWidth).toBeCloseTo(0.1, 5);
|
|
135
|
-
expect(elf.mainBeamSpeed).toBeCloseTo(9.0, 5);
|
|
136
|
-
expect(elf.mainBeamRepeat).toBeCloseTo(0.25, 5);
|
|
137
|
-
expect(elf.lightningWidth).toBeCloseTo(0.1, 5);
|
|
138
|
-
expect(elf.lightningDist).toBeCloseTo(0.15, 5);
|
|
139
|
-
expect(elf.textures).toEqual([
|
|
140
|
-
"special/ELFBeam",
|
|
141
|
-
"special/ELFLightning",
|
|
142
|
-
"special/BlueImpact",
|
|
143
|
-
]);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
it("decodes retail DefaultRepairBeam fields exactly", async () => {
|
|
147
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
148
|
-
let repair;
|
|
149
|
-
for (const [, db] of dataBlocks) {
|
|
150
|
-
if (db.className === "RepairProjectileData") {
|
|
151
|
-
repair = db.data;
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
expect(repair, "retail DefaultRepairBeam datablock").toBeDefined();
|
|
156
|
-
// repairpack.cs: binary-verified names (initPersistFields
|
|
157
|
-
// FUN_00644910, unpackData FUN_00644c40); numSegments is an S32
|
|
158
|
-
// whose raw bits the old decode read as a float.
|
|
159
|
-
expect(repair.beamRange).toBeCloseTo(10, 5);
|
|
160
|
-
expect(repair.beamWidth).toBeCloseTo(0.15, 5);
|
|
161
|
-
expect(repair.numSegments).toBe(20);
|
|
162
|
-
expect(repair.texRepeat).toBeCloseTo(0.2, 5);
|
|
163
|
-
expect(repair.blurFreq).toBeCloseTo(10.0, 5);
|
|
164
|
-
expect(repair.blurLifetime).toBeCloseTo(1.0, 5);
|
|
165
|
-
expect(repair.cutoffAngle).toBeCloseTo(25.0, 5);
|
|
166
|
-
expect(repair.textures).toEqual(["special/redbump2", "special/redflare"]);
|
|
167
|
-
});
|
|
168
|
-
it("decodes image-state transitions in the engine packing order", async () => {
|
|
169
|
-
// Every retail weapon image opens Activate → (timeout) →
|
|
170
|
-
// ActivateReady (e.g. blaster.cs stateTransitionOnTimeout[0]).
|
|
171
|
-
// With the old rotated names, the timeout transition surfaced under
|
|
172
|
-
// transitionGeneric0Out and consumers carried a remap table.
|
|
173
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
174
|
-
let checked = 0;
|
|
175
|
-
for (const [, db] of dataBlocks) {
|
|
176
|
-
if (db.className !== "ShapeBaseImageData")
|
|
177
|
-
continue;
|
|
178
|
-
const data = db.data;
|
|
179
|
-
const states = data.states;
|
|
180
|
-
if (!states || states.length < 2)
|
|
181
|
-
continue;
|
|
182
|
-
if (states[0].name.toLowerCase() !== "activate")
|
|
183
|
-
continue;
|
|
184
|
-
const readyIndex = states.findIndex((st) => st.name.toLowerCase() === "activateready");
|
|
185
|
-
if (readyIndex < 0)
|
|
186
|
-
continue;
|
|
187
|
-
// Transition values are 1-based state indices (0 = none).
|
|
188
|
-
expect(states[0].transitionOnTimeout).toBe(readyIndex + 1);
|
|
189
|
-
checked++;
|
|
190
|
-
}
|
|
191
|
-
expect(checked).toBeGreaterThan(3);
|
|
192
|
-
});
|
|
193
|
-
it("decodes retail ChaingunBullet tracer fields exactly", async () => {
|
|
194
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
195
|
-
let bullet;
|
|
196
|
-
for (const [, db] of dataBlocks) {
|
|
197
|
-
const data = db.data;
|
|
198
|
-
if (db.className === "TracerProjectileData" &&
|
|
199
|
-
data.tracerTex0 === "special/tracer00" &&
|
|
200
|
-
data.lifetimeMS === 3008) {
|
|
201
|
-
bullet = data;
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
expect(bullet, "ChaingunBullet datablock").toBeDefined();
|
|
206
|
-
// Values as recorded in this demo (a lightly modded server — tracer
|
|
207
|
-
// length/width are doubled from retail, everything else matches
|
|
208
|
-
// chaingun.cs). Each assertion pins a decode position: a swap with
|
|
209
|
-
// any neighboring field would produce a nonsensical value here.
|
|
210
|
-
expect(bullet.dryVelocity).toBe(750);
|
|
211
|
-
expect(bullet.wetVelocity).toBe(280);
|
|
212
|
-
expect(bullet.fizzleTimeMS).toBe(3008);
|
|
213
|
-
expect(bullet.explodeOnDeath).toBe(false);
|
|
214
|
-
// Chainguns are the only retail weapons with doDynamicClientHits.
|
|
215
|
-
expect(bullet.doDynamicClientHits).toBe(true);
|
|
216
|
-
// activateDelayMS = -1 (retail default) read as U32.
|
|
217
|
-
expect(bullet.activateDelayMS).toBe(0xffffffff);
|
|
218
|
-
expect(bullet.tracerLength).toBe(30);
|
|
219
|
-
expect(bullet.tracerMinPixels).toBe(6);
|
|
220
|
-
expect(bullet.tracerWidth).toBeCloseTo(0.2, 5);
|
|
221
|
-
expect(bullet.tracerAlpha).toBe(false);
|
|
222
|
-
// Retail chaingun.cs tracerColor: 211/255, 215/255, 120/255, 0.75.
|
|
223
|
-
expect(bullet.tracerColor.r).toBeCloseTo(211 / 255, 2);
|
|
224
|
-
expect(bullet.tracerColor.g).toBeCloseTo(215 / 255, 2);
|
|
225
|
-
expect(bullet.tracerColor.b).toBeCloseTo(120 / 255, 2);
|
|
226
|
-
expect(bullet.crossViewAng).toBeCloseTo(0.99, 5);
|
|
227
|
-
expect(bullet.crossSize).toBeCloseTo(0.2, 5);
|
|
228
|
-
expect(bullet.renderCross).toBe(true);
|
|
229
|
-
expect(bullet.tracerTex1).toBe("special/tracercross");
|
|
230
|
-
});
|
|
231
|
-
it("decodes PlayerData movement/jet fields with engine semantics", async () => {
|
|
232
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
233
|
-
let armor;
|
|
234
|
-
for (const [, db] of dataBlocks) {
|
|
235
|
-
const data = db.data;
|
|
236
|
-
// The armor the recorder wears: retail-value run/speed section.
|
|
237
|
-
if (db.className === "PlayerData" && data.maxForwardSpeed === 15) {
|
|
238
|
-
armor = data;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
expect(armor, "light armor PlayerData datablock").toBeDefined();
|
|
243
|
-
// Retail player.cs values — each pins a decode position (previously
|
|
244
|
-
// these landed in fields named jumpForce/runSurfaceAngle/etc.).
|
|
245
|
-
expect(armor.runForce).toBe(4968); // 55.2 * 90
|
|
246
|
-
expect(armor.maxBackwardSpeed).toBe(13);
|
|
247
|
-
expect(armor.maxSideSpeed).toBe(13);
|
|
248
|
-
expect(armor.underwaterVertJetFactor).toBeCloseTo(1.5, 5);
|
|
249
|
-
expect(armor.minLookAngle).toBeCloseTo(-1.5, 5);
|
|
250
|
-
expect(armor.maxLookAngle).toBeCloseTo(1.5, 5);
|
|
251
|
-
// Jet energy fields must decode as plausible per-tick magnitudes
|
|
252
|
-
// (this server mods them slightly, so no exact match): drain is a
|
|
253
|
-
// fraction of maxEnergy(60), threshold is a small energy value.
|
|
254
|
-
expect(armor.jetEnergyDrain).toBeGreaterThan(0);
|
|
255
|
-
expect(armor.jetEnergyDrain).toBeLessThan(5);
|
|
256
|
-
expect(armor.underwaterJetEnergyDrain).toBeGreaterThan(0);
|
|
257
|
-
expect(armor.underwaterJetEnergyDrain).toBeLessThan(5);
|
|
258
|
-
expect(armor.minJetEnergy).toBeGreaterThanOrEqual(0);
|
|
259
|
-
expect(armor.minJetEnergy).toBeLessThan(10);
|
|
260
|
-
// Heat signature rates — retail player.cs: 1/4 and 1/3. These are the
|
|
261
|
-
// first two reads of the ground-impact tail; the old decode shifted
|
|
262
|
-
// the whole section by two slots.
|
|
263
|
-
expect(armor.heatDecayPerSec).toBeCloseTo(0.25, 5);
|
|
264
|
-
expect(armor.heatIncreasePerSec).toBeCloseTo(1 / 3, 5);
|
|
265
|
-
expect(armor.groundImpactShakeDuration).toBeCloseTo(0.6, 5);
|
|
266
|
-
expect(armor.groundImpactShakeFalloff).toBeCloseTo(10, 5);
|
|
267
|
-
});
|
|
268
|
-
it("decodes StationFX node names and textures exactly", async () => {
|
|
269
|
-
const dataBlocks = await loadDataBlocks("exogen_Katabatic_vpad.rec");
|
|
270
|
-
let vehicleFX;
|
|
271
|
-
let personalFX;
|
|
272
|
-
for (const [, db] of dataBlocks) {
|
|
273
|
-
if (db.className === "StationFXVehicleData")
|
|
274
|
-
vehicleFX ??= db.data;
|
|
275
|
-
if (db.className === "StationFXPersonalData")
|
|
276
|
-
personalFX ??= db.data;
|
|
277
|
-
}
|
|
278
|
-
// serverVehicleHud.cs VehicleInvFX values.
|
|
279
|
-
expect(vehicleFX, "StationFXVehicleData").toBeDefined();
|
|
280
|
-
expect(vehicleFX.glowNodeName).toBe("GLOWFX");
|
|
281
|
-
expect(vehicleFX.leftNodeName0).toBe("LFX1");
|
|
282
|
-
expect(vehicleFX.rightNodeName0).toBe("RFX1");
|
|
283
|
-
expect(vehicleFX.leftNodeName3).toBe("LFX4");
|
|
284
|
-
expect(vehicleFX.rightNodeName3).toBe("RFX4");
|
|
285
|
-
expect(vehicleFX.texture0).toBe("special/stationGlow");
|
|
286
|
-
expect(vehicleFX.texture1).toBe("special/stationLight2");
|
|
287
|
-
// station.cs personal FX values.
|
|
288
|
-
expect(personalFX, "StationFXPersonalData").toBeDefined();
|
|
289
|
-
expect(personalFX.leftNodeName).toBe("FX1");
|
|
290
|
-
expect(personalFX.rightNodeName).toBe("FX2");
|
|
291
|
-
expect(personalFX.texture0).toBe("special/stationLight");
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
//# sourceMappingURL=DataBlockParsers.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DataBlockParsers.test.js","sourceRoot":"","sources":["../src/DataBlockParsers.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { DemoParser } from \"./DemoParser.js\";\nimport type {\n ELFProjectileDataBlock,\n EnergyProjectileDataBlock,\n LinearProjectileDataBlock,\n PlayerDataBlock,\n RepairProjectileDataBlock,\n ShapeBaseImageDataBlock,\n SniperProjectileDataBlock,\n TracerProjectileDataBlock,\n} from \"./dataBlockDataTypes.js\";\n\nconst DEMO_DIR = path.resolve(import.meta.dirname, \"..\", \"data\");\n\nasync function loadDataBlocks(file: string) {\n const buffer = fs.readFileSync(path.join(DEMO_DIR, file));\n const parser = new DemoParser(new Uint8Array(buffer));\n const { initialBlock } = await parser.load();\n return initialBlock.dataBlocks;\n}\n\n/**\n * Regression tests for projectile datablock field naming, which was\n * binary-verified against Tribes2.exe build 25034 (initPersistFields\n * FUN_0062b3c0 / FUN_0063fcb0 map names to the struct offsets that\n * unpackData reads in order). Prior to that verification several fields\n * were misassigned — most damagingly, the true lifetimeMS landed in a\n * field named `fizzleType`, breaking client-side projectile simulation.\n */\ndescribe(\"projectile datablock field decoding\", () => {\n it(\"decodes LinearProjectileData fields with engine semantics\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n const linears: LinearProjectileDataBlock[] = [];\n for (const [, db] of dataBlocks) {\n if (\n db.className === \"LinearProjectileData\" ||\n db.className === \"TracerProjectileData\" ||\n db.className === \"LinearFlareProjectileData\"\n ) {\n linears.push(db.data as LinearProjectileDataBlock);\n }\n }\n expect(linears.length).toBeGreaterThan(0);\n\n for (const block of linears) {\n // lifetimeMS/fizzleTimeMS are real milliseconds, rounded up to a\n // 32ms tick by the engine's onAdd, and clamped to 511 ticks.\n expect(block.lifetimeMS! % 32).toBe(0);\n expect(block.lifetimeMS).toBeGreaterThanOrEqual(32);\n expect(block.lifetimeMS).toBeLessThanOrEqual(511 * 32);\n expect(block.fizzleTimeMS! % 32).toBe(0);\n // Angles are ranged 0–90 degrees on the wire.\n expect(block.reflectOnWaterImpactAngle).toBeGreaterThanOrEqual(0);\n expect(block.reflectOnWaterImpactAngle).toBeLessThanOrEqual(90);\n expect(typeof block.explodeOnDeath).toBe(\"boolean\");\n expect(typeof block.doDynamicClientHits).toBe(\"boolean\");\n }\n });\n\n it(\"decodes retail DiscProjectile exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let disc: LinearProjectileDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n const data = db.data as LinearProjectileDataBlock;\n if (\n db.className === \"LinearProjectileData\" &&\n data.dryVelocity === 90 &&\n data.explodeOnDeath === true\n ) {\n disc = data;\n break;\n }\n }\n expect(disc, \"retail DiscProjectile datablock\").toBeDefined();\n // disc.cs: lifetimeMS = fizzleTimeMS = 5000 → tick-rounded to 5024.\n expect(disc!.lifetimeMS).toBe(5024);\n expect(disc!.fizzleTimeMS).toBe(5024);\n // disc.cs: reflectOnWaterImpactAngle = 15.0.\n expect(disc!.reflectOnWaterImpactAngle).toBe(15);\n });\n\n it(\"decodes retail EnergyBolt (blaster) fields exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let bolt: EnergyProjectileDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n if (db.className === \"EnergyProjectileData\") {\n bolt = db.data as EnergyProjectileDataBlock;\n break;\n }\n }\n expect(bolt, \"retail EnergyBolt datablock\").toBeDefined();\n // blaster.cs: binary-verified names (initPersistFields FUN_00694b40,\n // unpackData FUN_00694d80) — these were previously mislabeled with\n // ELF-style beam field names.\n expect(bolt!.crossViewAng).toBeCloseTo(0.99, 5);\n expect(bolt!.crossSize).toBeCloseTo(0.55, 5);\n expect(bolt!.blurLifetime).toBeCloseTo(0.2, 5);\n expect(bolt!.blurWidth).toBeCloseTo(0.25, 5);\n expect(bolt!.blurColor!.r).toBeCloseTo(0.4, 5);\n expect(bolt!.blurColor!.g).toBeCloseTo(0, 5);\n expect(bolt!.blurColor!.b).toBeCloseTo(0, 5);\n expect(bolt!.texture0).toBe(\"special/blasterBolt\");\n expect(bolt!.texture1).toBe(\"special/blasterBoltCross\");\n // blaster.cs: scale = \"0.25 20.0 1.0\" (bolt quad half-width / length).\n expect(bolt!.scale!.x).toBeCloseTo(0.25, 5);\n expect(bolt!.scale!.y).toBeCloseTo(20, 5);\n });\n\n it(\"decodes retail BasicSniperShot (laser rifle) fields exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let shot: SniperProjectileDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n if (db.className === \"SniperProjectileData\") {\n shot = db.data as SniperProjectileDataBlock;\n break;\n }\n }\n expect(shot, \"retail BasicSniperShot datablock\").toBeDefined();\n // sniperRifle.cs values.\n expect(shot!.maxRifleRange).toBeCloseTo(1000, 5);\n expect(shot!.beamColor!.r).toBeCloseTo(1, 2);\n expect(shot!.beamColor!.g).toBeCloseTo(0.1, 2);\n expect(shot!.beamColor!.b).toBeCloseTo(0.1, 2);\n expect(shot!.fadeTime).toBeCloseTo(1.0, 5);\n expect(shot!.startBeamWidth).toBeCloseTo(0.145, 5);\n expect(shot!.endBeamWidth).toBeCloseTo(0.25, 5);\n expect(shot!.pulseSpeed).toBeCloseTo(6.0, 5);\n expect(shot!.pulseLength).toBeCloseTo(0.15, 5);\n expect(shot!.textures![0]).toBe(\"special/flare\");\n expect(shot!.textures![1]).toBe(\"special/nonlingradient\");\n expect(shot!.textures![2]).toBe(\"special/laserrip01\");\n expect(shot!.textures![11]).toBe(\"special/sniper00\");\n });\n\n it(\"decodes both ELF datablocks (gun and turret) exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n const elfs: ELFProjectileDataBlock[] = [];\n for (const [, db] of dataBlocks) {\n if (db.className === \"ELFProjectileData\") {\n elfs.push(db.data as ELFProjectileDataBlock);\n }\n }\n // Binary-verified names (initPersistFields FUN_0064a860, unpackData\n // FUN_0064ae00) — previously fabricated beam names. Two datablocks\n // exist: the handheld BasicELF at beamRange 38 (the z0dd/ZOD server\n // patch every surviving server lineage runs — \"WHAT?? INCREASE ELF\n // RANGE?!!? was 37\"; the stock script says 30) and the stock\n // ELFTurretBolt at 75 (ELFBarrelLarge.cs).\n expect(elfs.map((e) => e.beamRange).sort((a, b) => a! - b!)).toEqual([\n 38, 75,\n ]);\n for (const elf of elfs) {\n expect(elf.mainBeamWidth).toBeCloseTo(0.1, 5);\n expect(elf.mainBeamSpeed).toBeCloseTo(9.0, 5);\n expect(elf.mainBeamRepeat).toBeCloseTo(0.25, 5);\n expect(elf.lightningWidth).toBeCloseTo(0.1, 5);\n expect(elf.lightningDist).toBeCloseTo(0.15, 5);\n expect(elf.textures).toEqual([\n \"special/ELFBeam\",\n \"special/ELFLightning\",\n \"special/BlueImpact\",\n ]);\n }\n });\n\n it(\"decodes retail DefaultRepairBeam fields exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let repair: RepairProjectileDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n if (db.className === \"RepairProjectileData\") {\n repair = db.data as RepairProjectileDataBlock;\n break;\n }\n }\n expect(repair, \"retail DefaultRepairBeam datablock\").toBeDefined();\n // repairpack.cs: binary-verified names (initPersistFields\n // FUN_00644910, unpackData FUN_00644c40); numSegments is an S32\n // whose raw bits the old decode read as a float.\n expect(repair!.beamRange).toBeCloseTo(10, 5);\n expect(repair!.beamWidth).toBeCloseTo(0.15, 5);\n expect(repair!.numSegments).toBe(20);\n expect(repair!.texRepeat).toBeCloseTo(0.2, 5);\n expect(repair!.blurFreq).toBeCloseTo(10.0, 5);\n expect(repair!.blurLifetime).toBeCloseTo(1.0, 5);\n expect(repair!.cutoffAngle).toBeCloseTo(25.0, 5);\n expect(repair!.textures).toEqual([\"special/redbump2\", \"special/redflare\"]);\n });\n\n it(\"decodes image-state transitions in the engine packing order\", async () => {\n // Every retail weapon image opens Activate → (timeout) →\n // ActivateReady (e.g. blaster.cs stateTransitionOnTimeout[0]).\n // With the old rotated names, the timeout transition surfaced under\n // transitionGeneric0Out and consumers carried a remap table.\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let checked = 0;\n for (const [, db] of dataBlocks) {\n if (db.className !== \"ShapeBaseImageData\") continue;\n const data = db.data as ShapeBaseImageDataBlock;\n const states = data.states;\n if (!states || states.length < 2) continue;\n if (states[0].name.toLowerCase() !== \"activate\") continue;\n const readyIndex = states.findIndex(\n (st) => st.name.toLowerCase() === \"activateready\",\n );\n if (readyIndex < 0) continue;\n // Transition values are 1-based state indices (0 = none).\n expect(states[0].transitionOnTimeout).toBe(readyIndex + 1);\n checked++;\n }\n expect(checked).toBeGreaterThan(3);\n });\n\n it(\"decodes retail ChaingunBullet tracer fields exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let bullet: TracerProjectileDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n const data = db.data as TracerProjectileDataBlock;\n if (\n db.className === \"TracerProjectileData\" &&\n data.tracerTex0 === \"special/tracer00\" &&\n data.lifetimeMS === 3008\n ) {\n bullet = data;\n break;\n }\n }\n expect(bullet, \"ChaingunBullet datablock\").toBeDefined();\n // Values as recorded in this demo (a lightly modded server — tracer\n // length/width are doubled from retail, everything else matches\n // chaingun.cs). Each assertion pins a decode position: a swap with\n // any neighboring field would produce a nonsensical value here.\n expect(bullet!.dryVelocity).toBe(750);\n expect(bullet!.wetVelocity).toBe(280);\n expect(bullet!.fizzleTimeMS).toBe(3008);\n expect(bullet!.explodeOnDeath).toBe(false);\n // Chainguns are the only retail weapons with doDynamicClientHits.\n expect(bullet!.doDynamicClientHits).toBe(true);\n // activateDelayMS = -1 (retail default) read as U32.\n expect(bullet!.activateDelayMS).toBe(0xffffffff);\n expect(bullet!.tracerLength).toBe(30);\n expect(bullet!.tracerMinPixels).toBe(6);\n expect(bullet!.tracerWidth).toBeCloseTo(0.2, 5);\n expect(bullet!.tracerAlpha).toBe(false);\n // Retail chaingun.cs tracerColor: 211/255, 215/255, 120/255, 0.75.\n expect(bullet!.tracerColor!.r).toBeCloseTo(211 / 255, 2);\n expect(bullet!.tracerColor!.g).toBeCloseTo(215 / 255, 2);\n expect(bullet!.tracerColor!.b).toBeCloseTo(120 / 255, 2);\n expect(bullet!.crossViewAng).toBeCloseTo(0.99, 5);\n expect(bullet!.crossSize).toBeCloseTo(0.2, 5);\n expect(bullet!.renderCross).toBe(true);\n expect(bullet!.tracerTex1).toBe(\"special/tracercross\");\n });\n\n it(\"decodes PlayerData movement/jet fields with engine semantics\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let armor: PlayerDataBlock | undefined;\n for (const [, db] of dataBlocks) {\n const data = db.data as PlayerDataBlock;\n // The armor the recorder wears: retail-value run/speed section.\n if (db.className === \"PlayerData\" && data.maxForwardSpeed === 15) {\n armor = data;\n break;\n }\n }\n expect(armor, \"light armor PlayerData datablock\").toBeDefined();\n // Retail player.cs values — each pins a decode position (previously\n // these landed in fields named jumpForce/runSurfaceAngle/etc.).\n expect(armor!.runForce).toBe(4968); // 55.2 * 90\n expect(armor!.maxBackwardSpeed).toBe(13);\n expect(armor!.maxSideSpeed).toBe(13);\n expect(armor!.underwaterVertJetFactor).toBeCloseTo(1.5, 5);\n expect(armor!.minLookAngle).toBeCloseTo(-1.5, 5);\n expect(armor!.maxLookAngle).toBeCloseTo(1.5, 5);\n // Jet energy fields must decode as plausible per-tick magnitudes\n // (this server mods them slightly, so no exact match): drain is a\n // fraction of maxEnergy(60), threshold is a small energy value.\n expect(armor!.jetEnergyDrain).toBeGreaterThan(0);\n expect(armor!.jetEnergyDrain).toBeLessThan(5);\n expect(armor!.underwaterJetEnergyDrain).toBeGreaterThan(0);\n expect(armor!.underwaterJetEnergyDrain).toBeLessThan(5);\n expect(armor!.minJetEnergy).toBeGreaterThanOrEqual(0);\n expect(armor!.minJetEnergy).toBeLessThan(10);\n // Heat signature rates — retail player.cs: 1/4 and 1/3. These are the\n // first two reads of the ground-impact tail; the old decode shifted\n // the whole section by two slots.\n expect(armor!.heatDecayPerSec).toBeCloseTo(0.25, 5);\n expect(armor!.heatIncreasePerSec).toBeCloseTo(1 / 3, 5);\n expect(armor!.groundImpactShakeDuration).toBeCloseTo(0.6, 5);\n expect(armor!.groundImpactShakeFalloff).toBeCloseTo(10, 5);\n });\n\n it(\"decodes StationFX node names and textures exactly\", async () => {\n const dataBlocks = await loadDataBlocks(\"exogen_Katabatic_vpad.rec\");\n let vehicleFX: Record<string, unknown> | undefined;\n let personalFX: Record<string, unknown> | undefined;\n for (const [, db] of dataBlocks) {\n if (db.className === \"StationFXVehicleData\") vehicleFX ??= db.data;\n if (db.className === \"StationFXPersonalData\") personalFX ??= db.data;\n }\n // serverVehicleHud.cs VehicleInvFX values.\n expect(vehicleFX, \"StationFXVehicleData\").toBeDefined();\n expect(vehicleFX!.glowNodeName).toBe(\"GLOWFX\");\n expect(vehicleFX!.leftNodeName0).toBe(\"LFX1\");\n expect(vehicleFX!.rightNodeName0).toBe(\"RFX1\");\n expect(vehicleFX!.leftNodeName3).toBe(\"LFX4\");\n expect(vehicleFX!.rightNodeName3).toBe(\"RFX4\");\n expect(vehicleFX!.texture0).toBe(\"special/stationGlow\");\n expect(vehicleFX!.texture1).toBe(\"special/stationLight2\");\n // station.cs personal FX values.\n expect(personalFX, \"StationFXPersonalData\").toBeDefined();\n expect(personalFX!.leftNodeName).toBe(\"FX1\");\n expect(personalFX!.rightNodeName).toBe(\"FX2\");\n expect(personalFX!.texture0).toBe(\"special/stationLight\");\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DemoParser.test.d.ts","sourceRoot":"","sources":["../src/DemoParser.test.ts"],"names":[],"mappings":""}
|