quake2ts 0.0.402 → 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
@@ -94,6 +94,9 @@ var CommandRegistry = class {
94
94
  this.commands.set(name, command);
95
95
  return command;
96
96
  }
97
+ registerCommand(name, callback) {
98
+ this.register(name, callback);
99
+ }
97
100
  get(name) {
98
101
  return this.commands.get(name);
99
102
  }
@@ -109,10 +112,33 @@ var CommandRegistry = class {
109
112
  command.execute(args);
110
113
  return true;
111
114
  }
115
+ this.onConsoleOutput?.(`Unknown command "${name}"`);
112
116
  return false;
113
117
  }
118
+ executeCommand(cmd) {
119
+ this.execute(cmd);
120
+ }
114
121
  tokenize(text) {
115
- return text.trim().split(/\s+/);
122
+ const args = [];
123
+ let currentArg = "";
124
+ let inQuote = false;
125
+ for (let i = 0; i < text.length; i++) {
126
+ const char = text[i];
127
+ if (char === '"') {
128
+ inQuote = !inQuote;
129
+ } else if (char === " " && !inQuote) {
130
+ if (currentArg.length > 0) {
131
+ args.push(currentArg);
132
+ currentArg = "";
133
+ }
134
+ } else {
135
+ currentArg += char;
136
+ }
137
+ }
138
+ if (currentArg.length > 0) {
139
+ args.push(currentArg);
140
+ }
141
+ return args;
116
142
  }
