quake2ts 0.0.181 → 0.0.183

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 (31) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js.map +1 -1
  3. package/packages/client/dist/cjs/index.cjs.map +1 -1
  4. package/packages/client/dist/esm/index.js.map +1 -1
  5. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  6. package/packages/engine/dist/browser/index.global.js +1 -1
  7. package/packages/engine/dist/browser/index.global.js.map +1 -1
  8. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  9. package/packages/engine/dist/esm/index.js.map +1 -1
  10. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  11. package/packages/game/dist/browser/index.global.js +2 -2
  12. package/packages/game/dist/browser/index.global.js.map +1 -1
  13. package/packages/game/dist/cjs/index.cjs.map +1 -1
  14. package/packages/game/dist/esm/index.js.map +1 -1
  15. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  16. package/packages/server/dist/index.cjs +281 -57
  17. package/packages/server/dist/index.d.cts +33 -10
  18. package/packages/server/dist/index.d.ts +33 -10
  19. package/packages/server/dist/index.js +270 -46
  20. package/packages/shared/dist/browser/index.global.js +1 -1
  21. package/packages/shared/dist/browser/index.global.js.map +1 -1
  22. package/packages/shared/dist/cjs/index.cjs +17 -0
  23. package/packages/shared/dist/cjs/index.cjs.map +1 -1
  24. package/packages/shared/dist/esm/index.js +12 -0
  25. package/packages/shared/dist/esm/index.js.map +1 -1
  26. package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  27. package/packages/shared/dist/types/protocol/constants.d.ts +6 -0
  28. package/packages/shared/dist/types/protocol/constants.d.ts.map +1 -0
  29. package/packages/shared/dist/types/protocol/index.d.ts +1 -0
  30. package/packages/shared/dist/types/protocol/index.d.ts.map +1 -1
  31. package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
@@ -155,37 +155,82 @@ var import_ws2 = require("ws");
155
155
  var import_game = require("@quake2ts/game");
156
156
 
157
157
  // src/client.ts
158
+ var import_shared = require("@quake2ts/shared");
158
159
  var ClientState = /* @__PURE__ */ ((ClientState2) => {
159
160
  ClientState2[ClientState2["Free"] = 0] = "Free";
160
- ClientState2[ClientState2["Connected"] = 1] = "Connected";
161
- ClientState2[ClientState2["Primed"] = 2] = "Primed";
162
- ClientState2[ClientState2["Active"] = 3] = "Active";
163
- ClientState2[ClientState2["Spawned"] = 4] = "Spawned";
161
+ ClientState2[ClientState2["Zombie"] = 1] = "Zombie";
162
+ ClientState2[ClientState2["Connected"] = 2] = "Connected";
163
+ ClientState2[ClientState2["Spawned"] = 3] = "Spawned";
164
+ ClientState2[ClientState2["Active"] = 4] = "Active";
164
165
  return ClientState2;
165
166
  })(ClientState || {});
