quake2ts 0.0.213 → 0.0.215

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.
@@ -201,7 +201,8 @@ function createClient(index, net) {
201
201
  lastMessage: 0,
202
202
  lastConnect: Date.now(),
203
203
  challenge: 0,
204
- messageQueue: []
204
+ messageQueue: [],
205
+ lastPacketEntities: []
205
206
  };
206
207
  }
207
208
  function createEmptyUserCommand() {
@@ -302,7 +303,7 @@ var ClientMessageParser = class {
302
303
  };
303
304
 
304
305
  // src/dedicated.ts
305
- var import_shared3 = require("@quake2ts/shared");
306
+ var import_shared4 = require("@quake2ts/shared");
306
307
  var import_engine = require("@quake2ts/engine");
307
308
  var import_promises = __toESM(require("fs/promises"), 1);
308
309
  var import_game2 = require("@quake2ts/game");
@@ -345,6 +346,24 @@ var NULL_STATE = {
345
346
  sound: 0,
346
347
  event: 0
347
348
  };
349
+ function writeRemoveEntity(number, writer) {
350
+ let bits = U_REMOVE;
351
+ if (number >= 256) {
352
+ bits |= U_NUMBER16;
353
+ }
354
+ if (bits & 65280) {
355
+ bits |= U_MOREBITS1;
356
+ }
357
+ writer.writeByte(bits & 255);
358
+ if (bits & U_MOREBITS1) {
359
+ writer.writeByte(bits >> 8 & 255);
360
+ }
361
+ if (bits & U_NUMBER16) {
362
+ writer.writeShort(number);
363
+ } else {
364
+ writer.writeByte(number);
365
+ }
366
+ }
348
367
  function writeDeltaEntity(from, to, writer, force, newEntity) {
349
368
  let bits = 0;
350
369
  if (newEntity) {
@@ -549,6 +568,113 @@ function writePlayerState(writer, ps) {
549
568
  }
550
569
  }
551
570
 
571
+ // src/protocol/write.ts
572
+ var import_shared3 = require("@quake2ts/shared");
573
+ function writeServerCommand(writer, event, ...args) {
574
+ writer.writeByte(event);
575
+ switch (event) {
576
+ case import_shared3.ServerCommand.print: {
577
+ const level = args[0];
578
+ const text = args[1];
579
+ writer.writeByte(level);
580
+ writer.writeString(text);
581
+ break;
582
+ }
583
+ case import_shared3.ServerCommand.muzzleflash: {
584
+ const entIndex = args[0];
585
+ const flashType = args[1];
586
+ writer.writeShort(entIndex);
587
+ writer.writeByte(flashType);
588
+ break;
589
+ }
590
+ case import_shared3.ServerCommand.temp_entity: {
591
+ const type = args[0];
592
+ writer.writeByte(type);
593
+ writeTempEntity(writer, type, args.slice(1));
594
+ break;
595
+ }
596
+ default:
597
+ console.warn(`writeServerCommand: Unhandled command ${event}`);
598
+ break;
599
+ }
600
+ }
601
+ function writeTempEntity(writer, type, args) {
602
+ switch (type) {
603
+ case import_shared3.TempEntity.ROCKET_EXPLOSION:
604
+ case import_shared3.TempEntity.GRENADE_EXPLOSION:
605
+ case import_shared3.TempEntity.EXPLOSION1:
606
+ case import_shared3.TempEntity.EXPLOSION2:
607
+ case import_shared3.TempEntity.ROCKET_EXPLOSION_WATER:
608
+ case import_shared3.TempEntity.GRENADE_EXPLOSION_WATER:
609
+ case import_shared3.TempEntity.BFG_EXPLOSION:
610
+ case import_shared3.TempEntity.BFG_BIGEXPLOSION:
611
+ case import_shared3.TempEntity.PLASMA_EXPLOSION:
612
+ case import_shared3.TempEntity.PLAIN_EXPLOSION:
613
+ case import_shared3.TempEntity.TRACKER_EXPLOSION:
614
+ case import_shared3.TempEntity.EXPLOSION1_BIG:
615
+ case import_shared3.TempEntity.EXPLOSION1_NP:
616
+ case import_shared3.TempEntity.EXPLOSION1_NL:
617
+ case import_shared3.TempEntity.EXPLOSION2_NL:
618
+ case import_shared3.TempEntity.BERSERK_SLAM:
619
+ writer.writePos(args[0]);
620
+ break;
621
+ case import_shared3.TempEntity.BLASTER:
622
+ case import_shared3.TempEntity.FLECHETTE:
623
+ writer.writePos(args[0]);
624
+ writer.writeDir(args[1]);
625
+ break;
626
+ case import_shared3.TempEntity.RAILTRAIL:
627
+ case import_shared3.TempEntity.DEBUGTRAIL:
628
+ case import_shared3.TempEntity.BUBBLETRAIL:
629
+ case import_shared3.TempEntity.BUBBLETRAIL2:
630
+ case import_shared3.TempEntity.BFG_LASER:
631
+ case import_shared3.TempEntity.LIGHTNING_BEAM:
632
+ case import_shared3.TempEntity.LIGHTNING:
633
+ writer.writePos(args[0]);
634
+ writer.writePos(args[1]);
635
+ break;
636
+ case import_shared3.TempEntity.LASER_SPARKS:
637
+ case import_shared3.TempEntity.WELDING_SPARKS:
638
+ case import_shared3.TempEntity.TUNNEL_SPARKS:
639
+ case import_shared3.TempEntity.ELECTRIC_SPARKS:
640
+ case import_shared3.TempEntity.HEATBEAM_SPARKS:
641
+ case import_shared3.TempEntity.HEATBEAM_STEAM:
642
+ case import_shared3.TempEntity.STEAM:
643
+ writer.writeByte(args[0]);
644
+ writer.writePos(args[1]);
645
+ writer.writeDir(args[2]);
646
+ writer.writeByte(args[3] || 0);
647
+ break;
648
+ case import_shared3.TempEntity.PARASITE_ATTACK:
649
+ case import_shared3.TempEntity.MEDIC_CABLE_ATTACK:
650
+ const ent = args[0];
651
+ writer.writeShort(ent ? ent.index : 0);
652
+ writer.writePos(args[1]);
653
+ writer.writePos(args[2]);
654
+ break;
655
+ case import_shared3.TempEntity.GUNSHOT:
656
+ case import_shared3.TempEntity.BLOOD:
657
+ case import_shared3.TempEntity.SPARKS:
658
+ case import_shared3.TempEntity.BULLET_SPARKS:
659
+ case import_shared3.TempEntity.SCREEN_SPARKS:
660
+ case import_shared3.TempEntity.SHIELD_SPARKS:
661
+ writer.writePos(args[0]);
662
+ writer.writeDir(args[1]);
663
+ break;
664
+ case import_shared3.TempEntity.SPLASH:
665
+ case import_shared3.TempEntity.POWER_SPLASH:
666
+ case import_shared3.TempEntity.WIDOWSPLASH:
667
+ writer.writeByte(args[0]);
668
+ writer.writePos(args[1]);
669
+ writer.writeDir(args[2]);
670
+ writer.writeByte(args[3] || 0);
671
+ break;
672
+ default:
673
+ console.warn(`writeTempEntity: Unhandled TempEntity ${type}`);
674
+ break;
675
+ }
676
+ }
677
+
552
678
  // src/dedicated.ts
553
679
  var MAX_CLIENTS = 16;
554
680
  var FRAME_RATE = 10;
@@ -579,11 +705,11 @@ var DedicatedServer = class {
579
705
  frame: 0,
580
706
  name: "",
581
707
  collisionModel: null,
582
- configStrings: new Array(import_shared3.MAX_CONFIGSTRINGS).fill(""),
583
- baselines: new Array(import_shared3.MAX_EDICTS).fill(null),
708
+ configStrings: new Array(import_shared4.MAX_CONFIGSTRINGS).fill(""),
709
+ baselines: new Array(import_shared4.MAX_EDICTS).fill(null),
584
710
  multicastBuf: new Uint8Array(0)
585
711
  };
586
- this.entityIndex = new import_shared3.CollisionEntityIndex();
712
+ this.entityIndex = new import_shared4.CollisionEntityIndex();
587
713
  }
588
714
  async start(mapName) {
589
715
  console.log(`Starting Dedicated Server on port ${this.port}...`);
@@ -634,7 +760,7 @@ var DedicatedServer = class {
634
760
  ent: hitEntity
635
761
  };
636
762
  }
637
- const worldResult = this.sv.collisionModel ? (0, import_shared3.traceBox)({
763
+ const worldResult = this.sv.collisionModel ? (0, import_shared4.traceBox)({
638
764
  start,
639
765
  end,
640
766
  mins: mins || void 0,
@@ -689,10 +815,33 @@ var DedicatedServer = class {
689
815
  });
690
816
  this.game.init(0);
691
817
  this.game.spawnWorld();
818
+ this.populateBaselines();
692
819
  this.sv.state = 2 /* Game */;
693
820
  this.frameInterval = setInterval(() => this.runFrame(), FRAME_TIME_MS);
694
821
  console.log("Server started.");
695
822
  }
823
+ populateBaselines() {
824
+ if (!this.game) return;
825
+ this.game.entities.forEachEntity((ent) => {
826
+ if (ent.index >= import_shared4.MAX_EDICTS) return;
827
+ if (ent.modelindex > 0 || ent.solid !== import_game.Solid.Not) {
828
+ this.sv.baselines[ent.index] = {
829
+ number: ent.index,
830
+ origin: { ...ent.origin },
831
+ angles: { ...ent.angles },
832
+ modelIndex: ent.modelindex,
833
+ frame: ent.frame,
834
+ skinNum: ent.skin,
835
+ effects: ent.effects,
836
+ renderfx: ent.renderfx,
837
+ solid: ent.solid,
838
+ sound: ent.sounds,
839
+ // Assuming ent.sounds maps to 'sound' field in EntityState
840
+ event: 0
841
+ };
842
+ }
843
+ });
844
+ }
696
845
  stop() {
697
846
  if (this.frameInterval) clearInterval(this.frameInterval);
698
847
  if (this.wss) this.wss.close();
@@ -755,14 +904,14 @@ var DedicatedServer = class {
755
904
  client.userInfo = userInfo;
756
905
  console.log(`Client ${client.index} connected: ${userInfo}`);
757
906
  this.sendServerData(client);
758
- const writer = new import_shared3.BinaryWriter();
759
- writer.writeByte(import_shared3.ServerCommand.stufftext);
907
+ const writer = new import_shared4.BinaryWriter();
908
+ writer.writeByte(import_shared4.ServerCommand.stufftext);
760
909
  writer.writeString("precache\n");
761
910
  client.net.send(writer.getData());
762
911
  } else {
763
912
  console.log(`Client ${client.index} rejected: ${result}`);
764
- const writer = new import_shared3.BinaryWriter();
765
- writer.writeByte(import_shared3.ServerCommand.print);
913
+ const writer = new import_shared4.BinaryWriter();
914
+ writer.writeByte(import_shared4.ServerCommand.print);
766
915
  writer.writeByte(2);
767
916
  writer.writeString(`Connection rejected: ${result}
768
917
  `);
@@ -786,31 +935,31 @@ var DedicatedServer = class {
786
935
  console.log(`Client ${client.index} entered game`);
787
936
  }
788
937
  sendServerData(client) {
789
- const writer = new import_shared3.BinaryWriter();
790
- writer.writeByte(import_shared3.ServerCommand.serverdata);
938
+ const writer = new import_shared4.BinaryWriter();
939
+ writer.writeByte(import_shared4.ServerCommand.serverdata);
791
940
  writer.writeLong(34);
792
941
  writer.writeLong(this.sv.frame);
793
942
  writer.writeByte(0);
794
943
  writer.writeString("baseq2");
795
944
  writer.writeShort(client.index);
796
945
  writer.writeString("maps/test.bsp");
797
- for (let i = 0; i < import_shared3.MAX_CONFIGSTRINGS; i++) {
946
+ for (let i = 0; i < import_shared4.MAX_CONFIGSTRINGS; i++) {
798
947
  if (this.sv.configStrings[i]) {
799
948
  this.SV_WriteConfigString(writer, i, this.sv.configStrings[i]);
800
949
  }
801
950
  }
802
- for (let i = 0; i < import_shared3.MAX_EDICTS; i++) {
951
+ for (let i = 0; i < import_shared4.MAX_EDICTS; i++) {
803
952
  if (this.sv.baselines[i]) {
804
- writer.writeByte(import_shared3.ServerCommand.spawnbaseline);
953
+ writer.writeByte(import_shared4.ServerCommand.spawnbaseline);
805
954
  writeDeltaEntity({}, this.sv.baselines[i], writer, true, true);
806
955
  }
807
956
  }
808
957
  client.net.send(writer.getData());
809
958
  }
810
959
  SV_SetConfigString(index, value) {
811
- if (index < 0 || index >= import_shared3.MAX_CONFIGSTRINGS) return;
960
+ if (index < 0 || index >= import_shared4.MAX_CONFIGSTRINGS) return;
812
961
  this.sv.configStrings[index] = value;
813
- const writer = new import_shared3.BinaryWriter();
962
+ const writer = new import_shared4.BinaryWriter();
814
963
  this.SV_WriteConfigString(writer, index, value);
815
964
  const data = writer.getData();
816
965
  for (const client of this.svs.clients) {
@@ -820,7 +969,7 @@ var DedicatedServer = class {
820
969
  }
821
970
  }
822
971
  SV_WriteConfigString(writer, index, value) {
823
- writer.writeByte(import_shared3.ServerCommand.configstring);
972
+ writer.writeByte(import_shared4.ServerCommand.configstring);
824
973
  writer.writeShort(index);
825
974
  writer.writeString(value);
826
975
  }
@@ -830,7 +979,7 @@ var DedicatedServer = class {
830
979
  while (client.messageQueue.length > 0) {
831
980
  const data = client.messageQueue.shift();
832
981
  if (!data) continue;
833
- const reader = new import_shared3.BinaryStream(data.buffer);
982
+ const reader = new import_shared4.BinaryStream(data.buffer);
834
983
  const parser = new ClientMessageParser(reader, {
835
984
  onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd),
836
985
  onUserInfo: (info) => this.handleUserInfo(client, info),
@@ -876,13 +1025,13 @@ var DedicatedServer = class {
876
1025
  }
877
1026
  }
878
1027
  SV_SendClientFrame(client, snapshot) {
879
- const writer = new import_shared3.BinaryWriter();
880
- writer.writeByte(import_shared3.ServerCommand.frame);
1028
+ const writer = new import_shared4.BinaryWriter();
1029
+ writer.writeByte(import_shared4.ServerCommand.frame);
881
1030
  writer.writeLong(this.sv.frame);
882
1031
  writer.writeLong(0);
883
1032
  writer.writeByte(0);
884
1033
  writer.writeByte(0);
885
- writer.writeByte(import_shared3.ServerCommand.playerinfo);
1034
+ writer.writeByte(import_shared4.ServerCommand.playerinfo);
886
1035
  const ps = {
887
1036
  pm_type: snapshot.pmType,
888
1037
  origin: snapshot.origin,
@@ -910,24 +1059,67 @@ var DedicatedServer = class {
910
1059
  stats: snapshot.stats
911
1060
  };
912
1061
  writePlayerState(writer, ps);
913
- writer.writeByte(import_shared3.ServerCommand.packetentities);
1062
+ writer.writeByte(import_shared4.ServerCommand.packetentities);
914
1063
  const entities = snapshot.packetEntities || [];
1064
+ const currentEntityIds = [];
915
1065
  for (const entity of entities) {
1066
+ currentEntityIds.push(entity.number);
916
1067
  writeDeltaEntity({}, entity, writer, false, true);
917
1068
  }
1069
+ for (const oldId of client.lastPacketEntities) {
1070
+ if (!currentEntityIds.includes(oldId)) {
1071
+ writeRemoveEntity(oldId, writer);
1072
+ }
1073
+ }
918
1074
  writer.writeShort(0);
919
1075
  client.net.send(writer.getData());
920
1076
  client.lastFrame = this.sv.frame;
1077
+ client.lastPacketEntities = currentEntityIds;
921
1078
  }
922
1079
  // GameEngine Implementation
923
1080
  trace(start, end) {
924
1081
  return { fraction: 1 };
925
1082
  }
926
1083
  multicast(origin, type, event, ...args) {
1084
+ const writer = new import_shared4.BinaryWriter();
1085
+ writeServerCommand(writer, event, ...args);
1086
+ const data = writer.getData();
1087
+ const reliable = event === import_shared4.ServerCommand.print || event === import_shared4.ServerCommand.configstring;
1088
+ for (const client of this.svs.clients) {
1089
+ if (!client || client.state < 4 /* Active */ || !client.edict) {
1090
+ continue;
1091
+ }
1092
+ let send = false;
1093
+ switch (type) {
1094
+ case import_game.MulticastType.All:
1095
+ send = true;
1096
+ break;
1097
+ case import_game.MulticastType.Pvs:
1098
+ if (this.sv.collisionModel) {
1099
+ send = (0, import_shared4.inPVS)(origin, client.edict.origin, this.sv.collisionModel);
1100
+ } else {
1101
+ send = true;
1102
+ }
1103
+ break;
1104
+ case import_game.MulticastType.Phs:
1105
+ if (this.sv.collisionModel) {
1106
+ send = (0, import_shared4.inPHS)(origin, client.edict.origin, this.sv.collisionModel);
1107
+ } else {
1108
+ send = true;
1109
+ }
1110
+ break;
1111
+ }
1112
+ if (send) {
1113
+ client.net.send(data);
1114
+ }
1115
+ }
927
1116
  }
928
1117
  unicast(ent, reliable, event, ...args) {
929
1118
  const client = this.svs.clients.find((c) => c?.edict === ent);
930
- if (client) {
1119
+ if (client && client.state >= 2 /* Connected */) {
1120
+ const writer = new import_shared4.BinaryWriter();
1121
+ writeServerCommand(writer, event, ...args);
1122
+ client.net.send(writer.getData());
931
1123
  }
932
1124
  }
933
1125
  configstring(index, value) {
@@ -27,6 +27,7 @@ declare class DedicatedServer implements GameEngine {
27
27
  private entityIndex;
28
28
  constructor(port?: number);
29
29
  start(mapName: string): Promise<void>;
30
+ private populateBaselines;
30
31
  stop(): void;
31
32
  private handleConnection;
32
33
  private onClientMessage;
@@ -90,6 +91,7 @@ interface Client {
90
91
  lastConnect: number;
91
92
  challenge: number;
92
93
  messageQueue: Uint8Array[];
94
+ lastPacketEntities: number[];
93
95
  }
94
96
  declare function createClient(index: number, net: NetDriver): Client;
95
97
 
@@ -27,6 +27,7 @@ declare class DedicatedServer implements GameEngine {
27
27
  private entityIndex;
28
28
  constructor(port?: number);
29
29
  start(mapName: string): Promise<void>;
30
+ private populateBaselines;
30
31
  stop(): void;
31
32
  private handleConnection;
32
33
  private onClientMessage;
@@ -90,6 +91,7 @@ interface Client {
90
91
  lastConnect: number;
91
92
  challenge: number;
92
93
  messageQueue: Uint8Array[];
94
+ lastPacketEntities: number[];
93
95
  }
94
96
  declare function createClient(index: number, net: NetDriver): Client;
95
97