t2-demo-parser 2.1.0 → 3.0.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.
Files changed (91) hide show
  1. package/README.md +170 -7
  2. package/dist/BitStream.d.ts +14 -3
  3. package/dist/BitStream.d.ts.map +1 -1
  4. package/dist/BitStream.js +28 -6
  5. package/dist/BitStream.js.map +1 -1
  6. package/dist/BitWriter.d.ts +29 -0
  7. package/dist/BitWriter.d.ts.map +1 -0
  8. package/dist/BitWriter.js +76 -0
  9. package/dist/BitWriter.js.map +1 -0
  10. package/dist/ClassRegistry.d.ts +23 -28
  11. package/dist/ClassRegistry.d.ts.map +1 -1
  12. package/dist/ClassRegistry.js +75 -84
  13. package/dist/ClassRegistry.js.map +1 -1
  14. package/dist/DataBlockParsers.d.ts.map +1 -1
  15. package/dist/DataBlockParsers.js +51 -76
  16. package/dist/DataBlockParsers.js.map +1 -1
  17. package/dist/DemoParser.d.ts +86 -23
  18. package/dist/DemoParser.d.ts.map +1 -1
  19. package/dist/DemoParser.js +409 -264
  20. package/dist/DemoParser.js.map +1 -1
  21. package/dist/EventParsers.d.ts.map +1 -1
  22. package/dist/EventParsers.js +87 -58
  23. package/dist/EventParsers.js.map +1 -1
  24. package/dist/GhostManager.d.ts +7 -1
  25. package/dist/GhostManager.d.ts.map +1 -1
  26. package/dist/GhostManager.js +231 -146
  27. package/dist/GhostManager.js.map +1 -1
  28. package/dist/HuffmanProcessor.d.ts +15 -0
  29. package/dist/HuffmanProcessor.d.ts.map +1 -1
  30. package/dist/HuffmanProcessor.js +45 -0
  31. package/dist/HuffmanProcessor.js.map +1 -1
  32. package/dist/LiveParser.d.ts +20 -1
  33. package/dist/LiveParser.d.ts.map +1 -1
  34. package/dist/LiveParser.js +31 -12
  35. package/dist/LiveParser.js.map +1 -1
  36. package/dist/PacketParser.d.ts +22 -2
  37. package/dist/PacketParser.d.ts.map +1 -1
  38. package/dist/PacketParser.js +111 -48
  39. package/dist/PacketParser.js.map +1 -1
  40. package/dist/Timeline.d.ts +5 -2
  41. package/dist/Timeline.d.ts.map +1 -1
  42. package/dist/Timeline.js +25 -12
  43. package/dist/Timeline.js.map +1 -1
  44. package/dist/cli.js +3 -0
  45. package/dist/cli.js.map +1 -1
  46. package/dist/dataBlockDataTypes.d.ts +111 -112
  47. package/dist/dataBlockDataTypes.d.ts.map +1 -1
  48. package/dist/dataBlockDataTypes.js.map +1 -1
  49. package/dist/defaultRegistry.d.ts +9 -0
  50. package/dist/defaultRegistry.d.ts.map +1 -0
  51. package/dist/defaultRegistry.js +43 -0
  52. package/dist/defaultRegistry.js.map +1 -0
  53. package/dist/eventDataTypes.d.ts +74 -72
  54. package/dist/eventDataTypes.d.ts.map +1 -1
  55. package/dist/eventDataTypes.js.map +1 -1
  56. package/dist/ghostDataTypes.d.ts +125 -131
  57. package/dist/ghostDataTypes.d.ts.map +1 -1
  58. package/dist/ghostDataTypes.js.map +1 -1
  59. package/dist/index.d.ts +6 -4
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +4 -2
  62. package/dist/index.js.map +1 -1
  63. package/dist/types.d.ts +51 -2
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/types.js +6 -0
  66. package/dist/types.js.map +1 -1
  67. package/package.json +4 -1
  68. package/dist/BitStream.test.d.ts +0 -2
  69. package/dist/BitStream.test.d.ts.map +0 -1
  70. package/dist/BitStream.test.js +0 -419
  71. package/dist/BitStream.test.js.map +0 -1
  72. package/dist/ClassRegistry.test.d.ts +0 -2
  73. package/dist/ClassRegistry.test.d.ts.map +0 -1
  74. package/dist/ClassRegistry.test.js +0 -83
  75. package/dist/ClassRegistry.test.js.map +0 -1
  76. package/dist/DataBlockParsers.test.d.ts +0 -2
  77. package/dist/DataBlockParsers.test.d.ts.map +0 -1
  78. package/dist/DataBlockParsers.test.js +0 -294
  79. package/dist/DataBlockParsers.test.js.map +0 -1
  80. package/dist/DemoParser.test.d.ts +0 -2
  81. package/dist/DemoParser.test.d.ts.map +0 -1
  82. package/dist/DemoParser.test.js +0 -472
  83. package/dist/DemoParser.test.js.map +0 -1
  84. package/dist/GhostStateAccumulator.test.d.ts +0 -2
  85. package/dist/GhostStateAccumulator.test.d.ts.map +0 -1
  86. package/dist/GhostStateAccumulator.test.js +0 -201
  87. package/dist/GhostStateAccumulator.test.js.map +0 -1
  88. package/dist/GhostTracker.test.d.ts +0 -2
  89. package/dist/GhostTracker.test.d.ts.map +0 -1
  90. package/dist/GhostTracker.test.js +0 -64
  91. package/dist/GhostTracker.test.js.map +0 -1
