quake2ts 0.0.298 → 0.0.301

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 (44) hide show
  1. package/package.json +1 -1
  2. package/packages/cgame/dist/index.cjs +52 -1
  3. package/packages/cgame/dist/index.cjs.map +1 -1
  4. package/packages/cgame/dist/index.d.cts +7 -0
  5. package/packages/cgame/dist/index.d.ts +7 -0
  6. package/packages/cgame/dist/index.js +53 -2
  7. package/packages/cgame/dist/index.js.map +1 -1
  8. package/packages/client/dist/browser/index.global.js +13 -13
  9. package/packages/client/dist/browser/index.global.js.map +1 -1
  10. package/packages/client/dist/cjs/index.cjs +166 -10
  11. package/packages/client/dist/cjs/index.cjs.map +1 -1
  12. package/packages/client/dist/esm/index.js +161 -5
  13. package/packages/client/dist/esm/index.js.map +1 -1
  14. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  15. package/packages/client/dist/types/cgameBridge.d.ts.map +1 -1
  16. package/packages/client/dist/types/index.d.ts.map +1 -1
  17. package/packages/client/dist/types/input/controller.d.ts +1 -0
  18. package/packages/client/dist/types/input/controller.d.ts.map +1 -1
  19. package/packages/client/dist/types/net/connection.d.ts +3 -0
  20. package/packages/client/dist/types/net/connection.d.ts.map +1 -1
  21. package/packages/client/dist/types/ui/demo-controls.d.ts +11 -0
  22. package/packages/client/dist/types/ui/demo-controls.d.ts.map +1 -0
  23. package/packages/engine/dist/browser/index.global.js.map +1 -1
  24. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  25. package/packages/engine/dist/esm/index.js.map +1 -1
  26. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  27. package/packages/game/dist/browser/index.global.js +4 -4
  28. package/packages/game/dist/browser/index.global.js.map +1 -1
  29. package/packages/game/dist/cjs/index.cjs +17 -10
  30. package/packages/game/dist/cjs/index.cjs.map +1 -1
  31. package/packages/game/dist/esm/index.js +17 -10
  32. package/packages/game/dist/esm/index.js.map +1 -1
  33. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  34. package/packages/game/dist/types/entities/gibs.d.ts +1 -1
  35. package/packages/game/dist/types/entities/gibs.d.ts.map +1 -1
  36. package/packages/server/dist/index.cjs +9 -2
  37. package/packages/server/dist/index.js +9 -2
  38. package/packages/shared/dist/browser/index.global.js.map +1 -1
  39. package/packages/shared/dist/cjs/index.cjs.map +1 -1
  40. package/packages/shared/dist/esm/index.js.map +1 -1
  41. package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  42. package/packages/shared/dist/types/protocol/usercmd.d.ts +3 -0
  43. package/packages/shared/dist/types/protocol/usercmd.d.ts.map +1 -1
  44. package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
@@ -22,15 +22,15 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  ClientConfigStrings: () => ClientConfigStrings,
24
24
  ClientMode: () => ClientMode,
25
- ClientPrediction: () => import_cgame4.ClientPrediction,
25
+ ClientPrediction: () => import_cgame5.ClientPrediction,
26
26
  InputAction: () => InputAction,
27
27
  InputBindings: () => InputBindings,
28
28
  InputCommandBuffer: () => InputCommandBuffer,
29
29
  InputController: () => InputController,
30
- ViewEffects: () => import_cgame5.ViewEffects,
30
+ ViewEffects: () => import_cgame6.ViewEffects,
31
31
  createClient: () => createClient,
32
32
  createDefaultBindings: () => createDefaultBindings,
33
- interpolatePredictionState: () => import_cgame4.interpolatePredictionState,
33
+ interpolatePredictionState: () => import_cgame5.interpolatePredictionState,
34
34
  normalizeCommand: () => normalizeCommand,
35
35
  normalizeInputCode: () => normalizeInputCode
36
36
  });
