teeworlds 2.3.9 → 2.4.1

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/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
 
@@ -24,7 +25,7 @@ enum States {
24
25
  }
25
26
 
26
27
 
27
- enum NETMSGTYPE {
28
+ enum NETMSG_Game {
28
29
  EX,
29
30
  SV_MOTD,
30
31
  SV_BROADCAST,
@@ -60,12 +61,58 @@ enum NETMSGTYPE {
60
61
  NUM
61
62
  }
62
63
 
64
+ enum NETMSG_Sys {
65
+ NETMSG_EX = 0,
66
+
67
+ // the first thing sent by the client
68
+ // contains the version info for the client
69
+ NETMSG_INFO = 1,
70
+
71
+ // sent by server
72
+ NETMSG_MAP_CHANGE, // sent when client should switch map
73
+ NETMSG_MAP_DATA, // map transfer, contains a chunk of the map file
74
+ NETMSG_CON_READY, // connection is ready, client should send start info
75
+ NETMSG_SNAP, // normal snapshot, multiple parts
76
+ NETMSG_SNAPEMPTY, // empty snapshot
77
+ NETMSG_SNAPSINGLE, // ?
78
+ NETMSG_SNAPSMALL, //
79
+ NETMSG_INPUTTIMING, // reports how off the input was
80
+ NETMSG_RCON_AUTH_STATUS, // result of the authentication
81
+ NETMSG_RCON_LINE, // line that should be printed to the remote console
82
+
83
+ NETMSG_AUTH_CHALLANGE, //
84
+ NETMSG_AUTH_RESULT, //
85
+
86
+ // sent by client
87
+ NETMSG_READY, //
88
+ NETMSG_ENTERGAME,
89
+ NETMSG_INPUT, // contains the inputdata from the client
90
+ NETMSG_RCON_CMD, //
91
+ NETMSG_RCON_AUTH, //
92
+ NETMSG_REQUEST_MAP_DATA, //
93
+
94
+ NETMSG_AUTH_START, //
95
+ NETMSG_AUTH_RESPONSE, //
96
+
97
+ // sent by both
98
+ NETMSG_PING,
99
+ NETMSG_PING_REPLY,
100
+ NETMSG_ERROR,
101
+
102
+ // sent by server (todo: move it up)
103
+ NETMSG_RCON_CMD_ADD,
104
+ NETMSG_RCON_CMD_REM,
105
+
106
+ NUM_NETMSGS,
107
+ }
108
+
63
109
  interface chunk {
64
110
  bytes: number,
65
111
  flags: number,
66
112
  sequence?: number,
67
113
  seq?: number,
68
- type: 'sys' | 'game',
114
+ // type: 'sys' | 'game',
115
+ sys: Boolean,
69
116
  msgid: number,
70
117
  msg: string,
71
118
  raw: Buffer,
@@ -127,6 +174,12 @@ declare interface iMessage {
127
174
  message: string
128
175
  }
129
176
 
177
+ declare interface iEmoticon {
178
+ client_id: number,
179
+ emoticon: number,
180
+ author?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo }
181
+ }
182
+
130
183
  declare interface iKillMsg {
131
184
  killer_id: number,
132
185
  killer?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
@@ -146,59 +199,61 @@ declare interface iOptions {
146
199
  }
147
200
 
148
201
  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
202
 
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
203
 
185
204
  on(event: 'connected', listener: () => void): this;
186
205
  on(event: 'disconnect', listener: (reason: string) => void): this;
187
206
 
207
+ on(event: 'emote', listener: (message: iEmoticon) => void): this;
188
208
  on(event: 'message', listener: (message: iMessage) => void): this;
189
209
  on(event: 'broadcast', listener: (message: string) => void): this;
190
210
  on(event: 'kill', listener: (kill: iKillMsg) => void): this;
191
211
  on(event: 'motd', listener: (message: string) => void): this;
192
212
 
193
- requestResend: boolean;
194
213
  }
195
214
 
215
+ export class Client extends EventEmitter {
196
216
 
217
+ private host: string;
218
+ private port: number;
219
+ private name: string;
220
+ private State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
221
+ private ack: number;
222
+ private clientAck: number;
223
+ private receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
224
+ private socket: net.Socket | undefined;
225
+ private TKEN: Buffer;
226
+ private time: number;
227
+ private SnapUnpacker: Snapshot;
228
+
229
+ private PredGameTick: number;
230
+ private AckGameTick: number;
231
+
232
+ private SnapshotParts: number;
233
+ private currentSnapshotGameTick: number;
197
234
 
198
- export class Client extends EventEmitter {
235
+
236
+ private snaps: Buffer[];
237
+
238
+ private sentChunkQueue: Buffer[];
239
+ private queueChunkEx: MsgPacker[];
240
+ private lastSendTime: number;
241
+ private lastRecvTime: number;
242
+
243
+ private lastSentMessages: {msg: MsgPacker, ack: number}[];
244
+
245
+
246
+ public movement: Movement;
247
+ public game: Game;
248
+
249
+ private VoteList: string[];
250
+ // eSnapHolder: eSnap[];
199
251
 
200
252
 
253
+ public readonly options?: iOptions;
254
+ private requestResend: boolean;
201
255
 
256
+
202
257
  constructor(ip: string, port: number, nickname: string, options?: iOptions) {
203
258
  super();
204
259
  this.host = ip;
@@ -214,13 +269,12 @@ export class Client extends EventEmitter {
214
269
  // this.eSnapHolder = [];
215
270
  this.requestResend = false;
216
271
 
272
+ this.VoteList = [];
273
+
217
274
  if (options)
218
275
  this.options = options;
219
276
 
220
- this.timer = 0;
221
-
222
- this.movement = new Movement();
223
-
277
+
224
278
  this.snaps = [];
225
279
 
226
280
  this.sentChunkQueue = [];
@@ -230,8 +284,7 @@ export class Client extends EventEmitter {
230
284
  this.ack = 0; // ack of messages the client has received
231
285
  this.clientAck = 0; // ack of messages the client has sent
232
286
  this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
233
- this.lastMsg = "";
234
- this.socket = net.createSocket("udp4")
287
+ this.socket = net.createSocket("udp4");
235
288
  this.socket.bind();
236
289
 
237
290
  this.TKEN = Buffer.from([255, 255, 255, 255])
@@ -240,10 +293,14 @@ export class Client extends EventEmitter {
240
293
  this.lastRecvTime = new Date().getTime();
241
294
 
242
295
  this.lastSentMessages = [];
296
+ this.movement = new Movement();
297
+
298
+ this.game = new Game(this);
299
+
243
300
 
244
301
  }
245
302
 
246
- ResendAfter(lastAck: number) {
303
+ private ResendAfter(lastAck: number) {
247
304
  this.clientAck = lastAck;
248
305
 
249
306
 
@@ -256,7 +313,7 @@ export class Client extends EventEmitter {
256
313
  this.SendMsgEx(toResend);
257
314
  }
258
315
 
259
- Unpack(packet: Buffer): _packet {
316
+ private Unpack(packet: Buffer): _packet {
260
317
  var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
261
318
 
262
319
 
@@ -285,7 +342,8 @@ export class Client extends EventEmitter {
285
342
  packet = packet.slice(3) // remove flags & size
286
343
  } else
287
344
  packet = packet.slice(2)
288
- chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
345
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
346
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
289
347
  chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
290
348
  chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
291
349
  chunk.raw = packet.slice(1, chunk.bytes)
@@ -301,6 +359,8 @@ export class Client extends EventEmitter {
301
359
  }
302
360
  return unpacked
303
361
  }
362
+
363
+ /* Send a Control Msg to the server. (used for disconnect)*/
304
364
  SendControlMsg(msg: number, ExtraMsg: string = "") {
305
365
  this.lastSendTime = new Date().getTime();
306
366
  return new Promise((resolve, reject) => {
@@ -320,6 +380,7 @@ export class Client extends EventEmitter {
320
380
  })
321
381
  }
322
382
 
383
+ /* Send a Msg (or Msg[]) to the server.*/
323
384
  SendMsgEx(Msgs: MsgPacker[] | MsgPacker) {
324
385
  if (this.State == States.STATE_OFFLINE)
325
386
  return;
@@ -378,11 +439,11 @@ export class Client extends EventEmitter {
378
439
  return;
379
440
  this.socket.send(packet, 0, packet.length, this.port, this.host)
380
441
  }
381
-
442
+ /* Queue a chunk (It will get sent in the next packet). */
382
443
  QueueChunkEx(Msg: MsgPacker) {
383
444
  this.queueChunkEx.push(Msg);
384
445
  }
385
-
446
+ /* Send a Raw Buffer (as chunk) to the server. */
386
447
  SendMsgRaw(chunks: Buffer[]) {
387
448
  if (this.State == States.STATE_OFFLINE)
388
449
  return;
@@ -399,7 +460,7 @@ export class Client extends EventEmitter {
399
460
  this.socket.send(packet, 0, packet.length, this.port, this.host)
400
461
  }
401
462
 
402
- MsgToChunk(packet: Buffer) {
463
+ private MsgToChunk(packet: Buffer) {
403
464
  var chunk: chunk = {} as chunk;
404
465
  chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
405
466
  chunk.flags = (packet[0] >> 6) & 3;
@@ -410,7 +471,8 @@ export class Client extends EventEmitter {
410
471
  packet = packet.slice(3) // remove flags & size
411
472
  } else
412
473
  packet = packet.slice(2)
413
- chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
474
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
475
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
414
476
  chunk.msgid = (packet[0]-(packet[0]&1))/2;
415
477
  chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
416
478
  chunk.raw = packet.slice(1, chunk.bytes)
@@ -423,6 +485,7 @@ export class Client extends EventEmitter {
423
485
  return chunk;
424
486
  }
425
487
 
488
+ /* Connect the client to the server. */
426
489
  connect() {
427
490
  this.State = States.STATE_CONNECTING;
428
491
 
@@ -475,14 +538,15 @@ export class Client extends EventEmitter {
475
538
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
476
539
 
477
540
  if (this.socket)
478
- this.socket.on("message", (a, rinfo) => {
541
+ this.socket.on("message", (packet, rinfo) => {
479
542
  if (this.State == 0 || rinfo.address != this.host || rinfo.port != this.port)
480
543
  return;
481
544
  clearInterval(connectInterval)
482
- if (a.toJSON().data[0] == 0x10) {
483
- if (a.toString().includes("TKEN") || a.toJSON().data[3] == 0x2) {
545
+
546
+ if (packet.toJSON().data[0] == 0x10) {
547
+ if (packet.toString().includes("TKEN") || packet.toJSON().data[3] == 0x2) {
484
548
  clearInterval(connectInterval);
485
- this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
549
+ this.TKEN = Buffer.from(packet.toJSON().data.slice(packet.toJSON().data.length - 4, packet.toJSON().data.length))
486
550
  this.SendControlMsg(3);
487
551
  this.State = States.STATE_LOADING; // loading state
488
552
  this.receivedSnaps = 0;
@@ -507,28 +571,26 @@ export class Client extends EventEmitter {
507
571
  }
508
572
 
509
573
  this.SendMsgEx([client_version, info])
510
- } else if (a.toJSON().data[3] == 0x4) {
574
+ } else if (packet.toJSON().data[3] == 0x4) {
511
575
  // disconnected
512
576
  this.State = States.STATE_OFFLINE;
513
- let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
577
+ let reason: string = (unpackString(packet.toJSON().data.slice(4)).result);
514
578
  this.emit("disconnect", reason);
515
579
  }
516
- if (a.toJSON().data[3] !== 0x0) { // keepalive
580
+ if (packet.toJSON().data[3] !== 0x0) { // keepalive
517
581
  this.lastRecvTime = new Date().getTime();
518
582
  }
519
583
  } else {
520
584
  this.lastRecvTime = new Date().getTime();
521
-
522
585
  }
523
-
524
586
 
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!;
587
+ var unpacked: _packet = this.Unpack(packet);
588
+ unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
589
+
590
+ unpacked.chunks.forEach(chunk => {
591
+ if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
592
+ if (chunk.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
593
+ this.ack = chunk.seq!;
532
594
 
533
595
  this.requestResend = false;
534
596
  }
@@ -536,10 +598,10 @@ export class Client extends EventEmitter {
536
598
  let Bottom = (this.ack - (1<<10)/2);
537
599
 
538
600
  if(Bottom < 0) {
539
- if((a.seq! <= this.ack) || (a.seq! >= (Bottom + (1<<10))))
601
+ if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10))))
540
602
  return;
541
603
  } else {
542
- if(a.seq! <= this.ack && a.seq! >= Bottom)
604
+ if(chunk.seq! <= this.ack && chunk.seq! >= Bottom)
543
605
  return;
544
606
  }
545
607
  this.requestResend = true;
@@ -548,11 +610,6 @@ export class Client extends EventEmitter {
548
610
  }
549
611
 
550
612
  })
551
- unpacked.chunks.filter(a => a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game').forEach(a => {
552
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
553
-
554
- this.emit("broadcast", unpacker.unpackString());
555
- })
556
613
  this.sentChunkQueue.forEach((buff, i) => {
557
614
  let chunk = this.MsgToChunk(buff);
558
615
  if (chunk.flags & 1) {
@@ -560,16 +617,61 @@ export class Client extends EventEmitter {
560
617
  this.sentChunkQueue.splice(i, 1);
561
618
  }
562
619
  })
563
- let snapChunks: chunk[] = [];
564
- if (this.options?.lightweight !== true)
565
- snapChunks = unpacked.chunks.filter(a => a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY");
566
- if (snapChunks.length > 0) {
567
- let part = 0;
568
- let num_parts = 1;
569
- if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
620
+
621
+
622
+ unpacked.chunks.forEach((chunk, index) => {
623
+ if (chunk.sys) {
624
+ // system messages
625
+ if (chunk.msgid == NETMSG_Sys.NETMSG_PING) { // ping
626
+ let packer = new MsgPacker(NETMSG_Sys.NETMSG_PING_REPLY, true, 0);
627
+
628
+ this.SendMsgEx(packer); // send ping reply
629
+ } else if (chunk.msgid == NETMSG_Sys.NETMSG_PING_REPLY) { // Ping reply
630
+ this.game._ping_resolve(new Date().getTime())
631
+ }
632
+
633
+ // packets neccessary for connection
634
+ // https://ddnet.org/docs/libtw2/connection/
635
+
636
+ if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_CHANGE) {
637
+ var Msg = new MsgPacker(NETMSG_Sys.NETMSG_READY, true, 1); /* ready */
638
+ this.SendMsgEx(Msg);
639
+ } else if (chunk.msgid == NETMSG_Sys.NETMSG_CON_READY) {
640
+ var info = new MsgPacker(NETMSG_Game.CL_STARTINFO, false, 1);
641
+ if (this.options?.identity) {
642
+ info.AddString(this.options.identity.name);
643
+ info.AddString(this.options.identity.clan);
644
+ info.AddInt(this.options.identity.country);
645
+ info.AddString(this.options.identity.skin);
646
+ info.AddInt(this.options.identity.use_custom_color);
647
+ info.AddInt(this.options.identity.color_body);
648
+ info.AddInt(this.options.identity.color_feet);
649
+ } else {
650
+ info.AddString(this.name); /* name */
651
+ info.AddString(""); /* clan */
652
+ info.AddInt(-1); /* country */
653
+ info.AddString("greyfox"); /* skin */
654
+ info.AddInt(1); /* use custom color */
655
+ info.AddInt(10346103); /* color body */
656
+ info.AddInt(65535); /* color feet */
657
+
658
+ }
659
+ var crashmeplx = new MsgPacker(17, true, 1); // rcon
660
+ crashmeplx.AddString("crashmeplx"); // 64 player support message
661
+ this.SendMsgEx([info, crashmeplx]);
662
+ }
663
+
664
+ if (chunk.msgid >= NETMSG_Sys.NETMSG_SNAP && chunk.msgid <= NETMSG_Sys.NETMSG_SNAPSINGLE) {
665
+ this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
666
+ if (this.receivedSnaps == 2) {
667
+ if (this.State != States.STATE_ONLINE)
668
+ this.emit('connected')
669
+ this.State = States.STATE_ONLINE;
670
+ }
671
+ if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
570
672
  this.PredGameTick = this.AckGameTick + 1;
571
673
 
572
- snapChunks.forEach(chunk => {
674
+ // snapChunks.forEach(chunk => {
573
675
  let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
574
676
 
575
677
  let NumParts = 1;
@@ -623,15 +725,65 @@ export class Client extends EventEmitter {
623
725
  }
624
726
 
625
727
 
626
- })
728
+ // })
729
+
730
+
627
731
  }
628
- var chunkMessages = unpacked.chunks.map(a => a.msg)
629
- if (chunkMessages.includes("SV_CHAT")) {
630
- var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
631
- chat.forEach(a => {
632
- if (a.msg == "SV_CHAT") {
633
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
634
- var unpacked: iMessage = {
732
+ } else {
733
+ // game messages
734
+
735
+ // vote list:
736
+ if (chunk.msgid == NETMSG_Game.SV_VOTECLEAROPTIONS) {
737
+ this.VoteList = [];
738
+ } else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONLISTADD) {
739
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data)
740
+ let NumOptions = unpacker.unpackInt()
741
+ let list: string[] = [];
742
+ for (let i = 0; i < 15; i++) {
743
+ list.push(unpacker.unpackString());
744
+ }
745
+ list = list.slice(NumOptions);
746
+
747
+ this.VoteList.push(...list);
748
+ } else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
749
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data)
750
+
751
+ this.VoteList.push(unpacker.unpackString());
752
+ } else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONREMOVE) {
753
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data)
754
+
755
+ let index = this.VoteList.indexOf(unpacker.unpackString());
756
+
757
+ if (index > -1)
758
+ this.VoteList = this.VoteList.splice(index, 1);
759
+
760
+ }
761
+
762
+ // events
763
+ if (chunk.msgid == NETMSG_Game.SV_EMOTICON) {
764
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
765
+ let unpacked = {
766
+ client_id: unpacker.unpackInt(),
767
+ emoticon: unpacker.unpackInt()
768
+ } as iEmoticon;
769
+
770
+ if (unpacked.client_id != -1) {
771
+ unpacked.author = {
772
+ ClientInfo: this.client_info(unpacked.client_id),
773
+ PlayerInfo: this.player_info(unpacked.client_id)
774
+ }
775
+ }
776
+ this.emit("emote", unpacked)
777
+
778
+
779
+
780
+ } else if (chunk.msgid == NETMSG_Game.SV_BROADCAST) {
781
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
782
+
783
+ this.emit("broadcast", unpacker.unpackString());
784
+ } if (chunk.msgid == NETMSG_Game.SV_CHAT) {
785
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
786
+ let unpacked: iMessage = {
635
787
  team: unpacker.unpackInt(),
636
788
  client_id: unpacker.unpackInt(),
637
789
  message: unpacker.unpackString()
@@ -644,14 +796,9 @@ export class Client extends EventEmitter {
644
796
  }
645
797
  }
646
798
  this.emit("message", unpacked)
647
- }
648
- })
649
- }
650
- var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG" || a.msg == "SV_MOTD");
651
- chat.forEach(a => {
652
- if (a.msg == "SV_KILL_MSG") {
653
- var unpacked: iKillMsg = {} as iKillMsg;
654
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
799
+ } else if (chunk.msgid == NETMSG_Game.SV_KILLMSG) {
800
+ let unpacked: iKillMsg = {} as iKillMsg;
801
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
655
802
  unpacked.killer_id = unpacker.unpackInt();
656
803
  unpacked.victim_id = unpacker.unpackInt();
657
804
  unpacked.weapon = unpacker.unpackInt();
@@ -663,59 +810,22 @@ export class Client extends EventEmitter {
663
810
  if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
664
811
  unpacked.killer = { ClientInfo: this.client_info(unpacked.killer_id), PlayerInfo: this.player_info(unpacked.killer_id) }
665
812
  this.emit("kill", unpacked)
666
- } else if (a.msg == "SV_MOTD") {
667
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
813
+ } else if (chunk.msgid == NETMSG_Game.SV_MOTD) {
814
+ let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
668
815
  let message = unpacker.unpackString();
669
816
  this.emit("motd", message);
670
817
  }
671
- })
672
-
673
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
674
- var Msg = new MsgPacker(15, true, 1); /* entergame */
675
- this.SendMsgEx(Msg);
676
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
677
- // send ready
678
- var Msg = new MsgPacker(14, true, 1); /* ready */
679
- this.SendMsgEx(Msg);
680
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY"))) {
681
- var info = new MsgPacker(20, false, 1);
682
- if (this.options?.identity) {
683
- info.AddString(this.options.identity.name);
684
- info.AddString(this.options.identity.clan);
685
- info.AddInt(this.options.identity.country);
686
- info.AddString(this.options.identity.skin);
687
- info.AddInt(this.options.identity.use_custom_color);
688
- info.AddInt(this.options.identity.color_body);
689
- info.AddInt(this.options.identity.color_feet);
690
- } else {
691
- info.AddString(this.name); /* name */
692
- info.AddString(""); /* clan */
693
- info.AddInt(-1); /* country */
694
- info.AddString("greyfox"); /* skin */
695
- info.AddInt(1); /* use custom color */
696
- info.AddInt(10346103); /* color body */
697
- info.AddInt(65535); /* color feet */
698
818
 
819
+ // packets neccessary for connection
820
+ // https://ddnet.org/docs/libtw2/connection/
821
+ if (chunk.msgid == NETMSG_Game.SV_READYTOENTER) {
822
+ var Msg = new MsgPacker(15, true, 1); /* entergame */
823
+ this.SendMsgEx(Msg);
824
+ }
699
825
  }
700
- var crashmeplx = new MsgPacker(17, true, 1); // rcon
701
- crashmeplx.AddString("crashmeplx"); // 64 player support message
702
- this.SendMsgEx([info, crashmeplx]);
703
-
704
-
705
-
706
- } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
707
- var info = new MsgPacker(23, true, 1);
708
- this.SendMsgEx(info)
709
- }
710
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
711
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
712
- if (this.receivedSnaps == 2) {
713
- if (this.State != States.STATE_ONLINE)
714
- this.emit('connected')
715
- this.State = States.STATE_ONLINE;
716
- }
826
+ })
717
827
 
718
- }
828
+
719
829
  if (new Date().getTime() - this.time >= 1000 && this.State == States.STATE_ONLINE) {
720
830
  this.time = new Date().getTime();
721
831
  this.SendControlMsg(0);
@@ -723,6 +833,7 @@ export class Client extends EventEmitter {
723
833
  })
724
834
  }
725
835
 
836
+ /* Sending the input. (automatically done unless options.lightweight is on) */
726
837
  sendInput(input = this.movement.input) {
727
838
  if (this.State != States.STATE_ONLINE)
728
839
  return;
@@ -754,6 +865,7 @@ export class Client extends EventEmitter {
754
865
  return this.movement.input;
755
866
  }
756
867
 
868
+ /* Disconnect the client. */
757
869
  Disconnect() {
758
870
  return new Promise((resolve) => {
759
871
  this.SendControlMsg(4).then(() => {
@@ -766,64 +878,11 @@ export class Client extends EventEmitter {
766
878
  })
767
879
  }
768
880
 
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
- }
881
+ /* Get the client_info from a specific player id. */
823
882
  client_info(id: number) {
824
- let delta = this.SnapUnpacker.deltas.filter(a =>
825
- a.type_id == 11
826
- && a.id == id
883
+ let delta = this.SnapUnpacker.deltas.filter(_delta =>
884
+ _delta.type_id == 11
885
+ && _delta.id == id
827
886
  );
828
887
 
829
888
  if (delta.length == 0)
@@ -832,25 +891,33 @@ export class Client extends EventEmitter {
832
891
  // .sort((a, b) => a.id - b.id)
833
892
  // .map(a => a.parsed as ClientInfo);
834
893
  }
894
+
895
+ /* Get all client infos. */
835
896
  get client_infos(): ClientInfo[] {
836
897
 
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) ;
898
+ return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 11)
899
+ .sort((a, b) => a.id - b.id)
900
+ .map(a => a.parsed as ClientInfo);
840
901
  }
902
+ /* Get the player info from a specific player id. */
841
903
  player_info(id: number) {
842
- let delta = this.SnapUnpacker.deltas.filter(a =>
843
- a.type_id == 10
844
- && a.id == id
904
+ let delta = this.SnapUnpacker.deltas.filter(_delta =>
905
+ _delta.type_id == 10
906
+ && _delta.id == id
845
907
  );
846
908
 
847
909
  if (delta.length == 0)
848
910
  return undefined;
849
911
  return delta[0].parsed as PlayerInfo;
850
912
  }
913
+ /* Get all player infos. */
851
914
  get player_infos(): PlayerInfo[] {
852
- return this.SnapUnpacker.deltas.filter(a => a.type_id == 10)
915
+ return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 10)
853
916
  .sort((a, b) => a.id - b.id)
854
- .map(a => a.parsed as PlayerInfo);
917
+ .map(player => player.parsed as PlayerInfo);
918
+ }
919
+
920
+ get VoteOptionList(): string[] {
921
+ return this.VoteList;
855
922
  }
856
923
  }