@@ -0,0 +1,76 @@
1
+ import { huffProcessor } from "./HuffmanProcessor.js";
2
+ /**
3
+ * LSB-first bit packer producing buffers that BitStream reads back
4
+ * bit-for-bit. Mirrors the write side of the engine's BitStream for the
5
+ * primitives the parser consumes; intended for building test vectors and
6
+ * synthetic packets.
7
+ */
8
+ export class BitWriter {
9
+ bytes = [];
10
+ bitNum = 0;
11
+ getCurPos() {
12
+ return this.bitNum;
13
+ }
14
+ writeFlag(value) {
15
+ return this.writeInt(value ? 1 : 0, 1);
16
+ }
17
+ /** Write the low `bitCount` bits of `value` (up to 32), LSB first. */
18
+ writeInt(value, bitCount) {
19
+ if (!(bitCount >= 0 && bitCount <= 32)) {
20
+ throw new RangeError(`writeInt bitCount out of range: ${bitCount}`);
21
+ }
22
+ for (let i = 0; i < bitCount; i++) {
23
+ const byteIndex = this.bitNum >> 3;
24
+ this.bytes[byteIndex] ??= 0;
25
+ // Use arithmetic on the float64 value so bit 31 works for values
26
+ // above 2^31 - 1.
27
+ const bit = Math.floor(value / 2 ** i) % 2 !== 0 ? 1 : 0;
28
+ this.bytes[byteIndex] |= bit << (this.bitNum & 7);
29
+ this.bitNum++;
30
+ }
31
+ return this;
32
+ }
33
+ writeSignedInt(value, bitCount) {
34
+ this.writeFlag(value < 0);
35
+ return this.writeInt(Math.abs(value), bitCount - 1);
36
+ }
37
+ writeU8(value) {
38
+ return this.writeInt(value & 0xff, 8);
39
+ }
40
+ writeU32(value) {
41
+ return this.writeInt(value >>> 0, 32);
42
+ }
43
+ writeS32(value) {
44
+ return this.writeInt(value >>> 0, 32);
45
+ }
46
+ writeF32(value) {
47
+ const view = new DataView(new ArrayBuffer(4));
48
+ view.setFloat32(0, value, true);
49
+ return this.writeInt(view.getUint32(0, true), 32);
50
+ }
51
+ /** Write a float in [0, 1] quantized to `bitCount` bits. */
52
+ writeFloat(value, bitCount) {
53
+ return this.writeInt(Math.round(value * (2 ** bitCount - 1)), bitCount);
54
+ }
55
+ writeBytes(bytes) {
56
+ for (let i = 0; i < bytes.length; i++)
57
+ this.writeU8(bytes[i]);
58
+ return this;
59
+ }
60
+ /** Write a Huffman string exactly as the engine's writeString does
61
+ * (no string-buffer prefix optimization). */
62
+ writeString(value) {
63
+ huffProcessor.writeHuffBuffer(this, value);
64
+ return this;
65
+ }
66
+ /** Pad to a byte boundary (plus optional extra zero bytes) and return
67
+ * the packed buffer. */
68
+ finish(padBytes = 0) {
69
+ const total = ((this.bitNum + 7) >> 3) + padBytes;
70
+ const out = new Uint8Array(total);
71
+ for (let i = 0; i < this.bytes.length; i++)
72
+ out[i] = this.bytes[i];
73
+ return out;
74
+ }
75
+ }
76
+ //# sourceMappingURL=BitWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BitWriter.js","sourceRoot":"","sources":["../src/BitWriter.ts"],"names":[],"mappings":"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","sourcesContent":["import { huffProcessor } from \"./HuffmanProcessor.js\";\n\n/**\n * LSB-first bit packer producing buffers that BitStream reads back\n * bit-for-bit. Mirrors the write side of the engine's BitStream for the\n * primitives the parser consumes; intended for building test vectors and\n * synthetic packets.\n */\nexport class BitWriter {\n private bytes: number[] = [];\n private bitNum = 0;\n\n getCurPos(): number {\n return this.bitNum;\n }\n\n writeFlag(value: boolean): this {\n return this.writeInt(value ? 1 : 0, 1);\n }\n\n /** Write the low `bitCount` bits of `value` (up to 32), LSB first. */\n writeInt(value: number, bitCount: number): this {\n if (!(bitCount >= 0 && bitCount <= 32)) {\n throw new RangeError(`writeInt bitCount out of range: ${bitCount}`);\n }\n for (let i = 0; i < bitCount; i++) {\n const byteIndex = this.bitNum >> 3;\n this.bytes[byteIndex] ??= 0;\n // Use arithmetic on the float64 value so bit 31 works for values\n // above 2^31 - 1.\n const bit = Math.floor(value / 2 ** i) % 2 !== 0 ? 1 : 0;\n this.bytes[byteIndex] |= bit << (this.bitNum & 7);\n this.bitNum++;\n }\n return this;\n }\n\n writeSignedInt(value: number, bitCount: number): this {\n this.writeFlag(value < 0);\n return this.writeInt(Math.abs(value), bitCount - 1);\n }\n\n writeU8(value: number): this {\n return this.writeInt(value & 0xff, 8);\n }\n\n writeU32(value: number): this {\n return this.writeInt(value >>> 0, 32);\n }\n\n writeS32(value: number): this {\n return this.writeInt(value >>> 0, 32);\n }\n\n writeF32(value: number): this {\n const view = new DataView(new ArrayBuffer(4));\n view.setFloat32(0, value, true);\n return this.writeInt(view.getUint32(0, true), 32);\n }\n\n /** Write a float in [0, 1] quantized to `bitCount` bits. */\n writeFloat(value: number, bitCount: number): this {\n return this.writeInt(Math.round(value * (2 ** bitCount - 1)), bitCount);\n }\n\n writeBytes(bytes: ArrayLike<number>): this {\n for (let i = 0; i < bytes.length; i++) this.writeU8(bytes[i]);\n return this;\n }\n\n /** Write a Huffman string exactly as the engine's writeString does\n * (no string-buffer prefix optimization). */\n writeString(value: string): this {\n huffProcessor.writeHuffBuffer(this, value);\n return this;\n }\n\n /** Pad to a byte boundary (plus optional extra zero bytes) and return\n * the packed buffer. */\n finish(padBytes = 0): Uint8Array {\n const total = ((this.bitNum + 7) >> 3) + padBytes;\n const out = new Uint8Array(total);\n for (let i = 0; i < this.bytes.length; i++) out[i] = this.bytes[i];\n return out;\n }\n}\n"]}
@@ -22,8 +22,6 @@ export interface ConnectionContext {
22
22
  getDataBlockParser?: (classId: number) => DataBlockParserEntry | undefined;
23
23
  getDataBlockData?: (objectId: number) => ParsedData | undefined;
24
24
  getGhostParser?: (classId: number) => GhostParserEntry | undefined;
25
- /** Ghost index of the ghost currently being parsed (set per-ghost in readGhosts). */
26
- currentGhostIndex?: number;
27
25
  }
