t2-demo-parser 1.0.3 → 2.1.0
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 +201 -87
- package/dist/DataBlockParsers.d.ts.map +1 -1
- package/dist/DataBlockParsers.js +89 -48
- package/dist/DataBlockParsers.js.map +1 -1
- package/dist/DataBlockParsers.test.js +128 -0
- package/dist/DataBlockParsers.test.js.map +1 -1
- package/dist/DemoParser.d.ts.map +1 -1
- package/dist/DemoParser.js +43 -8
- package/dist/DemoParser.js.map +1 -1
- package/dist/GhostManager.d.ts.map +1 -1
- package/dist/GhostManager.js +36 -68
- package/dist/GhostManager.js.map +1 -1
- package/dist/GhostStateAccumulator.d.ts +36 -0
- package/dist/GhostStateAccumulator.d.ts.map +1 -0
- package/dist/GhostStateAccumulator.js +124 -0
- package/dist/GhostStateAccumulator.js.map +1 -0
- package/dist/GhostStateAccumulator.test.d.ts +2 -0
- package/dist/GhostStateAccumulator.test.d.ts.map +1 -0
- package/dist/GhostStateAccumulator.test.js +201 -0
- package/dist/GhostStateAccumulator.test.js.map +1 -0
- package/dist/LiveParser.d.ts +39 -1
- package/dist/LiveParser.d.ts.map +1 -1
- package/dist/LiveParser.js +43 -1
- package/dist/LiveParser.js.map +1 -1
- package/dist/PacketParser.d.ts +23 -2
- package/dist/PacketParser.d.ts.map +1 -1
- package/dist/PacketParser.js +62 -19
- package/dist/PacketParser.js.map +1 -1
- package/dist/dataBlockDataTypes.d.ts +54 -29
- package/dist/dataBlockDataTypes.d.ts.map +1 -1
- package/dist/dataBlockDataTypes.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PacketParser.d.ts","sourceRoot":"","sources":["../src/PacketParser.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,uBAAuB,EAIvB,UAAU,
|
|
1
|
+
{"version":3,"file":"PacketParser.d.ts","sourceRoot":"","sources":["../src/PacketParser.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,uBAAuB,EAIvB,UAAU,EACV,YAAY,EAEb,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACV,aAAa,EAGb,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAwB;IAChD,OAAO,CAAC,gBAAgB,CAAC,CAA0B;IACnD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,uBAAuB,CAGvB;IAGR,mBAAmB,SAAK;IACxB,mBAAmB,SAAK;IACxB,YAAY,SAAK;IACjB,YAAY,SAAK;IACjB,kBAAkB,SAAK;IACvB,kBAAkB,SAAK;IACvB,YAAY,SAAK;IACjB,YAAY,SAAK;IACjB;;;oDAGgD;IAChD,qBAAqB,SAAK;IAC1B,aAAa,SAAK;IAClB,gBAAgB,SAAK;IACrB,kBAAkB,SAAK;gBAGrB,QAAQ,EAAE,aAAa,EACvB,YAAY,EAAE,YAAY,EAC1B,OAAO,CAAC,EAAE;QACR,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC3C,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;QAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,gBAAgB,CAAC,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvD,uBAAuB,CAAC,EAAE,KAAK,CAAC;YAC9B,sBAAsB,EAAE,MAAM,CAAC;YAC/B,KAAK,EAAE,YAAY,CAAC;SACrB,CAAC,CAAC;KACJ;IAqBH,mBAAmB,IAAI;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;IAI1D,mBAAmB,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS;IAI1D,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,oBAAoB;IAI5B,0BAA0B,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI;IAchE;;;;;;OAMG;IACH,0BAA0B,IAAI,uBAAuB;IAarD,mBAAmB,IAAI,MAAM;IAI7B,0BAA0B,IAAI,KAAK,CAAC;QAClC,sBAAsB,EAAE,MAAM,CAAC;QAC/B,KAAK,EAAE,YAAY,CAAC;KACrB,CAAC;IAIF;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAK3B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IA0D3B,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU;IA2EzC;;OAEG;IACH,OAAO,CAAC,cAAc;IAoBtB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,aAAa;IA2MrB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAoGlB,OAAO,CAAC,sBAAsB;IAkB9B,OAAO,CAAC,wBAAwB;IAgBhC;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA0C7B;;OAEG;IACH,OAAO,CAAC,UAAU;IA2JlB,OAAO,CAAC,cAAc;CAOvB"}
|
package/dist/PacketParser.js
CHANGED
|
@@ -15,7 +15,6 @@ export class PacketParser {
|
|
|
15
15
|
registry;
|
|
16
16
|
ghostTracker;
|
|
17
17
|
compressionPoint = { x: 0, y: 0, z: 0 };
|
|
18
|
-
controlParserByGhostIndex = new Map();
|
|
19
18
|
dataBlockDataMap;
|
|
20
19
|
lastSeqRecvdAtSend = new Array(32).fill(0);
|
|
21
20
|
lastSeqRecvd = 0;
|
|
@@ -54,6 +53,14 @@ export class PacketParser {
|
|
|
54
53
|
if (typeof options?.nextRecvEventSeq === "number") {
|
|
55
54
|
this.nextRecvEventSeq = options.nextRecvEventSeq >>> 0;
|
|
56
55
|
}
|
|
56
|
+
if (options?.compressionPoint) {
|
|
57
|
+
this.compressionPoint = { ...options.compressionPoint };
|
|
58
|
+
}
|
|
59
|
+
if (options?.pendingGuaranteedEvents) {
|
|
60
|
+
this.pendingGuaranteedEvents = options.pendingGuaranteedEvents
|
|
61
|
+
.map((entry) => ({ ...entry }))
|
|
62
|
+
.sort((a, b) => a.absoluteSequenceNumber - b.absoluteSequenceNumber);
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
getCompressionPoint() {
|
|
59
66
|
return this.compressionPoint;
|
|
@@ -89,6 +96,31 @@ export class PacketParser {
|
|
|
89
96
|
this.lastRecvAckAck = state.lastRecvAckAck >>> 0;
|
|
90
97
|
this._connectionEstablished = state.connectionEstablished;
|
|
91
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Export the current protocol window state. Together with
|
|
101
|
+
* `getNextRecvEventSeq`, `getPendingGuaranteedEvents`,
|
|
102
|
+
* `getCompressionPoint`, `getDataBlockDataMap`, and the ghost tracker
|
|
103
|
+
* contents, this captures all cross-packet parser state, so an
|
|
104
|
+
* identically seeded parser continues the stream in lockstep.
|
|
105
|
+
*/
|
|
106
|
+
getConnectionProtocolState() {
|
|
107
|
+
return {
|
|
108
|
+
lastSeqRecvdAtSend: this.lastSeqRecvdAtSend.slice(),
|
|
109
|
+
lastSeqRecvd: this.lastSeqRecvd,
|
|
110
|
+
highestAckedSeq: this.highestAckedSeq,
|
|
111
|
+
lastSendSeq: this.lastSendSeq,
|
|
112
|
+
ackMask: this.recvAckMask,
|
|
113
|
+
connectSequence: this.connectSequence,
|
|
114
|
+
lastRecvAckAck: this.lastRecvAckAck,
|
|
115
|
+
connectionEstablished: this._connectionEstablished,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
getNextRecvEventSeq() {
|
|
119
|
+
return this.nextRecvEventSeq;
|
|
120
|
+
}
|
|
121
|
+
getPendingGuaranteedEvents() {
|
|
122
|
+
return this.pendingGuaranteedEvents.map((entry) => ({ ...entry }));
|
|
123
|
+
}
|
|
92
124
|
/**
|
|
93
125
|
* Emulate ConnectionProtocol::buildSendPacketHeader (FUN_0043d2d0, data path).
|
|
94
126
|
* Demo block type 1 indicates a local send-packet trigger.
|
|
@@ -186,16 +218,21 @@ export class PacketParser {
|
|
|
186
218
|
// When an event can't be parsed, readEvents returns early and the
|
|
187
219
|
// stream position is wrong — reading ghosts would produce garbage.
|
|
188
220
|
const lastEvent = events[events.length - 1];
|
|
189
|
-
const eventsComplete = !lastEvent
|
|
221
|
+
const eventsComplete = !lastEvent?.failed;
|
|
190
222
|
// 5. Ghosts (NetConnection::readPacket -> ghostReadPacket)
|
|
191
|
-
const ghostSectionStart = gameStateComplete && eventsComplete
|
|
192
|
-
? bs.getCurPos()
|
|
193
|
-
: undefined;
|
|
223
|
+
const ghostSectionStart = gameStateComplete && eventsComplete ? bs.getCurPos() : undefined;
|
|
194
224
|
const ghosts = gameStateComplete && eventsComplete
|
|
195
225
|
? this.readGhosts(bs, dnetHeader.seqNumber)
|
|
196
226
|
: [];
|
|
197
227
|
bs.setStringBuffer(false);
|
|
198
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
dnetHeader,
|
|
230
|
+
rateInfo,
|
|
231
|
+
gameState,
|
|
232
|
+
events,
|
|
233
|
+
ghosts,
|
|
234
|
+
ghostSectionStart,
|
|
235
|
+
};
|
|
199
236
|
}
|
|
200
237
|
/**
|
|
201
238
|
* Parse the dnet packet header (from processRawPacket in dnet.cc).
|
|
@@ -303,9 +340,12 @@ export class PacketParser {
|
|
|
303
340
|
// writePacketData/readPacketData, so the control object is
|
|
304
341
|
// always one of these two classes. We try candidates in order:
|
|
305
342
|
// 1. Tracker classId (may be stale due to ghost index recycling)
|
|
306
|
-
// 2.
|
|
307
|
-
// 3.
|
|
308
|
-
//
|
|
343
|
+
// 2. Player (classId 25) — the normal control object
|
|
344
|
+
// 3. Camera (classId 4) — spectator mode
|
|
345
|
+
// The candidate order must be a pure function of seeded state
|
|
346
|
+
// (the tracker) so that a parser seeded mid-stream makes the same
|
|
347
|
+
// choice as one that has followed the stream from the start —
|
|
348
|
+
// no history-derived caches here.
|
|
309
349
|
const gIndex = bs.readInt(10);
|
|
310
350
|
controlObjectGhostIndex = gIndex;
|
|
311
351
|
controlObjectDataStart = bs.getCurPos();
|
|
@@ -314,7 +354,6 @@ export class PacketParser {
|
|
|
314
354
|
const preferredEntry = ghost
|
|
315
355
|
? this.registry.getGhostParser(ghost.classId)
|
|
316
356
|
: undefined;
|
|
317
|
-
const cachedEntry = this.controlParserByGhostIndex.get(gIndex);
|
|
318
357
|
const playerEntry = this.registry.getGhostParser(25); // Player
|
|
319
358
|
const cameraEntry = this.registry.getGhostParser(4); // Camera
|
|
320
359
|
// Build candidate list (deduplicated, only those with readPacketData)
|
|
@@ -329,7 +368,6 @@ export class PacketParser {
|
|
|
329
368
|
candidates.push(entry);
|
|
330
369
|
};
|
|
331
370
|
addCandidate(preferredEntry);
|
|
332
|
-
addCandidate(cachedEntry);
|
|
333
371
|
addCandidate(playerEntry);
|
|
334
372
|
addCandidate(cameraEntry);
|
|
335
373
|
let parsed = false;
|
|
@@ -344,7 +382,6 @@ export class PacketParser {
|
|
|
344
382
|
}
|
|
345
383
|
controlObjectData = data;
|
|
346
384
|
controlObjectDataEnd = bs.getCurPos();
|
|
347
|
-
this.controlParserByGhostIndex.set(gIndex, entry);
|
|
348
385
|
if (conn.compressionPoint !== this.compressionPoint) {
|
|
349
386
|
this.compressionPoint = conn.compressionPoint;
|
|
350
387
|
compressionPoint = this.compressionPoint;
|
|
@@ -493,6 +530,7 @@ export class PacketParser {
|
|
|
493
530
|
absoluteSequenceNumber,
|
|
494
531
|
dataBitsStart,
|
|
495
532
|
dataBitsEnd: dataBitsStart,
|
|
533
|
+
failed: true,
|
|
496
534
|
});
|
|
497
535
|
return dispatchedEvents;
|
|
498
536
|
}
|
|
@@ -506,6 +544,7 @@ export class PacketParser {
|
|
|
506
544
|
absoluteSequenceNumber,
|
|
507
545
|
dataBitsStart,
|
|
508
546
|
dataBitsEnd: dataBitsStart,
|
|
547
|
+
failed: true,
|
|
509
548
|
});
|
|
510
549
|
return dispatchedEvents;
|
|
511
550
|
}
|
|
@@ -564,11 +603,16 @@ export class PacketParser {
|
|
|
564
603
|
const eventType = parsedData.type;
|
|
565
604
|
if (eventType === "GhostingMessageEvent") {
|
|
566
605
|
const message = parsedData.message;
|
|
567
|
-
// Tribes 2 handleGhostMessage
|
|
568
|
-
//
|
|
606
|
+
// Tribes 2 handleGhostMessage EndGhosting (netGhost.cc:706) deletes
|
|
607
|
+
// ONLY local ghosts. Datablocks are connection-lifetime state and
|
|
608
|
+
// must be retained: the server's transmitDataBlocks skips datablocks
|
|
609
|
+
// already sent on this connection (gameConnection.cc
|
|
610
|
+
// cTransmitDataBlocks: only modifiedKey > mDataBlockModifiedKey is
|
|
611
|
+
// re-sent), so on a mission change most datablocks are NOT re-sent
|
|
612
|
+
// and clearing the map here leaves the new mission's ghosts
|
|
613
|
+
// unresolvable.
|
|
569
614
|
if (typeof message === "number" && message === GhostMsgEndGhosting) {
|
|
570
615
|
this.ghostTracker.clear();
|
|
571
|
-
this.dataBlockDataMap?.clear();
|
|
572
616
|
}
|
|
573
617
|
return;
|
|
574
618
|
}
|
|
@@ -580,12 +624,11 @@ export class PacketParser {
|
|
|
580
624
|
this.ghostTracker.createGhost(ghostIndex, classId, parserEntry?.name ?? `unknown_${classId}`);
|
|
581
625
|
}
|
|
582
626
|
}
|
|
583
|
-
// SimDataBlockEvent:
|
|
584
|
-
// (
|
|
627
|
+
// SimDataBlockEvent: accumulate parsed DataBlock data by objectId
|
|
628
|
+
// (exported for seeding and consumed by downstream renderers).
|
|
585
629
|
if (eventType === "SimDataBlockEvent" && this.dataBlockDataMap) {
|
|
586
630
|
const dbEvent = parsedData;
|
|
587
|
-
if (dbEvent.dataBlockData &&
|
|
588
|
-
typeof dbEvent.objectId === "number") {
|
|
631
|
+
if (dbEvent.dataBlockData && typeof dbEvent.objectId === "number") {
|
|
589
632
|
this.dataBlockDataMap.set(dbEvent.objectId, dbEvent.dataBlockData);
|
|
590
633
|
}
|
|
591
634
|
}
|
package/dist/PacketParser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PacketParser.js","sourceRoot":"","sources":["../src/PacketParser.ts"],"names":[],"mappings":"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","sourcesContent":["import createDebug from \"debug\";\nimport { BitStream } from \"./BitStream.js\";\nimport {\n DataPacket,\n MaxGhostCount,\n GhostMsgEndGhosting,\n NetEventClassBitSize,\n NetEventClassFirst,\n NetObjectClassBitSize,\n NetObjectClassFirst,\n} from \"./types.js\";\n\nconst debugGhosts = createDebug(\"t2-demo-parser:ghosts\");\nimport type {\n ConnectionProtocolState,\n DnetHeader,\n RateInfo,\n GameState,\n PacketData,\n NetEventInfo,\n GhostUpdate,\n} from \"./types.js\";\nimport type {\n ClassRegistry,\n ConnectionContext,\n GhostParserEntry,\n ParsedData,\n} from \"./ClassRegistry.js\";\nimport type { EventData, SimDataBlockEventData } from \"./eventDataTypes.js\";\nimport type { GhostTracker } from \"./GhostManager.js\";\n\n/**\n * Parses individual network packets from demo recording blocks.\n *\n * Packet layout (from the server connection perspective):\n * 1. dnet header (processRawPacket)\n * 2. Rate info (handlePacket)\n * 3. GameConnection::readPacket — game state, moves, etc.\n * 4. NetConnection::readPacket — events, then ghosts\n */\nexport class PacketParser {\n private registry: ClassRegistry;\n private ghostTracker: GhostTracker;\n private compressionPoint = { x: 0, y: 0, z: 0 };\n private controlParserByGhostIndex = new Map<number, GhostParserEntry>();\n private dataBlockDataMap?: Map<number, ParsedData>;\n private lastSeqRecvdAtSend = new Array<number>(32).fill(0);\n private lastSeqRecvd = 0;\n private highestAckedSeq = 0;\n private lastSendSeq = 0;\n private recvAckMask = 0;\n private connectSequence = 0;\n private lastRecvAckAck = 0;\n private _connectionEstablished = false;\n private nextRecvEventSeq = 0;\n private pendingGuaranteedEvents: Array<{\n absoluteSequenceNumber: number;\n event: NetEventInfo;\n }> = [];\n\n // Stats\n controlObjectParsed = 0;\n controlObjectFailed = 0;\n eventsParsed = 0;\n eventsFailed = 0;\n ghostCreatesParsed = 0;\n ghostUpdatesParsed = 0;\n ghostDeletes = 0;\n ghostsFailed = 0;\n /** Ghosts where our tracker says \"new\" but the classId is unregistered.\n * This indicates a tracker divergence: the server sent UPDATE data\n * (no classId prefix) for a ghost we don't have, so we misread the\n * first 7 bits of update data as a classId. */\n ghostsTrackerDiverged = 0;\n packetsParsed = 0;\n protocolRejected = 0;\n protocolNoDispatch = 0;\n\n constructor(\n registry: ClassRegistry,\n ghostTracker: GhostTracker,\n options?: {\n dataBlockDataMap?: Map<number, ParsedData>;\n connectionProtocolState?: ConnectionProtocolState;\n nextRecvEventSeq?: number;\n }\n ) {\n this.registry = registry;\n this.ghostTracker = ghostTracker;\n this.dataBlockDataMap = options?.dataBlockDataMap;\n if (options?.connectionProtocolState) {\n this.setConnectionProtocolState(options.connectionProtocolState);\n }\n if (typeof options?.nextRecvEventSeq === \"number\") {\n this.nextRecvEventSeq = options.nextRecvEventSeq >>> 0;\n }\n }\n\n getCompressionPoint(): { x: number; y: number; z: number } {\n return this.compressionPoint;\n }\n\n getDataBlockDataMap(): Map<number, ParsedData> | undefined {\n return this.dataBlockDataMap;\n }\n\n private getConnectionContext(): ConnectionContext {\n const dbMap = this.dataBlockDataMap;\n return {\n compressionPoint: this.compressionPoint,\n ghostTracker: this.ghostTracker,\n getDataBlockParser: (classId: number) =>\n this.registry.getDataBlockParser(classId),\n getDataBlockData: dbMap\n ? (objectId: number) => dbMap.get(objectId)\n : undefined,\n getGhostParser: (classId: number) => this.registry.getGhostParser(classId),\n };\n }\n\n private _setNextRecvEventSeq(nextRecvEventSeq: number): void {\n this.nextRecvEventSeq = nextRecvEventSeq >>> 0;\n }\n\n setConnectionProtocolState(state: ConnectionProtocolState): void {\n this.lastSeqRecvdAtSend = state.lastSeqRecvdAtSend.slice(0, 32);\n while (this.lastSeqRecvdAtSend.length < 32) {\n this.lastSeqRecvdAtSend.push(0);\n }\n this.lastSeqRecvd = state.lastSeqRecvd >>> 0;\n this.highestAckedSeq = state.highestAckedSeq >>> 0;\n this.lastSendSeq = state.lastSendSeq >>> 0;\n this.recvAckMask = state.ackMask >>> 0;\n this.connectSequence = state.connectSequence >>> 0;\n this.lastRecvAckAck = state.lastRecvAckAck >>> 0;\n this._connectionEstablished = state.connectionEstablished;\n }\n\n /**\n * Emulate ConnectionProtocol::buildSendPacketHeader (FUN_0043d2d0, data path).\n * Demo block type 1 indicates a local send-packet trigger.\n */\n onSendPacketTrigger(): void {\n this.lastSendSeq = (this.lastSendSeq + 1) >>> 0;\n this.lastSeqRecvdAtSend[this.lastSendSeq & 0x1f] = this.lastSeqRecvd >>> 0;\n }\n\n /**\n * Emulates ConnectionProtocol::processRawPacket (FUN_0043d4d0).\n * A packet may be syntactically valid but still ignored for game payload\n * dispatch due to sequence/connect-window checks or duplicate seq numbers.\n */\n private applyProtocolHeader(dnetHeader: DnetHeader): {\n accepted: boolean;\n dispatchData: boolean;\n } {\n if (dnetHeader.connectSeqBit !== (this.connectSequence & 1)) {\n return { accepted: false, dispatchData: false };\n }\n if (dnetHeader.ackByteCount > 4 || dnetHeader.packetType > 2) {\n return { accepted: false, dispatchData: false };\n }\n\n let seqNumber = (\n dnetHeader.seqNumber | (this.lastSeqRecvd & 0xffff_fe00)\n ) >>> 0;\n if (seqNumber < this.lastSeqRecvd) {\n seqNumber = (seqNumber + 0x200) >>> 0;\n }\n if (this.lastSeqRecvd + 0x1f < seqNumber) {\n return { accepted: false, dispatchData: false };\n }\n\n let highestAck = (\n dnetHeader.highestAck | (this.highestAckedSeq & 0xffff_fe00)\n ) >>> 0;\n if (highestAck < this.highestAckedSeq) {\n highestAck = (highestAck + 0x200) >>> 0;\n }\n if (this.lastSendSeq < highestAck) {\n return { accepted: false, dispatchData: false };\n }\n\n const seqShift = (seqNumber - this.lastSeqRecvd) & 0x1f;\n this.recvAckMask = (this.recvAckMask << seqShift) >>> 0;\n if (dnetHeader.packetType === DataPacket) {\n this.recvAckMask = (this.recvAckMask | 1) >>> 0;\n }\n\n for (let ackSeq = this.highestAckedSeq + 1; ackSeq <= highestAck; ackSeq++) {\n const isAcked = (\n dnetHeader.ackMask & (1 << ((highestAck - ackSeq) & 0x1f))\n ) !== 0;\n if (isAcked) {\n this.lastRecvAckAck = this.lastSeqRecvdAtSend[ackSeq & 0x1f] >>> 0;\n }\n }\n if (seqNumber - this.lastRecvAckAck > 0x20) {\n this.lastRecvAckAck = seqNumber - 0x20;\n }\n this.highestAckedSeq = highestAck;\n\n const dispatchData =\n this.lastSeqRecvd !== seqNumber && dnetHeader.packetType === DataPacket;\n this.lastSeqRecvd = seqNumber;\n\n return { accepted: true, dispatchData };\n }\n\n parsePacket(data: Uint8Array): PacketData {\n const bs = new BitStream(data);\n\n // 1. Parse dnet header\n const dnetHeader = this.readDnetHeader(bs);\n const protocol = this.applyProtocolHeader(dnetHeader);\n\n this.packetsParsed++;\n\n // ConnectionProtocol may reject this packet entirely (window/connect checks),\n // or accept it but suppress game dispatch (duplicate seq / non-data packet).\n if (!protocol.accepted) {\n this.protocolRejected++;\n return {\n dnetHeader,\n rateInfo: {},\n gameState: this.emptyGameState(),\n events: [],\n ghosts: [],\n };\n }\n if (!protocol.dispatchData) {\n this.protocolNoDispatch++;\n return {\n dnetHeader,\n rateInfo: {},\n gameState: this.emptyGameState(),\n events: [],\n ghosts: [],\n };\n }\n\n // 2. Rate info (NetConnection::handlePacket)\n const rateInfo = this.readRateInfo(bs);\n\n // 3. Game state (GameConnection::readPacket — server connection path)\n bs.setStringBuffer(true);\n const gameState = this.readGameState(bs);\n\n // Only read events/ghosts if game state completed successfully.\n // When game state bails (control object dirty with no parser), the\n // stream position is wrong and we'd read garbage data.\n const gameStateComplete =\n gameState.controlObjectDataStart === undefined ||\n gameState.controlObjectData !== undefined;\n\n // 4. Events (NetConnection::readPacket -> eventReadPacket)\n const events = gameStateComplete ? this.readEvents(bs) : [];\n\n // Only read ghosts if all events were successfully parsed.\n // When an event can't be parsed, readEvents returns early and the\n // stream position is wrong — reading ghosts would produce garbage.\n const lastEvent = events[events.length - 1];\n const eventsComplete =\n !lastEvent || lastEvent.dataBitsEnd !== lastEvent.dataBitsStart;\n\n // 5. Ghosts (NetConnection::readPacket -> ghostReadPacket)\n const ghostSectionStart = gameStateComplete && eventsComplete\n ? bs.getCurPos()\n : undefined;\n const ghosts =\n gameStateComplete && eventsComplete\n ? this.readGhosts(bs, dnetHeader.seqNumber)\n : [];\n\n bs.setStringBuffer(false);\n\n return { dnetHeader, rateInfo, gameState, events, ghosts, ghostSectionStart };\n }\n\n /**\n * Parse the dnet packet header (from processRawPacket in dnet.cc).\n */\n private readDnetHeader(bs: BitStream): DnetHeader {\n const gameFlag = bs.readFlag();\n const connectSeqBit = bs.readInt(1);\n const seqNumber = bs.readInt(9);\n const highestAck = bs.readInt(9);\n const packetType = bs.readInt(2);\n const ackByteCount = bs.readInt(3);\n const ackMask = ackByteCount > 0 ? bs.readInt(8 * ackByteCount) : 0;\n\n return {\n gameFlag,\n connectSeqBit,\n seqNumber,\n highestAck,\n packetType,\n ackByteCount,\n ackMask,\n };\n }\n\n /**\n * Parse rate info from handlePacket in netConnection.cc.\n */\n private readRateInfo(bs: BitStream): RateInfo {\n const info: RateInfo = {};\n if (bs.readFlag()) {\n info.updateDelay = bs.readInt(10);\n info.packetSize = bs.readInt(10);\n }\n if (bs.readFlag()) {\n info.maxUpdateDelay = bs.readInt(10);\n info.maxPacketSize = bs.readInt(10);\n }\n return info;\n }\n\n /**\n * Parse game state from GameConnection::readPacket (server connection path).\n */\n private readGameState(bs: BitStream): GameState {\n const lastMoveAck = bs.readInt(32);\n\n // Damage flash / whiteout\n let damageFlash: number | undefined;\n let whiteOut: number | undefined;\n if (bs.readFlag()) {\n if (bs.readFlag()) {\n damageFlash = bs.readFloat(7);\n }\n if (bs.readFlag()) {\n whiteOut = bs.readFloat(7) * 1.5;\n }\n }\n\n // Lock/homing state\n let selfLocked: boolean | undefined;\n let selfHomed: boolean | undefined;\n if (bs.readFlag()) {\n selfLocked = bs.readFlag();\n selfHomed = bs.readFlag();\n }\n\n // Seeker tracking state\n let seekerTracking: boolean | undefined;\n let seekerTrackingPos: { x: number; y: number; z: number } | undefined;\n let seekerMode: number | undefined;\n let seekerObjectGhostIndex: number | undefined;\n let targetPos: { x: number; y: number; z: number } | undefined;\n if (bs.readFlag()) {\n seekerTracking = bs.readFlag();\n // Binary 0x005fc020: when seekerTracking is true, reads 3×F32 (Point3F)\n // for the seeker target position (stored at connection+0x8454)\n if (seekerTracking) {\n seekerTrackingPos = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n }\n seekerMode = bs.readRangedU32(0, 2);\n if (seekerMode === 1) {\n if (bs.readFlag()) {\n seekerObjectGhostIndex = bs.readRangedU32(0, MaxGhostCount - 1);\n }\n } else if (seekerMode === 2) {\n targetPos = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n }\n }\n\n // Sensor ping/jam flags\n const pinged = bs.readFlag();\n const jammed = bs.readFlag();\n\n // Control object state\n let controlObjectGhostIndex: number | undefined;\n let controlObjectDataStart: number | undefined;\n let controlObjectDataEnd: number | undefined;\n let controlObjectData: ParsedData | undefined;\n let compressionPoint: { x: number; y: number; z: number } | undefined;\n\n if (bs.readFlag()) {\n if (bs.readFlag()) {\n // Control object is dirty — full update via readPacketData.\n // In Tribes 2 (build 25034), only Player and Camera override\n // writePacketData/readPacketData, so the control object is\n // always one of these two classes. We try candidates in order:\n // 1. Tracker classId (may be stale due to ghost index recycling)\n // 2. Cached parser from previous successful parse of this index\n // 3. Player (classId 25) — the normal control object\n // 4. Camera (classId 4) — spectator mode\n const gIndex = bs.readInt(10);\n controlObjectGhostIndex = gIndex;\n controlObjectDataStart = bs.getCurPos();\n const start = bs.savePos();\n const ghost = this.ghostTracker.getGhost(gIndex);\n\n const preferredEntry = ghost\n ? this.registry.getGhostParser(ghost.classId)\n : undefined;\n const cachedEntry = this.controlParserByGhostIndex.get(gIndex);\n const playerEntry = this.registry.getGhostParser(25); // Player\n const cameraEntry = this.registry.getGhostParser(4); // Camera\n\n // Build candidate list (deduplicated, only those with readPacketData)\n const candidates: GhostParserEntry[] = [];\n const seen = new Set<string>();\n const addCandidate = (entry: GhostParserEntry | undefined): void => {\n if (!entry?.readPacketData) return;\n if (seen.has(entry.name)) return;\n seen.add(entry.name);\n candidates.push(entry);\n };\n addCandidate(preferredEntry);\n addCandidate(cachedEntry);\n addCandidate(playerEntry);\n addCandidate(cameraEntry);\n\n let parsed = false;\n for (const entry of candidates) {\n bs.restorePos(start);\n try {\n const conn = this.getConnectionContext();\n const data = entry.readPacketData!(bs, conn);\n const bitsConsumed = bs.getCurPos() - controlObjectDataStart!;\n if (bitsConsumed <= 0 || bs.isError()) {\n continue;\n }\n\n controlObjectData = data;\n controlObjectDataEnd = bs.getCurPos();\n this.controlParserByGhostIndex.set(gIndex, entry);\n if (conn.compressionPoint !== this.compressionPoint) {\n this.compressionPoint = conn.compressionPoint;\n compressionPoint = this.compressionPoint;\n }\n this.controlObjectParsed++;\n parsed = true;\n break;\n } catch {\n // Try next candidate.\n }\n }\n\n if (!parsed) {\n bs.restorePos(start);\n // No parser — bail from game state\n controlObjectDataEnd = controlObjectDataStart;\n this.controlObjectFailed++;\n return {\n lastMoveAck,\n damageFlash,\n whiteOut,\n selfLocked,\n selfHomed,\n seekerTracking,\n seekerTrackingPos,\n seekerMode,\n seekerObjectGhostIndex,\n targetPos,\n pinged,\n jammed,\n controlObjectGhostIndex,\n controlObjectDataStart,\n controlObjectDataEnd,\n controlObjectData,\n targetVisibility: [],\n };\n }\n } else {\n // Compression point update — always accept (engine doesn't validate)\n compressionPoint = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n this.compressionPoint = compressionPoint;\n }\n }\n\n // Target visibility masks\n const targetVisibility: { index: number; mask: number }[] = [];\n while (bs.readFlag()) {\n targetVisibility.push({\n index: bs.readInt(4),\n mask: bs.readInt(32),\n });\n }\n\n // Camera FOV\n let cameraFov: number | undefined;\n if (bs.readFlag()) {\n cameraFov = bs.readInt(8);\n }\n\n return {\n lastMoveAck,\n damageFlash,\n whiteOut,\n selfLocked,\n selfHomed,\n seekerTracking,\n seekerTrackingPos,\n seekerMode,\n seekerObjectGhostIndex,\n targetPos,\n pinged,\n jammed,\n controlObjectGhostIndex,\n controlObjectDataStart,\n controlObjectDataEnd,\n controlObjectData,\n compressionPoint,\n targetVisibility: targetVisibility.length > 0 ? targetVisibility : undefined,\n cameraFov,\n };\n }\n\n /**\n * Parse events from eventReadPacket in netEvent.cc.\n *\n * Two phases:\n * 1. Unguaranteed events: while(readFlag) { classId(6b) + unpack }\n * 2. Guaranteed events: while(readFlag) { seqFlag(1b), [seq(7b)], classId(6b) + unpack }\n */\n private readEvents(bs: BitStream): NetEventInfo[] {\n const dispatchedEvents: NetEventInfo[] = [];\n let unguaranteedPhase = true;\n let prevGuaranteedSeq = -2;\n\n while (true) {\n const bit = bs.readFlag();\n if (unguaranteedPhase && !bit) {\n // Transition to guaranteed phase. Binary immediately reads one more\n // continuation bit for that phase.\n unguaranteedPhase = false;\n const guaranteedBit = bs.readFlag();\n if (!guaranteedBit) {\n this.dispatchGuaranteedEvents(dispatchedEvents);\n break;\n }\n } else if (!unguaranteedPhase && !bit) {\n this.dispatchGuaranteedEvents(dispatchedEvents);\n break;\n } else if (!bit) {\n break;\n }\n\n let sequenceNumber: number | undefined;\n let absoluteSequenceNumber: number | undefined;\n if (!unguaranteedPhase) {\n if (bs.readFlag()) {\n sequenceNumber = (prevGuaranteedSeq + 1) & 0x7f;\n } else {\n sequenceNumber = bs.readInt(7);\n }\n prevGuaranteedSeq = sequenceNumber;\n absoluteSequenceNumber =\n sequenceNumber | (this.nextRecvEventSeq & 0xffffff80);\n if (absoluteSequenceNumber < this.nextRecvEventSeq) {\n absoluteSequenceNumber += 0x80;\n }\n }\n\n const classId = bs.readInt(NetEventClassBitSize) + NetEventClassFirst;\n const dataBitsStart = bs.getCurPos();\n\n const parserEntry = this.registry.getEventParser(classId);\n let parsedData: ParsedData | undefined;\n\n if (parserEntry) {\n try {\n const conn = this.getConnectionContext();\n parsedData = parserEntry.unpack(bs, conn);\n this.eventsParsed++;\n } catch {\n this.eventsFailed++;\n dispatchedEvents.push({\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: dataBitsStart,\n });\n return dispatchedEvents;\n }\n } else {\n // No parser — cannot advance safely.\n dispatchedEvents.push({\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: dataBitsStart,\n });\n return dispatchedEvents;\n }\n\n const event: NetEventInfo = {\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: bs.getCurPos(),\n parsedData,\n };\n\n if (unguaranteedPhase) {\n dispatchedEvents.push(event);\n if (parsedData) {\n this.applyEventSideEffects(parsedData);\n }\n } else if (absoluteSequenceNumber !== undefined) {\n this.enqueueGuaranteedEvent(absoluteSequenceNumber, event);\n }\n }\n\n return dispatchedEvents;\n }\n\n private enqueueGuaranteedEvent(\n absoluteSequenceNumber: number,\n event: NetEventInfo\n ): void {\n let insertAt = 0;\n while (\n insertAt < this.pendingGuaranteedEvents.length &&\n this.pendingGuaranteedEvents[insertAt].absoluteSequenceNumber <\n absoluteSequenceNumber\n ) {\n insertAt++;\n }\n this.pendingGuaranteedEvents.splice(insertAt, 0, {\n absoluteSequenceNumber,\n event,\n });\n }\n\n private dispatchGuaranteedEvents(dispatchedEvents: NetEventInfo[]): void {\n while (\n this.pendingGuaranteedEvents.length > 0 &&\n this.pendingGuaranteedEvents[0].absoluteSequenceNumber ===\n this.nextRecvEventSeq\n ) {\n const queued = this.pendingGuaranteedEvents.shift();\n if (!queued) break;\n this.nextRecvEventSeq = (this.nextRecvEventSeq + 1) >>> 0;\n dispatchedEvents.push(queued.event);\n if (queued.event.parsedData) {\n this.applyEventSideEffects(queued.event.parsedData);\n }\n }\n }\n\n /**\n * Apply event side effects that alter ghost index state.\n * These effects occur before ghostReadPacket on the same packet.\n */\n private applyEventSideEffects(parsedData: ParsedData): void {\n const eventType = parsedData.type;\n\n if (eventType === \"GhostingMessageEvent\") {\n const message = parsedData.message;\n // Tribes 2 handleGhostMessage: EndGhosting clears all local ghosts\n // and invalidates all datablocks (they get re-sent for the new mission).\n if (typeof message === \"number\" && message === GhostMsgEndGhosting) {\n this.ghostTracker.clear();\n this.dataBlockDataMap?.clear();\n }\n return;\n }\n\n if (eventType === \"GhostAlwaysObjectEvent\") {\n const ghostIndex = parsedData.ghostIndex;\n const classId = parsedData.classId;\n if (typeof ghostIndex === \"number\" && typeof classId === \"number\") {\n const parserEntry = this.registry.getGhostParser(classId);\n this.ghostTracker.createGhost(\n ghostIndex,\n classId,\n parserEntry?.name ?? `unknown_${classId}`\n );\n }\n }\n\n // SimDataBlockEvent: store parsed DataBlock data so ghost parsers\n // (e.g., WheeledVehicle wheel count) can look it up by objectId.\n if (eventType === \"SimDataBlockEvent\" && this.dataBlockDataMap) {\n const dbEvent = parsedData as SimDataBlockEventData;\n if (\n dbEvent.dataBlockData &&\n typeof dbEvent.objectId === \"number\"\n ) {\n this.dataBlockDataMap.set(dbEvent.objectId, dbEvent.dataBlockData);\n }\n }\n }\n\n /**\n * Parse ghosts from ghostReadPacket in netGhost.cc.\n */\n private readGhosts(bs: BitStream, seqNumber: number): GhostUpdate[] {\n const ghosts: GhostUpdate[] = [];\n\n if (!bs.readFlag()) {\n return ghosts;\n }\n\n const idSize = bs.readInt(3) + 3;\n\n while (bs.readFlag()) {\n // If the stream ran out of data during the moreFlag read, the\n // readFlag returned false (terminating the loop) with error set.\n // Check here for the case where readFlag returned true but the\n // subsequent header reads exhaust the stream.\n if (bs.isError()) break;\n\n const index = bs.readInt(idSize);\n\n // If the stream ran out of data during header reads, stop processing.\n // Without this check, exhausted reads return 0 (defaulting index to 0,\n // classId to 0) and the ghost parser runs on empty data, producing\n // a \"successful\" parse that consumed 0 bits.\n if (bs.isError()) break;\n\n if (bs.readFlag()) {\n // Ghost is being deleted\n this.ghostTracker.deleteGhost(index);\n this.ghostDeletes++;\n ghosts.push({\n index,\n type: \"delete\",\n updateBitsStart: bs.getCurPos(),\n updateBitsEnd: bs.getCurPos(),\n });\n continue;\n }\n\n const isNew = !this.ghostTracker.hasGhost(index);\n let classId: number | undefined;\n\n if (isNew) {\n classId = bs.readInt(NetObjectClassBitSize) + NetObjectClassFirst;\n } else {\n classId = this.ghostTracker.getGhost(index)?.classId;\n }\n\n const updateBitsStart = bs.getCurPos();\n const parserEntry = classId !== undefined\n ? this.registry.getGhostParser(classId)\n : undefined;\n\n // Detect tracker divergence: we think this is a new ghost (not in our\n // tracker) but the classId is unregistered. This almost certainly means\n // the server sent UPDATE data (no classId prefix) for a ghost our tracker\n // lost, and we misread the first 7 bits of update data as a classId.\n // This is engine-consistent — the real client would also fail here\n // because classId lookup would return NULL, triggering packet drop.\n if (isNew && !parserEntry) {\n this.ghostsTrackerDiverged++;\n debugGhosts(\n \"DIVERGED pkt=%d seq=%d idx=%d classId=%d bit=%d/%d trackerSize=%d \" +\n \"(server sent UPDATE for ghost not in our tracker; 7-bit classId is actually update data)\",\n this.packetsParsed, seqNumber, index, classId,\n updateBitsStart, bs.getMaxPos(), this.ghostTracker.size()\n );\n ghosts.push({\n index,\n type: \"create\",\n classId,\n updateBitsStart,\n updateBitsEnd: updateBitsStart,\n });\n return ghosts;\n }\n\n let parsed = false;\n\n if (parserEntry) {\n try {\n const conn = this.getConnectionContext();\n conn.currentGhostIndex = index;\n const parsedData = parserEntry.unpackUpdate(bs, isNew, conn);\n const endPos = bs.getCurPos();\n\n if (isNew && classId !== undefined) {\n this.ghostTracker.createGhost(index, classId, parserEntry.name);\n this.ghostCreatesParsed++;\n } else {\n this.ghostUpdatesParsed++;\n }\n\n ghosts.push({\n index,\n type: isNew ? \"create\" : \"update\",\n classId,\n updateBitsStart,\n updateBitsEnd: endPos,\n parsedData,\n });\n parsed = true;\n } catch (e) {\n this.ghostsFailed++;\n const op = isNew ? \"create\" : \"update\";\n const message = e instanceof Error ? e.message : String(e);\n debugGhosts(\n \"FAIL pkt=%d seq=%d #%d idx=%d op=%s classId=%d parser=%s bit=%d/%d trackerSize=%d err=%s\",\n this.packetsParsed, seqNumber, ghosts.length, index, op, classId,\n parserEntry.name, updateBitsStart, bs.getMaxPos(),\n this.ghostTracker.size(), message\n );\n }\n }\n\n if (parsed) continue;\n\n debugGhosts(\n \"STOP pkt=%d seq=%d idx=%d op=%s classId=%d parser=%s bit=%d/%d\",\n this.packetsParsed, seqNumber, index, isNew ? \"create\" : \"update\",\n classId, parserEntry?.name ?? \"NONE\", updateBitsStart, bs.getMaxPos()\n );\n\n // Record and stop — can't parse this ghost's data.\n ghosts.push({\n index,\n type: isNew ? \"create\" : \"update\",\n classId,\n updateBitsStart,\n updateBitsEnd: updateBitsStart,\n });\n return ghosts;\n }\n\n return ghosts;\n }\n\n private emptyGameState(): GameState {\n return {\n lastMoveAck: 0,\n pinged: false,\n jammed: false,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PacketParser.js","sourceRoot":"","sources":["../src/PacketParser.ts"],"names":[],"mappings":"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","sourcesContent":["import createDebug from \"debug\";\nimport { BitStream } from \"./BitStream.js\";\nimport {\n DataPacket,\n MaxGhostCount,\n GhostMsgEndGhosting,\n NetEventClassBitSize,\n NetEventClassFirst,\n NetObjectClassBitSize,\n NetObjectClassFirst,\n} from \"./types.js\";\n\nconst debugGhosts = createDebug(\"t2-demo-parser:ghosts\");\nimport type {\n ConnectionProtocolState,\n DnetHeader,\n RateInfo,\n GameState,\n PacketData,\n NetEventInfo,\n GhostUpdate,\n} from \"./types.js\";\nimport type {\n ClassRegistry,\n ConnectionContext,\n GhostParserEntry,\n ParsedData,\n} from \"./ClassRegistry.js\";\nimport type { EventData, SimDataBlockEventData } from \"./eventDataTypes.js\";\nimport type { GhostTracker } from \"./GhostManager.js\";\n\n/**\n * Parses individual network packets from demo recording blocks.\n *\n * Packet layout (from the server connection perspective):\n * 1. dnet header (processRawPacket)\n * 2. Rate info (handlePacket)\n * 3. GameConnection::readPacket — game state, moves, etc.\n * 4. NetConnection::readPacket — events, then ghosts\n */\nexport class PacketParser {\n private registry: ClassRegistry;\n private ghostTracker: GhostTracker;\n private compressionPoint = { x: 0, y: 0, z: 0 };\n private dataBlockDataMap?: Map<number, ParsedData>;\n private lastSeqRecvdAtSend = new Array<number>(32).fill(0);\n private lastSeqRecvd = 0;\n private highestAckedSeq = 0;\n private lastSendSeq = 0;\n private recvAckMask = 0;\n private connectSequence = 0;\n private lastRecvAckAck = 0;\n private _connectionEstablished = false;\n private nextRecvEventSeq = 0;\n private pendingGuaranteedEvents: Array<{\n absoluteSequenceNumber: number;\n event: NetEventInfo;\n }> = [];\n\n // Stats\n controlObjectParsed = 0;\n controlObjectFailed = 0;\n eventsParsed = 0;\n eventsFailed = 0;\n ghostCreatesParsed = 0;\n ghostUpdatesParsed = 0;\n ghostDeletes = 0;\n ghostsFailed = 0;\n /** Ghosts where our tracker says \"new\" but the classId is unregistered.\n * This indicates a tracker divergence: the server sent UPDATE data\n * (no classId prefix) for a ghost we don't have, so we misread the\n * first 7 bits of update data as a classId. */\n ghostsTrackerDiverged = 0;\n packetsParsed = 0;\n protocolRejected = 0;\n protocolNoDispatch = 0;\n\n constructor(\n registry: ClassRegistry,\n ghostTracker: GhostTracker,\n options?: {\n dataBlockDataMap?: Map<number, ParsedData>;\n connectionProtocolState?: ConnectionProtocolState;\n nextRecvEventSeq?: number;\n compressionPoint?: { x: number; y: number; z: number };\n pendingGuaranteedEvents?: Array<{\n absoluteSequenceNumber: number;\n event: NetEventInfo;\n }>;\n },\n ) {\n this.registry = registry;\n this.ghostTracker = ghostTracker;\n this.dataBlockDataMap = options?.dataBlockDataMap;\n if (options?.connectionProtocolState) {\n this.setConnectionProtocolState(options.connectionProtocolState);\n }\n if (typeof options?.nextRecvEventSeq === \"number\") {\n this.nextRecvEventSeq = options.nextRecvEventSeq >>> 0;\n }\n if (options?.compressionPoint) {\n this.compressionPoint = { ...options.compressionPoint };\n }\n if (options?.pendingGuaranteedEvents) {\n this.pendingGuaranteedEvents = options.pendingGuaranteedEvents\n .map((entry) => ({ ...entry }))\n .sort((a, b) => a.absoluteSequenceNumber - b.absoluteSequenceNumber);\n }\n }\n\n getCompressionPoint(): { x: number; y: number; z: number } {\n return this.compressionPoint;\n }\n\n getDataBlockDataMap(): Map<number, ParsedData> | undefined {\n return this.dataBlockDataMap;\n }\n\n private getConnectionContext(): ConnectionContext {\n const dbMap = this.dataBlockDataMap;\n return {\n compressionPoint: this.compressionPoint,\n ghostTracker: this.ghostTracker,\n getDataBlockParser: (classId: number) =>\n this.registry.getDataBlockParser(classId),\n getDataBlockData: dbMap\n ? (objectId: number) => dbMap.get(objectId)\n : undefined,\n getGhostParser: (classId: number) =>\n this.registry.getGhostParser(classId),\n };\n }\n\n private _setNextRecvEventSeq(nextRecvEventSeq: number): void {\n this.nextRecvEventSeq = nextRecvEventSeq >>> 0;\n }\n\n setConnectionProtocolState(state: ConnectionProtocolState): void {\n this.lastSeqRecvdAtSend = state.lastSeqRecvdAtSend.slice(0, 32);\n while (this.lastSeqRecvdAtSend.length < 32) {\n this.lastSeqRecvdAtSend.push(0);\n }\n this.lastSeqRecvd = state.lastSeqRecvd >>> 0;\n this.highestAckedSeq = state.highestAckedSeq >>> 0;\n this.lastSendSeq = state.lastSendSeq >>> 0;\n this.recvAckMask = state.ackMask >>> 0;\n this.connectSequence = state.connectSequence >>> 0;\n this.lastRecvAckAck = state.lastRecvAckAck >>> 0;\n this._connectionEstablished = state.connectionEstablished;\n }\n\n /**\n * Export the current protocol window state. Together with\n * `getNextRecvEventSeq`, `getPendingGuaranteedEvents`,\n * `getCompressionPoint`, `getDataBlockDataMap`, and the ghost tracker\n * contents, this captures all cross-packet parser state, so an\n * identically seeded parser continues the stream in lockstep.\n */\n getConnectionProtocolState(): ConnectionProtocolState {\n return {\n lastSeqRecvdAtSend: this.lastSeqRecvdAtSend.slice(),\n lastSeqRecvd: this.lastSeqRecvd,\n highestAckedSeq: this.highestAckedSeq,\n lastSendSeq: this.lastSendSeq,\n ackMask: this.recvAckMask,\n connectSequence: this.connectSequence,\n lastRecvAckAck: this.lastRecvAckAck,\n connectionEstablished: this._connectionEstablished,\n };\n }\n\n getNextRecvEventSeq(): number {\n return this.nextRecvEventSeq;\n }\n\n getPendingGuaranteedEvents(): Array<{\n absoluteSequenceNumber: number;\n event: NetEventInfo;\n }> {\n return this.pendingGuaranteedEvents.map((entry) => ({ ...entry }));\n }\n\n /**\n * Emulate ConnectionProtocol::buildSendPacketHeader (FUN_0043d2d0, data path).\n * Demo block type 1 indicates a local send-packet trigger.\n */\n onSendPacketTrigger(): void {\n this.lastSendSeq = (this.lastSendSeq + 1) >>> 0;\n this.lastSeqRecvdAtSend[this.lastSendSeq & 0x1f] = this.lastSeqRecvd >>> 0;\n }\n\n /**\n * Emulates ConnectionProtocol::processRawPacket (FUN_0043d4d0).\n * A packet may be syntactically valid but still ignored for game payload\n * dispatch due to sequence/connect-window checks or duplicate seq numbers.\n */\n private applyProtocolHeader(dnetHeader: DnetHeader): {\n accepted: boolean;\n dispatchData: boolean;\n } {\n if (dnetHeader.connectSeqBit !== (this.connectSequence & 1)) {\n return { accepted: false, dispatchData: false };\n }\n if (dnetHeader.ackByteCount > 4 || dnetHeader.packetType > 2) {\n return { accepted: false, dispatchData: false };\n }\n\n let seqNumber =\n (dnetHeader.seqNumber | (this.lastSeqRecvd & 0xffff_fe00)) >>> 0;\n if (seqNumber < this.lastSeqRecvd) {\n seqNumber = (seqNumber + 0x200) >>> 0;\n }\n if (this.lastSeqRecvd + 0x1f < seqNumber) {\n return { accepted: false, dispatchData: false };\n }\n\n let highestAck =\n (dnetHeader.highestAck | (this.highestAckedSeq & 0xffff_fe00)) >>> 0;\n if (highestAck < this.highestAckedSeq) {\n highestAck = (highestAck + 0x200) >>> 0;\n }\n if (this.lastSendSeq < highestAck) {\n return { accepted: false, dispatchData: false };\n }\n\n const seqShift = (seqNumber - this.lastSeqRecvd) & 0x1f;\n this.recvAckMask = (this.recvAckMask << seqShift) >>> 0;\n if (dnetHeader.packetType === DataPacket) {\n this.recvAckMask = (this.recvAckMask | 1) >>> 0;\n }\n\n for (\n let ackSeq = this.highestAckedSeq + 1;\n ackSeq <= highestAck;\n ackSeq++\n ) {\n const isAcked =\n (dnetHeader.ackMask & (1 << ((highestAck - ackSeq) & 0x1f))) !== 0;\n if (isAcked) {\n this.lastRecvAckAck = this.lastSeqRecvdAtSend[ackSeq & 0x1f] >>> 0;\n }\n }\n if (seqNumber - this.lastRecvAckAck > 0x20) {\n this.lastRecvAckAck = seqNumber - 0x20;\n }\n this.highestAckedSeq = highestAck;\n\n const dispatchData =\n this.lastSeqRecvd !== seqNumber && dnetHeader.packetType === DataPacket;\n this.lastSeqRecvd = seqNumber;\n\n return { accepted: true, dispatchData };\n }\n\n parsePacket(data: Uint8Array): PacketData {\n const bs = new BitStream(data);\n\n // 1. Parse dnet header\n const dnetHeader = this.readDnetHeader(bs);\n const protocol = this.applyProtocolHeader(dnetHeader);\n\n this.packetsParsed++;\n\n // ConnectionProtocol may reject this packet entirely (window/connect checks),\n // or accept it but suppress game dispatch (duplicate seq / non-data packet).\n if (!protocol.accepted) {\n this.protocolRejected++;\n return {\n dnetHeader,\n rateInfo: {},\n gameState: this.emptyGameState(),\n events: [],\n ghosts: [],\n };\n }\n if (!protocol.dispatchData) {\n this.protocolNoDispatch++;\n return {\n dnetHeader,\n rateInfo: {},\n gameState: this.emptyGameState(),\n events: [],\n ghosts: [],\n };\n }\n\n // 2. Rate info (NetConnection::handlePacket)\n const rateInfo = this.readRateInfo(bs);\n\n // 3. Game state (GameConnection::readPacket — server connection path)\n bs.setStringBuffer(true);\n const gameState = this.readGameState(bs);\n\n // Only read events/ghosts if game state completed successfully.\n // When game state bails (control object dirty with no parser), the\n // stream position is wrong and we'd read garbage data.\n const gameStateComplete =\n gameState.controlObjectDataStart === undefined ||\n gameState.controlObjectData !== undefined;\n\n // 4. Events (NetConnection::readPacket -> eventReadPacket)\n const events = gameStateComplete ? this.readEvents(bs) : [];\n\n // Only read ghosts if all events were successfully parsed.\n // When an event can't be parsed, readEvents returns early and the\n // stream position is wrong — reading ghosts would produce garbage.\n const lastEvent = events[events.length - 1];\n const eventsComplete = !lastEvent?.failed;\n\n // 5. Ghosts (NetConnection::readPacket -> ghostReadPacket)\n const ghostSectionStart =\n gameStateComplete && eventsComplete ? bs.getCurPos() : undefined;\n const ghosts =\n gameStateComplete && eventsComplete\n ? this.readGhosts(bs, dnetHeader.seqNumber)\n : [];\n\n bs.setStringBuffer(false);\n\n return {\n dnetHeader,\n rateInfo,\n gameState,\n events,\n ghosts,\n ghostSectionStart,\n };\n }\n\n /**\n * Parse the dnet packet header (from processRawPacket in dnet.cc).\n */\n private readDnetHeader(bs: BitStream): DnetHeader {\n const gameFlag = bs.readFlag();\n const connectSeqBit = bs.readInt(1);\n const seqNumber = bs.readInt(9);\n const highestAck = bs.readInt(9);\n const packetType = bs.readInt(2);\n const ackByteCount = bs.readInt(3);\n const ackMask = ackByteCount > 0 ? bs.readInt(8 * ackByteCount) : 0;\n\n return {\n gameFlag,\n connectSeqBit,\n seqNumber,\n highestAck,\n packetType,\n ackByteCount,\n ackMask,\n };\n }\n\n /**\n * Parse rate info from handlePacket in netConnection.cc.\n */\n private readRateInfo(bs: BitStream): RateInfo {\n const info: RateInfo = {};\n if (bs.readFlag()) {\n info.updateDelay = bs.readInt(10);\n info.packetSize = bs.readInt(10);\n }\n if (bs.readFlag()) {\n info.maxUpdateDelay = bs.readInt(10);\n info.maxPacketSize = bs.readInt(10);\n }\n return info;\n }\n\n /**\n * Parse game state from GameConnection::readPacket (server connection path).\n */\n private readGameState(bs: BitStream): GameState {\n const lastMoveAck = bs.readInt(32);\n\n // Damage flash / whiteout\n let damageFlash: number | undefined;\n let whiteOut: number | undefined;\n if (bs.readFlag()) {\n if (bs.readFlag()) {\n damageFlash = bs.readFloat(7);\n }\n if (bs.readFlag()) {\n whiteOut = bs.readFloat(7) * 1.5;\n }\n }\n\n // Lock/homing state\n let selfLocked: boolean | undefined;\n let selfHomed: boolean | undefined;\n if (bs.readFlag()) {\n selfLocked = bs.readFlag();\n selfHomed = bs.readFlag();\n }\n\n // Seeker tracking state\n let seekerTracking: boolean | undefined;\n let seekerTrackingPos: { x: number; y: number; z: number } | undefined;\n let seekerMode: number | undefined;\n let seekerObjectGhostIndex: number | undefined;\n let targetPos: { x: number; y: number; z: number } | undefined;\n if (bs.readFlag()) {\n seekerTracking = bs.readFlag();\n // Binary 0x005fc020: when seekerTracking is true, reads 3×F32 (Point3F)\n // for the seeker target position (stored at connection+0x8454)\n if (seekerTracking) {\n seekerTrackingPos = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n }\n seekerMode = bs.readRangedU32(0, 2);\n if (seekerMode === 1) {\n if (bs.readFlag()) {\n seekerObjectGhostIndex = bs.readRangedU32(0, MaxGhostCount - 1);\n }\n } else if (seekerMode === 2) {\n targetPos = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n }\n }\n\n // Sensor ping/jam flags\n const pinged = bs.readFlag();\n const jammed = bs.readFlag();\n\n // Control object state\n let controlObjectGhostIndex: number | undefined;\n let controlObjectDataStart: number | undefined;\n let controlObjectDataEnd: number | undefined;\n let controlObjectData: ParsedData | undefined;\n let compressionPoint: { x: number; y: number; z: number } | undefined;\n\n if (bs.readFlag()) {\n if (bs.readFlag()) {\n // Control object is dirty — full update via readPacketData.\n // In Tribes 2 (build 25034), only Player and Camera override\n // writePacketData/readPacketData, so the control object is\n // always one of these two classes. We try candidates in order:\n // 1. Tracker classId (may be stale due to ghost index recycling)\n // 2. Player (classId 25) — the normal control object\n // 3. Camera (classId 4) — spectator mode\n // The candidate order must be a pure function of seeded state\n // (the tracker) so that a parser seeded mid-stream makes the same\n // choice as one that has followed the stream from the start —\n // no history-derived caches here.\n const gIndex = bs.readInt(10);\n controlObjectGhostIndex = gIndex;\n controlObjectDataStart = bs.getCurPos();\n const start = bs.savePos();\n const ghost = this.ghostTracker.getGhost(gIndex);\n\n const preferredEntry = ghost\n ? this.registry.getGhostParser(ghost.classId)\n : undefined;\n const playerEntry = this.registry.getGhostParser(25); // Player\n const cameraEntry = this.registry.getGhostParser(4); // Camera\n\n // Build candidate list (deduplicated, only those with readPacketData)\n const candidates: GhostParserEntry[] = [];\n const seen = new Set<string>();\n const addCandidate = (entry: GhostParserEntry | undefined): void => {\n if (!entry?.readPacketData) return;\n if (seen.has(entry.name)) return;\n seen.add(entry.name);\n candidates.push(entry);\n };\n addCandidate(preferredEntry);\n addCandidate(playerEntry);\n addCandidate(cameraEntry);\n\n let parsed = false;\n for (const entry of candidates) {\n bs.restorePos(start);\n try {\n const conn = this.getConnectionContext();\n const data = entry.readPacketData!(bs, conn);\n const bitsConsumed = bs.getCurPos() - controlObjectDataStart!;\n if (bitsConsumed <= 0 || bs.isError()) {\n continue;\n }\n\n controlObjectData = data;\n controlObjectDataEnd = bs.getCurPos();\n if (conn.compressionPoint !== this.compressionPoint) {\n this.compressionPoint = conn.compressionPoint;\n compressionPoint = this.compressionPoint;\n }\n this.controlObjectParsed++;\n parsed = true;\n break;\n } catch {\n // Try next candidate.\n }\n }\n\n if (!parsed) {\n bs.restorePos(start);\n // No parser — bail from game state\n controlObjectDataEnd = controlObjectDataStart;\n this.controlObjectFailed++;\n return {\n lastMoveAck,\n damageFlash,\n whiteOut,\n selfLocked,\n selfHomed,\n seekerTracking,\n seekerTrackingPos,\n seekerMode,\n seekerObjectGhostIndex,\n targetPos,\n pinged,\n jammed,\n controlObjectGhostIndex,\n controlObjectDataStart,\n controlObjectDataEnd,\n controlObjectData,\n targetVisibility: [],\n };\n }\n } else {\n // Compression point update — always accept (engine doesn't validate)\n compressionPoint = {\n x: bs.readF32(),\n y: bs.readF32(),\n z: bs.readF32(),\n };\n this.compressionPoint = compressionPoint;\n }\n }\n\n // Target visibility masks\n const targetVisibility: { index: number; mask: number }[] = [];\n while (bs.readFlag()) {\n targetVisibility.push({\n index: bs.readInt(4),\n mask: bs.readInt(32),\n });\n }\n\n // Camera FOV\n let cameraFov: number | undefined;\n if (bs.readFlag()) {\n cameraFov = bs.readInt(8);\n }\n\n return {\n lastMoveAck,\n damageFlash,\n whiteOut,\n selfLocked,\n selfHomed,\n seekerTracking,\n seekerTrackingPos,\n seekerMode,\n seekerObjectGhostIndex,\n targetPos,\n pinged,\n jammed,\n controlObjectGhostIndex,\n controlObjectDataStart,\n controlObjectDataEnd,\n controlObjectData,\n compressionPoint,\n targetVisibility:\n targetVisibility.length > 0 ? targetVisibility : undefined,\n cameraFov,\n };\n }\n\n /**\n * Parse events from eventReadPacket in netEvent.cc.\n *\n * Two phases:\n * 1. Unguaranteed events: while(readFlag) { classId(6b) + unpack }\n * 2. Guaranteed events: while(readFlag) { seqFlag(1b), [seq(7b)], classId(6b) + unpack }\n */\n private readEvents(bs: BitStream): NetEventInfo[] {\n const dispatchedEvents: NetEventInfo[] = [];\n let unguaranteedPhase = true;\n let prevGuaranteedSeq = -2;\n\n while (true) {\n const bit = bs.readFlag();\n if (unguaranteedPhase && !bit) {\n // Transition to guaranteed phase. Binary immediately reads one more\n // continuation bit for that phase.\n unguaranteedPhase = false;\n const guaranteedBit = bs.readFlag();\n if (!guaranteedBit) {\n this.dispatchGuaranteedEvents(dispatchedEvents);\n break;\n }\n } else if (!unguaranteedPhase && !bit) {\n this.dispatchGuaranteedEvents(dispatchedEvents);\n break;\n } else if (!bit) {\n break;\n }\n\n let sequenceNumber: number | undefined;\n let absoluteSequenceNumber: number | undefined;\n if (!unguaranteedPhase) {\n if (bs.readFlag()) {\n sequenceNumber = (prevGuaranteedSeq + 1) & 0x7f;\n } else {\n sequenceNumber = bs.readInt(7);\n }\n prevGuaranteedSeq = sequenceNumber;\n absoluteSequenceNumber =\n sequenceNumber | (this.nextRecvEventSeq & 0xffffff80);\n if (absoluteSequenceNumber < this.nextRecvEventSeq) {\n absoluteSequenceNumber += 0x80;\n }\n }\n\n const classId = bs.readInt(NetEventClassBitSize) + NetEventClassFirst;\n const dataBitsStart = bs.getCurPos();\n\n const parserEntry = this.registry.getEventParser(classId);\n let parsedData: ParsedData | undefined;\n\n if (parserEntry) {\n try {\n const conn = this.getConnectionContext();\n parsedData = parserEntry.unpack(bs, conn);\n this.eventsParsed++;\n } catch {\n this.eventsFailed++;\n dispatchedEvents.push({\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: dataBitsStart,\n failed: true,\n });\n return dispatchedEvents;\n }\n } else {\n // No parser — cannot advance safely.\n dispatchedEvents.push({\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: dataBitsStart,\n failed: true,\n });\n return dispatchedEvents;\n }\n\n const event: NetEventInfo = {\n classId,\n guaranteed: !unguaranteedPhase,\n sequenceNumber,\n absoluteSequenceNumber,\n dataBitsStart,\n dataBitsEnd: bs.getCurPos(),\n parsedData,\n };\n\n if (unguaranteedPhase) {\n dispatchedEvents.push(event);\n if (parsedData) {\n this.applyEventSideEffects(parsedData);\n }\n } else if (absoluteSequenceNumber !== undefined) {\n this.enqueueGuaranteedEvent(absoluteSequenceNumber, event);\n }\n }\n\n return dispatchedEvents;\n }\n\n private enqueueGuaranteedEvent(\n absoluteSequenceNumber: number,\n event: NetEventInfo,\n ): void {\n let insertAt = 0;\n while (\n insertAt < this.pendingGuaranteedEvents.length &&\n this.pendingGuaranteedEvents[insertAt].absoluteSequenceNumber <\n absoluteSequenceNumber\n ) {\n insertAt++;\n }\n this.pendingGuaranteedEvents.splice(insertAt, 0, {\n absoluteSequenceNumber,\n event,\n });\n }\n\n private dispatchGuaranteedEvents(dispatchedEvents: NetEventInfo[]): void {\n while (\n this.pendingGuaranteedEvents.length > 0 &&\n this.pendingGuaranteedEvents[0].absoluteSequenceNumber ===\n this.nextRecvEventSeq\n ) {\n const queued = this.pendingGuaranteedEvents.shift();\n if (!queued) break;\n this.nextRecvEventSeq = (this.nextRecvEventSeq + 1) >>> 0;\n dispatchedEvents.push(queued.event);\n if (queued.event.parsedData) {\n this.applyEventSideEffects(queued.event.parsedData);\n }\n }\n }\n\n /**\n * Apply event side effects that alter ghost index state.\n * These effects occur before ghostReadPacket on the same packet.\n */\n private applyEventSideEffects(parsedData: ParsedData): void {\n const eventType = parsedData.type;\n\n if (eventType === \"GhostingMessageEvent\") {\n const message = parsedData.message;\n // Tribes 2 handleGhostMessage EndGhosting (netGhost.cc:706) deletes\n // ONLY local ghosts. Datablocks are connection-lifetime state and\n // must be retained: the server's transmitDataBlocks skips datablocks\n // already sent on this connection (gameConnection.cc\n // cTransmitDataBlocks: only modifiedKey > mDataBlockModifiedKey is\n // re-sent), so on a mission change most datablocks are NOT re-sent\n // and clearing the map here leaves the new mission's ghosts\n // unresolvable.\n if (typeof message === \"number\" && message === GhostMsgEndGhosting) {\n this.ghostTracker.clear();\n }\n return;\n }\n\n if (eventType === \"GhostAlwaysObjectEvent\") {\n const ghostIndex = parsedData.ghostIndex;\n const classId = parsedData.classId;\n if (typeof ghostIndex === \"number\" && typeof classId === \"number\") {\n const parserEntry = this.registry.getGhostParser(classId);\n this.ghostTracker.createGhost(\n ghostIndex,\n classId,\n parserEntry?.name ?? `unknown_${classId}`,\n );\n }\n }\n\n // SimDataBlockEvent: accumulate parsed DataBlock data by objectId\n // (exported for seeding and consumed by downstream renderers).\n if (eventType === \"SimDataBlockEvent\" && this.dataBlockDataMap) {\n const dbEvent = parsedData as SimDataBlockEventData;\n if (dbEvent.dataBlockData && typeof dbEvent.objectId === \"number\") {\n this.dataBlockDataMap.set(dbEvent.objectId, dbEvent.dataBlockData);\n }\n }\n }\n\n /**\n * Parse ghosts from ghostReadPacket in netGhost.cc.\n */\n private readGhosts(bs: BitStream, seqNumber: number): GhostUpdate[] {\n const ghosts: GhostUpdate[] = [];\n\n if (!bs.readFlag()) {\n return ghosts;\n }\n\n const idSize = bs.readInt(3) + 3;\n\n while (bs.readFlag()) {\n // If the stream ran out of data during the moreFlag read, the\n // readFlag returned false (terminating the loop) with error set.\n // Check here for the case where readFlag returned true but the\n // subsequent header reads exhaust the stream.\n if (bs.isError()) break;\n\n const index = bs.readInt(idSize);\n\n // If the stream ran out of data during header reads, stop processing.\n // Without this check, exhausted reads return 0 (defaulting index to 0,\n // classId to 0) and the ghost parser runs on empty data, producing\n // a \"successful\" parse that consumed 0 bits.\n if (bs.isError()) break;\n\n if (bs.readFlag()) {\n // Ghost is being deleted\n this.ghostTracker.deleteGhost(index);\n this.ghostDeletes++;\n ghosts.push({\n index,\n type: \"delete\",\n updateBitsStart: bs.getCurPos(),\n updateBitsEnd: bs.getCurPos(),\n });\n continue;\n }\n\n const isNew = !this.ghostTracker.hasGhost(index);\n let classId: number | undefined;\n\n if (isNew) {\n classId = bs.readInt(NetObjectClassBitSize) + NetObjectClassFirst;\n } else {\n classId = this.ghostTracker.getGhost(index)?.classId;\n }\n\n const updateBitsStart = bs.getCurPos();\n const parserEntry =\n classId !== undefined\n ? this.registry.getGhostParser(classId)\n : undefined;\n\n // Detect tracker divergence: we think this is a new ghost (not in our\n // tracker) but the classId is unregistered. This almost certainly means\n // the server sent UPDATE data (no classId prefix) for a ghost our tracker\n // lost, and we misread the first 7 bits of update data as a classId.\n // This is engine-consistent — the real client would also fail here\n // because classId lookup would return NULL, triggering packet drop.\n if (isNew && !parserEntry) {\n this.ghostsTrackerDiverged++;\n debugGhosts(\n \"DIVERGED pkt=%d seq=%d idx=%d classId=%d bit=%d/%d trackerSize=%d \" +\n \"(server sent UPDATE for ghost not in our tracker; 7-bit classId is actually update data)\",\n this.packetsParsed,\n seqNumber,\n index,\n classId,\n updateBitsStart,\n bs.getMaxPos(),\n this.ghostTracker.size(),\n );\n ghosts.push({\n index,\n type: \"create\",\n classId,\n updateBitsStart,\n updateBitsEnd: updateBitsStart,\n });\n return ghosts;\n }\n\n let parsed = false;\n\n if (parserEntry) {\n try {\n const conn = this.getConnectionContext();\n conn.currentGhostIndex = index;\n const parsedData = parserEntry.unpackUpdate(bs, isNew, conn);\n const endPos = bs.getCurPos();\n\n if (isNew && classId !== undefined) {\n this.ghostTracker.createGhost(index, classId, parserEntry.name);\n this.ghostCreatesParsed++;\n } else {\n this.ghostUpdatesParsed++;\n }\n\n ghosts.push({\n index,\n type: isNew ? \"create\" : \"update\",\n classId,\n updateBitsStart,\n updateBitsEnd: endPos,\n parsedData,\n });\n parsed = true;\n } catch (e) {\n this.ghostsFailed++;\n const op = isNew ? \"create\" : \"update\";\n const message = e instanceof Error ? e.message : String(e);\n debugGhosts(\n \"FAIL pkt=%d seq=%d #%d idx=%d op=%s classId=%d parser=%s bit=%d/%d trackerSize=%d err=%s\",\n this.packetsParsed,\n seqNumber,\n ghosts.length,\n index,\n op,\n classId,\n parserEntry.name,\n updateBitsStart,\n bs.getMaxPos(),\n this.ghostTracker.size(),\n message,\n );\n }\n }\n\n if (parsed) continue;\n\n debugGhosts(\n \"STOP pkt=%d seq=%d idx=%d op=%s classId=%d parser=%s bit=%d/%d\",\n this.packetsParsed,\n seqNumber,\n index,\n isNew ? \"create\" : \"update\",\n classId,\n parserEntry?.name ?? \"NONE\",\n updateBitsStart,\n bs.getMaxPos(),\n );\n\n // Record and stop — can't parse this ghost's data.\n ghosts.push({\n index,\n type: isNew ? \"create\" : \"update\",\n classId,\n updateBitsStart,\n updateBitsEnd: updateBitsStart,\n });\n return ghosts;\n }\n\n return ghosts;\n }\n\n private emptyGameState(): GameState {\n return {\n lastMoveAck: 0,\n pinged: false,\n jammed: false,\n };\n }\n}\n"]}
|
|
@@ -8,8 +8,17 @@ export interface HudImageEntry {
|
|
|
8
8
|
renderDistance: boolean;
|
|
9
9
|
renderName: boolean;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* One ShapeBaseImageData state. Transition values are 1-based state
|
|
13
|
+
* indices (0 = no transition), in the engine's own packing order —
|
|
14
|
+
* binary-verified; earlier versions had the names rotated by two slots
|
|
15
|
+
* (no NotLoaded/Loaded, phantom generic0 pair) and consumers carried a
|
|
16
|
+
* remap table to compensate.
|
|
17
|
+
*/
|
|
11
18
|
export interface ImageState {
|
|
12
19
|
name: string;
|
|
20
|
+
transitionOnNotLoaded: number;
|
|
21
|
+
transitionOnLoaded: number;
|
|
13
22
|
transitionOnAmmo: number;
|
|
14
23
|
transitionOnNoAmmo: number;
|
|
15
24
|
transitionOnTarget: number;
|
|
@@ -19,8 +28,6 @@ export interface ImageState {
|
|
|
19
28
|
transitionOnTriggerUp: number;
|
|
20
29
|
transitionOnTriggerDown: number;
|
|
21
30
|
transitionOnTimeout: number;
|
|
22
|
-
transitionGeneric0In: number;
|
|
23
|
-
transitionGeneric0Out: number;
|
|
24
31
|
timeoutValue?: number;
|
|
25
32
|
waitForTimeout: boolean;
|
|
26
33
|
fire: boolean;
|
|
@@ -451,29 +458,39 @@ export interface ShockLanceProjectileDataBlock extends ProjectileDataBlock {
|
|
|
451
458
|
textures?: string[];
|
|
452
459
|
emitter?: number | null;
|
|
453
460
|
}
|
|
461
|
+
/**
|
|
462
|
+
* Field names binary-verified against Tribes2.exe (build 25034):
|
|
463
|
+
* initPersistFields FUN_0064a860 / unpackData FUN_0064ae00. The ELF
|
|
464
|
+
* gun: a wavy main beam with lightning arcs around it.
|
|
465
|
+
*/
|
|
454
466
|
export interface ELFProjectileDataBlock extends ProjectileDataBlock {
|
|
455
467
|
beamRange?: number;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
hitEmitter?: number | null;
|
|
468
|
+
mainBeamWidth?: number;
|
|
469
|
+
mainBeamSpeed?: number;
|
|
470
|
+
mainBeamRepeat?: number;
|
|
471
|
+
lightningWidth?: number;
|
|
472
|
+
lightningDist?: number;
|
|
473
|
+
/** textures[0..2] — retail: ELFBeam, ELFLightning, BlueImpact. */
|
|
474
|
+
textures?: string[];
|
|
475
|
+
emitter?: number | null;
|
|
465
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Field names binary-verified against Tribes2.exe (build 25034):
|
|
479
|
+
* initPersistFields FUN_00644910 / unpackData FUN_00644c40. The repair
|
|
480
|
+
* beam: a segmented ribbon (numSegments) with a blur trail.
|
|
481
|
+
*/
|
|
466
482
|
export interface RepairProjectileDataBlock extends ProjectileDataBlock {
|
|
467
483
|
beamRange?: number;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
484
|
+
beamWidth?: number;
|
|
485
|
+
/** Engine S32 (the old decode read its raw bits as a float). */
|
|
486
|
+
numSegments?: number;
|
|
487
|
+
beamSpeed?: number;
|
|
488
|
+
texRepeat?: number;
|
|
489
|
+
blurFreq?: number;
|
|
490
|
+
blurLifetime?: number;
|
|
491
|
+
cutoffAngle?: number;
|
|
492
|
+
/** textures[0..1] — retail: special/redbump2, special/redflare. */
|
|
493
|
+
textures?: string[];
|
|
477
494
|
}
|
|
478
495
|
export interface TargetProjectileDataBlock extends ProjectileDataBlock {
|
|
479
496
|
maxRifleRange?: number;
|
|
@@ -505,16 +522,24 @@ export interface TracerProjectileDataBlock extends LinearProjectileDataBlock {
|
|
|
505
522
|
tracerTex0?: string;
|
|
506
523
|
tracerTex1?: string;
|
|
507
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Field names binary-verified against Tribes2.exe (build 25034):
|
|
527
|
+
* initPersistFields FUN_00694b40 / unpackData FUN_00694d80. The blaster
|
|
528
|
+
* bolt: an oriented additive quad (texture0) with an edge-on cross
|
|
529
|
+
* (texture1) and an untextured motion-blur tail (blur* fields).
|
|
530
|
+
*/
|
|
508
531
|
export interface EnergyProjectileDataBlock extends GrenadeProjectileDataBlock {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
532
|
+
/** Cross fades in when the view angle cosine exceeds this (0x168). */
|
|
533
|
+
crossViewAng?: number;
|
|
534
|
+
crossSize?: number;
|
|
535
|
+
blurLifetime?: number;
|
|
536
|
+
blurWidth?: number;
|
|
537
|
+
/** Blur tail color; only RGB is transmitted (alpha defaults to 1). */
|
|
538
|
+
blurColor?: Color4;
|
|
539
|
+
/** texture[0] — the bolt quad (e.g. "special/blasterBolt"). */
|
|
540
|
+
texture0?: string;
|
|
541
|
+
/** texture[1] — the edge-on cross (e.g. "special/blasterBoltCross"). */
|
|
542
|
+
texture1?: string;
|
|
518
543
|
}
|
|
519
544
|
export interface LinearFlareProjectileDataBlock extends LinearProjectileDataBlock {
|
|
520
545
|
numFlares?: number;
|