117
143
  list() {
118
144
  return [...this.commands.values()].sort((a, b) => a.name.localeCompare(b.name));
@@ -1307,13 +1333,21 @@ var CvarRegistry = class {
1307
1333
  if (existing) {
1308
1334
  return existing;
1309
1335
  }
1310
- const cvar = new Cvar(def);
1336
+ const originalOnChange = def.onChange;
1337
+ const wrappedOnChange = (cvar2, prev) => {
1338
+ originalOnChange?.(cvar2, prev);
1339
+ this.onCvarChange?.(cvar2.name, cvar2.string);
1340
+ };
1341
+ const cvar = new Cvar({ ...def, onChange: wrappedOnChange });
1311
1342
  this.cvars.set(def.name, cvar);
1312
1343
  return cvar;
1313
1344
  }
1314
1345
  get(name) {
1315
1346
  return this.cvars.get(name);
1316
1347
  }
1348
+ getCvar(name) {
1349
+ return this.get(name);
1350
+ }
1317
1351
  setValue(name, value) {
1318
1352
  const cvar = this.get(name);
1319
1353
  if (!cvar) {
@@ -1322,6 +1356,9 @@ var CvarRegistry = class {
1322
1356
  cvar.set(value);
1323
1357
  return cvar;
1324
1358
  }
1359
+ setCvar(name, value) {
1360
+ this.setValue(name, value);
1361
+ }
1325
1362
  resetAll() {
1326
1363
  for (const cvar of this.cvars.values()) {
1327
1364
  cvar.reset();
@@ -1337,6 +1374,15 @@ var CvarRegistry = class {
1337
1374
  list() {
1338
1375
  return [...this.cvars.values()].sort((a, b) => a.name.localeCompare(b.name));
1339
1376
  }
1377
+ listCvars() {
1378
+ return this.list().map((cvar) => ({
1379
+ name: cvar.name,
1380
+ value: cvar.string,
1381
+ defaultValue: cvar.defaultValue,
1382
+ flags: cvar.flags,
1383
+ description: cvar.description
1384
+ }));
1385
+ }
1340
1386
  };
1341
1387
 
1342
1388
  // src/host.ts
@@ -12426,6 +12472,165 @@ var NetworkMessageParser = class _NetworkMessageParser {
12426
12472
  }
12427
12473
  };
12428
12474
 
12475
+ // src/demo/analyzer.ts
12476
+ var DemoAnalyzer = class {
12477
+ constructor(buffer) {
12478
+ this.events = [];
12479
+ this.summary = {
12480
+ totalKills: 0,
12481
+ totalDeaths: 0,
12482
+ damageDealt: 0,
12483
+ damageReceived: 0,
12484
+ weaponUsage: /* @__PURE__ */ new Map()
12485
+ };
12486
+ this.header = null;
12487
+ this.configStrings = /* @__PURE__ */ new Map();
12488
+ this.serverInfo = {};
12489
+ this.statistics = null;
12490
+ this.playerStats = /* @__PURE__ */ new Map();
12491
+ // By playerNum
12492
+ this.weaponStats = /* @__PURE__ */ new Map();
12493
+ this.buffer = buffer;
12494
+ }
12495
+ analyze() {
12496
+ const reader = new DemoReader(this.buffer);
12497
+ let currentFrameIndex = -1;
12498
+ let currentTime = 0;
12499
+ let frameDuration = 0.1;
12500
+ let protocolVersion = 0;
12501
+ const handler = {
12502
+ onServerData: (protocol, serverCount, attractLoop, gameDir, playerNum, levelName, tickRate, demoType) => {
12503
+ protocolVersion = protocol;
12504
+ this.header = {
12505
+ protocolVersion: protocol,
12506
+ gameDir,
12507
+ levelName,
12508
+ playerNum,
12509
+ serverCount,
12510
+ spawnCount: serverCount,
12511
+ // Mapping generic arg
12512
+ tickRate,
12513
+ demoType
12514
+ };
12515
+ if (tickRate && tickRate > 0) {
12516
+ frameDuration = 1 / tickRate;
12517
+ }
12518
+ },
12519
+ onConfigString: (index, str) => {
12520
+ this.configStrings.set(index, str);
12521
+ if (index === 0) {
12522
+ this.parseServerInfo(str);
12523
+ }
12524
+ },
12525
+ onSpawnBaseline: (entity) => {
12526
+ },
12527
+ onFrame: (frame) => {
12528
+ },
12529
+ onPrint: (level, msg) => {
12530
+ if (msg.includes("died") || msg.includes("killed")) {
12531
+ this.summary.totalDeaths++;
12532
+ this.recordEvent({
12533
+ type: 4 /* Death */,
12534
+ frame: currentFrameIndex,
12535
+ time: currentTime,
12536
+ description: msg.trim()
12537
+ });
12538
+ }
12539
+ },
12540
+ onCenterPrint: () => {
12541
+ },
12542
+ onStuffText: () => {
12543
+ },
12544
+ onSound: () => {
12545
+ },
12546
+ onTempEntity: () => {
12547
+ },
12548
+ onLayout: () => {
12549
+ },
12550
+ onInventory: () => {
12551
+ },
12552
+ onMuzzleFlash: (ent, weapon) => {
12553
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12554
+ },
12555
+ onMuzzleFlash2: (ent, weapon) => {
12556
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12557
+ },
12558
+ onMuzzleFlash3: (ent, weapon) => {
12559
+ this.handleWeaponFire(ent, weapon, currentFrameIndex, currentTime);
12560
+ },
12561
+ onDisconnect: () => {
12562
+ },
12563
+ onReconnect: () => {
12564
+ },
12565
+ onDownload: () => {
12566
+ },
12567
+ // Rerelease specific
12568
+ onDamage: (indicators) => {
12569
+ for (const ind of indicators) {
12570
+ this.recordEvent({
12571
+ type: 2 /* DamageReceived */,
12572
+ frame: currentFrameIndex,
12573
+ time: currentTime,
12574
+ value: ind.damage,
12575
+ position: ind.dir,
12576
+ description: `Took ${ind.damage} damage`
12577
+ });
12578
+ this.summary.damageReceived += ind.damage;
12579
+ }
12580
+ }
12581
+ };
12582
+ while (reader.hasMore()) {
12583
+ const block = reader.readNextBlock();
12584
+ if (!block) break;
12585
+ currentFrameIndex++;
12586
+ currentTime = currentFrameIndex * frameDuration;
12587
+ const parser = new NetworkMessageParser(block.data, handler);
12588
+ parser.setProtocolVersion(protocolVersion);
12589
+ parser.parseMessage();
12590
+ protocolVersion = parser.getProtocolVersion();
12591
+ }
12592
+ this.statistics = {
12593
+ duration: currentTime,
12594
+ frameCount: currentFrameIndex + 1,
12595
+ averageFps: (currentFrameIndex + 1) / (currentTime || 1),
12596
+ mapName: this.header?.levelName || "unknown",
12597
+ playerCount: 1
12598
+ // Default to 1 for SP/client demo
12599
+ };
12600
+ return {
12601
+ events: this.events,
12602
+ summary: this.summary,
12603
+ header: this.header,
12604
+ configStrings: this.configStrings,
12605
+ serverInfo: this.serverInfo,
12606
+ statistics: this.statistics
12607
+ };
12608
+ }
12609
+ handleWeaponFire(ent, weapon, frame, time) {
12610
+ this.recordEvent({
12611
+ type: 0 /* WeaponFire */,
12612
+ frame,
12613
+ time,
12614
+ entityId: ent,
12615
+ value: weapon,
12616
+ description: `Weapon ${weapon} fired by ${ent}`
12617
+ });
12618
+ const count = this.summary.weaponUsage.get(weapon) || 0;
12619
+ this.summary.weaponUsage.set(weapon, count + 1);
12620
+ }
12621
+ recordEvent(event) {
12622
+ this.events.push(event);
12623
+ }
12624
+ parseServerInfo(str) {
12625
+ const parts = str.split("\\");
12626
+ for (let i = 1; i < parts.length; i += 2) {
12627
+ if (i + 1 < parts.length) {
12628
+ this.serverInfo[parts[i]] = parts[i + 1];
12629
+ }
12630
+ }
12631
+ }
12632
+ };
12633
+
12429
12634
  // src/demo/playback.ts
12430
12635
  var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
12431
12636
  PlaybackState2[PlaybackState2["Stopped"] = 0] = "Stopped";
@@ -12437,6 +12642,8 @@ var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
12437
12642
  var DemoPlaybackController = class {
12438
12643
  constructor() {
12439
12644
  this.reader = null;
12645
+ this.buffer = null;
12646
+ // Keep reference for analysis
12440
12647
  this.state = 0 /* Stopped */;
12441
12648
  this.playbackSpeed = 1;
12442
12649
  this.currentProtocolVersion = 0;
@@ -12452,6 +12659,13 @@ var DemoPlaybackController = class {
12452
12659
  this.snapshotInterval = 100;
12453
12660
  // frames
12454
12661
  this.snapshots = /* @__PURE__ */ new Map();
12662
+ // Analysis Cache
12663
+ this.cachedEvents = null;
12664
+ this.cachedSummary = null;
12665
+ this.cachedHeader = null;
12666
+ this.cachedConfigStrings = null;
12667
+ this.cachedServerInfo = null;
12668
+ this.cachedStatistics = null;
12455
12669
  }
12456
12670
  setHandler(handler) {
12457
12671
  this.handler = handler;
@@ -12460,6 +12674,7 @@ var DemoPlaybackController = class {
12460
12674
  this.callbacks = callbacks;
12461
12675
  }
12462
12676
  loadDemo(buffer) {
12677
+ this.buffer = buffer;
12463
12678
  this.reader = new DemoReader(buffer);
12464
12679
  this.transitionState(0 /* Stopped */);
12465
12680
  this.accumulatedTime = 0;
@@ -12467,6 +12682,12 @@ var DemoPlaybackController = class {
12467
12682
  this.currentFrameIndex = -1;
12468
12683
  this.snapshots.clear();
12469
12684
  this.lastFrameData = null;
12685
+ this.cachedEvents = null;
12686
+ this.cachedSummary = null;
12687
+ this.cachedHeader = null;
12688
+ this.cachedConfigStrings = null;
12689
+ this.cachedServerInfo = null;
12690
+ this.cachedStatistics = null;
12470
12691
  }
12471
12692
  play() {
12472
12693
  if (this.reader && this.state !== 1 /* Playing */) {
@@ -12779,6 +13000,57 @@ var DemoPlaybackController = class {
12779
13000
  this.seek(originalFrame);
12780
13001
  return trajectory;
12781
13002
  }
13003
+ // 3.2.3 Event Log Extraction & 3.3 Metadata
13004
+ getDemoEvents() {
13005
+ this.ensureAnalysis();
13006
+ return this.cachedEvents || [];
13007
+ }
13008
+ filterEvents(type, entityId) {
13009
+ const events = this.getDemoEvents();
13010
+ return events.filter((e) => {
13011
+ if (e.type !== type) return false;
13012
+ if (entityId !== void 0 && e.entityId !== entityId) return false;
13013
+ return true;
13014
+ });
13015
+ }
13016
+ getEventSummary() {
13017
+ this.ensureAnalysis();
13018
+ return this.cachedSummary || {
13019
+ totalKills: 0,
13020
+ totalDeaths: 0,
13021
+ damageDealt: 0,
13022
+ damageReceived: 0,
13023
+ weaponUsage: /* @__PURE__ */ new Map()
13024
+ };
13025
+ }
13026
+ getDemoHeader() {
13027
+ this.ensureAnalysis();
13028
+ return this.cachedHeader;
13029
+ }
13030
+ getDemoConfigStrings() {
13031
+ this.ensureAnalysis();
13032
+ return this.cachedConfigStrings || /* @__PURE__ */ new Map();
13033
+ }
13034
+ getDemoServerInfo() {
13035
+ this.ensureAnalysis();
13036
+ return this.cachedServerInfo || {};
13037
+ }
13038
+ getDemoStatistics() {
13039
+ this.ensureAnalysis();
13040
+ return this.cachedStatistics;
13041
+ }
13042
+ ensureAnalysis() {
13043
+ if (!this.cachedEvents && this.buffer) {
13044
+ const analyzer = new DemoAnalyzer(this.buffer);
13045
+ const result = analyzer.analyze();
13046
+ this.cachedEvents = result.events;
13047
+ this.cachedSummary = result.summary;
13048
+ this.cachedHeader = result.header;
13049
+ this.cachedConfigStrings = result.configStrings;
13050
+ this.cachedServerInfo = result.serverInfo;
13051
+ this.cachedStatistics = result.statistics;
13052
+ }
13053
+ }
12782
13054
  };
12783
13055
 
12784
13056
  // src/demo/recorder.ts