166
167
  function createClient(index, net) {
168
+ const frames = [];
169
+ for (let i = 0; i < import_shared.UPDATE_BACKUP; i++) {
170
+ frames.push({
171
+ areaBytes: 0,
172
+ areaBits: new Uint8Array(0),
173
+ // Size depends on map areas
174
+ playerState: createEmptyPlayerState(),
175
+ numEntities: 0,
176
+ firstEntity: 0,
177
+ sentTime: 0
178
+ });
179
+ }
167
180
  return {
168
181
  index,
169
- state: 1 /* Connected */,
182
+ state: 2 /* Connected */,
170
183
  net,
171
- name: `Player ${index}`,
172
184
  userInfo: "",
185
+ lastFrame: 0,
186
+ lastCmd: createEmptyUserCommand(),
187
+ commandMsec: 0,
188
+ frameLatency: [],
189
+ ping: 0,
190
+ messageSize: [],
191
+ rate: 25e3,
192
+ // Default rate
193
+ suppressCount: 0,
173
194
  edict: null,
174
- lastCmd: {
175
- msec: 0,
176
- buttons: 0,
177
- angles: { x: 0, y: 0, z: 0 },
178
- forwardmove: 0,
179
- sidemove: 0,
180
- upmove: 0
181
- },
182
- lastPacketTime: Date.now(),
183
- lastFrame: 0
195
+ name: `Player ${index}`,
196
+ messageLevel: 0,
197
+ datagram: new Uint8Array(0),
198
+ frames,
199
+ downloadSize: 0,
200
+ downloadCount: 0,
201
+ lastMessage: 0,
202
+ lastConnect: Date.now(),
203
+ challenge: 0,
204
+ messageQueue: []
205
+ };
206
+ }
207
+ function createEmptyUserCommand() {
208
+ return {
209
+ msec: 0,
210
+ buttons: 0,
211
+ angles: { x: 0, y: 0, z: 0 },
212
+ forwardmove: 0,
213
+ sidemove: 0,
214
+ upmove: 0
215
+ };
216
+ }
217
+ function createEmptyPlayerState() {
218
+ return {
219
+ origin: { x: 0, y: 0, z: 0 },
220
+ velocity: { x: 0, y: 0, z: 0 },
221
+ viewAngles: { x: 0, y: 0, z: 0 },
222
+ onGround: false,
223
+ waterLevel: 0,
224
+ mins: { x: -16, y: -16, z: -24 },
225
+ maxs: { x: 16, y: 16, z: 32 },
226
+ damageAlpha: 0,
227
+ damageIndicators: [],
228
+ blend: [0, 0, 0, 0]
184
229
  };
185
230
  }
186
231
 
187
232
  // src/protocol.ts
