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.
@@ -112,7 +112,7 @@ var WebSocketNetDriver = class {
112
112
 
113
113
  // src/dedicated.ts
114
114
  import { WebSocketServer } from "ws";
115
- import { createGame } from "@quake2ts/game";
115
+ import { createGame, MulticastType, Solid } from "@quake2ts/game";
116
116
 
117
117
  // src/client.ts
118
118
  import { UPDATE_BACKUP } from "@quake2ts/shared";
@@ -161,7 +161,8 @@ function createClient(index, net) {
161
161
  lastMessage: 0,
162
162
  lastConnect: Date.now(),
163
163
  challenge: 0,
164
- messageQueue: []
164
+ messageQueue: [],
165
+ lastPacketEntities: []
165
166
  };
166
167
  }
167
168
  function createEmptyUserCommand() {
@@ -262,7 +263,7 @@ var ClientMessageParser = class {
262
263
  };
263
264
 
264
265
  // src/dedicated.ts
265
- import { BinaryWriter, ServerCommand, BinaryStream as BinaryStream2, traceBox, MAX_CONFIGSTRINGS as MAX_CONFIGSTRINGS2, MAX_EDICTS, CollisionEntityIndex } from "@quake2ts/shared";
266
+ import { BinaryWriter as BinaryWriter2, ServerCommand as ServerCommand2, BinaryStream as BinaryStream2, traceBox, MAX_CONFIGSTRINGS as MAX_CONFIGSTRINGS2, MAX_EDICTS, CollisionEntityIndex, inPVS, inPHS } from "@quake2ts/shared";
266
267
  import { parseBsp } from "@quake2ts/engine";
267
268
  import fs from "fs/promises";
268
269
  import { createPlayerInventory, createPlayerWeaponStates } from "@quake2ts/game";
@@ -305,6 +306,24 @@ var NULL_STATE = {
305
306
  sound: 0,
306
307
  event: 0
307
308
  };
309
+ function writeRemoveEntity(number, writer) {
310
+ let bits = U_REMOVE;
311
+ if (number >= 256) {
312
+ bits |= U_NUMBER16;
313
+ }
314
+ if (bits & 65280) {
315
+ bits |= U_MOREBITS1;
316
+ }
317
+ writer.writeByte(bits & 255);
318
+ if (bits & U_MOREBITS1) {
319
+ writer.writeByte(bits >> 8 & 255);
320
+ }
321
+ if (bits & U_NUMBER16) {
322
+ writer.writeShort(number);
323
+ } else {
324
+ writer.writeByte(number);
325
+ }
326
+ }
308
327
  function writeDeltaEntity(from, to, writer, force, newEntity) {
309
328
  let bits = 0;
310
329
  if (newEntity) {
@@ -509,6 +528,113 @@ function writePlayerState(writer, ps) {
509
528
  }
510
529
  }
511
530
 
531
+ // src/protocol/write.ts
532
+ import { ServerCommand, TempEntity } from "@quake2ts/shared";
533
+ function writeServerCommand(writer, event, ...args) {
534
+ writer.writeByte(event);
535
+ switch (event) {
536
+ case ServerCommand.print: {
537
+ const level = args[0];
538
+ const text = args[1];
539
+ writer.writeByte(level);
540
+ writer.writeString(text);
541
+ break;
542
+ }
543
+ case ServerCommand.muzzleflash: {
544
+ const entIndex = args[0];
545
+ const flashType = args[1];
546
+ writer.writeShort(entIndex);
547
+ writer.writeByte(flashType);
548
+ break;
549
+ }
550
+ case ServerCommand.temp_entity: {
551
+ const type = args[0];
552
+ writer.writeByte(type);
553
+ writeTempEntity(writer, type, args.slice(1));
554
+ break;
555
+ }
556
+ default:
557
+ console.warn(`writeServerCommand: Unhandled command ${event}`);
558
+ break;
559
+ }
560
+ }
561
+ function writeTempEntity(writer, type, args) {
562
+ switch (type) {
563
+ case TempEntity.ROCKET_EXPLOSION:
564
+ case TempEntity.GRENADE_EXPLOSION:
565
+ case TempEntity.EXPLOSION1:
566
+ case TempEntity.EXPLOSION2:
567
+ case TempEntity.ROCKET_EXPLOSION_WATER:
568
+ case TempEntity.GRENADE_EXPLOSION_WATER:
569
+ case TempEntity.BFG_EXPLOSION:
570
+ case TempEntity.BFG_BIGEXPLOSION:
571
+ case TempEntity.PLASMA_EXPLOSION:
572
+ case TempEntity.PLAIN_EXPLOSION:
573
+ case TempEntity.TRACKER_EXPLOSION:
574
+ case TempEntity.EXPLOSION1_BIG:
575
+ case TempEntity.EXPLOSION1_NP:
576
+ case TempEntity.EXPLOSION1_NL:
577
+ case TempEntity.EXPLOSION2_NL:
578
+ case TempEntity.BERSERK_SLAM:
579
+ writer.writePos(args[0]);
580
+ break;
581
+ case TempEntity.BLASTER:
582
+ case TempEntity.FLECHETTE:
583
+ writer.writePos(args[0]);
584
+ writer.writeDir(args[1]);
585
+ break;
586
+ case TempEntity.RAILTRAIL:
587
+ case TempEntity.DEBUGTRAIL:
588
+ case TempEntity.BUBBLETRAIL:
589
+ case TempEntity.BUBBLETRAIL2:
590
+ case TempEntity.BFG_LASER:
591
+ case TempEntity.LIGHTNING_BEAM:
592
+ case TempEntity.LIGHTNING:
593
+ writer.writePos(args[0]);
594
+ writer.writePos(args[1]);
595
+ break;
596
+ case TempEntity.LASER_SPARKS:
597
+ case TempEntity.WELDING_SPARKS:
598
+ case TempEntity.TUNNEL_SPARKS:
599
+ case TempEntity.ELECTRIC_SPARKS:
600
+ case TempEntity.HEATBEAM_SPARKS:
601
+ case TempEntity.HEATBEAM_STEAM:
602
+ case TempEntity.STEAM:
603
+ writer.writeByte(args[0]);
604
+ writer.writePos(args[1]);
605
+ writer.writeDir(args[2]);
606
+ writer.writeByte(args[3] || 0);
607
+ break;
608
+ case TempEntity.PARASITE_ATTACK:
609
+ case TempEntity.MEDIC_CABLE_ATTACK:
610
+ const ent = args[0];
611
+ writer.writeShort(ent ? ent.index : 0);
612
+ writer.writePos(args[1]);
613
+ writer.writePos(args[2]);
614
+ break;
615
+ case TempEntity.GUNSHOT:
616
+ case TempEntity.BLOOD:
617
+ case TempEntity.SPARKS:
618
+ case TempEntity.BULLET_SPARKS:
619
+ case TempEntity.SCREEN_SPARKS:
620
+ case TempEntity.SHIELD_SPARKS:
621
+ writer.writePos(args[0]);
622
+ writer.writeDir(args[1]);
623
+ break;
624
+ case TempEntity.SPLASH:
625
+ case TempEntity.POWER_SPLASH:
626
+ case TempEntity.WIDOWSPLASH:
627
+ writer.writeByte(args[0]);
628
+ writer.writePos(args[1]);
629
+ writer.writeDir(args[2]);
630
+ writer.writeByte(args[3] || 0);
631
+ break;
632
+ default:
633
+ console.warn(`writeTempEntity: Unhandled TempEntity ${type}`);
634
+ break;
635
+ }
636
+ }
637
+
512
638
  // src/dedicated.ts
513
639
  var MAX_CLIENTS = 16;
514
640
  var FRAME_RATE = 10;
@@ -649,10 +775,33 @@ var DedicatedServer = class {
649
775
  });
650
776
  this.game.init(0);
651
777
  this.game.spawnWorld();
778
+ this.populateBaselines();
652
779
  this.sv.state = 2 /* Game */;
653
780
  this.frameInterval = setInterval(() => this.runFrame(), FRAME_TIME_MS);
654
781
  console.log("Server started.");
655
782
  }
