t2-demo-parser 5.0.1 → 5.0.2

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 CHANGED
@@ -502,21 +502,21 @@ interface ControlObjectKeyframe {
502
502
 
503
503
  Available via `parser.getPacketParser()`. Exposes parse statistics.
504
504
 
505
- | Property | Type | Description |
506
- | ----------------------- | -------- | --------------------------------------------------------------- |
507
- | `packetsParsed` | `number` | Total packets successfully parsed. |
508
- | `ghostCreatesParsed` | `number` | Ghost create operations parsed. |
509
- | `ghostUpdatesParsed` | `number` | Ghost update operations parsed. |
510
- | `ghostDeletes` | `number` | Ghost delete operations. |
511
- | `ghostsFailed` | `number` | Ghost operations that failed to parse. |
512
- | `ghostsTrackerDiverged` | `number` | Ghost tracker inconsistencies detected. |
513
- | `eventsParsed` | `number` | Events parsed. |
514
- | `eventsFailed` | `number` | Events that failed to parse. |
515
- | `controlObjectParsed` | `number` | Control object updates parsed. |
516
- | `controlObjectFailed` | `number` | Control object updates that failed. |
517
- | `protocolRejected` | `number` | Packets rejected by the dnet protocol window. |
518
- | `protocolNoDispatch` | `number` | Packets accepted but not dispatched (duplicates/out-of-window). |
519
- | `packetsDroppedAfterFault` | `number` | Packets returned empty because the parser had halted. |
505
+ | Property | Type | Description |
506
+ | -------------------------- | -------- | --------------------------------------------------------------- |
507
+ | `packetsParsed` | `number` | Total packets successfully parsed. |
508
+ | `ghostCreatesParsed` | `number` | Ghost create operations parsed. |
509
+ | `ghostUpdatesParsed` | `number` | Ghost update operations parsed. |
510
+ | `ghostDeletes` | `number` | Ghost delete operations. |
511
+ | `ghostsFailed` | `number` | Ghost operations that failed to parse. |
512
+ | `ghostsTrackerDiverged` | `number` | Ghost tracker inconsistencies detected. |
513
+ | `eventsParsed` | `number` | Events parsed. |
514
+ | `eventsFailed` | `number` | Events that failed to parse. |
515
+ | `controlObjectParsed` | `number` | Control object updates parsed. |
516
+ | `controlObjectFailed` | `number` | Control object updates that failed. |
517
+ | `protocolRejected` | `number` | Packets rejected by the dnet protocol window. |
518
+ | `protocolNoDispatch` | `number` | Packets accepted but not dispatched (duplicates/out-of-window). |
519
+ | `packetsDroppedAfterFault` | `number` | Packets returned empty because the parser had halted. |
520
520
 
521
521
  #### Faults and halting
522
522
 
@@ -527,10 +527,10 @@ parser **halts** — every later `parsePacket()` returns an empty `PacketData`
527
527
  carrying the same fault and touches no state, because it would otherwise be
528
528
  parsing against ghost and event state that no longer mirrors the server.
529
529
 
530
- | Member | Description |
531
- | ----------- | ------------------------------------------------------------------- |
532
- | `fault` | The first `ParseFault`, or `undefined`. |
533
- | `faulted` | `true` once a fault has been recorded. |
530
+ | Member | Description |
531
+ | --------- | --------------------------------------- |
532
+ | `fault` | The first `ParseFault`, or `undefined`. |
533
+ | `faulted` | `true` once a fault has been recorded. |
534
534
 
535
535
  Pass `haltOnFault: false` (to `new PacketParser`, `new DemoParser`, or
536
536
  `createLiveParser`) to keep parsing regardless; each later packet still
@@ -565,7 +565,9 @@ parsing packets from a live Tribes 2 connection (e.g. via a network proxy).
565
565
  ```typescript
566
566
  import { createLiveParser } from "t2-demo-parser";
567
567
 
568
- const { registry, ghostTracker, packetParser } = createLiveParser();
568
+ const { registry, ghostTracker, packetParser } = createLiveParser({
569
+ protocolVersion: 51, // version negotiated by the connection handshake
570
+ });
569
571
  // Feed raw packet data through packetParser...
570
572
  ```
571
573
 
@@ -584,6 +586,7 @@ with the exporter (the late-joiner catch-up scenario — see
584
586
 
585
587
  ```typescript
586
588
  interface LiveParserSeed {
589
+ protocolVersion?: number | null; // negotiated wire version; null/omitted = unknown
587
590
  dataBlocks?: Iterable<[number, ParsedData]>; // objectId → parsed datablock
588
591
  ghosts?: Iterable<{ index: number; classId: number }>;
589
592
  connectionProtocolState?: ConnectionProtocolState;
@@ -597,6 +600,19 @@ interface LiveParserSeed {
597
600
  }
598
601
  ```
599
602
 
603
+ Pass the **negotiated connection version**, not the server's maximum version:
604
+ 51 selects retail terrain payloads; 52 selects the QoL terrain extension.
605
+ Export it with `packetParser.getProtocolVersion()` when seeding another parser.
606
+ It is separate from `connectionProtocolState`, which holds packet sequence windows.
607
+
608
+ Recordings do not preserve this version: even QoL recordings use the
609
+ `0x330004` header and retail saved-ghost layout. With no known version, the
610
+ parser recognizes an eight-slot terrain material string using non-consuming
611
+ lookahead within the current packet. This fallback is necessarily heuristic;
612
+ it never learns a global format from one map. If the recording's wire version
613
+ is known externally, use `new DemoParser(bytes, { packetProtocolVersion: 52 })`.
614
+ Saved initial ghosts still use retail layout, including with that override.
615
+
600
616
  ### `passiveObserverProtocolState(firstPacketByte): ConnectionProtocolState`
601
617
 
602
618
  Protocol state for a parser that passively observes the server→client
@@ -689,7 +705,11 @@ as the engine's `writeString`). `finish()` returns the packed `Uint8Array`.
689
705
  ```typescript
690
706
  import { BitWriter, BitStream } from "t2-demo-parser";
691
707
 
692
- const bytes = new BitWriter().writeFlag(true).writeInt(5, 4).writeString("hi").finish();
708
+ const bytes = new BitWriter()
709
+ .writeFlag(true)
710
+ .writeInt(5, 4)
711
+ .writeString("hi")
712
+ .finish();
693
713
  const bs = new BitStream(bytes); // readFlag() → true, readInt(4) → 5, readString() → "hi"
694
714
  ```
695
715
 
@@ -699,12 +719,12 @@ const bs = new BitStream(bytes); // readFlag() → true, readInt(4) → 5, readS
699
719
  to its deterministic classId — the same setup `DemoParser` and
700
720
  `createLiveParser` use. Lookups go both ways:
701
721
 
702
- | Method | Returns |
703
- | --------------------------------------- | -------------------------------- |
704
- | `getGhostParser(classId)` etc. | Parser entry for a bound classId |
705
- | `getGhostClassId(name)` etc. | Bound classId for a class name |
706
- | `getGhostCatalog()` etc. | `ReadonlyMap<name, entry>` |
707
- | `getGhostBindings()` etc. | `Map<classId, name>` (debug) |
722
+ | Method | Returns |
723
+ | ------------------------------ | -------------------------------- |
724
+ | `getGhostParser(classId)` etc. | Parser entry for a bound classId |
725
+ | `getGhostClassId(name)` etc. | Bound classId for a class name |
726
+ | `getGhostCatalog()` etc. | `ReadonlyMap<name, entry>` |
727
+ | `getGhostBindings()` etc. | `Map<classId, name>` (debug) |
708
728
 
709
729
  The `Event` and `DataBlock` variants of each method exist too.
710
730
 
@@ -11,6 +11,8 @@ export declare class BitStream {
11
11
  private error;
12
12
  private stringBuffer;
13
13
  constructor(data: Uint8Array, bitOffset?: number);
14
+ /** Independent lookahead, including string-prefix and error state. */
15
+ fork(): BitStream;
14
16
  getCurPos(): number;
15
17
  setCurPos(pos: number): void;
16
18
  /** Byte position (rounded up), matching C++ getPosition() */
@@ -1 +1 @@
1
- {"version":3,"file":"BitStream.d.ts","sourceRoot":"","sources":["../src/BitStream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BitStream.d.ts","sourceRoot":"","sources":["../src/BitStream.ts"],"names":[],"mappings":"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"}
package/dist/BitStream.js CHANGED
@@ -17,6 +17,14 @@ export class BitStream {
17
17
  this.maxReadBitNum = data.length << 3;
18
18
  this.error = false;
19
19
  }
20
+ /** Independent lookahead, including string-prefix and error state. */
21
+ fork() {
22
+ const copy = new BitStream(this.data, this.bitNum);
23
+ copy.maxReadBitNum = this.maxReadBitNum;
24
+ copy.error = this.error;
25
+ copy.stringBuffer = this.stringBuffer;
26
+ return copy;
27
+ }
20
28
  getCurPos() {
21
29
  return this.bitNum;
22
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BitStream.js","sourceRoot":"","sources":["../src/BitStream.ts"],"names":[],"mappings":"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","sourcesContent":["import { huffProcessor } from \"./HuffmanProcessor.js\";\n\n/**\n * Bit-level stream reader, ported from the V12 engine's BitStream class.\n *\n * Bits are read LSB-first within each byte:\n * byte[bitNum >> 3] & (1 << (bitNum & 7))\n */\nexport class BitStream {\n private data: Uint8Array;\n private bitNum: number;\n private maxReadBitNum: number;\n private error: boolean;\n private stringBuffer: string | null = null;\n\n constructor(data: Uint8Array, bitOffset = 0) {\n this.data = data;\n this.bitNum = bitOffset;\n this.maxReadBitNum = data.length << 3;\n this.error = false;\n }\n\n getCurPos(): number {\n return this.bitNum;\n }\n\n setCurPos(pos: number): void {\n this.bitNum = pos;\n }\n\n /** Byte position (rounded up), matching C++ getPosition() */\n getBytePosition(): number {\n return (this.bitNum + 7) >> 3;\n }\n\n isError(): boolean {\n return this.error;\n }\n\n /** Returns true if we've read past the buffer */\n isFull(): boolean {\n return this.bitNum > this.maxReadBitNum;\n }\n\n getRemainingBits(): number {\n return this.maxReadBitNum - this.bitNum;\n }\n\n getMaxPos(): number {\n return this.maxReadBitNum;\n }\n\n /** Read a single bit as a boolean. */\n readFlag(): boolean {\n if (this.bitNum >= this.maxReadBitNum) {\n this.error = true;\n return false;\n }\n const mask = 1 << (this.bitNum & 0x7);\n const ret = (this.data[this.bitNum >> 3] & mask) !== 0;\n this.bitNum++;\n return ret;\n }\n\n /**\n * Read N bits as an unsigned integer (up to 32 bits).\n * Bits are read LSB-first, matching the C++ implementation.\n */\n readInt(bitCount: number): number {\n if (bitCount === 0) return 0;\n if (!(bitCount > 0 && bitCount <= 32)) {\n throw new RangeError(`readInt bitCount out of range: ${bitCount}`);\n }\n if (this.bitNum + bitCount > this.maxReadBitNum) {\n this.error = true;\n return 0;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n this.bitNum += bitCount;\n\n // When bitCount + downShift <= 32, everything fits in 32-bit operations\n if (bitCount + downShift <= 32) {\n let ret = 0;\n const bytesNeeded = (bitCount + downShift + 7) >> 3;\n for (\n let i = 0;\n i < bytesNeeded && startByte + i < this.data.length;\n i++\n ) {\n ret |= this.data[startByte + i] << (i * 8);\n }\n ret = ret >>> downShift;\n if (bitCount === 32) return ret >>> 0;\n return ret & ((1 << bitCount) - 1);\n }\n\n // Slow path: bitCount + downShift > 32 (e.g., reading 32 bits at a\n // non-byte-aligned position). Use float64 arithmetic to avoid the\n // 32-bit limit of JavaScript bitwise operators.\n let ret = 0;\n const bytesNeeded = (bitCount + downShift + 7) >> 3;\n for (let i = 0; i < bytesNeeded && startByte + i < this.data.length; i++) {\n ret += this.data[startByte + i] * 2 ** (i * 8);\n }\n ret = Math.floor(ret / 2 ** downShift);\n // Mask to bitCount bits using float64-safe operations\n if (bitCount === 32) return ret >>> 0;\n return ret & ((1 << bitCount) - 1);\n }\n\n /** Read a signed integer: 1-bit sign flag + (bitCount-1) magnitude bits. */\n readSignedInt(bitCount: number): number {\n if (this.readFlag()) {\n return -this.readInt(bitCount - 1);\n }\n return this.readInt(bitCount - 1);\n }\n\n /** Read a float normalized to [0, 1]. */\n readFloat(bitCount: number): number {\n return this.readInt(bitCount) / (2 ** bitCount - 1);\n }\n\n /** Read a float normalized to [-1, 1]. */\n readSignedFloat(bitCount: number): number {\n return (this.readInt(bitCount) * 2) / (2 ** bitCount - 1) - 1.0;\n }\n\n /**\n * Read a ranged unsigned 32-bit integer. Bit width is\n * getBinLog2(getNextPow2(rangeSize)) as in the engine (FUN_0043f120 /\n * FUN_0043f150): a range of one value reads zero bits.\n */\n readRangedU32(rangeStart: number, rangeEnd: number): number {\n const rangeSize = rangeEnd - rangeStart + 1;\n const rangeBits = Math.ceil(Math.log2(rangeSize));\n return this.readInt(rangeBits) + rangeStart;\n }\n\n /**\n * Read raw bits into a new Uint8Array. Like every other read, an\n * over-length request sets the error flag, leaves the cursor in place,\n * and returns zeroed bytes (the engine's BitStream::readBits).\n */\n readBitsBuffer(bitCount: number): Uint8Array {\n if (bitCount === 0) return new Uint8Array(0);\n const byteCount = (bitCount + 7) >> 3;\n const result = new Uint8Array(byteCount);\n if (bitCount < 0 || this.bitNum + bitCount > this.maxReadBitNum) {\n this.error = true;\n return result;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n const upShift = 8 - downShift;\n\n if (downShift === 0) {\n // Byte-aligned, fast path\n result.set(this.data.subarray(startByte, startByte + byteCount));\n } else {\n let curB = this.data[startByte];\n for (let i = 0; i < byteCount; i++) {\n const nextB =\n startByte + i + 1 < this.data.length\n ? this.data[startByte + i + 1]\n : 0;\n result[i] = ((curB >> downShift) | (nextB << upShift)) & 0xff;\n curB = nextB;\n }\n }\n\n // Mask off extra bits in the last byte\n const extraBits = bitCount & 0x7;\n if (extraBits !== 0) {\n result[byteCount - 1] &= (1 << extraBits) - 1;\n }\n\n this.bitNum += bitCount;\n return result;\n }\n\n /** Read N raw bytes. Returns an array of byte values. */\n readBytes(count: number): number[] {\n const buf = this.readBitsBuffer(count * 8);\n return Array.from(buf);\n }\n\n /** Read a U8 (8 bits). */\n readU8(): number {\n return this.readInt(8);\n }\n\n /** Read a U16 (16 bits, little-endian). */\n readU16(): number {\n return this.readInt(16);\n }\n\n /** Read a U32 (32 bits, little-endian). */\n readU32(): number {\n return this.readInt(32);\n }\n\n /** Read a signed 32-bit integer (via stream.read in C++). */\n readS32(): number {\n const val = this.readU32();\n return val | 0; // convert to signed\n }\n\n /** Shared buffer for allocation-free F32 reads. */\n private static readonly f32Buf = new ArrayBuffer(4);\n private static readonly f32View = new DataView(BitStream.f32Buf);\n private static readonly f32U8 = new Uint8Array(BitStream.f32Buf);\n\n /** Read a 32-bit float (IEEE 754), allocation-free. */\n readF32(): number {\n if (this.bitNum + 32 > this.maxReadBitNum) {\n this.error = true;\n return 0;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n const u8 = BitStream.f32U8;\n\n if (downShift === 0) {\n u8[0] = this.data[startByte];\n u8[1] = this.data[startByte + 1];\n u8[2] = this.data[startByte + 2];\n u8[3] = this.data[startByte + 3];\n } else {\n const upShift = 8 - downShift;\n for (let i = 0; i < 4; i++) {\n const curB = this.data[startByte + i];\n const nextB =\n startByte + i + 1 < this.data.length\n ? this.data[startByte + i + 1]\n : 0;\n u8[i] = ((curB >> downShift) | (nextB << upShift)) & 0xff;\n }\n }\n\n this.bitNum += 32;\n return BitStream.f32View.getFloat32(0, true);\n }\n\n /** Read a boolean value stored as a U8 (matches C++ Stream::read(bool*)). */\n readBool(): boolean {\n return this.readU8() !== 0;\n }\n\n /**\n * Read a compressed 3D normal vector.\n * Uses phi (azimuth) and theta (elevation) angles.\n */\n readNormalVector(bitCount: number): { x: number; y: number; z: number } {\n const phi = this.readSignedFloat(bitCount + 1) * Math.PI;\n const theta = this.readSignedFloat(bitCount) * (Math.PI / 2.0);\n\n return {\n x: Math.sin(phi) * Math.cos(theta),\n y: Math.cos(phi) * Math.cos(theta),\n z: Math.sin(theta),\n };\n }\n\n /**\n * Read an affine transform (position + quaternion rotation).\n * Position: 3x F32\n * Quaternion: 3x F32 (x,y,z) + 1-bit sign for w\n */\n readAffineTransform(): {\n position: { x: number; y: number; z: number };\n rotation: { x: number; y: number; z: number; w: number };\n } {\n const position = {\n x: this.readF32(),\n y: this.readF32(),\n z: this.readF32(),\n };\n\n const qx = this.readF32();\n const qy = this.readF32();\n const qz = this.readF32();\n let qw = Math.sqrt(Math.max(0, 1.0 - (qx * qx + qy * qy + qz * qz)));\n if (this.readFlag()) {\n qw = -qw;\n }\n\n return {\n position,\n rotation: { x: qx, y: qy, z: qz, w: qw },\n };\n }\n\n /**\n * Read a Huffman-encoded string.\n * Handles the stringBuffer optimization (shared prefix with previous string).\n * Each wire byte becomes one UTF-16 code unit (Latin-1 decoding); Tribes 2\n * strings are not UTF-8, and tagged strings begin with \"\\x01\".\n */\n readString(): string {\n if (this.stringBuffer !== null) {\n if (this.readFlag()) {\n // Shared prefix optimization: offset into previous string\n const offset = this.readInt(8);\n const suffix = huffProcessor.readHuffBuffer(this);\n const result = this.stringBuffer.substring(0, offset) + suffix;\n this.stringBuffer = result;\n return result;\n }\n }\n const result = huffProcessor.readHuffBuffer(this);\n if (this.stringBuffer !== null) {\n this.stringBuffer = result;\n }\n return result;\n }\n\n /** Enable/disable the string buffer for shared-prefix optimization. */\n setStringBuffer(enable: boolean): void {\n this.stringBuffer = enable ? \"\" : null;\n }\n\n /** Skip N bits. Sets the error flag (without moving) if that would\n * pass the end of the buffer. */\n skipBits(count: number): void {\n if (count < 0 || this.bitNum + count > this.maxReadBitNum) {\n this.error = true;\n return;\n }\n this.bitNum += count;\n }\n\n /** Read 3x F32 as a Point3F. */\n readPoint3F(): { x: number; y: number; z: number } {\n return { x: this.readF32(), y: this.readF32(), z: this.readF32() };\n }\n\n /**\n * Read a compressed 3D point relative to a compression point.\n * Format: 2-bit type, then either absolute (3x F32) or relative delta.\n * Used by Player MoveMask and Vehicle PositionMask.\n */\n readCompressedPoint(\n compressionPoint: { x: number; y: number; z: number },\n scale: number = 0.01,\n ): { x: number; y: number; z: number } {\n const type = this.readInt(2);\n if (type === 3) {\n // Absolute position\n return { x: this.readF32(), y: this.readF32(), z: this.readF32() };\n }\n // Relative to compression point\n const bitCounts = [16, 18, 20];\n const bits = bitCounts[type];\n const dx = this.readSignedInt(bits);\n const dy = this.readSignedInt(bits);\n const dz = this.readSignedInt(bits);\n return {\n x: compressionPoint.x + dx * scale,\n y: compressionPoint.y + dy * scale,\n z: compressionPoint.z + dz * scale,\n };\n }\n\n /**\n * Read a MatrixF written via mathWrite(MatrixF).\n * Format: 16x F32 (row-major in Torque's MatrixF storage) = 512 bits.\n */\n readMatrixF(): {\n elements: number[];\n position: { x: number; y: number; z: number };\n } {\n const elements = new Array<number>(16);\n for (let i = 0; i < 16; i++) {\n elements[i] = this.readF32();\n }\n\n // MatrixF is written row-major: each row is [r0, r1, r2, translation].\n // Position (column 3) is at indices 3, 7, 11 (last element of each row).\n return {\n elements,\n position: { x: elements[3], y: elements[7], z: elements[11] },\n };\n }\n\n /**\n * Read a Torque \"packed string\" with 2-bit type code.\n * Used by RemoteCommandEvent for script arguments.\n * Tribes 2 binary (FUN_00588690) format:\n * 0 = null/empty\n * 1 = normal packed string\n * 2 = tagged string id (10-bit), decoded to \"\\x01<decimalId>\"\n * 3 = integer string (sign + 7/15/31-bit magnitude)\n */\n unpackNetString(): string {\n const code = this.readInt(2);\n switch (code) {\n case 0:\n return \"\"; // null string\n case 1:\n return this.readString(); // Huffman-encoded string\n case 2: {\n // Decompiled binary uses 10 bits here, not 12.\n const tag = this.readInt(10);\n // Keep Tribes 2 tagged-string wire representation.\n return `\\x01${tag}`;\n }\n case 3: {\n // Integer encoding: sign + variable-size magnitude\n const neg = this.readFlag();\n let num: number;\n if (this.readFlag()) {\n num = this.readInt(7); // small\n } else if (this.readFlag()) {\n num = this.readInt(15); // medium\n } else {\n num = this.readInt(31); // large\n }\n if (neg) num = -num;\n return String(num);\n }\n default:\n return \"\";\n }\n }\n\n /** Create a snapshot of the current position that can be restored. */\n savePos(): number {\n return this.bitNum;\n }\n\n /** Restore a previously saved position. */\n restorePos(pos: number): void {\n this.bitNum = pos;\n this.error = false;\n }\n\n /** Get the underlying data buffer. */\n getBuffer(): Uint8Array {\n return this.data;\n }\n}\n"]}
1
+ {"version":3,"file":"BitStream.js","sourceRoot":"","sources":["../src/BitStream.ts"],"names":[],"mappings":"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","sourcesContent":["import { huffProcessor } from \"./HuffmanProcessor.js\";\n\n/**\n * Bit-level stream reader, ported from the V12 engine's BitStream class.\n *\n * Bits are read LSB-first within each byte:\n * byte[bitNum >> 3] & (1 << (bitNum & 7))\n */\nexport class BitStream {\n private data: Uint8Array;\n private bitNum: number;\n private maxReadBitNum: number;\n private error: boolean;\n private stringBuffer: string | null = null;\n\n constructor(data: Uint8Array, bitOffset = 0) {\n this.data = data;\n this.bitNum = bitOffset;\n this.maxReadBitNum = data.length << 3;\n this.error = false;\n }\n\n /** Independent lookahead, including string-prefix and error state. */\n fork(): BitStream {\n const copy = new BitStream(this.data, this.bitNum);\n copy.maxReadBitNum = this.maxReadBitNum;\n copy.error = this.error;\n copy.stringBuffer = this.stringBuffer;\n return copy;\n }\n\n getCurPos(): number {\n return this.bitNum;\n }\n\n setCurPos(pos: number): void {\n this.bitNum = pos;\n }\n\n /** Byte position (rounded up), matching C++ getPosition() */\n getBytePosition(): number {\n return (this.bitNum + 7) >> 3;\n }\n\n isError(): boolean {\n return this.error;\n }\n\n /** Returns true if we've read past the buffer */\n isFull(): boolean {\n return this.bitNum > this.maxReadBitNum;\n }\n\n getRemainingBits(): number {\n return this.maxReadBitNum - this.bitNum;\n }\n\n getMaxPos(): number {\n return this.maxReadBitNum;\n }\n\n /** Read a single bit as a boolean. */\n readFlag(): boolean {\n if (this.bitNum >= this.maxReadBitNum) {\n this.error = true;\n return false;\n }\n const mask = 1 << (this.bitNum & 0x7);\n const ret = (this.data[this.bitNum >> 3] & mask) !== 0;\n this.bitNum++;\n return ret;\n }\n\n /**\n * Read N bits as an unsigned integer (up to 32 bits).\n * Bits are read LSB-first, matching the C++ implementation.\n */\n readInt(bitCount: number): number {\n if (bitCount === 0) return 0;\n if (!(bitCount > 0 && bitCount <= 32)) {\n throw new RangeError(`readInt bitCount out of range: ${bitCount}`);\n }\n if (this.bitNum + bitCount > this.maxReadBitNum) {\n this.error = true;\n return 0;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n this.bitNum += bitCount;\n\n // When bitCount + downShift <= 32, everything fits in 32-bit operations\n if (bitCount + downShift <= 32) {\n let ret = 0;\n const bytesNeeded = (bitCount + downShift + 7) >> 3;\n for (\n let i = 0;\n i < bytesNeeded && startByte + i < this.data.length;\n i++\n ) {\n ret |= this.data[startByte + i] << (i * 8);\n }\n ret = ret >>> downShift;\n if (bitCount === 32) return ret >>> 0;\n return ret & ((1 << bitCount) - 1);\n }\n\n // Slow path: bitCount + downShift > 32 (e.g., reading 32 bits at a\n // non-byte-aligned position). Use float64 arithmetic to avoid the\n // 32-bit limit of JavaScript bitwise operators.\n let ret = 0;\n const bytesNeeded = (bitCount + downShift + 7) >> 3;\n for (let i = 0; i < bytesNeeded && startByte + i < this.data.length; i++) {\n ret += this.data[startByte + i] * 2 ** (i * 8);\n }\n ret = Math.floor(ret / 2 ** downShift);\n // Mask to bitCount bits using float64-safe operations\n if (bitCount === 32) return ret >>> 0;\n return ret & ((1 << bitCount) - 1);\n }\n\n /** Read a signed integer: 1-bit sign flag + (bitCount-1) magnitude bits. */\n readSignedInt(bitCount: number): number {\n if (this.readFlag()) {\n return -this.readInt(bitCount - 1);\n }\n return this.readInt(bitCount - 1);\n }\n\n /** Read a float normalized to [0, 1]. */\n readFloat(bitCount: number): number {\n return this.readInt(bitCount) / (2 ** bitCount - 1);\n }\n\n /** Read a float normalized to [-1, 1]. */\n readSignedFloat(bitCount: number): number {\n return (this.readInt(bitCount) * 2) / (2 ** bitCount - 1) - 1.0;\n }\n\n /**\n * Read a ranged unsigned 32-bit integer. Bit width is\n * getBinLog2(getNextPow2(rangeSize)) as in the engine (FUN_0043f120 /\n * FUN_0043f150): a range of one value reads zero bits.\n */\n readRangedU32(rangeStart: number, rangeEnd: number): number {\n const rangeSize = rangeEnd - rangeStart + 1;\n const rangeBits = Math.ceil(Math.log2(rangeSize));\n return this.readInt(rangeBits) + rangeStart;\n }\n\n /**\n * Read raw bits into a new Uint8Array. Like every other read, an\n * over-length request sets the error flag, leaves the cursor in place,\n * and returns zeroed bytes (the engine's BitStream::readBits).\n */\n readBitsBuffer(bitCount: number): Uint8Array {\n if (bitCount === 0) return new Uint8Array(0);\n const byteCount = (bitCount + 7) >> 3;\n const result = new Uint8Array(byteCount);\n if (bitCount < 0 || this.bitNum + bitCount > this.maxReadBitNum) {\n this.error = true;\n return result;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n const upShift = 8 - downShift;\n\n if (downShift === 0) {\n // Byte-aligned, fast path\n result.set(this.data.subarray(startByte, startByte + byteCount));\n } else {\n let curB = this.data[startByte];\n for (let i = 0; i < byteCount; i++) {\n const nextB =\n startByte + i + 1 < this.data.length\n ? this.data[startByte + i + 1]\n : 0;\n result[i] = ((curB >> downShift) | (nextB << upShift)) & 0xff;\n curB = nextB;\n }\n }\n\n // Mask off extra bits in the last byte\n const extraBits = bitCount & 0x7;\n if (extraBits !== 0) {\n result[byteCount - 1] &= (1 << extraBits) - 1;\n }\n\n this.bitNum += bitCount;\n return result;\n }\n\n /** Read N raw bytes. Returns an array of byte values. */\n readBytes(count: number): number[] {\n const buf = this.readBitsBuffer(count * 8);\n return Array.from(buf);\n }\n\n /** Read a U8 (8 bits). */\n readU8(): number {\n return this.readInt(8);\n }\n\n /** Read a U16 (16 bits, little-endian). */\n readU16(): number {\n return this.readInt(16);\n }\n\n /** Read a U32 (32 bits, little-endian). */\n readU32(): number {\n return this.readInt(32);\n }\n\n /** Read a signed 32-bit integer (via stream.read in C++). */\n readS32(): number {\n const val = this.readU32();\n return val | 0; // convert to signed\n }\n\n /** Shared buffer for allocation-free F32 reads. */\n private static readonly f32Buf = new ArrayBuffer(4);\n private static readonly f32View = new DataView(BitStream.f32Buf);\n private static readonly f32U8 = new Uint8Array(BitStream.f32Buf);\n\n /** Read a 32-bit float (IEEE 754), allocation-free. */\n readF32(): number {\n if (this.bitNum + 32 > this.maxReadBitNum) {\n this.error = true;\n return 0;\n }\n\n const startByte = this.bitNum >> 3;\n const downShift = this.bitNum & 0x7;\n const u8 = BitStream.f32U8;\n\n if (downShift === 0) {\n u8[0] = this.data[startByte];\n u8[1] = this.data[startByte + 1];\n u8[2] = this.data[startByte + 2];\n u8[3] = this.data[startByte + 3];\n } else {\n const upShift = 8 - downShift;\n for (let i = 0; i < 4; i++) {\n const curB = this.data[startByte + i];\n const nextB =\n startByte + i + 1 < this.data.length\n ? this.data[startByte + i + 1]\n : 0;\n u8[i] = ((curB >> downShift) | (nextB << upShift)) & 0xff;\n }\n }\n\n this.bitNum += 32;\n return BitStream.f32View.getFloat32(0, true);\n }\n\n /** Read a boolean value stored as a U8 (matches C++ Stream::read(bool*)). */\n readBool(): boolean {\n return this.readU8() !== 0;\n }\n\n /**\n * Read a compressed 3D normal vector.\n * Uses phi (azimuth) and theta (elevation) angles.\n */\n readNormalVector(bitCount: number): { x: number; y: number; z: number } {\n const phi = this.readSignedFloat(bitCount + 1) * Math.PI;\n const theta = this.readSignedFloat(bitCount) * (Math.PI / 2.0);\n\n return {\n x: Math.sin(phi) * Math.cos(theta),\n y: Math.cos(phi) * Math.cos(theta),\n z: Math.sin(theta),\n };\n }\n\n /**\n * Read an affine transform (position + quaternion rotation).\n * Position: 3x F32\n * Quaternion: 3x F32 (x,y,z) + 1-bit sign for w\n */\n readAffineTransform(): {\n position: { x: number; y: number; z: number };\n rotation: { x: number; y: number; z: number; w: number };\n } {\n const position = {\n x: this.readF32(),\n y: this.readF32(),\n z: this.readF32(),\n };\n\n const qx = this.readF32();\n const qy = this.readF32();\n const qz = this.readF32();\n let qw = Math.sqrt(Math.max(0, 1.0 - (qx * qx + qy * qy + qz * qz)));\n if (this.readFlag()) {\n qw = -qw;\n }\n\n return {\n position,\n rotation: { x: qx, y: qy, z: qz, w: qw },\n };\n }\n\n /**\n * Read a Huffman-encoded string.\n * Handles the stringBuffer optimization (shared prefix with previous string).\n * Each wire byte becomes one UTF-16 code unit (Latin-1 decoding); Tribes 2\n * strings are not UTF-8, and tagged strings begin with \"\\x01\".\n */\n readString(): string {\n if (this.stringBuffer !== null) {\n if (this.readFlag()) {\n // Shared prefix optimization: offset into previous string\n const offset = this.readInt(8);\n const suffix = huffProcessor.readHuffBuffer(this);\n const result = this.stringBuffer.substring(0, offset) + suffix;\n this.stringBuffer = result;\n return result;\n }\n }\n const result = huffProcessor.readHuffBuffer(this);\n if (this.stringBuffer !== null) {\n this.stringBuffer = result;\n }\n return result;\n }\n\n /** Enable/disable the string buffer for shared-prefix optimization. */\n setStringBuffer(enable: boolean): void {\n this.stringBuffer = enable ? \"\" : null;\n }\n\n /** Skip N bits. Sets the error flag (without moving) if that would\n * pass the end of the buffer. */\n skipBits(count: number): void {\n if (count < 0 || this.bitNum + count > this.maxReadBitNum) {\n this.error = true;\n return;\n }\n this.bitNum += count;\n }\n\n /** Read 3x F32 as a Point3F. */\n readPoint3F(): { x: number; y: number; z: number } {\n return { x: this.readF32(), y: this.readF32(), z: this.readF32() };\n }\n\n /**\n * Read a compressed 3D point relative to a compression point.\n * Format: 2-bit type, then either absolute (3x F32) or relative delta.\n * Used by Player MoveMask and Vehicle PositionMask.\n */\n readCompressedPoint(\n compressionPoint: { x: number; y: number; z: number },\n scale: number = 0.01,\n ): { x: number; y: number; z: number } {\n const type = this.readInt(2);\n if (type === 3) {\n // Absolute position\n return { x: this.readF32(), y: this.readF32(), z: this.readF32() };\n }\n // Relative to compression point\n const bitCounts = [16, 18, 20];\n const bits = bitCounts[type];\n const dx = this.readSignedInt(bits);\n const dy = this.readSignedInt(bits);\n const dz = this.readSignedInt(bits);\n return {\n x: compressionPoint.x + dx * scale,\n y: compressionPoint.y + dy * scale,\n z: compressionPoint.z + dz * scale,\n };\n }\n\n /**\n * Read a MatrixF written via mathWrite(MatrixF).\n * Format: 16x F32 (row-major in Torque's MatrixF storage) = 512 bits.\n */\n readMatrixF(): {\n elements: number[];\n position: { x: number; y: number; z: number };\n } {\n const elements = new Array<number>(16);\n for (let i = 0; i < 16; i++) {\n elements[i] = this.readF32();\n }\n\n // MatrixF is written row-major: each row is [r0, r1, r2, translation].\n // Position (column 3) is at indices 3, 7, 11 (last element of each row).\n return {\n elements,\n position: { x: elements[3], y: elements[7], z: elements[11] },\n };\n }\n\n /**\n * Read a Torque \"packed string\" with 2-bit type code.\n * Used by RemoteCommandEvent for script arguments.\n * Tribes 2 binary (FUN_00588690) format:\n * 0 = null/empty\n * 1 = normal packed string\n * 2 = tagged string id (10-bit), decoded to \"\\x01<decimalId>\"\n * 3 = integer string (sign + 7/15/31-bit magnitude)\n */\n unpackNetString(): string {\n const code = this.readInt(2);\n switch (code) {\n case 0:\n return \"\"; // null string\n case 1:\n return this.readString(); // Huffman-encoded string\n case 2: {\n // Decompiled binary uses 10 bits here, not 12.\n const tag = this.readInt(10);\n // Keep Tribes 2 tagged-string wire representation.\n return `\\x01${tag}`;\n }\n case 3: {\n // Integer encoding: sign + variable-size magnitude\n const neg = this.readFlag();\n let num: number;\n if (this.readFlag()) {\n num = this.readInt(7); // small\n } else if (this.readFlag()) {\n num = this.readInt(15); // medium\n } else {\n num = this.readInt(31); // large\n }\n if (neg) num = -num;\n return String(num);\n }\n default:\n return \"\";\n }\n }\n\n /** Create a snapshot of the current position that can be restored. */\n savePos(): number {\n return this.bitNum;\n }\n\n /** Restore a previously saved position. */\n restorePos(pos: number): void {\n this.bitNum = pos;\n this.error = false;\n }\n\n /** Get the underlying data buffer. */\n getBuffer(): Uint8Array {\n return this.data;\n }\n}\n"]}
@@ -13,6 +13,10 @@ export type GhostPacketDataParser = (bs: BitStream, conn: ConnectionContext) =>
13
13
  export type DataBlockParser = (bs: BitStream) => ParsedData;
14
14
  /** Shared context passed to parsers from the connection state. */
15
15
  export interface ConnectionContext {
16
+ /** Saved demo ghosts use the retail layout even on TribesNEXT clients. */
17
+ isDemoStartBlock?: boolean;
18
+ /** Negotiated wire protocol (51 retail, 52 QoL); absent/null for demos. */
19
+ protocolVersion?: number | null;
16
20
  compressionPoint: {
17
21
  x: number;
18
22
  y: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ClassRegistry.d.ts","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAIpD,MAAM,MAAM,WAAW,GAAG,CACxB,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,iBAAiB,KACpB,SAAS,CAAC;AAEf,MAAM,MAAM,iBAAiB,GAAG,CAC9B,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,iBAAiB,KACpB,UAAU,CAAC;AAEhB,MAAM,MAAM,qBAAqB,GAAG,CAClC,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,iBAAiB,KACpB,UAAU,CAAC;AAEhB,MAAM,MAAM,eAAe,GAAG,CAC5B,EAAE,EAAE,SAAS,KACV,UAAU,CAAC;AAEhB,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,YAAY,EAAE,qBAAqB,CAAC;IACpC,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,oBAAoB,GAAG,SAAS,CAAC;IAC3E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;IAChE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,gBAAgB,GAAG,SAAS,CAAC;CACpE;AAED,2DAA2D;AAC3D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,eAAe,CAAC;CAC7B;AA+CD;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAAsC;IASxD,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE;QACvC,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,CAAC;KACvD,GAAG,IAAI;IAIR,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,UAAU,EAAE,KAAK,EAAE;QACnE,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,CAAC;QAChF,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,CAAC;KAChE,GAAG,IAAI;IAIR,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE;QACxC,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,CAAC,CAAC;KAClC,GAAG,IAAI;IAUR,2BAA2B,CACzB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAIvC,uBAAuB,CACrB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAIvC,uBAAuB,CACrB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,UAAU,EAAE,MAAM,GACjB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAMvC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI7D,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI7D,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS;IAMrE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMrD,eAAe,IAAI,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxD,eAAe,IAAI,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxD,mBAAmB,IAAI,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC;IAMhE,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIvC,gBAAgB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAIvC,oBAAoB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAG5C"}
1
+ {"version":3,"file":"ClassRegistry.d.ts","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ClassRegistry.js","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BitStream } from \"./BitStream.js\";\nimport type { EventData } from \"./eventDataTypes.js\";\n\n/**\n * Base type for all parsed data objects. Individual parsers return more\n * specific interfaces, but the registry stores them generically.\n */\nexport type ParsedData = { [key: string]: unknown };\n\n// --- Parser function signatures ---\n\nexport type EventParser = (\n bs: BitStream,\n conn: ConnectionContext\n) => EventData;\n\nexport type GhostUpdateParser = (\n bs: BitStream,\n isInitial: boolean,\n conn: ConnectionContext\n) => ParsedData;\n\nexport type GhostPacketDataParser = (\n bs: BitStream,\n conn: ConnectionContext\n) => ParsedData;\n\nexport type DataBlockParser = (\n bs: BitStream\n) => ParsedData;\n\n/** Shared context passed to parsers from the connection state. */\nexport interface ConnectionContext {\n compressionPoint: { x: number; y: number; z: number };\n ghostTracker: GhostTrackerInterface;\n getDataBlockParser?: (classId: number) => DataBlockParserEntry | undefined;\n getDataBlockData?: (objectId: number) => ParsedData | undefined;\n getGhostParser?: (classId: number) => GhostParserEntry | undefined;\n}\n\n/** Minimal interface for ghost tracker used by parsers. */\nexport interface GhostTrackerInterface {\n getGhost(index: number): GhostEntry | undefined;\n}\n\nexport interface GhostEntry {\n classId: number;\n className: string;\n}\n\nexport interface GhostParserEntry {\n name: string;\n unpackUpdate: GhostUpdateParser;\n readPacketData?: GhostPacketDataParser;\n}\n\nexport interface EventParserEntry {\n name: string;\n unpack: EventParser;\n}\n\nexport interface DataBlockParserEntry {\n name: string;\n unpackData: DataBlockParser;\n}\n\ninterface Catalog<T extends { name: string }> {\n byName: Map<string, T>;\n byClassId: Map<number, T>;\n classIdByName: Map<string, number>;\n}\n\nfunction newCatalog<T extends { name: string }>(): Catalog<T> {\n return { byName: new Map(), byClassId: new Map(), classIdByName: new Map() };\n}\n\n/**\n * Bind every cataloged parser whose name appears in `classNames` to\n * classId = classFirst + index. Names without a parser are reported.\n */\nfunction bindCatalog<T extends { name: string }>(\n catalog: Catalog<T>,\n classNames: readonly string[],\n classFirst: number,\n): { bound: number; missing: string[] } {\n let bound = 0;\n const missing: string[] = [];\n for (let i = 0; i < classNames.length; i++) {\n const name = classNames[i];\n const entry = catalog.byName.get(name);\n if (entry) {\n catalog.byClassId.set(classFirst + i, entry);\n catalog.classIdByName.set(name, classFirst + i);\n bound++;\n } else {\n missing.push(name);\n }\n }\n return { bound, missing };\n}\n\nfunction bindingsOf<T extends { name: string }>(\n catalog: Catalog<T>,\n): Map<number, string> {\n const bindings = new Map<number, string>();\n for (const [id, entry] of catalog.byClassId) {\n bindings.set(id, entry.name);\n }\n return bindings;\n}\n\n/**\n * Registry mapping classIds to parser functions.\n * ClassIds are assigned deterministically by alphabetical sort (C strcmp)\n * at engine link time. Parsers are cataloged by name and bound to classIds\n * via bindDeterministicGhosts/Events/DataBlocks.\n */\nexport class ClassRegistry {\n private events = newCatalog<EventParserEntry>();\n private ghosts = newCatalog<GhostParserEntry>();\n private dataBlocks = newCatalog<DataBlockParserEntry>();\n\n // --- Catalog registration (name → parser, no classId yet) ---\n //\n // Parsers return precisely typed objects (PlayerGhostData, ...), which\n // are stored behind the generic ParsedData shape. The generic signatures\n // let each parser keep its own return type — so a misspelled field is a\n // compile error inside the parser — while the registry erases it.\n\n catalogEvent<T extends EventData>(entry: {\n name: string;\n unpack: (bs: BitStream, conn: ConnectionContext) => T;\n }): void {\n this.events.byName.set(entry.name, entry);\n }\n\n catalogGhost<T extends object, P extends object = ParsedData>(entry: {\n name: string;\n unpackUpdate: (bs: BitStream, isInitial: boolean, conn: ConnectionContext) => T;\n readPacketData?: (bs: BitStream, conn: ConnectionContext) => P;\n }): void {\n this.ghosts.byName.set(entry.name, entry as unknown as GhostParserEntry);\n }\n\n catalogDataBlock<T extends object>(entry: {\n name: string;\n unpackData: (bs: BitStream) => T;\n }): void {\n this.dataBlocks.byName.set(\n entry.name,\n entry as unknown as DataBlockParserEntry,\n );\n }\n\n // --- Deterministic binding (classId = classFirst + index in the sorted\n // class name list) ---\n\n bindDeterministicDataBlocks(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.dataBlocks, classNames, classFirst);\n }\n\n bindDeterministicEvents(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.events, classNames, classFirst);\n }\n\n bindDeterministicGhosts(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.ghosts, classNames, classFirst);\n }\n\n // --- Lookup by classId ---\n\n getEventParser(classId: number): EventParserEntry | undefined {\n return this.events.byClassId.get(classId);\n }\n\n getGhostParser(classId: number): GhostParserEntry | undefined {\n return this.ghosts.byClassId.get(classId);\n }\n\n getDataBlockParser(classId: number): DataBlockParserEntry | undefined {\n return this.dataBlocks.byClassId.get(classId);\n }\n\n // --- Lookup by class name (bound classId) ---\n\n getEventClassId(name: string): number | undefined {\n return this.events.classIdByName.get(name);\n }\n\n getGhostClassId(name: string): number | undefined {\n return this.ghosts.classIdByName.get(name);\n }\n\n getDataBlockClassId(name: string): number | undefined {\n return this.dataBlocks.classIdByName.get(name);\n }\n\n // --- Catalog access (name → parser, independent of binding) ---\n\n getEventCatalog(): ReadonlyMap<string, EventParserEntry> {\n return this.events.byName;\n }\n\n getGhostCatalog(): ReadonlyMap<string, GhostParserEntry> {\n return this.ghosts.byName;\n }\n\n getDataBlockCatalog(): ReadonlyMap<string, DataBlockParserEntry> {\n return this.dataBlocks.byName;\n }\n\n // --- Debug: classId → parser name ---\n\n getEventBindings(): Map<number, string> {\n return bindingsOf(this.events);\n }\n\n getGhostBindings(): Map<number, string> {\n return bindingsOf(this.ghosts);\n }\n\n getDataBlockBindings(): Map<number, string> {\n return bindingsOf(this.dataBlocks);\n }\n}\n"]}
1
+ {"version":3,"file":"ClassRegistry.js","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BitStream } from \"./BitStream.js\";\nimport type { EventData } from \"./eventDataTypes.js\";\n\n/**\n * Base type for all parsed data objects. Individual parsers return more\n * specific interfaces, but the registry stores them generically.\n */\nexport type ParsedData = { [key: string]: unknown };\n\n// --- Parser function signatures ---\n\nexport type EventParser = (bs: BitStream, conn: ConnectionContext) => EventData;\n\nexport type GhostUpdateParser = (\n bs: BitStream,\n isInitial: boolean,\n conn: ConnectionContext,\n) => ParsedData;\n\nexport type GhostPacketDataParser = (\n bs: BitStream,\n conn: ConnectionContext,\n) => ParsedData;\n\nexport type DataBlockParser = (bs: BitStream) => ParsedData;\n\n/** Shared context passed to parsers from the connection state. */\nexport interface ConnectionContext {\n /** Saved demo ghosts use the retail layout even on TribesNEXT clients. */\n isDemoStartBlock?: boolean;\n /** Negotiated wire protocol (51 retail, 52 QoL); absent/null for demos. */\n protocolVersion?: number | null;\n compressionPoint: { x: number; y: number; z: number };\n ghostTracker: GhostTrackerInterface;\n getDataBlockParser?: (classId: number) => DataBlockParserEntry | undefined;\n getDataBlockData?: (objectId: number) => ParsedData | undefined;\n getGhostParser?: (classId: number) => GhostParserEntry | undefined;\n}\n\n/** Minimal interface for ghost tracker used by parsers. */\nexport interface GhostTrackerInterface {\n getGhost(index: number): GhostEntry | undefined;\n}\n\nexport interface GhostEntry {\n classId: number;\n className: string;\n}\n\nexport interface GhostParserEntry {\n name: string;\n unpackUpdate: GhostUpdateParser;\n readPacketData?: GhostPacketDataParser;\n}\n\nexport interface EventParserEntry {\n name: string;\n unpack: EventParser;\n}\n\nexport interface DataBlockParserEntry {\n name: string;\n unpackData: DataBlockParser;\n}\n\ninterface Catalog<T extends { name: string }> {\n byName: Map<string, T>;\n byClassId: Map<number, T>;\n classIdByName: Map<string, number>;\n}\n\nfunction newCatalog<T extends { name: string }>(): Catalog<T> {\n return { byName: new Map(), byClassId: new Map(), classIdByName: new Map() };\n}\n\n/**\n * Bind every cataloged parser whose name appears in `classNames` to\n * classId = classFirst + index. Names without a parser are reported.\n */\nfunction bindCatalog<T extends { name: string }>(\n catalog: Catalog<T>,\n classNames: readonly string[],\n classFirst: number,\n): { bound: number; missing: string[] } {\n let bound = 0;\n const missing: string[] = [];\n for (let i = 0; i < classNames.length; i++) {\n const name = classNames[i];\n const entry = catalog.byName.get(name);\n if (entry) {\n catalog.byClassId.set(classFirst + i, entry);\n catalog.classIdByName.set(name, classFirst + i);\n bound++;\n } else {\n missing.push(name);\n }\n }\n return { bound, missing };\n}\n\nfunction bindingsOf<T extends { name: string }>(\n catalog: Catalog<T>,\n): Map<number, string> {\n const bindings = new Map<number, string>();\n for (const [id, entry] of catalog.byClassId) {\n bindings.set(id, entry.name);\n }\n return bindings;\n}\n\n/**\n * Registry mapping classIds to parser functions.\n * ClassIds are assigned deterministically by alphabetical sort (C strcmp)\n * at engine link time. Parsers are cataloged by name and bound to classIds\n * via bindDeterministicGhosts/Events/DataBlocks.\n */\nexport class ClassRegistry {\n private events = newCatalog<EventParserEntry>();\n private ghosts = newCatalog<GhostParserEntry>();\n private dataBlocks = newCatalog<DataBlockParserEntry>();\n\n // --- Catalog registration (name → parser, no classId yet) ---\n //\n // Parsers return precisely typed objects (PlayerGhostData, ...), which\n // are stored behind the generic ParsedData shape. The generic signatures\n // let each parser keep its own return type — so a misspelled field is a\n // compile error inside the parser — while the registry erases it.\n\n catalogEvent<T extends EventData>(entry: {\n name: string;\n unpack: (bs: BitStream, conn: ConnectionContext) => T;\n }): void {\n this.events.byName.set(entry.name, entry);\n }\n\n catalogGhost<T extends object, P extends object = ParsedData>(entry: {\n name: string;\n unpackUpdate: (\n bs: BitStream,\n isInitial: boolean,\n conn: ConnectionContext,\n ) => T;\n readPacketData?: (bs: BitStream, conn: ConnectionContext) => P;\n }): void {\n this.ghosts.byName.set(entry.name, entry as unknown as GhostParserEntry);\n }\n\n catalogDataBlock<T extends object>(entry: {\n name: string;\n unpackData: (bs: BitStream) => T;\n }): void {\n this.dataBlocks.byName.set(\n entry.name,\n entry as unknown as DataBlockParserEntry,\n );\n }\n\n // --- Deterministic binding (classId = classFirst + index in the sorted\n // class name list) ---\n\n bindDeterministicDataBlocks(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.dataBlocks, classNames, classFirst);\n }\n\n bindDeterministicEvents(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.events, classNames, classFirst);\n }\n\n bindDeterministicGhosts(\n classNames: readonly string[],\n classFirst: number,\n ): { bound: number; missing: string[] } {\n return bindCatalog(this.ghosts, classNames, classFirst);\n }\n\n // --- Lookup by classId ---\n\n getEventParser(classId: number): EventParserEntry | undefined {\n return this.events.byClassId.get(classId);\n }\n\n getGhostParser(classId: number): GhostParserEntry | undefined {\n return this.ghosts.byClassId.get(classId);\n }\n\n getDataBlockParser(classId: number): DataBlockParserEntry | undefined {\n return this.dataBlocks.byClassId.get(classId);\n }\n\n // --- Lookup by class name (bound classId) ---\n\n getEventClassId(name: string): number | undefined {\n return this.events.classIdByName.get(name);\n }\n\n getGhostClassId(name: string): number | undefined {\n return this.ghosts.classIdByName.get(name);\n }\n\n getDataBlockClassId(name: string): number | undefined {\n return this.dataBlocks.classIdByName.get(name);\n }\n\n // --- Catalog access (name → parser, independent of binding) ---\n\n getEventCatalog(): ReadonlyMap<string, EventParserEntry> {\n return this.events.byName;\n }\n\n getGhostCatalog(): ReadonlyMap<string, GhostParserEntry> {\n return this.ghosts.byName;\n }\n\n getDataBlockCatalog(): ReadonlyMap<string, DataBlockParserEntry> {\n return this.dataBlocks.byName;\n }\n\n // --- Debug: classId → parser name ---\n\n getEventBindings(): Map<number, string> {\n return bindingsOf(this.events);\n }\n\n getGhostBindings(): Map<number, string> {\n return bindingsOf(this.ghosts);\n }\n\n getDataBlockBindings(): Map<number, string> {\n return bindingsOf(this.dataBlocks);\n }\n}\n"]}
@@ -31,6 +31,7 @@ export declare class DemoParser {
31
31
  private _decompressedLength;
32
32
  private _scanOffset;
33
33
  private _bufferedMoveTicks;
34
+ private readonly _packetProtocolVersion;
34
35
  private readonly _ignoreProtocolVersion;
35
36
  private readonly _haltOnFault;
36
37
  constructor(buffer: Uint8Array, options?: {
@@ -42,6 +43,9 @@ export declare class DemoParser {
42
43
  * formats are not guaranteed to match, so expect faults.
43
44
  */
44
45
  ignoreProtocolVersion?: boolean;
46
+ /** Override the negotiated wire version, which .rec files do not save.
47
+ * Null/omitted detects terrain extensions independently per payload. */
48
+ packetProtocolVersion?: number | null;
45
49
  /** Passed to PacketParser; see its `haltOnFault` option. */
46
50
  haltOnFault?: boolean;
47
51
  });
@@ -129,6 +133,7 @@ export declare class DemoParser {
129
133
  ghosts: ReadonlyMap<number, import("./ClassRegistry.js").GhostEntry>;
130
134
  packetState: {
131
135
  dataBlockDataMap: Map<number, import("./ClassRegistry.js").ParsedData> | undefined;
136
+ protocolVersion: number | null;
132
137
  compressionPoint: {
133
138
  x: number;
134
139
  y: number;
@@ -1 +1 @@
1
- {"version":3,"file":"DemoParser.d.ts","sourceRoot":"","sources":["../src/DemoParser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DemoParser.d.ts","sourceRoot":"","sources":["../src/DemoParser.ts"],"names":[],"mappings":"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"}
@@ -6,12 +6,6 @@ import { createDefaultRegistry } from "./defaultRegistry.js";
6
6
  import { GhostTracker, readMove } from "./GhostManager.js";
7
7
  import { BlockTypePacket, BlockTypeSendPacket, BlockTypeMove, BlockTypeInfo, DemoIdentString, DemoProtocolVersion, MaxTriggerKeys, NetEventClassBitSize, NetEventClassFirst, NetObjectClassBitSize, NetObjectClassFirst, GhostIdBitSize, DataBlockClassFirst, DataBlockClassNames, SimDBEventObjectIdBits, SimDBEventClassIdBits, SimDBEventIndexBits, SimDBEventTotalBits, } from "./types.js";
8
8
  const debug = createDebug("t2-demo-parser");
9
- /**
10
- * Sequence numbers assigned to ordered events carried in the demo start
11
- * block: past every 32-bit sequence, so they stay queued without ever
12
- * matching `nextRecvEventSeq` (see setupPacketParser).
13
- */
14
- const START_BLOCK_EVENT_SEQ_BASE = 0x1_0000_0000;
15
9
  const debugInitial = createDebug("t2-demo-parser:initial");
16
10
  const debugBlocks = createDebug("t2-demo-parser:blocks");
17
11
  /**
@@ -60,6 +54,7 @@ export class DemoParser {
60
54
  // Buffered-time scan state (see bufferedMoveTicks).
61
55
  _scanOffset = 0;
62
56
  _bufferedMoveTicks = 0;
57
+ _packetProtocolVersion;
63
58
  _ignoreProtocolVersion;
64
59
  _haltOnFault;
65
60
  constructor(buffer, options) {
@@ -69,11 +64,15 @@ export class DemoParser {
69
64
  this._incremental = options?.incremental === true;
70
65
  this._ignoreProtocolVersion = options?.ignoreProtocolVersion === true;
71
66
  this._haltOnFault = options?.haltOnFault;
67
+ this._packetProtocolVersion = options?.packetProtocolVersion ?? null;
72
68
  // Parser catalogs bound to their deterministic classIds (derived from
73
69
  // binary analysis of the Tribes 2 executable).
74
70
  this.registry = createDefaultRegistry();
75
71
  this.ghostTracker = new GhostTracker();
76
- this.packetParser = new PacketParser(this.registry, this.ghostTracker);
72
+ this.packetParser = new PacketParser(this.registry, this.ghostTracker, {
73
+ protocolVersion: this._packetProtocolVersion,
74
+ haltOnFault: this._haltOnFault,
75
+ });
77
76
  }
78
77
  getRegistry() {
79
78
  return this.registry;
@@ -476,21 +475,19 @@ export class DemoParser {
476
475
  gt.createGhost(ghost.index, ghost.classId, parserEntry?.name ?? `unknown_${ghost.classId}`);
477
476
  }
478
477
  const pp = new PacketParser(this.registry, gt, {
478
+ protocolVersion: this._packetProtocolVersion,
479
479
  dataBlockDataMap,
480
480
  connectionProtocolState: initialBlock.connectionState,
481
481
  nextRecvEventSeq: initialBlock.nextRecvEventSeq,
482
482
  compressionPoint: initialBlock.initialCompressionPoint,
483
- // NetConnection::eventReadStartBlock (FUN_00583ac0) appends the
484
- // start block's in-flight ordered events to the wait queue without
485
- // a sequence number (the field is never written; NetEvent's
486
- // constructor leaves it uninitialized), so in the engine they can
487
- // only ever dispatch by accident of heap contents. The deterministic
488
- // stand-in keeps them queued behind every possible real sequence
489
- // number, where they neither dispatch nor block dispatch.
490
- pendingGuaranteedEvents: initialBlock.initialEvents
491
- .filter((event) => !event.failed)
492
- .map((event, i) => ({
493
- absoluteSequenceNumber: START_BLOCK_EVENT_SEQ_BASE + i,
483
+ // Recording can start inside an ordered event's process callback:
484
+ // nextRecvEventSeq has advanced, but the remaining events in that
485
+ // packet are still in the wait queue. The demo saves their payloads
486
+ // in order, without sequence numbers. Restore that contiguous prefix
487
+ // at the saved receive sequence; parking it beyond the sequence range
488
+ // leaves a permanent hole and misinterprets later 7-bit wraps.
489
+ pendingGuaranteedEvents: initialBlock.initialEvents.map((event, i) => ({
490
+ absoluteSequenceNumber: (initialBlock.nextRecvEventSeq + i) >>> 0,
494
491
  event,
495
492
  })),
496
493
  haltOnFault: this._haltOnFault,
@@ -934,6 +931,7 @@ export class DemoParser {
934
931
  const conn = {
935
932
  compressionPoint: { x: 0, y: 0, z: 0 },
936
933
  ghostTracker: this.ghostTracker,
934
+ isDemoStartBlock: true,
937
935
  getDataBlockParser: (cid) => this.registry.getDataBlockParser(cid),
938
936
  };
939
937
  parsedData = parserEntry.unpack(bs, conn);
@@ -1014,6 +1012,7 @@ export class DemoParser {
1014
1012
  entry.unpackUpdate(bs, true, {
1015
1013
  compressionPoint: { x: 0, y: 0, z: 0 },
1016
1014
  ghostTracker: this.ghostTracker,
1015
+ isDemoStartBlock: true,
1017
1016
  getDataBlockData: (objectId) => dataBlockDataMap.get(objectId),
1018
1017
  getDataBlockParser: (cid) => this.registry.getDataBlockParser(cid),
1019
1018
  }) ?? {};