quake2ts 0.0.403 → 0.0.405

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 (30) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +16 -16
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +383 -11
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +382 -11
  7. package/packages/client/dist/esm/index.js.map +1 -1
  8. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  9. package/packages/client/dist/types/demo/camera.d.ts +16 -0
  10. package/packages/client/dist/types/demo/camera.d.ts.map +1 -0
  11. package/packages/client/dist/types/index.d.ts +7 -1
  12. package/packages/client/dist/types/index.d.ts.map +1 -1
  13. package/packages/engine/dist/browser/index.global.js +16 -16
  14. package/packages/engine/dist/browser/index.global.js.map +1 -1
  15. package/packages/engine/dist/cjs/index.cjs +274 -2
  16. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  17. package/packages/engine/dist/esm/index.js +274 -2
  18. package/packages/engine/dist/esm/index.js.map +1 -1
  19. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  20. package/packages/engine/dist/types/commands.d.ts +3 -0
  21. package/packages/engine/dist/types/commands.d.ts.map +1 -1
  22. package/packages/engine/dist/types/cvars.d.ts +11 -0
  23. package/packages/engine/dist/types/cvars.d.ts.map +1 -1
  24. package/packages/engine/dist/types/demo/analysis.d.ts +33 -0
  25. package/packages/engine/dist/types/demo/analysis.d.ts.map +1 -1
  26. package/packages/engine/dist/types/demo/analyzer.d.ts +28 -0
  27. package/packages/engine/dist/types/demo/analyzer.d.ts.map +1 -0
  28. package/packages/engine/dist/types/demo/playback.d.ts +16 -1
  29. package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
  30. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
@@ -295,6 +295,9 @@ var CommandRegistry = class {
295
295
  this.commands.set(name, command);
296
296
  return command;
297
297
  }
298
+ registerCommand(name, callback) {
299
+ this.register(name, callback);
300
+ }
298
301
  get(name) {
299
302
  return this.commands.get(name);
300
303
  }
@@ -310,10 +313,33 @@ var CommandRegistry = class {
310
313
  command.execute(args);
311
314
  return true;
312
315
  }
316
+ this.onConsoleOutput?.(`Unknown command "${name}"`);
313
317
  return false;
314
318
  }
319
+ executeCommand(cmd) {
320
+ this.execute(cmd);
321
+ }
315
322
  tokenize(text) {
316
- return text.trim().split(/\s+/);
323
+ const args = [];
324
+ let currentArg = "";
325
+ let inQuote = false;
326
+ for (let i = 0; i < text.length; i++) {
327
+ const char = text[i];
328
+ if (char === '"') {
329
+ inQuote = !inQuote;
330
+ } else if (char === " " && !inQuote) {
331
+ if (currentArg.length > 0) {
332
+ args.push(currentArg);
333
+ currentArg = "";
334
+ }
335
+ } else {
336
+ currentArg += char;
337
+ }
338
+ }
339
+ if (currentArg.length > 0) {
340
+ args.push(currentArg);
341
+ }
342
+ return args;
317
343
  }