@@ -8341,6 +8341,13 @@ var NetworkMessageParser = class _NetworkMessageParser {
8341
8341
  if (bitsHigh & U_OLD_FRAME_HIGH) to.oldFrame = this.stream.readShort();
8342
8342
  }
8343
8343
  };
8344
+ var PlaybackState = /* @__PURE__ */ ((PlaybackState22) => {
8345
+ PlaybackState22[PlaybackState22["Stopped"] = 0] = "Stopped";
8346
+ PlaybackState22[PlaybackState22["Playing"] = 1] = "Playing";
8347
+ PlaybackState22[PlaybackState22["Paused"] = 2] = "Paused";
8348
+ PlaybackState22[PlaybackState22["Finished"] = 3] = "Finished";
8349
+ return PlaybackState22;
8350
+ })(PlaybackState || {});
8344
8351
  var DemoPlaybackController = class {
8345
8352
  // ms (10Hz default)
8346
8353
  constructor() {
@@ -9655,8 +9662,8 @@ var AmmoType2 = /* @__PURE__ */ ((AmmoType22) => {
9655
9662
  var AMMO_TYPE_COUNT2 = Object.keys(AmmoType2).length / 2;
9656
9663
 
9657
9664
  // src/index.ts
9658
- var import_cgame2 = require("@quake2ts/cgame");
9659
9665
  var import_cgame3 = require("@quake2ts/cgame");
9666
+ var import_cgame4 = require("@quake2ts/cgame");
9660
9667
 
9661
9668
  // src/cgameBridge.ts
9662
9669
  function createCGameImport(imports, state) {
@@ -9706,6 +9713,24 @@ function createCGameImport(imports, state) {
9706
9713
  flags
9707
9714
  });
9708
9715
  },
9716
+ Cvar_Get: (name, value, flags) => {
9717
+ if (!imports.host || !imports.host.cvars) {
9718
+ return { value: parseInt(value) || 0 };
9719
+ }
9720
+ let existing = imports.host.cvars.get(name);
9721
+ if (!existing) {
9722
+ existing = imports.host.cvars.register({
9723
+ name,
9724
+ defaultValue: value,
9725
+ flags
9726
+ });
9727
+ }
9728
+ return {
9729
+ get value() {
9730
+ return existing.number;
9731
+ }
9732
+ };
9733
+ },
9709
9734
  cvar_set: (name, value) => {
9710
9735
  imports.host?.cvars?.setValue(name, value);
9711
9736
  },
@@ -11643,6 +11668,7 @@ var BrowserWebSocketNetDriver = class {
11643
11668
  };
11644
11669
 
11645
11670
  // src/net/connection.ts
11671
+ var import_cgame2 = require("@quake2ts/cgame");
11646
11672
  var MultiplayerConnection = class {
11647
11673
  constructor(options) {
11648
11674
  this.state = 0 /* Disconnected */;
@@ -11660,6 +11686,8 @@ var MultiplayerConnection = class {
11660
11686
  this.connectPacketTime = 0;
11661
11687
  this.latestServerFrame = 0;
11662
11688
  this.commandHistory = [];
11689
+ // Prediction
11690
+ this.prediction = null;
11663
11691
  this.driver = new BrowserWebSocketNetDriver();
11664
11692
  this.options = options;
11665
11693
  this.netchan = new NetChan();
@@ -11667,6 +11695,9 @@ var MultiplayerConnection = class {
11667
11695
  this.driver.onClose(() => this.handleDisconnect());
11668
11696
  this.driver.onError((err2) => console.error("Network Error:", err2));
11669
11697
  }
11698
+ setPrediction(prediction) {
11699
+ this.prediction = prediction;
11700
+ }
11670
11701
  async connect(url) {
11671
11702
  if (this.state !== 0 /* Disconnected */) {
11672
11703
  this.disconnect();
@@ -11704,6 +11735,9 @@ var MultiplayerConnection = class {
11704
11735
  if (this.commandHistory.length > CMD_BACKUP) {
11705
11736
  this.commandHistory.shift();
11706
11737
  }
11738
+ if (this.prediction) {
11739
+ this.prediction.enqueueCommand(commandWithFrame);
11740
+ }
11707
11741
  const writer = new BinaryWriter2();
11708
11742
  writer.writeByte(ClientCommand.move);
11709
11743
  writer.writeByte(0);
@@ -11801,6 +11835,31 @@ var MultiplayerConnection = class {
11801
11835
  if (frame.serverFrame > this.latestServerFrame) {
11802
11836
  this.latestServerFrame = frame.serverFrame;
11803
11837
  }
11838
+ if (this.prediction && frame.playerState) {
11839
+ const ps = frame.playerState;
11840
+ const predState = {
11841
+ ...(0, import_cgame2.defaultPredictionState)(),
11842
+ // Manual mapping due to type mismatch (MutableVec3 vs Vec3, and property names)
11843
+ origin: { x: ps.origin.x, y: ps.origin.y, z: ps.origin.z },
11844
+ velocity: { x: ps.velocity.x, y: ps.velocity.y, z: ps.velocity.z },
11845
+ viewAngles: { x: ps.viewangles.x, y: ps.viewangles.y, z: ps.viewangles.z },
11846
+ deltaAngles: { x: ps.delta_angles.x, y: ps.delta_angles.y, z: ps.delta_angles.z },
11847
+ pmFlags: ps.pm_flags,
11848
+ pmType: ps.pm_type,
11849
+ gravity: ps.gravity,
11850
+ // Copy other matching fields
11851
+ health: ps.stats[0]
11852
+ // Assuming stat 0 is health? Or generic copy
11853
+ // ...
11854
+ };
11855
+ const gameFrame = {
11856
+ frame: frame.serverFrame,
11857
+ timeMs: 0,
11858
+ // Should be server time, but frame doesn't always have it explicitly?
11859
+ state: predState
11860
+ };
11861
+ this.prediction.setAuthoritative(gameFrame);
11862
+ }
11804
11863
  }
11805
11864
  // Stubs for other handlers
11806
11865
  onCenterPrint(msg) {
@@ -11828,6 +11887,89 @@ var MultiplayerConnection = class {
11828
11887
  }
11829
11888
  };
11830
11889
 
11890
+ // src/ui/demo-controls.ts
11891
+ var DemoControls = class {
11892
+ constructor(playback) {
11893
+ this.isVisible = true;
11894
+ this.playback = playback;
11895
+ }
11896
+ render(renderer, width, height) {
11897
+ if (!this.isVisible) return;
11898
+ const state = this.playback.getState();
11899
+ const isPlaying = state === PlaybackState.Playing;
11900
+ const overlayHeight = 60;
11901
+ const y = height - overlayHeight;
11902
+ renderer.drawfillRect(0, y, width, overlayHeight, [0, 0, 0, 0.5]);
11903
+ const iconSize = 24;
11904
+ const iconY = y + (overlayHeight - iconSize) / 2;
11905
+ const iconX = 20;
11906
+ const statusText = isPlaying ? "PAUSE" : "PLAY";
11907
+ renderer.drawString(iconX, iconY, statusText);
11908
+ const speed = this.playback.getSpeed();
11909
+ const speedText = `Speed: ${speed}x`;
11910
+ renderer.drawCenterString(iconY, speedText);
11911
+ const currentTime = this.playback.getCurrentTime();
11912
+ const currentFormatted = this.formatTime(currentTime);
11913
+ const totalFormatted = "--:--";
11914
+ const timeText = `${currentFormatted} / ${totalFormatted}`;
11915
+ const timeX = width - 150;
11916
+ renderer.drawString(timeX, iconY, timeText);
11917
+ const timelineY = y + 5;
11918
+ const timelineHeight = 4;
11919
+ const timelineWidth = width - 40;
11920
+ const timelineX = 20;
11921
+ renderer.drawfillRect(timelineX, timelineY, timelineWidth, timelineHeight, [0.3, 0.3, 0.3, 1]);
11922
+ const progress = 0;
11923
+ const progressWidth = timelineWidth * progress;
11924
+ renderer.drawfillRect(timelineX, timelineY, progressWidth, timelineHeight, [1, 1, 1, 1]);
11925
+ const markerX = timelineX + progressWidth;
11926
+ renderer.drawfillRect(markerX - 2, timelineY - 2, 4, timelineHeight + 4, [1, 0, 0, 1]);
11927
+ const helpText = "[Space] Toggle [< >] Step [ [ ] ] Speed [Esc] Stop";
11928
+ renderer.drawCenterString(y + 35, helpText);
11929
+ }
11930
+ handleInput(key, down) {
11931
+ if (!down) return false;
11932
+ switch (key.toLowerCase()) {
11933
+ case " ":
11934
+ if (this.playback.getState() === PlaybackState.Playing) {
11935
+ this.playback.pause();
11936
+ } else if (this.playback.getState() === PlaybackState.Paused) {
11937
+ this.playback.play();
11938
+ }
11939
+ return true;
11940
+ case "arrowright":
11941
+ this.playback.stepForward();
11942
+ return true;
11943
+ case "arrowleft":
11944
+ this.playback.stepBackward();
11945
+ return true;
11946
+ case "]":
11947
+ this.changeSpeed(2);
11948
+ return true;
11949
+ case "[":
11950
+ this.changeSpeed(0.5);
11951
+ return true;
11952
+ case "escape":
11953
+ this.playback.stop();
11954
+ return true;
11955
+ }
11956
+ return false;
11957
+ }
11958
+ changeSpeed(factor) {
11959
+ const current = this.playback.getSpeed();
11960
+ let newSpeed = current * factor;
11961
+ if (newSpeed < 0.1) newSpeed = 0.1;
11962
+ if (newSpeed > 16) newSpeed = 16;
11963
+ this.playback.setSpeed(newSpeed);
11964
+ }
11965
+ formatTime(ms) {
11966
+ const totalSeconds = Math.floor(ms / 1e3);
11967
+ const minutes = Math.floor(totalSeconds / 60);
11968
+ const seconds = totalSeconds % 60;
11969
+ return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
11970
+ }
11971
+ };
11972
+
11831
11973
  // src/input/bindings.ts
11832
11974
  var DEFAULT_BINDINGS = [
11833
11975
  { code: "KeyW", command: "+forward" },
@@ -11992,6 +12134,7 @@ var InputController = class {
11992
12134
  this.touchButtons = /* @__PURE__ */ new Map();
11993
12135
  this.touchMove = { x: 0, y: 0 };
11994
12136
  this.touchLook = { x: 0, y: 0 };
12137
+ this.sequence = 0;
11995
12138
  this.bindings = bindings;
11996
12139
  this.forwardSpeed = options.forwardSpeed ?? DEFAULT_FORWARD_SPEED;
11997
12140
  this.sideSpeed = options.sideSpeed ?? DEFAULT_SIDE_SPEED;
@@ -12130,6 +12273,7 @@ var InputController = class {
12130
12273
  buttons |= PlayerButton.Any;
12131
12274
  }
12132
12275
  this.anyPressed = false;
12276
+ this.sequence++;
12133
12277
  return {
12134
12278
  msec,
12135
12279
  buttons,
@@ -12137,7 +12281,10 @@ var InputController = class {
12137
12281
  forwardmove,
12138
12282
  sidemove,
12139
12283
  upmove,
12140
- serverFrame
12284
+ serverFrame,
12285
+ sequence: this.sequence,
12286
+ lightlevel: 0,
12287
+ impulse: 0
12141
12288
  };
12142
12289
  }
12143
12290
  consumeConsoleCommands() {
@@ -12317,8 +12464,8 @@ var InputCommandBuffer = class {
12317
12464
  };
12318
12465
 
12319
12466
  // src/index.ts
12320
- var import_cgame4 = require("@quake2ts/cgame");
12321
12467
  var import_cgame5 = require("@quake2ts/cgame");
12468
+ var import_cgame6 = require("@quake2ts/cgame");
12322
12469
  var ZERO_VEC32 = { x: 0, y: 0, z: 0 };
12323
12470
  var ClientMode = /* @__PURE__ */ ((ClientMode2) => {
12324
12471
  ClientMode2[ClientMode2["Normal"] = 0] = "Normal";
@@ -12332,9 +12479,10 @@ function createClient(imports) {
12332
12479
  const tr = imports.engine.trace(point, point, zero3, zero3);
12333
12480
  return tr.contents || 0;
12334
12481
  };
12335
- const prediction = new import_cgame2.ClientPrediction(imports.engine.trace, pointContents);
12336
- const view = new import_cgame3.ViewEffects();
12482
+ const prediction = new import_cgame3.ClientPrediction(imports.engine.trace, pointContents);
12483
+ const view = new import_cgame4.ViewEffects();
12337
12484
  const demoPlayback = new DemoPlaybackController();
12485
+ const demoControls = new DemoControls(demoPlayback);
12338
12486
  const demoHandler = new ClientNetworkHandler(imports);
12339
12487
  demoHandler.setView(view);
12340
12488
  let isDemoPlaying = false;
@@ -12375,7 +12523,7 @@ function createClient(imports) {
12375
12523
  }
12376
12524
  };
12377
12525
  const cgameImport = createCGameImport(imports, stateProvider);
12378
- const cg = (0, import_cgame2.GetCGameAPI)(cgameImport);
12526
+ const cg = (0, import_cgame3.GetCGameAPI)(cgameImport);
12379
12527
  const multiplayer = new MultiplayerConnection({
12380
12528
  get username() {
12381
12529
  return imports.host?.cvars?.get("name")?.string || "Player";
@@ -12529,6 +12677,11 @@ function createClient(imports) {
12529
12677
  return prediction.enqueueCommand(command);
12530
12678
  },
12531
12679
  handleInput(key, down) {
12680
+ if (isDemoPlaying) {
12681
+ if (demoControls.handleInput(key, down)) {
12682
+ return true;
12683
+ }
12684
+ }
12532
12685
  if (!menuSystem.isActive()) return false;
12533
12686
  if (!down) return true;
12534
12687
  const lowerKey = key.toLowerCase();
@@ -12614,7 +12767,7 @@ function createClient(imports) {
12614
12767
  latestFrame = sample.latest;
12615
12768
  }
12616
12769
  if (sample.previous?.state && sample.latest?.state) {
12617
- lastRendered = (0, import_cgame2.interpolatePredictionState)(sample.previous.state, sample.latest.state, sample.alpha);
12770
+ lastRendered = (0, import_cgame3.interpolatePredictionState)(sample.previous.state, sample.latest.state, sample.alpha);
12618
12771
  if (sample.latest.state.packetEntities && sample.previous.state.packetEntities) {
12619
12772
  renderEntities = buildRenderableEntities(
12620
12773
  sample.latest.state.packetEntities,
@@ -12748,6 +12901,9 @@ function createClient(imports) {
12748
12901
  if (menuSystem.isActive()) {
12749
12902
  Draw_Menu(imports.engine.renderer, menuSystem.getState(), imports.engine.renderer.width, imports.engine.renderer.height);
12750
12903
  }
12904
+ if (isDemoPlaying) {
12905
+ demoControls.render(imports.engine.renderer, imports.engine.renderer.width, imports.engine.renderer.height);
12906
+ }
12751
12907
  if (lastRendered && lastRendered.client) {
12752
12908
  wheelMenuSystem.render(imports.engine.renderer, imports.engine.renderer.width, imports.engine.renderer.height, lastRendered.client);
12753
12909
  }