188
- var import_shared = require("@quake2ts/shared");
233
+ var import_shared2 = require("@quake2ts/shared");
189
234
  var ClientMessageParser = class {
190
235
  constructor(stream, handler) {
191
236
  this.stream = stream;
@@ -196,16 +241,16 @@ var ClientMessageParser = class {
196
241
  const cmdId = this.stream.readByte();
197
242
  if (cmdId === -1) break;
198
243
  switch (cmdId) {
199
- case import_shared.ClientCommand.move:
244
+ case import_shared2.ClientCommand.move:
200
245
  this.parseMove();
201
246
  break;
202
- case import_shared.ClientCommand.userinfo:
247
+ case import_shared2.ClientCommand.userinfo:
203
248
  this.parseUserInfo();
204
249
  break;
205
- case import_shared.ClientCommand.stringcmd:
250
+ case import_shared2.ClientCommand.stringcmd:
206
251
  this.parseStringCmd();
207
252
  break;
208
- case import_shared.ClientCommand.nop:
253
+ case import_shared2.ClientCommand.nop:
209
254
  this.handler.onNop();
210
255
  break;
211
256
  default:
@@ -251,10 +296,150 @@ var ClientMessageParser = class {
251
296
  };
252
297
 
253
298
  // src/dedicated.ts
254
- var import_shared2 = require("@quake2ts/shared");
299
+ var import_shared3 = require("@quake2ts/shared");
255
300
  var import_engine = require("@quake2ts/engine");
256
301
  var import_promises = __toESM(require("fs/promises"), 1);
257
302
  var import_game2 = require("@quake2ts/game");
303
+
304
+ // src/protocol/entity.ts
305
+ var U_ORIGIN1 = 1 << 0;
306
+ var U_ORIGIN2 = 1 << 1;
307
+ var U_ANGLE2 = 1 << 2;
308
+ var U_ANGLE3 = 1 << 3;
309
+ var U_FRAME8 = 1 << 4;
310
+ var U_EVENT = 1 << 5;
311
+ var U_REMOVE = 1 << 6;
312
+ var U_MOREBITS1 = 1 << 7;
313
+ var U_NUMBER16 = 1 << 8;
314
+ var U_ORIGIN3 = 1 << 9;
315
+ var U_ANGLE1 = 1 << 10;
316
+ var U_MODEL = 1 << 11;
317
+ var U_RENDERFX8 = 1 << 12;
318
+ var U_EFFECTS8 = 1 << 14;
319
+ var U_MOREBITS2 = 1 << 15;
320
+ var U_SKIN8 = 1 << 16;
321
+ var U_FRAME16 = 1 << 17;
322
+ var U_RENDERFX16 = 1 << 18;
323
+ var U_EFFECTS16 = 1 << 19;
324
+ var U_MOREBITS3 = 1 << 23;
325
+ var U_OLDORIGIN = 1 << 24;
326
+ var U_SKIN16 = 1 << 25;
327
+ var U_SOUND = 1 << 26;
328
+ var U_SOLID = 1 << 27;
329
+ var NULL_STATE = {
330
+ number: 0,
331
+ origin: { x: 0, y: 0, z: 0 },
332
+ angles: { x: 0, y: 0, z: 0 },
333
+ modelIndex: 0,
334
+ frame: 0,
335
+ skinNum: 0,
336
+ effects: 0,
337
+ renderfx: 0,
338
+ solid: 0,
339
+ sound: 0,
340
+ event: 0
341
+ };
342
+ function writeDeltaEntity(from, to, writer, force, newEntity) {
343
+ let bits = 0;
344
+ if (newEntity) {
345
+ from = NULL_STATE;
346
+ }
347
+ if (to.modelIndex !== from.modelIndex || force) {
348
+ bits |= U_MODEL;
349
+ }
350
+ if (to.origin.x !== from.origin.x || force) {
351
+ bits |= U_ORIGIN1;
352
+ }
353
+ if (to.origin.y !== from.origin.y || force) {
354
+ bits |= U_ORIGIN2;
355
+ }
356
+ if (to.origin.z !== from.origin.z || force) {
357
+ bits |= U_ORIGIN3;
358
+ }
359
+ if (to.angles.x !== from.angles.x || force) {
360
+ bits |= U_ANGLE1;
361
+ }
362
+ if (to.angles.y !== from.angles.y || force) {
363
+ bits |= U_ANGLE2;
364
+ }
365
+ if (to.angles.z !== from.angles.z || force) {
366
+ bits |= U_ANGLE3;
367
+ }
368
+ if (to.frame !== from.frame || force) {
369
+ if (to.frame >= 256) bits |= U_FRAME16;
370
+ else bits |= U_FRAME8;
371
+ }
372
+ if (to.skinNum !== from.skinNum || force) {
373
+ if (to.skinNum >= 256) bits |= U_SKIN16;
374
+ else bits |= U_SKIN8;
375
+ }
376
+ if (to.effects !== from.effects || force) {
377
+ if (to.effects >= 256) bits |= U_EFFECTS16;
378
+ else bits |= U_EFFECTS8;
379
+ }
380
+ if (to.renderfx !== from.renderfx || force) {
381
+ if (to.renderfx >= 256) bits |= U_RENDERFX16;
382
+ else bits |= U_RENDERFX8;
383
+ }
384
+ if (to.solid !== from.solid || force) {
385
+ bits |= U_SOLID;
386
+ }
387
+ if (to.sound !== from.sound || force) {
388
+ bits |= U_SOUND;
389
+ }
390
+ if (to.event !== from.event || force) {
391
+ bits |= U_EVENT;
392
+ }
393
+ if (to.number >= 256) {
394
+ bits |= U_NUMBER16;
395
+ }
396
+ if (bits & 4294967040) {
397
+ bits |= U_MOREBITS1;
398
+ }
399
+ if (bits & 4294901760) {
400
+ bits |= U_MOREBITS2;
401
+ }
402
+ if (bits & 4278190080) {
403
+ bits |= U_MOREBITS3;
404
+ }
405
+ writer.writeByte(bits & 255);
406
+ if (bits & U_MOREBITS1) {
407
+ writer.writeByte(bits >> 8 & 255);
408
+ }
409
+ if (bits & U_MOREBITS2) {
410
+ writer.writeByte(bits >> 16 & 255);
411
+ }
412
+ if (bits & U_MOREBITS3) {
413
+ writer.writeByte(bits >> 24 & 255);
414
+ }
415
+ if (bits & U_NUMBER16) {
416
+ writer.writeShort(to.number);
417
+ } else {
418
+ writer.writeByte(to.number);
419
+ }
420
+ if (bits & U_MODEL) writer.writeByte(to.modelIndex);
421
+ if (bits & U_FRAME8) writer.writeByte(to.frame);
422
+ if (bits & U_FRAME16) writer.writeShort(to.frame);
423
+ if (bits & U_SKIN8) writer.writeByte(to.skinNum);
424
+ if (bits & U_SKIN16) writer.writeShort(to.skinNum);
425
+ if (bits & U_EFFECTS8) writer.writeByte(to.effects);
426
+ if (bits & U_EFFECTS16) writer.writeShort(to.effects);
427
+ if (bits & U_RENDERFX8) writer.writeByte(to.renderfx);
428
+ if (bits & U_RENDERFX16) writer.writeShort(to.renderfx);
429
+ if (bits & U_ORIGIN1) writer.writeCoord(to.origin.x);
430
+ if (bits & U_ORIGIN2) writer.writeCoord(to.origin.y);
431
+ if (bits & U_ORIGIN3) writer.writeCoord(to.origin.z);
432
+ if (bits & U_ANGLE1) writer.writeAngle(to.angles.x);
433
+ if (bits & U_ANGLE2) writer.writeAngle(to.angles.y);
434
+ if (bits & U_ANGLE3) writer.writeAngle(to.angles.z);
435
+ if (bits & U_OLDORIGIN) {
436
+ }
437
+ if (bits & U_SOUND) writer.writeByte(to.sound ?? 0);
438
+ if (bits & U_EVENT) writer.writeByte(to.event ?? 0);
439
+ if (bits & U_SOLID) writer.writeShort(to.solid);
440
+ }
441
+
442
+ // src/dedicated.ts
258
443
  var MAX_CLIENTS = 16;
259
444
  var FRAME_RATE = 10;
260
445
  var FRAME_TIME_MS = 1e3 / FRAME_RATE;
@@ -265,27 +450,43 @@ var DedicatedServer = class {
265
450
  this.game = null;
266
451
  this.frameInterval = null;
267
452
  this.svs = {
268
- clients: new Array(MAX_CLIENTS).fill(null)
453
+ initialized: false,
454
+ realTime: 0,
455
+ mapCmd: "",
456
+ spawnCount: 0,
457
+ clients: new Array(MAX_CLIENTS).fill(null),
458
+ lastHeartbeat: 0,
459
+ challenges: []
269
460
  };
270
461
  this.sv = {
462
+ state: 0 /* Dead */,
463
+ attractLoop: false,
464
+ loadGame: false,
271
465
  startTime: 0,
466
+ // Initialize startTime
272
467
  time: 0,
273
468
  frame: 0,
274
- mapName: "",
275
- collisionModel: null
469
+ name: "",
470
+ collisionModel: null,
471
+ configStrings: new Array(import_shared3.MAX_CONFIGSTRINGS).fill(""),
472
+ baselines: new Array(import_shared3.MAX_EDICTS).fill(null),
473
+ multicastBuf: new Uint8Array(0)
276
474
  };
277
475
  }
278
476
  async start(mapName) {
279
477
  console.log(`Starting Dedicated Server on port ${this.port}...`);
280
- this.sv.mapName = mapName;
478
+ this.sv.name = mapName;
479
+ this.svs.initialized = true;
480
+ this.svs.spawnCount++;
281
481
  this.wss = new import_ws2.WebSocketServer({ port: this.port });
282
482
  this.wss.on("connection", (ws) => {
283
483
  console.log("New connection");
284
484
  this.handleConnection(ws);
285
485
  });
286
486
  try {
287
- console.log(`Loading map ${this.sv.mapName}...`);
288
- const mapData = await import_promises.default.readFile(this.sv.mapName);
487
+ console.log(`Loading map ${this.sv.name}...`);
488
+ this.sv.state = 1 /* Loading */;
489
+ const mapData = await import_promises.default.readFile(this.sv.name);
289
490
  const arrayBuffer = mapData.buffer.slice(mapData.byteOffset, mapData.byteOffset + mapData.byteLength);
290
491
  const bspMap = (0, import_engine.parseBsp)(arrayBuffer);
291
492
  this.sv.collisionModel = bspMap;
@@ -308,7 +509,7 @@ var DedicatedServer = class {
308
509
  ent: null
309
510
  };
310
511
  }
311
- const result = (0, import_shared2.traceBox)({
512
+ const result = (0, import_shared3.traceBox)({
312
513
  start,
313
514
  end,
314
515
  mins: mins || void 0,
@@ -342,6 +543,7 @@ var DedicatedServer = class {
342
543
  });
343
544
  this.game.init(0);
344
545
  this.game.spawnWorld();
546
+ this.sv.state = 2 /* Game */;
345
547
  this.frameInterval = setInterval(() => this.runFrame(), FRAME_TIME_MS);
346
548
  console.log("Server started.");
347
549
  }
@@ -349,6 +551,7 @@ var DedicatedServer = class {
349
551
  if (this.frameInterval) clearInterval(this.frameInterval);
350
552
  if (this.wss) this.wss.close();
351
553
  this.game?.shutdown();
554
+ this.sv.state = 0 /* Dead */;
352
555
  }
353
556
  handleConnection(ws) {
354
557
  let clientIndex = -1;
@@ -371,19 +574,7 @@ var DedicatedServer = class {
371
574
  }
372
575
  onClientMessage(client, data) {
373
576
  const buffer = data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ? data.buffer : data.slice().buffer;
374
- const reader = new import_shared2.BinaryStream(buffer);
375
- const parser = new ClientMessageParser(reader, {
376
- onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd),
377
- onUserInfo: (info) => this.handleUserInfo(client, info),
378
- onStringCmd: (cmd) => this.handleStringCmd(client, cmd),
379
- onNop: () => {
380
- },
381
- onBad: () => {
382
- console.warn(`Bad command from client ${client.index}`);
383
- client.net.disconnect();
384
- }
385
- });
386
- parser.parseMessage();
577
+ client.messageQueue.push(new Uint8Array(buffer));
387
578
  }
388
579
  onClientDisconnect(client) {
389
580
  console.log(`Client ${client.index} disconnected`);
@@ -391,8 +582,8 @@ var DedicatedServer = class {
391
582
  }
392
583
  handleMove(client, cmd) {
393
584
  client.lastCmd = cmd;
394
- client.lastPacketTime = Date.now();
395
- if (client.state === 1 /* Connected */) {
585
+ client.lastMessage = this.sv.frame;
586
+ if (client.state === 2 /* Connected */) {
396
587
  this.spawnClient(client);
397
588
  }
398
589
  }
@@ -409,12 +600,12 @@ var DedicatedServer = class {
409
600
  buttons: 0
410
601
  });
411
602
  client.edict = ent;
412
- client.state = 3 /* Active */;
603
+ client.state = 4 /* Active */;
413
604
  this.sendServerData(client);
414
605
  }
415
606
  sendServerData(client) {
416
- const writer = new import_shared2.BinaryWriter();
417
- writer.writeByte(import_shared2.ServerCommand.serverdata);
607
+ const writer = new import_shared3.BinaryWriter();
608
+ writer.writeByte(import_shared3.ServerCommand.serverdata);
418
609
  writer.writeLong(34);
419
610
  writer.writeLong(this.sv.frame);
420
611
  writer.writeByte(0);
@@ -424,41 +615,74 @@ var DedicatedServer = class {
424
615
  client.net.send(writer.getData());
425
616
  }
426
617
  SV_ReadPackets() {
618
+ for (const client of this.svs.clients) {
619
+ if (!client || client.state === 0 /* Free */) continue;
620
+ while (client.messageQueue.length > 0) {
621
+ const data = client.messageQueue.shift();
622
+ if (!data) continue;
623
+ const reader = new import_shared3.BinaryStream(data.buffer);
624
+ const parser = new ClientMessageParser(reader, {
625
+ onMove: (checksum, lastFrame, cmd) => this.handleMove(client, cmd),
626
+ onUserInfo: (info) => this.handleUserInfo(client, info),
627
+ onStringCmd: (cmd) => this.handleStringCmd(client, cmd),
628
+ onNop: () => {
629
+ },
630
+ onBad: () => {
631
+ console.warn(`Bad command from client ${client.index}`);
632
+ }
633
+ });
634
+ try {
635
+ parser.parseMessage();
636
+ } catch (e) {
637
+ console.error(`Error parsing message from client ${client.index}:`, e);
638
+ }
639
+ }
640
+ }
427
641
  }
428
642
  runFrame() {
429
643
  if (!this.game) return;
430
644
  this.sv.frame++;
645
+ this.sv.time += 100;
431
646
  this.SV_ReadPackets();
432
647
  for (const client of this.svs.clients) {
433
- if (client && client.state === 3 /* Active */ && client.edict) {
648
+ if (client && client.state === 4 /* Active */ && client.edict) {
434
649
  this.game.clientThink(client.edict, client.lastCmd);
435
650
  }
436
651
  }
437
- this.game.frame({
652
+ const snapshot = this.game.frame({
438
653
  frame: this.sv.frame,
439
654
  deltaMs: FRAME_TIME_MS,
440
655
  nowMs: Date.now()
441
656
  });
442
- this.SV_SendClientMessages();
657
+ if (snapshot && snapshot.state) {
658
+ this.SV_SendClientMessages(snapshot.state);
659
+ }
443
660
  }
444
- SV_SendClientMessages() {
661
+ SV_SendClientMessages(snapshot) {
445
662
  for (const client of this.svs.clients) {
446
- if (client && client.state === 3 /* Active */) {
447
- this.SV_SendClientFrame(client);
663
+ if (client && client.state === 4 /* Active */) {
664
+ this.SV_SendClientFrame(client, snapshot);
448
665
  }
449
666
  }
450
667
  }
451
- SV_SendClientFrame(client) {
452
- const writer = new import_shared2.BinaryWriter();
453
- writer.writeByte(import_shared2.ServerCommand.frame);
668
+ SV_SendClientFrame(client, snapshot) {
669
+ const writer = new import_shared3.BinaryWriter();
670
+ writer.writeByte(import_shared3.ServerCommand.frame);
454
671
  writer.writeLong(this.sv.frame);
455
672
  writer.writeLong(0);
456
673
  writer.writeByte(0);
457
674
  writer.writeByte(0);
458
- writer.writeByte(import_shared2.ServerCommand.playerinfo);
675
+ writer.writeByte(import_shared3.ServerCommand.playerinfo);
459
676
  writer.writeShort(0);
460
677
  writer.writeLong(0);
678
+ writer.writeByte(import_shared3.ServerCommand.packetentities);
679
+ const entities = snapshot.packetEntities || [];
680
+ for (const entity of entities) {
681
+ writeDeltaEntity({}, entity, writer, false, true);
682
+ }
683
+ writer.writeShort(0);
461
684
  client.net.send(writer.getData());
685
+ client.lastFrame = this.sv.frame;
462
686
  }
463
687
  // GameEngine Implementation
464
688
  trace(start, end) {
@@ -1,4 +1,4 @@
1
- import { NetDriver, ServerCommand, UserCommand, BinaryStream } from '@quake2ts/shared';
1
+ import { NetDriver, ServerCommand, PlayerState, UserCommand, BinaryStream } from '@quake2ts/shared';
2
2
  import WebSocket from 'ws';
3
3
  import { GameEngine, MulticastType, Entity } from '@quake2ts/game';
4
4
 
@@ -46,21 +46,44 @@ declare class DedicatedServer implements GameEngine {
46
46
 
47
47
  declare enum ClientState {
48
48
  Free = 0,
49
- Connected = 1,// Connection established, waiting for challenge/info
50
- Primed = 2,// Sent info, waiting for gamestate
51
- Active = 3,// In game
52
- Spawned = 4
49
+ Zombie = 1,// client has been disconnected, but don't reuse connection for a couple seconds
50
+ Connected = 2,// has been assigned to a client_t, but not in game yet
51
+ Spawned = 3,// client is fully in game
52
+ Active = 4
53
+ }
54
+ interface ClientFrame {
55
+ areaBytes: number;
56
+ areaBits: Uint8Array;
57
+ playerState: PlayerState;
58
+ numEntities: number;
59
+ firstEntity: number;
60
+ sentTime: number;
53
61
  }
54
62
  interface Client {
55
63
  index: number;
56
64
  state: ClientState;
57
65
  net: NetDriver;
58
- name: string;
59
66
  userInfo: string;
60
- edict: Entity | null;
61
- lastCmd: UserCommand;
62
- lastPacketTime: number;
63
67
  lastFrame: number;
68
+ lastCmd: UserCommand;
69
+ commandMsec: number;
70
+ frameLatency: number[];
71
+ ping: number;
72
+ messageSize: number[];
73
+ rate: number;
74
+ suppressCount: number;
75
+ edict: Entity | null;
76
+ name: string;
77
+ messageLevel: number;
78
+ datagram: Uint8Array;
79
+ frames: ClientFrame[];
80
+ download?: Uint8Array;
81
+ downloadSize: number;
82
+ downloadCount: number;
83
+ lastMessage: number;
84
+ lastConnect: number;
85
+ challenge: number;
86
+ messageQueue: Uint8Array[];
64
87
  }
65
88
  declare function createClient(index: number, net: NetDriver): Client;
66
89
 
@@ -81,4 +104,4 @@ declare class ClientMessageParser {
81
104
  private parseStringCmd;
82
105
  }
83
106
 
84
- export { type Client, type ClientMessageHandler, ClientMessageParser, ClientState, DedicatedServer, WebSocketNetDriver, createClient };
107
+ export { type Client, type ClientFrame, type ClientMessageHandler, ClientMessageParser, ClientState, DedicatedServer, WebSocketNetDriver, createClient };
@@ -1,4 +1,4 @@
1
- import { NetDriver, ServerCommand, UserCommand, BinaryStream } from '@quake2ts/shared';
1
+ import { NetDriver, ServerCommand, PlayerState, UserCommand, BinaryStream } from '@quake2ts/shared';
2
2
  import WebSocket from 'ws';
3
3
  import { GameEngine, MulticastType, Entity } from '@quake2ts/game';
4
4
 
@@ -46,21 +46,44 @@ declare class DedicatedServer implements GameEngine {
46
46
 
47
47
  declare enum ClientState {
48
48
  Free = 0,
49
- Connected = 1,// Connection established, waiting for challenge/info
50
- Primed = 2,// Sent info, waiting for gamestate
51
- Active = 3,// In game
52
- Spawned = 4
49
+ Zombie = 1,// client has been disconnected, but don't reuse connection for a couple seconds
50
+ Connected = 2,// has been assigned to a client_t, but not in game yet
51
+ Spawned = 3,// client is fully in game
52
+ Active = 4
53
+ }
54
+ interface ClientFrame {
55
+ areaBytes: number;
56
+ areaBits: Uint8Array;
57
+ playerState: PlayerState;
58
+ numEntities: number;
59
+ firstEntity: number;
60
+ sentTime: number;
53
61
  }
54
62
  interface Client {
55
63
  index: number;
56
64
  state: ClientState;
57
65
  net: NetDriver;
58
- name: string;
59
66
  userInfo: string;
60
- edict: Entity | null;
61
- lastCmd: UserCommand;
62
- lastPacketTime: number;
63
67
  lastFrame: number;
68
+ lastCmd: UserCommand;
69
+ commandMsec: number;
70
+ frameLatency: number[];
71
+ ping: number;
72
+ messageSize: number[];
73
+ rate: number;
74
+ suppressCount: number;
75
+ edict: Entity | null;
76
+ name: string;
77
+ messageLevel: number;
78
+ datagram: Uint8Array;
79
+ frames: ClientFrame[];
80
+ download?: Uint8Array;
81
+ downloadSize: number;
82
+ downloadCount: number;
83
+ lastMessage: number;
84
+ lastConnect: number;
85
+ challenge: number;
86
+ messageQueue: Uint8Array[];
64
87
  }
65
88
  declare function createClient(index: number, net: NetDriver): Client;
66
89
 
@@ -81,4 +104,4 @@ declare class ClientMessageParser {
81
104
  private parseStringCmd;
82
105
  }
83
106
 
84
- export { type Client, type ClientMessageHandler, ClientMessageParser, ClientState, DedicatedServer, WebSocketNetDriver, createClient };
107
+ export { type Client, type ClientFrame, type ClientMessageHandler, ClientMessageParser, ClientState, DedicatedServer, WebSocketNetDriver, createClient };