318
344
  list() {
319
345
  return [...this.commands.values()].sort((a, b) => a.name.localeCompare(b.name));
@@ -1508,13 +1534,21 @@ var CvarRegistry = class {
1508
1534
  if (existing) {
1509
1535
  return existing;
1510
1536
  }
1511
- const cvar = new Cvar(def);
1537
+ const originalOnChange = def.onChange;
1538
+ const wrappedOnChange = (cvar2, prev) => {
1539
+ originalOnChange?.(cvar2, prev);
1540
+ this.onCvarChange?.(cvar2.name, cvar2.string);
1541
+ };
1542
+ const cvar = new Cvar({ ...def, onChange: wrappedOnChange });
1512
1543
  this.cvars.set(def.name, cvar);
1513
1544
  return cvar;
1514
1545
  }
1515
1546
  get(name) {
1516
1547
  return this.cvars.get(name);
1517
1548
  }
1549
+ getCvar(name) {
1550
+ return this.get(name);
1551
+ }
1518
1552
  setValue(name, value) {
1519
1553
  const cvar = this.get(name);
1520
1554
  if (!cvar) {
@@ -1523,6 +1557,9 @@ var CvarRegistry = class {
1523
1557
  cvar.set(value);
1524
1558
  return cvar;
1525
1559
  }
1560
+ setCvar(name, value) {
1561
+ this.setValue(name, value);
1562
+ }
1526
1563
  resetAll() {
1527
1564
  for (const cvar of this.cvars.values()) {
1528
1565
  cvar.reset();
@@ -1538,6 +1575,15 @@ var CvarRegistry = class {
1538
1575
  list() {
1539
1576
  return [...this.cvars.values()].sort((a, b) => a.name.localeCompare(b.name));
1540
1577
  }
1578
+ listCvars() {
1579
+ return this.list().map((cvar) => ({
1580
+ name: cvar.name,
1581
+ value: cvar.string,
1582
+ defaultValue: cvar.defaultValue,
1583
+ flags: cvar.flags,
1584
+ description: cvar.description
1585
+ }));
1586
+ }
1541
1587
  };
1542
1588
 
1543
1589
  // src/host.ts
@@ -12627,6 +12673,165 @@ var NetworkMessageParser = class _NetworkMessageParser {
12627
12673
  }
12628
12674
  };
12629
12675
 
12676
+ // src/demo/analyzer.ts
12677
+ var DemoAnalyzer = class {
12678
+ constructor(buffer) {
12679
+ this.events = [];
12680
+ this.summary = {
12681
+ totalKills: 0,
12682
+ totalDeaths: 0,
12683
+ damageDealt: 0,
12684
+ damageReceived: 0,
12685
+ weaponUsage: /* @__PURE__ */ new Map()
12686
+ };
12687
+ this.header = null;
12688
+ this.configStrings = /* @__PURE__ */ new Map();
12689
+ this.serverInfo = {};
12690
+ this.statistics = null;
12691
+ this.playerStats = /* @__PURE__ */ new Map();
12692
+ // By playerNum
12693
+ this.weaponStats = /* @__PURE__ */ new Map();
12694
+ this.buffer = buffer;
12695
+ }
12696
+ analyze() {
12697
+ const reader = new DemoReader(this.buffer);
12698
+ let currentFrameIndex = -1;
12699
+ let currentTime = 0;
12700
+ let frameDuration = 0.1;
12701
+ let protocolVersion = 0;
12702
+ const handler = {
12703
+ onServerData: (protocol, serverCount, attractLoop, gameDir, playerNum, levelName, tickRate, demoType) => {
12704
+ protocolVersion = protocol;
12705
+ this.header = {
12706
+ protocolVersion: protocol,
12707
+ gameDir,
12708
+ levelName,
12709
+ playerNum,
12710
+ serverCount,
12711
+ spawnCount: serverCount,
12712
+ // Mapping generic arg
12713
+ tickRate,
12714
+ demoType
12715
+ };
12716
+ if (tickRate && tickRate > 0) {
12717
+ frameDuration = 1 / tickRate;
12718
+ }
12719
+ },
12720
+ onConfigString: (index, str) => {
12721
+ this.configStrings.set(index, str);
12722
+ if (index === 0) {
12723
+ this.parseServerInfo(str);
12724
+ }
12725
+ },
12726
+ onSpawnBaseline: (entity) => {
12727
+ },
12728
+ onFrame: (frame) => {
12729
+ },
12730
+ onPrint: (level, msg) => {
12731
+ if (msg.includes("died") || msg.includes("killed")) {
12732
+ this.summary.totalDeaths++;
12733
+ this.recordEvent({
12734
+ type: 4 /* Death */,
12735
+ frame: currentFrameIndex,
12736
+ time: currentTime,
12737
+ description: msg.trim()
12738
+ });
12739
+ }
12740
+ },
12741
+ onCenterPrint: () => {
12742
+ },
12743
+ onStuffText: () => {
12744
+ },
12745
+ onSound: () => {
12746
+ },
12747
+ onTempEntity: () => {
12748
+ },
12749
+ onLayout: () => {
12750
+ },
12751
+ onInventory: () => {
12752
+ },
12753
+ onMuzzleFlash: (ent, weapon) => {
12754
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12755
+ },
12756
+ onMuzzleFlash2: (ent, weapon) => {
12757
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12758
+ },
12759
+ onMuzzleFlash3: (ent, weapon) => {
12760
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12761
+ },
12762
+ onDisconnect: () => {
12763
+ },
12764
+ onReconnect: () => {
12765
+ },
12766
+ onDownload: () => {
12767
+ },
12768
+ // Rerelease specific
12769
+ onDamage: (indicators) => {
12770
+ for (const ind of indicators) {
12771
+ this.recordEvent({
12772
+ type: 2 /* DamageReceived */,
12773
+ frame: currentFrameIndex,
12774
+ time: currentTime,
12775
+ value: ind.damage,
12776
+ position: ind.dir,
12777
+ description: `Took ${ind.damage} damage`
12778
+ });
12779
+ this.summary.damageReceived += ind.damage;
12780
+ }
12781
+ }
12782
+ };
12783
+ while (reader.hasMore()) {
12784
+ const block = reader.readNextBlock();
12785
+ if (!block) break;
12786
+ currentFrameIndex++;
12787
+ currentTime = currentFrameIndex * frameDuration;
12788
+ const parser = new NetworkMessageParser(block.data, handler);
12789
+ parser.setProtocolVersion(protocolVersion);
12790
+ parser.parseMessage();
12791
+ protocolVersion = parser.getProtocolVersion();
12792
+ }
12793
+ this.statistics = {
12794
+ duration: currentTime,
12795
+ frameCount: currentFrameIndex + 1,
12796
+ averageFps: (currentFrameIndex + 1) / (currentTime || 1),
12797
+ mapName: this.header?.levelName || "unknown",
12798
+ playerCount: 1
12799
+ // Default to 1 for SP/client demo
12800
+ };
12801
+ return {
12802
+ events: this.events,
12803
+ summary: this.summary,
12804
+ header: this.header,
12805
+ configStrings: this.configStrings,
12806
+ serverInfo: this.serverInfo,
12807
+ statistics: this.statistics
12808
+ };
12809
+ }
12810
+ handleWeaponFire(ent, weapon, frame, time) {
12811
+ this.recordEvent({
12812
+ type: 0 /* WeaponFire */,
12813
+ frame,
12814
+ time,
12815
+ entityId: ent,
12816
+ value: weapon,
12817
+ description: `Weapon ${weapon} fired by ${ent}`
12818
+ });
12819
+ const count = this.summary.weaponUsage.get(weapon) || 0;
12820
+ this.summary.weaponUsage.set(weapon, count + 1);
12821
+ }
12822
+ recordEvent(event) {
12823
+ this.events.push(event);
12824
+ }
12825
+ parseServerInfo(str) {
12826
+ const parts = str.split("\\");
12827
+ for (let i = 1; i < parts.length; i += 2) {
12828
+ if (i + 1 < parts.length) {
12829
+ this.serverInfo[parts[i]] = parts[i + 1];
12830
+ }
12831
+ }
12832
+ }
12833
+ };
12834
+
12630
12835
  // src/demo/playback.ts
12631
12836
  var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
12632
12837
  PlaybackState2[PlaybackState2["Stopped"] = 0] = "Stopped";
@@ -12638,6 +12843,8 @@ var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
12638
12843
  var DemoPlaybackController = class {
12639
12844
  constructor() {
12640
12845
  this.reader = null;
12846
+ this.buffer = null;
12847
+ // Keep reference for analysis
12641
12848
  this.state = 0 /* Stopped */;
12642
12849
  this.playbackSpeed = 1;
12643
12850
  this.currentProtocolVersion = 0;
@@ -12653,6 +12860,13 @@ var DemoPlaybackController = class {
12653
12860
  this.snapshotInterval = 100;
12654
12861
  // frames
12655
12862
  this.snapshots = /* @__PURE__ */ new Map();
12863
+ // Analysis Cache
12864
+ this.cachedEvents = null;
12865
+ this.cachedSummary = null;
12866
+ this.cachedHeader = null;
12867
+ this.cachedConfigStrings = null;
12868
+ this.cachedServerInfo = null;
12869
+ this.cachedStatistics = null;
12656
12870
  }
12657
12871
  setHandler(handler) {
12658
12872
  this.handler = handler;
@@ -12661,6 +12875,7 @@ var DemoPlaybackController = class {
12661
12875
  this.callbacks = callbacks;
12662
12876
  }
12663
12877
  loadDemo(buffer) {
12878
+ this.buffer = buffer;
12664
12879
  this.reader = new DemoReader(buffer);
12665
12880
  this.transitionState(0 /* Stopped */);
12666
12881
  this.accumulatedTime = 0;
@@ -12668,6 +12883,12 @@ var DemoPlaybackController = class {
12668
12883
  this.currentFrameIndex = -1;
12669
12884
  this.snapshots.clear();
12670
12885
  this.lastFrameData = null;
12886
+ this.cachedEvents = null;
12887
+ this.cachedSummary = null;
12888
+ this.cachedHeader = null;
12889
+ this.cachedConfigStrings = null;
12890
+ this.cachedServerInfo = null;
12891
+ this.cachedStatistics = null;
12671
12892
  }
12672
12893
  play() {
12673
12894
  if (this.reader && this.state !== 1 /* Playing */) {
@@ -12980,6 +13201,57 @@ var DemoPlaybackController = class {
12980
13201
  this.seek(originalFrame);
12981
13202
  return trajectory;
12982
13203
  }
13204
+ // 3.2.3 Event Log Extraction & 3.3 Metadata
13205
+ getDemoEvents() {
13206
+ this.ensureAnalysis();
13207
+ return this.cachedEvents || [];
13208
+ }
13209
+ filterEvents(type, entityId) {
13210
+ const events = this.getDemoEvents();
13211
+ return events.filter((e) => {
13212
+ if (e.type !== type) return false;
13213
+ if (entityId !== void 0 && e.entityId !== entityId) return false;
13214
+ return true;
13215
+ });
13216
+ }
13217
+ getEventSummary() {
13218
+ this.ensureAnalysis();
13219
+ return this.cachedSummary || {
13220
+ totalKills: 0,
13221
+ totalDeaths: 0,
13222
+ damageDealt: 0,
13223
+ damageReceived: 0,
13224
+ weaponUsage: /* @__PURE__ */ new Map()
13225
+ };
13226
+ }
13227
+ getDemoHeader() {
13228
+ this.ensureAnalysis();
13229
+ return this.cachedHeader;
13230
+ }
13231
+ getDemoConfigStrings() {
13232
+ this.ensureAnalysis();
13233
+ return this.cachedConfigStrings || /* @__PURE__ */ new Map();
13234
+ }
13235
+ getDemoServerInfo() {
13236
+ this.ensureAnalysis();
13237
+ return this.cachedServerInfo || {};
13238
+ }
13239
+ getDemoStatistics() {
13240
+ this.ensureAnalysis();
13241
+ return this.cachedStatistics;
13242
+ }
13243
+ ensureAnalysis() {
13244
+ if (!this.cachedEvents && this.buffer) {
13245
+ const analyzer = new DemoAnalyzer(this.buffer);
13246
+ const result = analyzer.analyze();
13247
+ this.cachedEvents = result.events;
13248
+ this.cachedSummary = result.summary;
13249
+ this.cachedHeader = result.header;
13250
+ this.cachedConfigStrings = result.configStrings;
13251
+ this.cachedServerInfo = result.serverInfo;
13252
+ this.cachedStatistics = result.statistics;
13253
+ }
13254
+ }
12983
13255
  };
12984
13256
 
12985
13257
  // src/demo/recorder.ts