783
+ populateBaselines() {
784
+ if (!this.game) return;
785
+ this.game.entities.forEachEntity((ent) => {
786
+ if (ent.index >= MAX_EDICTS) return;
787
+ if (ent.modelindex > 0 || ent.solid !== Solid.Not) {
788
+ this.sv.baselines[ent.index] = {
789
+ number: ent.index,
790
+ origin: { ...ent.origin },
791
+ angles: { ...ent.angles },
792
+ modelIndex: ent.modelindex,
793
+ frame: ent.frame,
794
+ skinNum: ent.skin,
795
+ effects: ent.effects,
796
+ renderfx: ent.renderfx,
797
+ solid: ent.solid,
798
+ sound: ent.sounds,
799
+ // Assuming ent.sounds maps to 'sound' field in EntityState
800
+ event: 0
801
+ };
802
+ }
803
+ });
804
+ }
656
805
  stop() {
657
806
  if (this.frameInterval) clearInterval(this.frameInterval);
658
807
  if (this.wss) this.wss.close();
@@ -715,14 +864,14 @@ var DedicatedServer = class {
715
864
  client.userInfo = userInfo;
716
865
  console.log(`Client ${client.index} connected: ${userInfo}`);
717
866
  this.sendServerData(client);
718
- const writer = new BinaryWriter();
719
- writer.writeByte(ServerCommand.stufftext);
867
+ const writer = new BinaryWriter2();
868
+ writer.writeByte(ServerCommand2.stufftext);
720
869
  writer.writeString("precache\n");
721
870
  client.net.send(writer.getData());
722
871
  } else {
723
872
  console.log(`Client ${client.index} rejected: ${result}`);
724
- const writer = new BinaryWriter();
725
- writer.writeByte(ServerCommand.print);
873
+ const writer = new BinaryWriter2();
874
+ writer.writeByte(ServerCommand2.print);
726
875
  writer.writeByte(2);
727
876
  writer.writeString(`Connection rejected: ${result}
728
877
  `);
@@ -746,8 +895,8 @@ var DedicatedServer = class {
746
895
  console.log(`Client ${client.index} entered game`);
747
896
  }
748
897
  sendServerData(client) {
749
- const writer = new BinaryWriter();
750
- writer.writeByte(ServerCommand.serverdata);
898
+ const writer = new BinaryWriter2();
899
+ writer.writeByte(ServerCommand2.serverdata);
751
900
  writer.writeLong(34);
752
901
  writer.writeLong(this.sv.frame);
753
902
  writer.writeByte(0);
@@ -761,7 +910,7 @@ var DedicatedServer = class {
761
910
  }
762
911
  for (let i = 0; i < MAX_EDICTS; i++) {
763
912
  if (this.sv.baselines[i]) {
764
- writer.writeByte(ServerCommand.spawnbaseline);
913
+ writer.writeByte(ServerCommand2.spawnbaseline);
765
914
  writeDeltaEntity({}, this.sv.baselines[i], writer, true, true);
766
915
  }
767
916
  }
@@ -770,7 +919,7 @@ var DedicatedServer = class {
770
919
  SV_SetConfigString(index, value) {
771
920
  if (index < 0 || index >= MAX_CONFIGSTRINGS2) return;
772
921
  this.sv.configStrings[index] = value;
773
- const writer = new BinaryWriter();
922
+ const writer = new BinaryWriter2();
774
923
  this.SV_WriteConfigString(writer, index, value);
775
924
  const data = writer.getData();
776
925
  for (const client of this.svs.clients) {
@@ -780,7 +929,7 @@ var DedicatedServer = class {
780
929
  }
781
930
  }
782
931
  SV_WriteConfigString(writer, index, value) {
783
- writer.writeByte(ServerCommand.configstring);
932
+ writer.writeByte(ServerCommand2.configstring);
784
933
  writer.writeShort(index);
785
934
  writer.writeString(value);
786
935
  }
@@ -836,13 +985,13 @@ var DedicatedServer = class {
836
985
  }
837
986
  }
838
987
  SV_SendClientFrame(client, snapshot) {
839
- const writer = new BinaryWriter();
840
- writer.writeByte(ServerCommand.frame);
988
+ const writer = new BinaryWriter2();
989
+ writer.writeByte(ServerCommand2.frame);
841
990
  writer.writeLong(this.sv.frame);
842
991
  writer.writeLong(0);
843
992
  writer.writeByte(0);
844
993
  writer.writeByte(0);
845
- writer.writeByte(ServerCommand.playerinfo);
994
+ writer.writeByte(ServerCommand2.playerinfo);
846
995
  const ps = {
847
996
  pm_type: snapshot.pmType,
848
997
  origin: snapshot.origin,
@@ -870,24 +1019,67 @@ var DedicatedServer = class {
870
1019
  stats: snapshot.stats
871
1020
  };
872
1021
  writePlayerState(writer, ps);
873
- writer.writeByte(ServerCommand.packetentities);
1022
+ writer.writeByte(ServerCommand2.packetentities);
874
1023
  const entities = snapshot.packetEntities || [];
1024
+ const currentEntityIds = [];
875
1025
  for (const entity of entities) {
1026
+ currentEntityIds.push(entity.number);
876
1027
  writeDeltaEntity({}, entity, writer, false, true);
877
1028
  }
1029
+ for (const oldId of client.lastPacketEntities) {
1030
+ if (!currentEntityIds.includes(oldId)) {
1031
+ writeRemoveEntity(oldId, writer);
1032
+ }
1033
+ }
878
1034
  writer.writeShort(0);
879
1035
  client.net.send(writer.getData());
880
1036
  client.lastFrame = this.sv.frame;
1037
+ client.lastPacketEntities = currentEntityIds;
881
1038
  }
882
1039
  // GameEngine Implementation
883
1040
  trace(start, end) {
884
1041
  return { fraction: 1 };
885
1042
  }
886
1043
  multicast(origin, type, event, ...args) {
1044
+ const writer = new BinaryWriter2();
1045
+ writeServerCommand(writer, event, ...args);
1046
+ const data = writer.getData();
1047
+ const reliable = event === ServerCommand2.print || event === ServerCommand2.configstring;
1048
+ for (const client of this.svs.clients) {
1049
+ if (!client || client.state < 4 /* Active */ || !client.edict) {
1050
+ continue;
1051
+ }
1052
+ let send = false;
1053
+ switch (type) {
1054
+ case MulticastType.All:
1055
+ send = true;
1056
+ break;
1057
+ case MulticastType.Pvs:
1058
+ if (this.sv.collisionModel) {
1059
+ send = inPVS(origin, client.edict.origin, this.sv.collisionModel);
1060
+ } else {
1061
+ send = true;
1062
+ }
1063
+ break;
1064
+ case MulticastType.Phs:
1065
+ if (this.sv.collisionModel) {
1066
+ send = inPHS(origin, client.edict.origin, this.sv.collisionModel);
1067
+ } else {
1068
+ send = true;
1069
+ }
1070
+ break;
1071
+ }
1072
+ if (send) {
1073
+ client.net.send(data);
1074
+ }
1075
+ }
887
1076
  }
888
1077
  unicast(ent, reliable, event, ...args) {
889
1078
  const client = this.svs.clients.find((c) => c?.edict === ent);
890
- if (client) {
1079
+ if (client && client.state >= 2 /* Connected */) {
1080
+ const writer = new BinaryWriter2();
1081
+ writeServerCommand(writer, event, ...args);
1082
+ client.net.send(writer.getData());
891
1083
  }
892
1084
  }
893
1085
  configstring(index, value) {