teeworlds 2.3.9 → 2.4.0

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.
package/README.md CHANGED
@@ -83,7 +83,7 @@ process.on("SIGINT", () => {
83
83
  // process.exit()
84
84
  })
85
85
  process.stdin.on("data", data => {
86
- client.Say(data.toString()); // write input in chat
86
+ client.game.Say(data.toString()); // write input in chat
87
87
 
88
88
  })
89
89
  ```
package/lib/client.js CHANGED
@@ -25,6 +25,7 @@ var movement_1 = __importDefault(require("./movement"));
25
25
  var MsgPacker_1 = require("./MsgPacker");
26
26
  var snapshot_1 = require("./snapshot");
27
27
  var huffman_1 = __importDefault(require("./huffman"));
28
+ var game_1 = require("./components/game");
28
29
  var huff = new huffman_1.default();
29
30
  var States;
30
31
  (function (States) {
@@ -115,8 +116,6 @@ var Client = /** @class */ (function (_super) {
115
116
  _this.requestResend = false;
116
117
  if (options)
117
118
  _this.options = options;
118
- _this.timer = 0;
119
- _this.movement = new movement_1.default();
120
119
  _this.snaps = [];
121
120
  _this.sentChunkQueue = [];
122
121
  _this.queueChunkEx = [];
@@ -124,7 +123,6 @@ var Client = /** @class */ (function (_super) {
124
123
  _this.ack = 0; // ack of messages the client has received
125
124
  _this.clientAck = 0; // ack of messages the client has sent
126
125
  _this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
127
- _this.lastMsg = "";
128
126
  _this.socket = dgram_1.default.createSocket("udp4");
129
127
  _this.socket.bind();
130
128
  _this.TKEN = Buffer.from([255, 255, 255, 255]);
@@ -132,6 +130,8 @@ var Client = /** @class */ (function (_super) {
132
130
  _this.lastSendTime = new Date().getTime();
133
131
  _this.lastRecvTime = new Date().getTime();
134
132
  _this.lastSentMessages = [];
133
+ _this.movement = new movement_1.default();
134
+ _this.game = new game_1.Game(_this);
135
135
  return _this;
136
136
  }
137
137
  Client.prototype.ResendAfter = function (lastAck) {
@@ -183,6 +183,7 @@ var Client = /** @class */ (function (_super) {
183
183
  }
184
184
  return unpacked;
185
185
  };
186
+ /* Send a Control Msg to the server. (used for disconnect)*/
186
187
  Client.prototype.SendControlMsg = function (msg, ExtraMsg) {
187
188
  var _this = this;
188
189
  if (ExtraMsg === void 0) { ExtraMsg = ""; }
@@ -202,6 +203,7 @@ var Client = /** @class */ (function (_super) {
202
203
  */
203
204
  });
204
205
  };
206
+ /* Send a Msg (or Msg[]) to the server.*/
205
207
  Client.prototype.SendMsgEx = function (Msgs) {
206
208
  var _this = this;
207
209
  if (this.State == States.STATE_OFFLINE)
@@ -260,9 +262,11 @@ var Client = /** @class */ (function (_super) {
260
262
  return;
261
263
  this.socket.send(packet, 0, packet.length, this.port, this.host);
262
264
  };
265
+ /* Queue a chunk (It will get sent in the next packet). */
263
266
  Client.prototype.QueueChunkEx = function (Msg) {
264
267
  this.queueChunkEx.push(Msg);
265
268
  };
269
+ /* Send a Raw Buffer (as chunk) to the server. */
266
270
  Client.prototype.SendMsgRaw = function (chunks) {
267
271
  if (this.State == States.STATE_OFFLINE)
268
272
  return;
@@ -298,6 +302,7 @@ var Client = /** @class */ (function (_super) {
298
302
  });
299
303
  return chunk;
300
304
  };
305
+ /* Connect the client to the server. */
301
306
  Client.prototype.connect = function () {
302
307
  var _this = this;
303
308
  var _a;
@@ -348,15 +353,15 @@ var Client = /** @class */ (function (_super) {
348
353
  }, 5000);
349
354
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
350
355
  if (this.socket)
351
- this.socket.on("message", function (a, rinfo) {
356
+ this.socket.on("message", function (packet, rinfo) {
352
357
  var _a, _b, _c, _d, _e, _f, _g, _h;
353
358
  if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
354
359
  return;
355
360
  clearInterval(connectInterval);
356
- if (a.toJSON().data[0] == 0x10) {
357
- if (a.toString().includes("TKEN") || a.toJSON().data[3] == 0x2) {
361
+ if (packet.toJSON().data[0] == 0x10) {
362
+ if (packet.toString().includes("TKEN") || packet.toJSON().data[3] == 0x2) {
358
363
  clearInterval(connectInterval);
359
- _this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length));
364
+ _this.TKEN = Buffer.from(packet.toJSON().data.slice(packet.toJSON().data.length - 4, packet.toJSON().data.length));
360
365
  _this.SendControlMsg(3);
361
366
  _this.State = States.STATE_LOADING; // loading state
362
367
  _this.receivedSnaps = 0;
@@ -378,42 +383,42 @@ var Client = /** @class */ (function (_super) {
378
383
  }
379
384
  _this.SendMsgEx([client_version, info]);
380
385
  }
381
- else if (a.toJSON().data[3] == 0x4) {
386
+ else if (packet.toJSON().data[3] == 0x4) {
382
387
  // disconnected
383
388
  _this.State = States.STATE_OFFLINE;
384
- var reason = (MsgUnpacker_1.unpackString(a.toJSON().data.slice(4)).result);
389
+ var reason = (MsgUnpacker_1.unpackString(packet.toJSON().data.slice(4)).result);
385
390
  _this.emit("disconnect", reason);
386
391
  }
387
- if (a.toJSON().data[3] !== 0x0) { // keepalive
392
+ if (packet.toJSON().data[3] !== 0x0) { // keepalive
388
393
  _this.lastRecvTime = new Date().getTime();
389
394
  }
390
395
  }
391
396
  else {
392
397
  _this.lastRecvTime = new Date().getTime();
393
398
  }
394
- var unpacked = _this.Unpack(a);
395
- unpacked.chunks = unpacked.chunks.filter(function (a) { return ((a.flags & 2) && (a.flags & 1)) ? a.seq > _this.ack : true; }); // filter out already received chunks
396
- unpacked.chunks.forEach(function (a) {
397
- if (a.flags & 1 && (a.flags !== 15)) { // vital and not connless
398
- if (a.seq === (_this.ack + 1) % (1 << 10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
399
- _this.ack = a.seq;
399
+ var unpacked = _this.Unpack(packet);
400
+ unpacked.chunks = unpacked.chunks.filter(function (chunk) { return ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq > _this.ack : true; }); // filter out already received chunks
401
+ unpacked.chunks.forEach(function (chunk) {
402
+ if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
403
+ if (chunk.seq === (_this.ack + 1) % (1 << 10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
404
+ _this.ack = chunk.seq;
400
405
  _this.requestResend = false;
401
406
  }
402
407
  else { //IsSeqInBackroom (old packet that we already got)
403
408
  var Bottom = (_this.ack - (1 << 10) / 2);
404
409
  if (Bottom < 0) {
405
- if ((a.seq <= _this.ack) || (a.seq >= (Bottom + (1 << 10))))
410
+ if ((chunk.seq <= _this.ack) || (chunk.seq >= (Bottom + (1 << 10))))
406
411
  return;
407
412
  }
408
413
  else {
409
- if (a.seq <= _this.ack && a.seq >= Bottom)
414
+ if (chunk.seq <= _this.ack && chunk.seq >= Bottom)
410
415
  return;
411
416
  }
412
417
  _this.requestResend = true;
413
418
  }
414
419
  }
415
420
  });
416
- unpacked.chunks.filter(function (a) { return a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game'; }).forEach(function (a) {
421
+ unpacked.chunks.filter(function (chunk) { return chunk.msgid == NETMSGTYPE.SV_BROADCAST && chunk.type == 'game'; }).forEach(function (a) {
417
422
  var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
418
423
  _this.emit("broadcast", unpacker.unpackString());
419
424
  });
@@ -426,10 +431,8 @@ var Client = /** @class */ (function (_super) {
426
431
  });
427
432
  var snapChunks = [];
428
433
  if (((_g = _this.options) === null || _g === void 0 ? void 0 : _g.lightweight) !== true)
429
- snapChunks = unpacked.chunks.filter(function (a) { return a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY"; });
434
+ snapChunks = unpacked.chunks.filter(function (chunk) { return chunk.msg === "SNAP" || chunk.msg === "SNAP_SINGLE" || chunk.msg === "SNAP_EMPTY"; });
430
435
  if (snapChunks.length > 0) {
431
- var part = 0;
432
- var num_parts = 1;
433
436
  if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
434
437
  _this.PredGameTick = _this.AckGameTick + 1;
435
438
  snapChunks.forEach(function (chunk) {
@@ -474,6 +477,9 @@ var Client = /** @class */ (function (_super) {
474
477
  });
475
478
  }
476
479
  var chunkMessages = unpacked.chunks.map(function (a) { return a.msg; });
480
+ if (unpacked.chunks.findIndex(function (chunk) { return chunk.msgid == 23 && chunk.type == "sys"; }) !== -1) {
481
+ _this.game._ping_resolve(new Date().getTime());
482
+ }
477
483
  if (chunkMessages.includes("SV_CHAT")) {
478
484
  var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_CHAT"; });
479
485
  chat.forEach(function (a) {
@@ -494,7 +500,7 @@ var Client = /** @class */ (function (_super) {
494
500
  }
495
501
  });
496
502
  }
497
- var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_KILL_MSG" || a.msg == "SV_MOTD"; });
503
+ var chat = unpacked.chunks.filter(function (chunk) { return chunk.msg == "SV_KILL_MSG" || chunk.msg == "SV_MOTD"; });
498
504
  chat.forEach(function (a) {
499
505
  if (a.msg == "SV_KILL_MSG") {
500
506
  var unpacked = {};
@@ -567,6 +573,7 @@ var Client = /** @class */ (function (_super) {
567
573
  }
568
574
  });
569
575
  };
576
+ /* Sending the input. (automatically done unless options.lightweight is on) */
570
577
  Client.prototype.sendInput = function (input) {
571
578
  if (input === void 0) { input = this.movement.input; }
572
579
  if (this.State != States.STATE_ONLINE)
@@ -599,6 +606,7 @@ var Client = /** @class */ (function (_super) {
599
606
  enumerable: false,
600
607
  configurable: true
601
608
  });
609
+ /* Disconnect the client. */
602
610
  Client.prototype.Disconnect = function () {
603
611
  var _this = this;
604
612
  return new Promise(function (resolve) {
@@ -611,71 +619,11 @@ var Client = /** @class */ (function (_super) {
611
619
  });
612
620
  });
613
621
  };
614
- Client.prototype.Say = function (message, team) {
615
- var _a;
616
- if (team === void 0) { team = false; }
617
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
618
- packer.AddInt(team ? 1 : 0); // team
619
- packer.AddString(message);
620
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
621
- this.QueueChunkEx(packer);
622
- else
623
- this.SendMsgEx(packer);
624
- };
625
- Client.prototype.Vote = function (vote) {
626
- var _a;
627
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
628
- packer.AddInt(vote ? 1 : -1);
629
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
630
- this.QueueChunkEx(packer);
631
- else
632
- this.SendMsgEx(packer);
633
- };
634
- Client.prototype.ChangePlayerInfo = function (playerInfo) {
635
- var _a;
636
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
637
- packer.AddString(playerInfo.name); //m_pName);
638
- packer.AddString(playerInfo.clan); //m_pClan);
639
- packer.AddInt(playerInfo.country); //m_Country);
640
- packer.AddString(playerInfo.skin); //m_pSkin);
641
- packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
642
- packer.AddInt(playerInfo.color_body); //m_ColorBody);
643
- packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
644
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
645
- this.QueueChunkEx(packer);
646
- else
647
- this.SendMsgEx(packer);
648
- };
649
- Client.prototype.Kill = function () {
650
- var _a;
651
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
652
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
653
- this.QueueChunkEx(packer);
654
- else
655
- this.SendMsgEx(packer);
656
- };
657
- Client.prototype.ChangeTeam = function (team) {
658
- var _a;
659
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
660
- packer.AddInt(team);
661
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
662
- this.QueueChunkEx(packer);
663
- else
664
- this.SendMsgEx(packer);
665
- };
666
- Client.prototype.Emote = function (emote) {
667
- var _a;
668
- var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
669
- packer.AddInt(emote);
670
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight))
671
- this.QueueChunkEx(packer);
672
- else
673
- this.SendMsgEx(packer);
674
- };
622
+ /* Get the client_info from a specific player id. */
675
623
  Client.prototype.client_info = function (id) {
676
- var delta = this.SnapUnpacker.deltas.filter(function (a) {
677
- return a.type_id == 11
678
- && a.id == id;
624
+ var delta = this.SnapUnpacker.deltas.filter(function (_delta) {
625
+ return _delta.type_id == 11
626
+ && _delta.id == id;
679
627
  });
680
628
  if (delta.length == 0)
681
629
  return undefined;
@@ -684,28 +632,31 @@ var Client = /** @class */ (function (_super) {
684
632
  // .map(a => a.parsed as ClientInfo);
685
633
  };
686
634
  Object.defineProperty(Client.prototype, "client_infos", {
635
+ /* Get all client infos. */
687
636
  get: function () {
688
- return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 11; })
637
+ return this.SnapUnpacker.deltas.filter(function (_delta) { return _delta.type_id == 11; })
689
638
  .sort(function (a, b) { return a.id - b.id; })
690
639
  .map(function (a) { return a.parsed; });
691
640
  },
692
641
  enumerable: false,
693
642
  configurable: true
694
643
  });
644
+ /* Get the player info from a specific player id. */
695
645
  Client.prototype.player_info = function (id) {
696
- var delta = this.SnapUnpacker.deltas.filter(function (a) {
697
- return a.type_id == 10
698
- && a.id == id;
646
+ var delta = this.SnapUnpacker.deltas.filter(function (_delta) {
647
+ return _delta.type_id == 10
648
+ && _delta.id == id;
699
649
  });
700
650
  if (delta.length == 0)
701
651
  return undefined;
702
652
  return delta[0].parsed;
703
653
  };
704
654
  Object.defineProperty(Client.prototype, "player_infos", {
655
+ /* Get all player infos. */
705
656
  get: function () {
706
- return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 10; })
657
+ return this.SnapUnpacker.deltas.filter(function (_delta) { return _delta.type_id == 10; })
707
658
  .sort(function (a, b) { return a.id - b.id; })
708
- .map(function (a) { return a.parsed; });
659
+ .map(function (player) { return player.parsed; });
709
660
  },
710
661
  enumerable: false,
711
662
  configurable: true
package/lib/client.ts CHANGED
@@ -10,6 +10,7 @@ import Movement from './movement';
10
10
  import { MsgPacker } from './MsgPacker';
11
11
  import { Snapshot } from './snapshot';
12
12
  import Huffman from "./huffman";
13
+ import { Game } from "./components/game";
13
14
 
14
15
  const huff = new Huffman();
15
16
 
@@ -146,41 +147,7 @@ declare interface iOptions {
146
147
  }
147
148
 
148
149
  export declare interface Client {
149
- host: string;
150
- port: number;
151
- name: string;
152
- State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
153
- ack: number;
154
- clientAck: number;
155
- receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
156
- lastMsg: string;
157
- socket: net.Socket | undefined;
158
- TKEN: Buffer;
159
- time: number;
160
- SnapUnpacker: Snapshot;
161
-
162
- timer: number;
163
- PredGameTick: number;
164
- AckGameTick: number;
165
150
 
166
- SnapshotParts: number;
167
- currentSnapshotGameTick: number;
168
-
169
- movement: Movement;
170
-
171
- snaps: Buffer[];
172
-
173
- sentChunkQueue: Buffer[];
174
- queueChunkEx: MsgPacker[];
175
- lastSendTime: number;
176
- lastRecvTime: number;
177
-
178
- lastSentMessages: {msg: MsgPacker, ack: number}[];
179
-
180
- // eSnapHolder: eSnap[];
181
-
182
-
183
- options?: iOptions;
184
151
 
185
152
  on(event: 'connected', listener: () => void): this;
186
153
  on(event: 'disconnect', listener: (reason: string) => void): this;
@@ -190,15 +157,49 @@ export declare interface Client {
190
157
  on(event: 'kill', listener: (kill: iKillMsg) => void): this;
191
158
  on(event: 'motd', listener: (message: string) => void): this;
192
159
 
193
- requestResend: boolean;
194
160
  }
195
161
 
162
+ export class Client extends EventEmitter {
196
163
 
164
+ private host: string;
165
+ private port: number;
166
+ private name: string;
167
+ private State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
168
+ private ack: number;
169
+ private clientAck: number;
170
+ private receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
171
+ private socket: net.Socket | undefined;
172
+ private TKEN: Buffer;
173
+ private time: number;
174
+ private SnapUnpacker: Snapshot;
175
+
176
+ private PredGameTick: number;
177
+ private AckGameTick: number;
178
+
179
+ private SnapshotParts: number;
180
+ private currentSnapshotGameTick: number;
197
181
 
198
- export class Client extends EventEmitter {
182
+
183
+ private snaps: Buffer[];
184
+
185
+ private sentChunkQueue: Buffer[];
186
+ private queueChunkEx: MsgPacker[];
187
+ private lastSendTime: number;
188
+ private lastRecvTime: number;
189
+
190
+ private lastSentMessages: {msg: MsgPacker, ack: number}[];
191
+
192
+
193
+ public movement: Movement;
194
+ public game: Game;
195
+
196
+ // eSnapHolder: eSnap[];
199
197
 
200
198
 
199
+ public readonly options?: iOptions;
200
+ private requestResend: boolean;
201
201
 
202
+
202
203
  constructor(ip: string, port: number, nickname: string, options?: iOptions) {
203
204
  super();
204
205
  this.host = ip;
@@ -217,10 +218,7 @@ export class Client extends EventEmitter {
217
218
  if (options)
218
219
  this.options = options;
219
220
 
220
- this.timer = 0;
221
-
222
- this.movement = new Movement();
223
-
221
+
224
222
  this.snaps = [];
225
223
 
226
224
  this.sentChunkQueue = [];
@@ -230,8 +228,7 @@ export class Client extends EventEmitter {
230
228
  this.ack = 0; // ack of messages the client has received
231
229
  this.clientAck = 0; // ack of messages the client has sent
232
230
  this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
233
- this.lastMsg = "";
234
- this.socket = net.createSocket("udp4")
231
+ this.socket = net.createSocket("udp4");
235
232
  this.socket.bind();
236
233
 
237
234
  this.TKEN = Buffer.from([255, 255, 255, 255])
@@ -240,10 +237,14 @@ export class Client extends EventEmitter {
240
237
  this.lastRecvTime = new Date().getTime();
241
238
 
242
239
  this.lastSentMessages = [];
240
+ this.movement = new Movement();
241
+
242
+ this.game = new Game(this);
243
+
243
244
 
244
245
  }
245
246
 
246
- ResendAfter(lastAck: number) {
247
+ private ResendAfter(lastAck: number) {
247
248
  this.clientAck = lastAck;
248
249
 
249
250
 
@@ -256,7 +257,7 @@ export class Client extends EventEmitter {
256
257
  this.SendMsgEx(toResend);
257
258
  }
258
259
 
259
- Unpack(packet: Buffer): _packet {
260
+ private Unpack(packet: Buffer): _packet {
260
261
  var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
261
262
 
262
263
 
@@ -301,6 +302,8 @@ export class Client extends EventEmitter {
301
302
  }
302
303
  return unpacked
303
304
  }
305
+
306
+ /* Send a Control Msg to the server. (used for disconnect)*/
304
307
  SendControlMsg(msg: number, ExtraMsg: string = "") {
305
308
  this.lastSendTime = new Date().getTime();
306
309
  return new Promise((resolve, reject) => {
@@ -320,6 +323,7 @@ export class Client extends EventEmitter {
320
323
  })
321
324
  }
322
325
 
326
+ /* Send a Msg (or Msg[]) to the server.*/
323
327
  SendMsgEx(Msgs: MsgPacker[] | MsgPacker) {
324
328
  if (this.State == States.STATE_OFFLINE)
325
329
  return;
@@ -378,11 +382,11 @@ export class Client extends EventEmitter {
378
382
  return;
379
383
  this.socket.send(packet, 0, packet.length, this.port, this.host)
380
384
  }
381
-
385
+ /* Queue a chunk (It will get sent in the next packet). */
382
386
  QueueChunkEx(Msg: MsgPacker) {
383
387
  this.queueChunkEx.push(Msg);
384
388
  }
385
-
389
+ /* Send a Raw Buffer (as chunk) to the server. */
386
390
  SendMsgRaw(chunks: Buffer[]) {
387
391
  if (this.State == States.STATE_OFFLINE)
388
392
  return;
@@ -399,7 +403,7 @@ export class Client extends EventEmitter {
399
403
  this.socket.send(packet, 0, packet.length, this.port, this.host)
400
404
  }
401
405
 
402
- MsgToChunk(packet: Buffer) {
406
+ private MsgToChunk(packet: Buffer) {
403
407
  var chunk: chunk = {} as chunk;
404
408
  chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
405
409
  chunk.flags = (packet[0] >> 6) & 3;
@@ -423,6 +427,7 @@ export class Client extends EventEmitter {
423
427
  return chunk;
424
428
  }
425
429
 
430
+ /* Connect the client to the server. */
426
431
  connect() {
427
432
  this.State = States.STATE_CONNECTING;
428
433
 
@@ -475,14 +480,15 @@ export class Client extends EventEmitter {
475
480
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
476
481
 
477
482
  if (this.socket)
478
- this.socket.on("message", (a, rinfo) => {
483
+ this.socket.on("message", (packet, rinfo) => {
479
484
  if (this.State == 0 || rinfo.address != this.host || rinfo.port != this.port)
480
485
  return;
481
486
  clearInterval(connectInterval)
482
- if (a.toJSON().data[0] == 0x10) {
483
- if (a.toString().includes("TKEN") || a.toJSON().data[3] == 0x2) {
487
+
488
+ if (packet.toJSON().data[0] == 0x10) {
489
+ if (packet.toString().includes("TKEN") || packet.toJSON().data[3] == 0x2) {
484
490
  clearInterval(connectInterval);
485
- this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
491
+ this.TKEN = Buffer.from(packet.toJSON().data.slice(packet.toJSON().data.length - 4, packet.toJSON().data.length))
486
492
  this.SendControlMsg(3);
487
493
  this.State = States.STATE_LOADING; // loading state
488
494
  this.receivedSnaps = 0;
@@ -507,28 +513,26 @@ export class Client extends EventEmitter {
507
513
  }
508
514
 
509
515
  this.SendMsgEx([client_version, info])
510
- } else if (a.toJSON().data[3] == 0x4) {
516
+ } else if (packet.toJSON().data[3] == 0x4) {
511
517
  // disconnected
512
518
  this.State = States.STATE_OFFLINE;
513
- let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
519
+ let reason: string = (unpackString(packet.toJSON().data.slice(4)).result);
514
520
  this.emit("disconnect", reason);
515
521
  }
516
- if (a.toJSON().data[3] !== 0x0) { // keepalive
522
+ if (packet.toJSON().data[3] !== 0x0) { // keepalive
517
523
  this.lastRecvTime = new Date().getTime();
518
524
  }
519
525
  } else {
520
526
  this.lastRecvTime = new Date().getTime();
521
-
522
527
  }
523
-
524
528
 
525
- var unpacked: _packet = this.Unpack(a);
526
- unpacked.chunks = unpacked.chunks.filter(a => ((a.flags & 2) && (a.flags & 1)) ? a.seq! > this.ack : true); // filter out already received chunks
527
-
528
- unpacked.chunks.forEach(a => {
529
- if (a.flags & 1 && (a.flags !== 15)) { // vital and not connless
530
- if (a.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
531
- this.ack = a.seq!;
529
+ var unpacked: _packet = this.Unpack(packet);
530
+ unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
531
+
532
+ unpacked.chunks.forEach(chunk => {
533
+ if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
534
+ if (chunk.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
535
+ this.ack = chunk.seq!;
532
536
 
533
537
  this.requestResend = false;
534
538
  }
@@ -536,10 +540,10 @@ export class Client extends EventEmitter {
536
540
  let Bottom = (this.ack - (1<<10)/2);
537
541
 
538
542
  if(Bottom < 0) {
539
- if((a.seq! <= this.ack) || (a.seq! >= (Bottom + (1<<10))))
543
+ if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10))))
540
544
  return;
541
545
  } else {
542
- if(a.seq! <= this.ack && a.seq! >= Bottom)
546
+ if(chunk.seq! <= this.ack && chunk.seq! >= Bottom)
543
547
  return;
544
548
  }
545
549
  this.requestResend = true;
@@ -548,7 +552,7 @@ export class Client extends EventEmitter {
548
552
  }
549
553
 
550
554
  })
551
- unpacked.chunks.filter(a => a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game').forEach(a => {
555
+ unpacked.chunks.filter(chunk => chunk.msgid == NETMSGTYPE.SV_BROADCAST && chunk.type == 'game').forEach(a => {
552
556
  let unpacker = new MsgUnpacker(a.raw.toJSON().data);
553
557
 
554
558
  this.emit("broadcast", unpacker.unpackString());
@@ -562,10 +566,8 @@ export class Client extends EventEmitter {
562
566
  })
563
567
  let snapChunks: chunk[] = [];
564
568
  if (this.options?.lightweight !== true)
565
- snapChunks = unpacked.chunks.filter(a => a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY");
569
+ snapChunks = unpacked.chunks.filter(chunk => chunk.msg === "SNAP" || chunk.msg === "SNAP_SINGLE" || chunk.msg === "SNAP_EMPTY");
566
570
  if (snapChunks.length > 0) {
567
- let part = 0;
568
- let num_parts = 1;
569
571
  if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
570
572
  this.PredGameTick = this.AckGameTick + 1;
571
573
 
@@ -625,7 +627,11 @@ export class Client extends EventEmitter {
625
627
 
626
628
  })
627
629
  }
630
+
628
631
  var chunkMessages = unpacked.chunks.map(a => a.msg)
632
+ if (unpacked.chunks.findIndex(chunk => chunk.msgid == 23 && chunk.type == "sys") !== -1) {
633
+ this.game._ping_resolve(new Date().getTime())
634
+ }
629
635
  if (chunkMessages.includes("SV_CHAT")) {
630
636
  var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
631
637
  chat.forEach(a => {
@@ -647,7 +653,7 @@ export class Client extends EventEmitter {
647
653
  }
648
654
  })
649
655
  }
650
- var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG" || a.msg == "SV_MOTD");
656
+ var chat = unpacked.chunks.filter(chunk => chunk.msg == "SV_KILL_MSG" || chunk.msg == "SV_MOTD");
651
657
  chat.forEach(a => {
652
658
  if (a.msg == "SV_KILL_MSG") {
653
659
  var unpacked: iKillMsg = {} as iKillMsg;
@@ -723,6 +729,7 @@ export class Client extends EventEmitter {
723
729
  })
724
730
  }
725
731
 
732
+ /* Sending the input. (automatically done unless options.lightweight is on) */
726
733
  sendInput(input = this.movement.input) {
727
734
  if (this.State != States.STATE_ONLINE)
728
735
  return;
@@ -754,6 +761,7 @@ export class Client extends EventEmitter {
754
761
  return this.movement.input;
755
762
  }
756
763
 
764
+ /* Disconnect the client. */
757
765
  Disconnect() {
758
766
  return new Promise((resolve) => {
759
767
  this.SendControlMsg(4).then(() => {
@@ -766,64 +774,11 @@ export class Client extends EventEmitter {
766
774
  })
767
775
  }
768
776
 
769
- Say(message: string, team = false) {
770
- var packer = new MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
771
- packer.AddInt(team ? 1 : 0); // team
772
- packer.AddString(message);
773
- if (!this.options?.lightweight)
774
- this.QueueChunkEx(packer);
775
- else
776
- this.SendMsgEx(packer);
777
- }
778
- Vote(vote: boolean) {
779
- var packer = new MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
780
- packer.AddInt(vote ? 1 : -1);
781
- if (!this.options?.lightweight)
782
- this.QueueChunkEx(packer);
783
- else
784
- this.SendMsgEx(packer);
785
- }
786
- ChangePlayerInfo(playerInfo: ClientInfo) {
787
- var packer = new MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
788
- packer.AddString(playerInfo.name); //m_pName);
789
- packer.AddString(playerInfo.clan); //m_pClan);
790
- packer.AddInt(playerInfo.country); //m_Country);
791
- packer.AddString(playerInfo.skin); //m_pSkin);
792
- packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
793
- packer.AddInt(playerInfo.color_body); //m_ColorBody);
794
- packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
795
- if (!this.options?.lightweight)
796
- this.QueueChunkEx(packer);
797
- else
798
- this.SendMsgEx(packer);
799
- }
800
- Kill() {
801
- var packer = new MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
802
- if (!this.options?.lightweight)
803
- this.QueueChunkEx(packer);
804
- else
805
- this.SendMsgEx(packer);
806
- }
807
- ChangeTeam(team: number) {
808
- var packer = new MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
809
- packer.AddInt(team);
810
- if (!this.options?.lightweight)
811
- this.QueueChunkEx(packer);
812
- else
813
- this.SendMsgEx(packer);
814
- }
815
- Emote(emote: number) {
816
- var packer = new MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
817
- packer.AddInt(emote);
818
- if (!this.options?.lightweight)
819
- this.QueueChunkEx(packer);
820
- else
821
- this.SendMsgEx(packer);
822
- }
777
+ /* Get the client_info from a specific player id. */
823
778
  client_info(id: number) {
824
- let delta = this.SnapUnpacker.deltas.filter(a =>
825
- a.type_id == 11
826
- && a.id == id
779
+ let delta = this.SnapUnpacker.deltas.filter(_delta =>
780
+ _delta.type_id == 11
781
+ && _delta.id == id
827
782
  );
828
783
 
829
784
  if (delta.length == 0)
@@ -832,25 +787,29 @@ export class Client extends EventEmitter {
832
787
  // .sort((a, b) => a.id - b.id)
833
788
  // .map(a => a.parsed as ClientInfo);
834
789
  }
790
+
791
+ /* Get all client infos. */
835
792
  get client_infos(): ClientInfo[] {
836
793
 
837
- return this.SnapUnpacker.deltas.filter(a => a.type_id == 11)
838
- .sort((a, b) => a.id - b.id)
839
- .map(a => a.parsed as ClientInfo) ;
794
+ return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 11)
795
+ .sort((a, b) => a.id - b.id)
796
+ .map(a => a.parsed as ClientInfo);
840
797
  }
798
+ /* Get the player info from a specific player id. */
841
799
  player_info(id: number) {
842
- let delta = this.SnapUnpacker.deltas.filter(a =>
843
- a.type_id == 10
844
- && a.id == id
800
+ let delta = this.SnapUnpacker.deltas.filter(_delta =>
801
+ _delta.type_id == 10
802
+ && _delta.id == id
845
803
  );
846
804
 
847
805
  if (delta.length == 0)
848
806
  return undefined;
849
807
  return delta[0].parsed as PlayerInfo;
850
808
  }
809
+ /* Get all player infos. */
851
810
  get player_infos(): PlayerInfo[] {
852
- return this.SnapUnpacker.deltas.filter(a => a.type_id == 10)
811
+ return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 10)
853
812
  .sort((a, b) => a.id - b.id)
854
- .map(a => a.parsed as PlayerInfo);
813
+ .map(player => player.parsed as PlayerInfo);
855
814
  }
856
815
  }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Game = void 0;
4
+ var MsgPacker_1 = require("../MsgPacker");
5
+ var NETMSGTYPE;
6
+ (function (NETMSGTYPE) {
7
+ NETMSGTYPE[NETMSGTYPE["EX"] = 0] = "EX";
8
+ NETMSGTYPE[NETMSGTYPE["SV_MOTD"] = 1] = "SV_MOTD";
9
+ NETMSGTYPE[NETMSGTYPE["SV_BROADCAST"] = 2] = "SV_BROADCAST";
10
+ NETMSGTYPE[NETMSGTYPE["SV_CHAT"] = 3] = "SV_CHAT";
11
+ NETMSGTYPE[NETMSGTYPE["SV_KILLMSG"] = 4] = "SV_KILLMSG";
12
+ NETMSGTYPE[NETMSGTYPE["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
13
+ NETMSGTYPE[NETMSGTYPE["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
14
+ NETMSGTYPE[NETMSGTYPE["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
15
+ NETMSGTYPE[NETMSGTYPE["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
16
+ NETMSGTYPE[NETMSGTYPE["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
17
+ NETMSGTYPE[NETMSGTYPE["SV_EMOTICON"] = 10] = "SV_EMOTICON";
18
+ NETMSGTYPE[NETMSGTYPE["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
19
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
20
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
21
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
22
+ NETMSGTYPE[NETMSGTYPE["SV_VOTESET"] = 15] = "SV_VOTESET";
23
+ NETMSGTYPE[NETMSGTYPE["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
24
+ NETMSGTYPE[NETMSGTYPE["CL_SAY"] = 17] = "CL_SAY";
25
+ NETMSGTYPE[NETMSGTYPE["CL_SETTEAM"] = 18] = "CL_SETTEAM";
26
+ NETMSGTYPE[NETMSGTYPE["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
27
+ NETMSGTYPE[NETMSGTYPE["CL_STARTINFO"] = 20] = "CL_STARTINFO";
28
+ NETMSGTYPE[NETMSGTYPE["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
29
+ NETMSGTYPE[NETMSGTYPE["CL_KILL"] = 22] = "CL_KILL";
30
+ NETMSGTYPE[NETMSGTYPE["CL_EMOTICON"] = 23] = "CL_EMOTICON";
31
+ NETMSGTYPE[NETMSGTYPE["CL_VOTE"] = 24] = "CL_VOTE";
32
+ NETMSGTYPE[NETMSGTYPE["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
33
+ NETMSGTYPE[NETMSGTYPE["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
34
+ NETMSGTYPE[NETMSGTYPE["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
35
+ NETMSGTYPE[NETMSGTYPE["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
36
+ NETMSGTYPE[NETMSGTYPE["UNUSED"] = 29] = "UNUSED";
37
+ NETMSGTYPE[NETMSGTYPE["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
38
+ NETMSGTYPE[NETMSGTYPE["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
39
+ NETMSGTYPE[NETMSGTYPE["NUM"] = 32] = "NUM";
40
+ })(NETMSGTYPE || (NETMSGTYPE = {}));
41
+ ;
42
+ var Game = /** @class */ (function () {
43
+ function Game(_client) {
44
+ // this.SendMsgEx = callback;
45
+ this._client = _client;
46
+ this._ping_resolve = function () { };
47
+ }
48
+ Game.prototype.send = function (packer) {
49
+ var _a;
50
+ if (!((_a = this._client.options) === null || _a === void 0 ? void 0 : _a.lightweight))
51
+ this._client.QueueChunkEx(packer);
52
+ else
53
+ this._client.SendMsgEx(packer);
54
+ };
55
+ Game.prototype.Say = function (message, team) {
56
+ if (team === void 0) { team = false; }
57
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
58
+ packer.AddInt(team ? 1 : 0); // team
59
+ packer.AddString(message);
60
+ this.send(packer);
61
+ };
62
+ /* Set the team of an bot. (-1 spectator team, 0 team red/normal team, 1 team blue) */
63
+ Game.prototype.SetTeam = function (team) {
64
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
65
+ packer.AddInt(team);
66
+ this.send(packer);
67
+ };
68
+ /* Spectate an player, taking their id as parameter. pretty useless */
69
+ Game.prototype.SpectatorMode = function (SpectatorID) {
70
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETSPECTATORMODE, false, 1);
71
+ packer.AddInt(SpectatorID);
72
+ this.send(packer);
73
+ };
74
+ /* Change the player info */
75
+ Game.prototype.ChangePlayerInfo = function (playerInfo) {
76
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
77
+ packer.AddString(playerInfo.name);
78
+ packer.AddString(playerInfo.clan);
79
+ packer.AddInt(playerInfo.country);
80
+ packer.AddString(playerInfo.skin);
81
+ packer.AddInt(playerInfo.use_custom_color ? 1 : 0);
82
+ packer.AddInt(playerInfo.color_body);
83
+ packer.AddInt(playerInfo.color_feet);
84
+ this.send(packer);
85
+ };
86
+ /* Kill */
87
+ Game.prototype.Kill = function () {
88
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
89
+ this.send(packer);
90
+ };
91
+ /* Send emote */
92
+ Game.prototype.Emote = function (emote) {
93
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
94
+ packer.AddInt(emote);
95
+ this.send(packer);
96
+ };
97
+ /* Vote for an already running vote (f3 / f4) */
98
+ Game.prototype.Vote = function (vote) {
99
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
100
+ packer.AddInt(vote ? 1 : -1);
101
+ this.send(packer);
102
+ };
103
+ Game.prototype.CallVote = function (Type, Value, Reason) {
104
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CALLVOTE, false, 1);
105
+ packer.AddString(Type);
106
+ packer.AddString(String(Value));
107
+ packer.AddString(Reason);
108
+ this.send(packer);
109
+ };
110
+ /* Call a vote for an server option (for example ddnet maps) */
111
+ Game.prototype.CallVoteOption = function (Value, Reason) {
112
+ this.CallVote("option", Value, Reason);
113
+ };
114
+ /* Call a vote to kick a player. Requires the player id */
115
+ Game.prototype.CallVoteKick = function (PlayerID, Reason) {
116
+ this.CallVote("kick", PlayerID, Reason);
117
+ };
118
+ /* Call a vote to set a player in spectator mode. Requires the player id */
119
+ Game.prototype.CallVoteSpectate = function (PlayerID, Reason) {
120
+ this.CallVote("spectate", PlayerID, Reason);
121
+ };
122
+ /** probably some verification of using ddnet client.*/
123
+ Game.prototype.IsDDNetLegacy = function () {
124
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_ISDDNETLEGACY, false, 1);
125
+ this.send(packer);
126
+ };
127
+ /* returns the ping in ms */
128
+ Game.prototype.Ping = function () {
129
+ var _this = this;
130
+ return new Promise(function (resolve, reject) {
131
+ var packer = new MsgPacker_1.MsgPacker(22, true, 0);
132
+ var startTime = new Date().getTime();
133
+ _this.send(packer);
134
+ var callback = function (_time) {
135
+ resolve(_time - startTime);
136
+ _this._ping_resolve = function () { };
137
+ };
138
+ _this._ping_resolve = callback;
139
+ });
140
+ };
141
+ return Game;
142
+ }());
143
+ exports.Game = Game;
@@ -0,0 +1,122 @@
1
+
2
+ import { MsgPacker } from "../MsgPacker";
3
+
4
+ import { Client } from "../client";
5
+ enum NETMSGTYPE { EX, SV_MOTD, SV_BROADCAST, SV_CHAT, SV_KILLMSG, SV_SOUNDGLOBAL, SV_TUNEPARAMS, SV_EXTRAPROJECTILE, SV_READYTOENTER, SV_WEAPONPICKUP, SV_EMOTICON, SV_VOTECLEAROPTIONS, SV_VOTEOPTIONLISTADD, SV_VOTEOPTIONADD, SV_VOTEOPTIONREMOVE, SV_VOTESET, SV_VOTESTATUS, CL_SAY, CL_SETTEAM, CL_SETSPECTATORMODE, CL_STARTINFO, CL_CHANGEINFO, CL_KILL, CL_EMOTICON, CL_VOTE, CL_CALLVOTE, CL_ISDDNETLEGACY, SV_DDRACETIMELEGACY, SV_RECORDLEGACY, UNUSED, SV_TEAMSSTATELEGACY, CL_SHOWOTHERSLEGACY, NUM };
6
+
7
+ export class Game {
8
+ // SendMsgEx: (Msgs: MsgPacker[] | MsgPacker) => void;
9
+ private _client: Client;
10
+ _ping_resolve: (_time: number) => void;
11
+ constructor(_client: Client) {
12
+ // this.SendMsgEx = callback;
13
+ this._client = _client;
14
+ this._ping_resolve = () => {};
15
+
16
+ }
17
+ private send(packer: MsgPacker) {
18
+ if (!this._client.options?.lightweight)
19
+ this._client.QueueChunkEx(packer);
20
+ else
21
+ this._client.SendMsgEx(packer);
22
+
23
+ }
24
+
25
+ Say(message: string, team = false) {
26
+ var packer = new MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
27
+ packer.AddInt(team ? 1 : 0); // team
28
+ packer.AddString(message);
29
+ this.send(packer);
30
+ }
31
+
32
+ /* Set the team of an bot. (-1 spectator team, 0 team red/normal team, 1 team blue) */
33
+ SetTeam(team: number) {
34
+ var packer = new MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
35
+ packer.AddInt(team);
36
+ this.send(packer);
37
+ }
38
+
39
+ /* Spectate an player, taking their id as parameter. pretty useless */
40
+ SpectatorMode(SpectatorID: number) {
41
+ var packer = new MsgPacker(NETMSGTYPE.CL_SETSPECTATORMODE, false, 1);
42
+ packer.AddInt(SpectatorID);
43
+ this.send(packer);
44
+ }
45
+
46
+
47
+ /* Change the player info */
48
+ ChangePlayerInfo(playerInfo: ClientInfo) {
49
+ var packer = new MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
50
+ packer.AddString(playerInfo.name);
51
+ packer.AddString(playerInfo.clan);
52
+ packer.AddInt(playerInfo.country);
53
+ packer.AddString(playerInfo.skin);
54
+ packer.AddInt(playerInfo.use_custom_color ? 1 : 0);
55
+ packer.AddInt(playerInfo.color_body);
56
+ packer.AddInt(playerInfo.color_feet);
57
+ this.send(packer);
58
+ }
59
+
60
+ /* Kill */
61
+ Kill() {
62
+ var packer = new MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
63
+ this.send(packer);
64
+ }
65
+
66
+ /* Send emote */
67
+ Emote(emote: number) {
68
+ var packer = new MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
69
+ packer.AddInt(emote);
70
+ this.send(packer);
71
+ }
72
+
73
+ /* Vote for an already running vote (f3 / f4) */
74
+ Vote(vote: boolean) {
75
+ var packer = new MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
76
+ packer.AddInt(vote ? 1 : -1);
77
+ this.send(packer);
78
+ }
79
+
80
+ private CallVote(Type: "option" | "kick" | "spectate", Value: string|number, Reason: string) {
81
+ var packer = new MsgPacker(NETMSGTYPE.CL_CALLVOTE, false, 1);
82
+ packer.AddString(Type);
83
+ packer.AddString(String(Value));
84
+ packer.AddString(Reason);
85
+ this.send(packer);
86
+ }
87
+ /* Call a vote for an server option (for example ddnet maps) */
88
+ CallVoteOption(Value: string, Reason: string) {
89
+ this.CallVote("option", Value, Reason)
90
+ }
91
+ /* Call a vote to kick a player. Requires the player id */
92
+ CallVoteKick(PlayerID: string|number, Reason: string) {
93
+ this.CallVote("kick", PlayerID, Reason)
94
+ }
95
+ /* Call a vote to set a player in spectator mode. Requires the player id */
96
+ CallVoteSpectate(PlayerID: string|number, Reason: string) {
97
+ this.CallVote("spectate", PlayerID, Reason)
98
+ }
99
+
100
+
101
+ /** probably some verification of using ddnet client.*/
102
+ IsDDNetLegacy() {
103
+ var packer = new MsgPacker(NETMSGTYPE.CL_ISDDNETLEGACY, false, 1);
104
+ this.send(packer);
105
+ }
106
+ /* returns the ping in ms */
107
+ Ping(): Promise<number> {
108
+ return new Promise((resolve, reject) => {
109
+ var packer = new MsgPacker(22, true, 0);
110
+ let startTime = new Date().getTime();
111
+ this.send(packer);
112
+
113
+ let callback = (_time: number) => {
114
+ resolve(_time - startTime);
115
+ this._ping_resolve = () => {};
116
+ }
117
+ this._ping_resolve = callback;
118
+ })
119
+ }
120
+
121
+
122
+ }
package/lib/movement.js CHANGED
@@ -19,7 +19,7 @@ var Movement = /** @class */ (function () {
19
19
  this.input.m_Jump = state ? 1 : 0;
20
20
  };
21
21
  Movement.prototype.Fire = function () {
22
- this.input.m_Fire = 1;
22
+ this.input.m_Fire++;
23
23
  };
24
24
  Movement.prototype.Hook = function (state) {
25
25
  if (state === void 0) { state = true; }
package/lib/movement.ts CHANGED
@@ -30,7 +30,7 @@ class Movement {
30
30
  this.input.m_Jump = state ? 1 : 0;
31
31
  }
32
32
  Fire() {
33
- this.input.m_Fire = 1;
33
+ this.input.m_Fire++;
34
34
  }
35
35
  Hook(state = true) {
36
36
  this.input.m_Hook = state ? 1 : 0;
package/lib/snapshot.js CHANGED
@@ -74,7 +74,7 @@ var Snapshot = /** @class */ (function () {
74
74
  pStr = pStr.replace(/\0.*/g, ''); // Remove content from first null char to end.
75
75
  return pStr;
76
76
  };
77
- Snapshot.prototype.parseItem = function (data, Type) {
77
+ Snapshot.prototype.parseItem = function (data, Type, id) {
78
78
  var _item = {};
79
79
  switch (Type) {
80
80
  case items.OBJ_EX:
@@ -212,6 +212,7 @@ var Snapshot = /** @class */ (function () {
212
212
  use_custom_color: Number(data.slice(14, 15)),
213
213
  color_body: Number(data.slice(15, 16)),
214
214
  color_feet: Number(data.slice(16, 17)),
215
+ id: id
215
216
  };
216
217
  break;
217
218
  case items.OBJ_SPECTATOR_INFO:
@@ -381,7 +382,7 @@ var Snapshot = /** @class */ (function () {
381
382
  data = out;
382
383
  } // else no previous, use new data
383
384
  }
384
- var parsed = this_2.parseItem(data, type_id);
385
+ var parsed = this_2.parseItem(data, type_id, id);
385
386
  this_2.eSnapHolder.push({ Snapshot: { Data: data, Key: key }, ack: recvTick });
386
387
  items.items.push({ data: data, parsed: parsed, type_id: type_id, id: id, key: key });
387
388
  };
@@ -396,11 +397,11 @@ var Snapshot = /** @class */ (function () {
396
397
  if (deltatick > -1) {
397
398
  var ____index = this_3.deltas.findIndex(function (delta) { return delta.key == newSnap.Snapshot.Key; });
398
399
  if (____index > -1) {
399
- this_3.deltas[____index] = { data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_3.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff)) };
400
+ this_3.deltas[____index] = { data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_3.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff)) };
400
401
  return "continue";
401
402
  }
402
403
  } // else
403
- this_3.deltas.push({ data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_3.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff)) });
404
+ this_3.deltas.push({ data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_3.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff)) });
404
405
  };
405
406
  var this_3 = this;
406
407
  for (var _i = 0, newSnaps_1 = newSnaps; _i < newSnaps_1.length; _i++) {
package/lib/snapshot.ts CHANGED
@@ -77,7 +77,7 @@ export class Snapshot {
77
77
  pStr = pStr.replace(/\0.*/g, ''); // Remove content from first null char to end.
78
78
  return pStr;
79
79
  }
80
- private parseItem(data: number[], Type: number): Item {
80
+ private parseItem(data: number[], Type: number, id: number): Item {
81
81
  var _item = {} as Item;
82
82
  switch (Type) {
83
83
  case items.OBJ_EX:
@@ -216,6 +216,7 @@ export class Snapshot {
216
216
  use_custom_color: Number(data.slice(14, 15)),
217
217
  color_body: Number(data.slice(15, 16)),
218
218
  color_feet: Number(data.slice(16, 17)),
219
+ id: id
219
220
  } as ClientInfo
220
221
  break;
221
222
  case items.OBJ_SPECTATOR_INFO:
@@ -375,7 +376,7 @@ export class Snapshot {
375
376
  } else
376
377
  _size = unpacker.unpackInt();
377
378
 
378
- let data = [];
379
+ let data: number[] = [];
379
380
  for (let j = 0; j < _size; j++) {
380
381
  if (unpacker.remaining.length > 0)
381
382
  data.push(unpacker.unpackInt());
@@ -393,7 +394,7 @@ export class Snapshot {
393
394
  } // else no previous, use new data
394
395
  }
395
396
 
396
- let parsed = this.parseItem(data, type_id)
397
+ let parsed = this.parseItem(data, type_id, id)
397
398
  this.eSnapHolder.push({Snapshot: {Data: data, Key: key}, ack: recvTick});
398
399
 
399
400
  items.items.push({data, parsed, type_id, id, key})
@@ -409,11 +410,11 @@ export class Snapshot {
409
410
  let ____index = this.deltas.findIndex(delta => delta.key == newSnap.Snapshot.Key)
410
411
 
411
412
  if (____index > -1) {
412
- this.deltas[____index] = {data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff))};
413
+ this.deltas[____index] = {data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff))};
413
414
  continue;
414
415
  }
415
416
  } // else
416
- this.deltas.push({data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff))});
417
+ this.deltas.push({data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff))});
417
418
  }
418
419
 
419
420
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teeworlds",
3
- "version": "2.3.9",
3
+ "version": "2.4.0",
4
4
  "description": "Library for (ingame) teeworlds bots.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",