28
26
  /** Minimal interface for ghost tracker used by parsers. */
29
27
  export interface GhostTrackerInterface {
@@ -32,7 +30,6 @@ export interface GhostTrackerInterface {
32
30
  export interface GhostEntry {
33
31
  classId: number;
34
32
  className: string;
35
- state: ParsedData;
36
33
  }
37
34
  export interface GhostParserEntry {
38
35
  name: string;
@@ -54,38 +51,30 @@ export interface DataBlockParserEntry {
54
51
  * via bindDeterministicGhosts/Events/DataBlocks.
55
52
  */
56
53
  export declare class ClassRegistry {
57
- private eventParsers;
58
- private ghostParsers;
59
- private dataBlockParsers;
60
- private eventCatalog;
61
- private ghostCatalog;
62
- private dataBlockCatalog;
63
- catalogEvent(entry: EventParserEntry): void;
64
- catalogGhost(entry: GhostParserEntry): void;
65
- catalogDataBlock(entry: DataBlockParserEntry): void;
66
- /**
67
- * Bind all DataBlock parsers deterministically using the known class name
68
- * mapping. For each class name in the sorted list, if we have a parser
69
- * in the catalog, bind it to classId = DataBlockClassFirst + index.
70
- */
54
+ private events;
55
+ private ghosts;
56
+ private dataBlocks;
57
+ catalogEvent<T extends EventData>(entry: {
58
+ name: string;
59
+ unpack: (bs: BitStream, conn: ConnectionContext) => T;
60
+ }): void;
61
+ catalogGhost<T extends object, P extends object = ParsedData>(entry: {
62
+ name: string;
63
+ unpackUpdate: (bs: BitStream, isInitial: boolean, conn: ConnectionContext) => T;
64
+ readPacketData?: (bs: BitStream, conn: ConnectionContext) => P;
65
+ }): void;
66
+ catalogDataBlock<T extends object>(entry: {
67
+ name: string;
68
+ unpackData: (bs: BitStream) => T;
69
+ }): void;
71
70
  bindDeterministicDataBlocks(classNames: readonly string[], classFirst: number): {
72
71
  bound: number;
73
72
  missing: string[];
74
73
  };
75
- /**
76
- * Bind all event (NetEvent) parsers deterministically using the known
77
- * class name mapping. For each class name in the sorted list, if we have
78
- * a parser in the catalog, bind it to classId = classFirst + index.
79
- */
80
74
  bindDeterministicEvents(classNames: readonly string[], classFirst: number): {
81
75
  bound: number;
82
76
  missing: string[];
83
77
  };
84
- /**
85
- * Bind all ghost (NetObject) parsers deterministically using the known
86
- * class name mapping. For each class name in the sorted list, if we have
87
- * a parser in the catalog, bind it to classId = classFirst + index.
88
- */
89
78
  bindDeterministicGhosts(classNames: readonly string[], classFirst: number): {
90
79
  bound: number;
91
80
  missing: string[];
@@ -93,8 +82,14 @@ export declare class ClassRegistry {
93
82
  getEventParser(classId: number): EventParserEntry | undefined;
94
83
  getGhostParser(classId: number): GhostParserEntry | undefined;
95
84
  getDataBlockParser(classId: number): DataBlockParserEntry | undefined;
96
- getGhostCatalog(): Map<string, GhostParserEntry>;
85
+ getEventClassId(name: string): number | undefined;
86
+ getGhostClassId(name: string): number | undefined;
87
+ getDataBlockClassId(name: string): number | undefined;
88
+ getEventCatalog(): ReadonlyMap<string, EventParserEntry>;
89
+ getGhostCatalog(): ReadonlyMap<string, GhostParserEntry>;
90
+ getDataBlockCatalog(): ReadonlyMap<string, DataBlockParserEntry>;
97
91
  getEventBindings(): Map<number, string>;
98
92
  getGhostBindings(): Map<number, string>;
93
+ getDataBlockBindings(): Map<number, string>;
99
94
  }
100
95
  //# sourceMappingURL=ClassRegistry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClassRegistry.d.ts","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClassRegistry.d.ts","sourceRoot":"","sources":["../src/ClassRegistry.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,34 @@
1
+ function newCatalog() {
2
+ return { byName: new Map(), byClassId: new Map(), classIdByName: new Map() };
3
+ }
4
+ /**
5
+ * Bind every cataloged parser whose name appears in `classNames` to
6
+ * classId = classFirst + index. Names without a parser are reported.
7
+ */
8
+ function bindCatalog(catalog, classNames, classFirst) {
9
+ let bound = 0;
10
+ const missing = [];
11
+ for (let i = 0; i < classNames.length; i++) {
12
+ const name = classNames[i];
13
+ const entry = catalog.byName.get(name);
14
+ if (entry) {
15
+ catalog.byClassId.set(classFirst + i, entry);
16
+ catalog.classIdByName.set(name, classFirst + i);
17
+ bound++;
18
+ }
19
+ else {
20
+ missing.push(name);
21
+ }
22
+ }
23
+ return { bound, missing };
24
+ }
25
+ function bindingsOf(catalog) {
26
+ const bindings = new Map();
27
+ for (const [id, entry] of catalog.byClassId) {
28
+ bindings.set(id, entry.name);
29
+ }
30
+ return bindings;
31
+ }
1
32
  /**
2
33
  * Registry mapping classIds to parser functions.
3
34
  * ClassIds are assigned deterministically by alphabetical sort (C strcmp)
@@ -5,114 +36,74 @@
5
36
  * via bindDeterministicGhosts/Events/DataBlocks.
6
37
  */
7
38
  export class ClassRegistry {
8
- eventParsers = new Map();
9
- ghostParsers = new Map();
10
- dataBlockParsers = new Map();
11
- // Named parser catalog (classId-independent)
12
- eventCatalog = new Map();
13
- ghostCatalog = new Map();
14
- dataBlockCatalog = new Map();
39
+ events = newCatalog();
40
+ ghosts = newCatalog();
41
+ dataBlocks = newCatalog();
15
42
  // --- Catalog registration (name → parser, no classId yet) ---
43
+ //
44
+ // Parsers return precisely typed objects (PlayerGhostData, ...), which
45
+ // are stored behind the generic ParsedData shape. The generic signatures
46
+ // let each parser keep its own return type — so a misspelled field is a
47
+ // compile error inside the parser — while the registry erases it.
16
48
  catalogEvent(entry) {
17
- this.eventCatalog.set(entry.name, entry);
49
+ this.events.byName.set(entry.name, entry);
18
50
  }
19
51
  catalogGhost(entry) {
20
- this.ghostCatalog.set(entry.name, entry);
52
+ this.ghosts.byName.set(entry.name, entry);
21
53
  }
22
54
  catalogDataBlock(entry) {
23
- this.dataBlockCatalog.set(entry.name, entry);
55
+ this.dataBlocks.byName.set(entry.name, entry);
24
56
  }
25
- /**
26
- * Bind all DataBlock parsers deterministically using the known class name
27
- * mapping. For each class name in the sorted list, if we have a parser
28
- * in the catalog, bind it to classId = DataBlockClassFirst + index.
29
- */
57
+ // --- Deterministic binding (classId = classFirst + index in the sorted
58
+ // class name list) ---
30
59
  bindDeterministicDataBlocks(classNames, classFirst) {
31
- let bound = 0;
32
- const missing = [];
33
- for (let i = 0; i < classNames.length; i++) {
34
- const name = classNames[i];
35
- const entry = this.dataBlockCatalog.get(name);
36
- if (entry) {
37
- this.dataBlockParsers.set(classFirst + i, entry);
38
- bound++;
39
- }
40
- else {
41
- missing.push(name);
42
- }
43
- }
44
- return { bound, missing };
60
+ return bindCatalog(this.dataBlocks, classNames, classFirst);
45
61
  }
46
- /**
47
- * Bind all event (NetEvent) parsers deterministically using the known
48
- * class name mapping. For each class name in the sorted list, if we have
49
- * a parser in the catalog, bind it to classId = classFirst + index.
50
- */
51
62
  bindDeterministicEvents(classNames, classFirst) {
52
- let bound = 0;
53
- const missing = [];
54
- for (let i = 0; i < classNames.length; i++) {
55
- const name = classNames[i];
56
- const entry = this.eventCatalog.get(name);
57
- if (entry) {
58
- this.eventParsers.set(classFirst + i, entry);
59
- bound++;
60
- }
61
- else {
62
- missing.push(name);
63
- }
64
- }
65
- return { bound, missing };
63
+ return bindCatalog(this.events, classNames, classFirst);
66
64
  }
67
- /**
68
- * Bind all ghost (NetObject) parsers deterministically using the known
69
- * class name mapping. For each class name in the sorted list, if we have
70
- * a parser in the catalog, bind it to classId = classFirst + index.
71
- */
72
65
  bindDeterministicGhosts(classNames, classFirst) {
73
- let bound = 0;
74
- const missing = [];
75
- for (let i = 0; i < classNames.length; i++) {
76
- const name = classNames[i];
77
- const entry = this.ghostCatalog.get(name);
78
- if (entry) {
79
- this.ghostParsers.set(classFirst + i, entry);
80
- bound++;
81
- }
82
- else {
83
- missing.push(name);
84
- }
85
- }
86
- return { bound, missing };
66
+ return bindCatalog(this.ghosts, classNames, classFirst);
87
67
  }
88
- // --- Lookup ---
68
+ // --- Lookup by classId ---
89
69
  getEventParser(classId) {
90
- return this.eventParsers.get(classId);
70
+ return this.events.byClassId.get(classId);
91
71
  }
92
72
  getGhostParser(classId) {
93
- return this.ghostParsers.get(classId);
73
+ return this.ghosts.byClassId.get(classId);
94
74
  }
95
75
  getDataBlockParser(classId) {
96
- return this.dataBlockParsers.get(classId);
76
+ return this.dataBlocks.byClassId.get(classId);
77
+ }
78
+ // --- Lookup by class name (bound classId) ---
79
+ getEventClassId(name) {
80
+ return this.events.classIdByName.get(name);
81
+ }
82
+ getGhostClassId(name) {
83
+ return this.ghosts.classIdByName.get(name);
84
+ }
85
+ getDataBlockClassId(name) {
86
+ return this.dataBlocks.classIdByName.get(name);
87
+ }
88
+ // --- Catalog access (name → parser, independent of binding) ---
89
+ getEventCatalog() {
90
+ return this.events.byName;
97
91
  }
98
- // --- Catalog access ---
99
92
  getGhostCatalog() {
100
- return this.ghostCatalog;
93
+ return this.ghosts.byName;
94
+ }
95
+ getDataBlockCatalog() {
96
+ return this.dataBlocks.byName;
101
97
  }
102
- // --- Debug ---
98
+ // --- Debug: classId → parser name ---
103
99
  getEventBindings() {
104
- const bindings = new Map();
105
- for (const [id, entry] of this.eventParsers) {
106
- bindings.set(id, entry.name);
107
- }
108
- return bindings;
100
+ return bindingsOf(this.events);
109
101
  }
110
102
  getGhostBindings() {
111
- const bindings = new Map();
112
- for (const [id, entry] of this.ghostParsers) {
113
- bindings.set(id, entry.name);
114
- }
115
- return bindings;
103
+ return bindingsOf(this.ghosts);
104
+ }
105
+ getDataBlockBindings() {
106
+ return bindingsOf(this.dataBlocks);
116
107
  }
117
108
  }
118
109
  //# sourceMappingURL=ClassRegistry.js.map
@@ -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 /** Ghost index of the ghost currently being parsed (set per-ghost in readGhosts). */\n currentGhostIndex?: number;\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 state: ParsedData;\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\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 eventParsers = new Map<number, EventParserEntry>();\n private ghostParsers = new Map<number, GhostParserEntry>();\n private dataBlockParsers = new Map<number, DataBlockParserEntry>();\n\n // Named parser catalog (classId-independent)\n private eventCatalog = new Map<string, EventParserEntry>();\n private ghostCatalog = new Map<string, GhostParserEntry>();\n private dataBlockCatalog = new Map<string, DataBlockParserEntry>();\n\n // --- Catalog registration (name → parser, no classId yet) ---\n\n catalogEvent(entry: EventParserEntry): void {\n this.eventCatalog.set(entry.name, entry);\n }\n\n catalogGhost(entry: GhostParserEntry): void {\n this.ghostCatalog.set(entry.name, entry);\n }\n\n catalogDataBlock(entry: DataBlockParserEntry): void {\n this.dataBlockCatalog.set(entry.name, entry);\n }\n\n /**\n * Bind all DataBlock parsers deterministically using the known class name\n * mapping. For each class name in the sorted list, if we have a parser\n * in the catalog, bind it to classId = DataBlockClassFirst + index.\n */\n bindDeterministicDataBlocks(\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 = this.dataBlockCatalog.get(name);\n if (entry) {\n this.dataBlockParsers.set(classFirst + i, entry);\n bound++;\n } else {\n missing.push(name);\n }\n }\n return { bound, missing };\n }\n\n /**\n * Bind all event (NetEvent) parsers deterministically using the known\n * class name mapping. For each class name in the sorted list, if we have\n * a parser in the catalog, bind it to classId = classFirst + index.\n */\n bindDeterministicEvents(\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 = this.eventCatalog.get(name);\n if (entry) {\n this.eventParsers.set(classFirst + i, entry);\n bound++;\n } else {\n missing.push(name);\n }\n }\n return { bound, missing };\n }\n\n /**\n * Bind all ghost (NetObject) parsers deterministically using the known\n * class name mapping. For each class name in the sorted list, if we have\n * a parser in the catalog, bind it to classId = classFirst + index.\n */\n bindDeterministicGhosts(\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 = this.ghostCatalog.get(name);\n if (entry) {\n this.ghostParsers.set(classFirst + i, entry);\n bound++;\n } else {\n missing.push(name);\n }\n }\n return { bound, missing };\n }\n\n // --- Lookup ---\n\n getEventParser(classId: number): EventParserEntry | undefined {\n return this.eventParsers.get(classId);\n }\n\n getGhostParser(classId: number): GhostParserEntry | undefined {\n return this.ghostParsers.get(classId);\n }\n\n getDataBlockParser(classId: number): DataBlockParserEntry | undefined {\n return this.dataBlockParsers.get(classId);\n }\n\n // --- Catalog access ---\n\n getGhostCatalog(): Map<string, GhostParserEntry> {\n return this.ghostCatalog;\n }\n\n // --- Debug ---\n\n getEventBindings(): Map<number, string> {\n const bindings = new Map<number, string>();\n for (const [id, entry] of this.eventParsers) {\n bindings.set(id, entry.name);\n }\n return bindings;\n }\n\n getGhostBindings(): Map<number, string> {\n const bindings = new Map<number, string>();\n for (const [id, entry] of this.ghostParsers) {\n bindings.set(id, entry.name);\n }\n return bindings;\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 = (\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 +1 @@
1
- {"version":3,"file":"DataBlockParsers.d.ts","sourceRoot":"","sources":["../src/DataBlockParsers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAqxExD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CA0OtE"}
1
+ {"version":3,"file":"DataBlockParsers.d.ts","sourceRoot":"","sources":["../src/DataBlockParsers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAuwExD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CA0OtE"}