teeworlds 2.1.3 → 2.1.5

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
@@ -1,12 +1,13 @@
1
1
  import { randomBytes } from "crypto";
2
2
 
3
3
  import net from 'dgram';
4
- import fs from 'fs';
4
+ import fs, { stat } from 'fs';
5
5
  import { EventEmitter } from 'stream';
6
6
  import { spawn } from 'child_process';
7
7
 
8
8
  import { unpackInt, unpackString, MsgUnpacker } from "./MsgUnpacker";
9
9
 
10
+ import Movement from './movement';
10
11
 
11
12
  import MsgPacker from './MsgPacker';
12
13
  import { Snapshot } from './snapshot';
@@ -15,6 +16,29 @@ import Huffman from "./huffman";
15
16
  const huff = new Huffman();
16
17
  const SnapUnpacker = new Snapshot();
17
18
 
19
+ enum States {
20
+ STATE_OFFLINE = 0,
21
+ STATE_CONNECTING,
22
+ STATE_LOADING,
23
+ STATE_ONLINE,
24
+ STATE_DEMOPLAYBACK,
25
+ STATE_QUITTING,
26
+ STATE_RESTARTING
27
+ }
28
+
29
+ interface NetObj_PlayerInput {
30
+ m_Direction: number,
31
+ m_TargetX: number,
32
+ m_TargetY: number,
33
+ m_Jump: number,
34
+ m_Fire: number,
35
+ m_Hook: number,
36
+ m_PlayerFlags: number,
37
+ m_WantedWeapon: number,
38
+ m_NextWeapon: number,
39
+ m_PrevWeapon: number
40
+ };
41
+
18
42
  enum NETMSGTYPE {
19
43
  EX,
20
44
  SV_MOTD,
@@ -118,21 +142,21 @@ function arrStartsWith(arr: number[], arrStart: number[], start = 0) {
118
142
  return true;
119
143
  }
120
144
  declare interface PlayerInfo {
121
- local: number,
122
- client_id: number,
123
- team: number,
124
- score: number,
125
- latency: number,
145
+ local: number,
146
+ client_id: number,
147
+ team: number,
148
+ score: number,
149
+ latency: number,
126
150
  }
127
151
 
128
152
  declare interface ClientInfo {
129
- name: string,
130
- clan: string,
131
- country: number,
132
- skin: string,
133
- use_custom_color: number,
134
- color_body: number,
135
- color_feet: number,
153
+ name: string,
154
+ clan: string,
155
+ country: number,
156
+ skin: string,
157
+ use_custom_color: number,
158
+ color_body: number,
159
+ color_feet: number,
136
160
  }
137
161
  declare interface iMessage {
138
162
  team: number,
@@ -159,15 +183,24 @@ declare interface Client {
159
183
  clientAck: number;
160
184
  receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
161
185
  lastMsg: string;
162
- _port: number;
163
186
  socket: net.Socket | undefined;
164
187
  TKEN: Buffer;
165
188
  time: number;
166
189
 
190
+ timer: number;
191
+ PredGameTick: number;
192
+ AckGameTick: number;
193
+
194
+ movement: Movement;
195
+
167
196
  snaps: Buffer[];
168
197
  client_infos: ClientInfo[];
169
198
  player_infos: PlayerInfo[];
170
199
 
200
+ sentChunkQueue: Buffer[];
201
+
202
+ lastSendTime: number;
203
+
171
204
 
172
205
  on(event: 'connected', listener: () => void): this;
173
206
  on(event: 'disconnect', listener: (reason: string) => void): this;
@@ -185,25 +218,34 @@ class Client extends EventEmitter {
185
218
  this.host = ip;
186
219
  this.port = port;
187
220
  this.name = nickname;
221
+ this.AckGameTick = 0;
222
+ this.PredGameTick = 0;
223
+
224
+ this.timer = 0;
225
+
226
+ this.movement = new Movement();
188
227
 
189
228
  this.snaps = [];
190
229
  this.client_infos = [];
191
230
  this.player_infos = [];
192
231
 
193
- this.State = 0; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
232
+ this.sentChunkQueue = [];
233
+
234
+ this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
194
235
  this.ack = 0; // ack of messages the client has received
195
236
  this.clientAck = 0; // ack of messages the client has sent
196
237
  this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
197
238
  this.lastMsg = "";
198
- this._port = Math.floor(Math.random() * 65535)
199
239
  this.socket = net.createSocket("udp4")
200
240
  this.socket.bind();
201
241
 
202
242
  this.TKEN = Buffer.from([255, 255, 255, 255])
203
243
  this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
204
- this.State = 0;
244
+
245
+ this.lastSendTime = new Date().getTime();
246
+
205
247
  }
206
- async Unpack(packet: Buffer): Promise<_packet> {
248
+ Unpack(packet: Buffer): _packet {
207
249
  var unpacked: _packet = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
208
250
 
209
251
 
@@ -245,6 +287,7 @@ class Client extends EventEmitter {
245
287
  return unpacked
246
288
  }
247
289
  SendControlMsg(msg: number, ExtraMsg: string = "") {
290
+ this.lastSendTime = new Date().getTime();
248
291
  return new Promise((resolve, reject) => {
249
292
  if (this.socket) {
250
293
  var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg])
@@ -262,10 +305,11 @@ class Client extends EventEmitter {
262
305
  })
263
306
  }
264
307
  SendMsgEx(Msg: MsgPacker, Flags: number) {
265
- if (this.State == -1)
308
+ if (this.State == States.STATE_OFFLINE)
266
309
  throw new Error("Client is not connected");
267
310
  if (!this.socket)
268
311
  return;
312
+ this.lastSendTime = new Date().getTime();
269
313
  var header = []
270
314
  header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
271
315
  header[1] = (Msg.size & 0xf);
@@ -273,27 +317,41 @@ class Client extends EventEmitter {
273
317
  this.clientAck = (this.clientAck + 1) % (1 << 10);
274
318
  header[1] |= (this.clientAck >> 2) & 0xf0;
275
319
  header[2] = this.clientAck & 0xff;
320
+
321
+ this.sentChunkQueue.push(Buffer.concat([Buffer.from(header), Msg.buffer]));
276
322
  }
277
- var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
278
- var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
279
323
 
280
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host)
324
+ let latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1]]);
325
+ if (Flags & 1)
326
+ latestBuf = Buffer.concat([latestBuf, Buffer.from([this.clientAck])]);
327
+ latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
328
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host);
329
+
281
330
  }
282
331
  SendMsgExWithChunks(Msgs: MsgPacker[], Flags: number) {
283
- if (this.State == -1)
332
+ if (this.State == States.STATE_OFFLINE)
284
333
  throw new Error("Client is not connected");
285
334
  if (!this.socket)
286
335
  return;
287
- var header: any[][] = [];
336
+ this.lastSendTime = new Date().getTime();
337
+ var header: Buffer[] = [];
288
338
 
289
339
  Msgs.forEach((Msg: MsgPacker, index) => {
290
- header[index] = new Array(2);
340
+ header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
291
341
  header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
292
342
  header[index][1] = (Msg.size & 0xf);
293
343
  if (Flags & 1) {
294
344
  this.clientAck = (this.clientAck + 1) % (1 << 10);
295
345
  header[index][1] |= (this.clientAck >> 2) & 0xf0;
296
346
  header[index][2] = this.clientAck & 0xff;
347
+
348
+ header[index][0] = (((Flags | 2)&3)<<6)|((Msg.size>>4)&0x3f); // 2 is resend flag (ugly hack for queue)
349
+
350
+ this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
351
+
352
+ header[index][0] = (((Flags)&3)<<6)|((Msg.size>>4)&0x3f);
353
+
354
+
297
355
  }
298
356
  })
299
357
  var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
@@ -305,140 +363,167 @@ class Client extends EventEmitter {
305
363
 
306
364
  this.socket.send(packet, 0, packet.length, this.port, this.host)
307
365
  }
366
+ SendMsgRaw(chunks: Buffer[]) {
367
+ if (this.State == States.STATE_OFFLINE)
368
+ throw new Error("Client is not connected");
369
+ if (!this.socket)
370
+ return;
371
+
372
+ this.lastSendTime = new Date().getTime();
373
+
374
+ var packetHeader = Buffer.from([0x0+(((16<<4)&0xf0)|((this.ack>>8)&0xf)), this.ack&0xff, chunks.length]);
375
+
376
+ var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
377
+
378
+ this.socket.send(packet, 0, packet.length, this.port, this.host)
379
+ }
380
+
381
+ MsgToChunk(packet: Buffer) {
382
+ var chunk: chunk = {} as chunk;
383
+ // let packet = Msg.buffer;
384
+ chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
385
+ chunk.flags = (packet[0] >> 6) & 3;
386
+ chunk.sequence = -1;
387
+
388
+ if (chunk.flags & 1) {
389
+ chunk.seq = ((packet[1]&0xf0)<<2) | packet[2];
390
+ packet = packet.slice(3) // remove flags & size
391
+ } else
392
+ packet = packet.slice(2)
393
+ chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
394
+ chunk.msgid = (packet[0]-(packet[0]&1))/2;
395
+ chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
396
+ // if (chunk.msg == undefined)
397
+ // console.log(packet)
398
+ chunk.raw = packet.slice(1, chunk.bytes)
399
+ Object.values(messageUUIDs).forEach((a, i) => {
400
+ if (a.compare(packet.slice(0, 16)) === 0) {
401
+ chunk.extended_msgid = a;
402
+ // chunk.type = 'sys';
403
+ chunk.msg = Object.keys(messageUUIDs)[i];
404
+ }
405
+ })
406
+ return chunk;
407
+ }
408
+
308
409
  connect() {
410
+
411
+ this.State = States.STATE_CONNECTING;
412
+
413
+ let predTimer = setInterval(() => {
414
+ if (this.State == States.STATE_ONLINE && this.AckGameTick > 0) {
415
+ this.PredGameTick++;
416
+ // console.log(this.PredGameTick, this.AckGameTick)
417
+ }
418
+ }, 20);
419
+
309
420
  this.SendControlMsg(1, "TKEN")
310
421
  let connectInterval = setInterval(() => {
311
- if (this.State == 0)
422
+ if (this.State == States.STATE_CONNECTING)
312
423
  this.SendControlMsg(1, "TKEN")
313
424
  else
314
425
  clearInterval(connectInterval)
426
+ }, 500);
427
+
428
+ setInterval(() => {
429
+ // if (new Date().getTime() - this.time >= 1000) {
430
+ if (this.State != States.STATE_ONLINE)
431
+ return;
432
+ this.time = new Date().getTime();
433
+ // this.SendControlMsg(0);
434
+ // console.log("sending with " + this.AckGameTick)
435
+ this.sendInput();
436
+ // }
315
437
  }, 500)
438
+
439
+ let resendTimeout = setInterval(() => {
440
+ // this.sentChunkQueue.forEach((chunk) => {
441
+ // if (this.State == 0) // disconnected
442
+ // return;
443
+ if (this.State != 0) {
444
+ if (((new Date().getTime()) - this.lastSendTime) > 900) {
445
+ this.SendMsgRaw([this.sentChunkQueue[0]]);
446
+ console.log(this.sentChunkQueue);
447
+ }
448
+ }
449
+ // })
450
+ }, 1000)
451
+
452
+
316
453
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
454
+
317
455
  if (this.socket)
318
- this.socket.on("message", async (a) => {
319
- var unpacked: _packet = await this.Unpack(a)
320
- if (unpacked.twprotocol.flags != 128 && unpacked.twprotocol.ack) {
456
+ this.socket.on("message", (a) => {
457
+ clearInterval(connectInterval)
458
+ if (a.toJSON().data[0] == 0x10) {
459
+ if (a.toString().includes("TKEN") || a.toJSON().data[3] == 0x2) {
460
+ clearInterval(connectInterval);
461
+ this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
462
+ this.SendControlMsg(3);
463
+ this.State = States.STATE_LOADING; // loading state
464
+ var info = new MsgPacker(1, true);
465
+ info.AddString("0.6 626fce9a778df4d4");
466
+ info.AddString(""); // password
467
+
468
+ var client_version = new MsgPacker(0, true);
469
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
470
+ let randomUuid = Buffer.alloc(16);
471
+
472
+ randomBytes(16).copy(randomUuid);
473
+
474
+ client_version.AddBuffer(randomUuid);
475
+ client_version.AddInt(16003);
476
+ client_version.AddString("DDNet 16.0.3");
477
+
478
+ this.SendMsgExWithChunks([client_version, info], 1)
479
+ } else if (a.toJSON().data[3] == 0x4) {
480
+ // disconnected
481
+ this.State = States.STATE_OFFLINE;
482
+ let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
483
+ // this.State = -1;
484
+ this.emit("disconnect", reason);
485
+ }
486
+
487
+ }
488
+
489
+ var unpacked: _packet = this.Unpack(a)
321
490
  unpacked.chunks.forEach(a => {
322
- if (a.msg && !a.msg.startsWith("SNAP")) {
491
+ if (a.flags & 1) { // vital
323
492
  if (a.seq != undefined && a.seq != -1)
324
493
  this.ack = a.seq
325
-
494
+ else
495
+ console.log("no seq", a)
326
496
  }
327
497
  })
328
- }
329
- var chunkMessages = unpacked.chunks.map(a => a.msg)
330
- if (chunkMessages.includes("SV_CHAT")) {
331
- var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
332
- chat.forEach(a => {
333
- if (a.msg == "SV_CHAT") {
334
- var unpacked: iMessage = {} as iMessage;
335
- unpacked.team = unpackInt(a.raw.toJSON().data).result;
336
- var remaining: number[] = unpackInt(a.raw.toJSON().data).remaining;
337
- unpacked.client_id = unpackInt(remaining).result;
338
- remaining = unpackInt(remaining).remaining;
339
- unpacked.message = unpackString(remaining).result;
340
- if (unpacked.client_id != -1)
341
- unpacked.author = { ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id] }
342
- // console.log(unpacked)
343
- this.emit("message", unpacked)
344
- }
345
- })
346
- }
347
- if (chunkMessages.includes("SV_KILL_MSG")) {
348
- var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
349
- chat.forEach(a => {
350
- if (a.msg == "SV_KILL_MSG") {
351
- var unpacked: iKillMsg = {} as iKillMsg;
352
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
353
- unpacked.killer_id = unpacker.unpackInt();
354
- unpacked.victim_id = unpacker.unpackInt();
355
- unpacked.weapon = unpacker.unpackInt();
356
- unpacked.special_mode = unpacker.unpackInt();
357
- if (unpacked.victim_id != -1)
358
- unpacked.victim = { ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id] }
359
- if (unpacked.killer_id != -1)
360
- unpacked.killer = { ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id] }
361
- // console.log(unpacked)
362
- this.emit("kill", unpacked)
363
- }
498
+ this.sentChunkQueue.forEach((buff, i) => {
499
+ let chunk = this.MsgToChunk(buff);
500
+ if (chunk.flags & 1) {
501
+ if (chunk.seq && chunk.seq < this.ack) {
502
+ this.sentChunkQueue.splice(i, 1);
503
+ // this.ack = (this.ack + 1) % (1 << 10);
504
+ }
505
+ }
364
506
  })
365
- }
366
- if (a.toJSON().data[0] == 0x10) {
367
- if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
368
- clearInterval(connectInterval);
369
- this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
370
- this.SendControlMsg(3);
371
- this.State = 2; // loading state
372
- var info = new MsgPacker(1, true);
373
- info.AddString("0.6 626fce9a778df4d4");
374
- info.AddString(""); // password
375
-
376
- var client_version = new MsgPacker(0, true);
377
- client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
378
- let randomUuid = new Uint8Array(16);
379
-
380
- randomBytes(16).copy(randomUuid);
381
-
382
- client_version.AddBuffer(Buffer.from(randomUuid));
383
- client_version.AddInt(15091);
384
- client_version.AddString("DDNet 15.9.1");
385
-
386
- this.SendMsgExWithChunks([client_version, info], 1)
387
- } else if (a.toJSON().data[3] == 0x4) {
388
- // disconnected
389
- this.State = 0;
390
- let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
391
- this.State = -1;
392
- this.emit("disconnect", reason);
393
- }
394
-
395
- }
396
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
397
- var Msg = new MsgPacker(15, true); /* entergame */
398
- this.SendMsgEx(Msg, 1);
399
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
400
- // send ready
401
- var Msg = new MsgPacker(14, true); /* ready */
402
- this.SendMsgEx(Msg, 1);
403
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
404
- var info = new MsgPacker(20, false);
405
- info.AddString(this.name); /* name */
406
- info.AddString(""); /* clan */
407
- info.AddInt(-1); /* country */
408
- info.AddString("greyfox"); /* skin */
409
- info.AddInt(1); /* use custom color */
410
- info.AddInt(10346103); /* color body */
411
- info.AddInt(65535); /* color feet */
412
- this.SendMsgEx(info, 1);
413
-
414
-
415
- } else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
416
-
417
- if (this.State != 3) {
418
- this.emit('connected');
419
- }
420
- this.State = 3
421
- } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
422
- var info = new MsgPacker(23, true);
423
- this.SendMsgEx(info, 1)
424
- }
425
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
426
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
427
- if (this.receivedSnaps >= 2) {
428
- if (this.State != 3)
429
- this.emit('connected')
430
- this.State = 3
431
- }
432
-
433
- var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
434
- if (chunks.length > 0) {
507
+ var snapChunks = unpacked.chunks.filter(a => a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY");
508
+ // console.log(unpacked.chunks.length, unpacked)
509
+ if (snapChunks.length > 0) {
435
510
  let part = 0;
436
511
  let num_parts = 1;
437
- chunks.forEach(chunk => {
512
+ snapChunks.forEach(chunk => {
438
513
  let AckGameTick = (unpackInt(chunk.raw.toJSON().data).result);
514
+ // setImmediate(() => {
515
+ // console.log(AckGameTick, this.AckGameTick, chunk.msg)
516
+ if (AckGameTick > this.AckGameTick) {
517
+ this.AckGameTick = AckGameTick;
518
+ if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
519
+ this.PredGameTick = AckGameTick + 1;
520
+ // console.log(this.AckGameTick)
521
+ }
522
+ // })
523
+
439
524
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
440
525
  let DeltaTick = unpackInt(chunk?.raw?.toJSON().data).result
441
- if (chunk.msg == "SNAP") {
526
+ if (chunk.msg === "SNAP") {
442
527
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
443
528
  num_parts = (unpackInt(chunk?.raw?.toJSON().data).result)
444
529
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
@@ -448,40 +533,192 @@ class Client extends EventEmitter {
448
533
  if (chunk.msg != "SNAP_EMPTY")
449
534
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
450
535
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
451
- if (part == 0 || this.snaps.length > 30) {
536
+ if (part === 0 || this.snaps.length > 30) {
452
537
  this.snaps = [];
453
538
  }
539
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
454
540
  this.snaps.push(chunk.raw)
541
+ // console.log(this.PredGameTick - this.AckGameTick, this.PredGameTick, this.AckGameTick)
455
542
 
456
- if ((num_parts - 1) == part && this.snaps.length == num_parts) {
543
+ if ((num_parts - 1) === part && this.snaps.length === num_parts) {
457
544
  let mergedSnaps = Buffer.concat(this.snaps);
458
545
  let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
546
+ // console.log(snapUnpacked)
459
547
 
460
548
  snapUnpacked.items.forEach((a, i) => {
461
- if (a.type_id == items.OBJ_CLIENT_INFO) {
462
- this.client_infos[a.id] = a.parsed as ClientInfo;
549
+ if (a.type_id === items.OBJ_CLIENT_INFO) {
463
550
  // console.log(a.parsed, i)
464
- // console.log(this.client_infos[a.id])
465
- } else if (a.type_id == items.OBJ_PLAYER_INFO) {
466
- this.player_infos[i] = a.parsed as PlayerInfo;
467
- } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
468
- if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
469
- // var packer = new MsgPacker(22, false)
470
- // this.SendMsgEx(packer, 1)
551
+ this.client_infos[a.id] = a.parsed as ClientInfo;
552
+ if ((a.parsed as ClientInfo).name.includes("������")) {
553
+ console.log(this.PredGameTick, this.AckGameTick, mergedSnaps.toJSON().data.toString())
471
554
  }
555
+ console.log(this.client_infos[a.id].name, this.client_infos[a.id].clan, [a.id])
472
556
  }
557
+ // else if (a.type_id === items.OBJ_PLAYER_INFO) {
558
+ // this.player_infos[a.id] = a.parsed as PlayerInfo;
559
+ // }
473
560
  })
474
561
  }
475
562
 
563
+
564
+ })
565
+ }
566
+ var chunkMessages = unpacked.chunks.map(a => a.msg)
567
+ if (chunkMessages.includes("SV_CHAT")) {
568
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
569
+ chat.forEach(a => {
570
+ if (a.msg == "SV_CHAT") {
571
+ var unpacked: iMessage = {} as iMessage;
572
+ unpacked.team = unpackInt(a.raw.toJSON().data).result;
573
+ var remaining: number[] = unpackInt(a.raw.toJSON().data).remaining;
574
+ unpacked.client_id = unpackInt(remaining).result;
575
+ remaining = unpackInt(remaining).remaining;
576
+ unpacked.message = unpackString(remaining).result;
577
+ if (unpacked.client_id != -1)
578
+ unpacked.author = { ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id] }
579
+ // console.log(unpacked)
580
+ this.emit("message", unpacked)
581
+ }
582
+ })
583
+ }
584
+ if (chunkMessages.includes("SV_KILL_MSG")) {
585
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
586
+ chat.forEach(a => {
587
+ if (a.msg == "SV_KILL_MSG") {
588
+ var unpacked: iKillMsg = {} as iKillMsg;
589
+ let unpacker = new MsgUnpacker(a.raw.toJSON().data);
590
+ unpacked.killer_id = unpacker.unpackInt();
591
+ unpacked.victim_id = unpacker.unpackInt();
592
+ unpacked.weapon = unpacker.unpackInt();
593
+ unpacked.special_mode = unpacker.unpackInt();
594
+ if (unpacked.victim_id != -1)
595
+ unpacked.victim = { ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id] }
596
+ if (unpacked.killer_id != -1)
597
+ unpacked.killer = { ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id] }
598
+ // console.log(unpacked)
599
+ this.emit("kill", unpacked)
600
+ }
476
601
  })
477
602
  }
478
603
 
479
- }
480
- if (new Date().getTime() - this.time >= 1000) {
481
- this.time = new Date().getTime();
482
- this.SendControlMsg(0);
483
- }
484
- })
604
+ if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
605
+ var Msg = new MsgPacker(15, true); /* entergame */
606
+ this.SendMsgEx(Msg, 1);
607
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
608
+ // send ready
609
+ var Msg = new MsgPacker(14, true); /* ready */
610
+ this.SendMsgEx(Msg, 1);
611
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
612
+ var info = new MsgPacker(20, false);
613
+ info.AddString(this.name); /* name */
614
+ info.AddString(""); /* clan */
615
+ info.AddInt(-1); /* country */
616
+ info.AddString("greyfox"); /* skin */
617
+ info.AddInt(1); /* use custom color */
618
+ info.AddInt(10346103); /* color body */
619
+ info.AddInt(65535); /* color feet */
620
+ this.SendMsgEx(info, 1);
621
+
622
+
623
+ } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
624
+ var info = new MsgPacker(23, true);
625
+ this.SendMsgEx(info, 1)
626
+ }
627
+ if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
628
+ this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
629
+ if (this.receivedSnaps == 2) {
630
+ if (this.State != States.STATE_ONLINE)
631
+ this.emit('connected')
632
+ this.State = States.STATE_ONLINE;
633
+ }
634
+
635
+ var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
636
+ if (chunks.length > 0) {
637
+ let part = 0;
638
+ let num_parts = 1;
639
+ chunks.forEach(chunk => {
640
+ let AckGameTick = (unpackInt(chunk.raw.toJSON().data).result);
641
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
642
+ let DeltaTick = unpackInt(chunk?.raw?.toJSON().data).result
643
+ if (chunk.msg == "SNAP") {
644
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
645
+ num_parts = (unpackInt(chunk?.raw?.toJSON().data).result)
646
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
647
+ part = (unpackInt(chunk?.raw?.toJSON().data).result)
648
+ }
649
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // part
650
+ if (chunk.msg != "SNAP_EMPTY")
651
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
652
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
653
+ if (part == 0 || this.snaps.length > 30) {
654
+ this.snaps = [];
655
+ }
656
+ this.snaps.push(chunk.raw)
657
+
658
+ if ((num_parts - 1) == part && this.snaps.length == num_parts) {
659
+ let mergedSnaps = Buffer.concat(this.snaps);
660
+ let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
661
+
662
+ snapUnpacked.items.forEach((a, i) => {
663
+ if (a.type_id == items.OBJ_CLIENT_INFO) {
664
+ this.client_infos[a.id] = a.parsed as ClientInfo;
665
+ // console.log(a.parsed, i)
666
+ // console.log(this.client_infos[a.id])
667
+ } else if (a.type_id == items.OBJ_PLAYER_INFO) {
668
+ this.player_infos[i] = a.parsed as PlayerInfo;
669
+ } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
670
+ if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
671
+ // var packer = new MsgPacker(22, false)
672
+ // this.SendMsgEx(packer, 1)
673
+ }
674
+ }
675
+ })
676
+ }
677
+
678
+ })
679
+ }
680
+
681
+ }
682
+ if (new Date().getTime() - this.time >= 1000 && this.State == States.STATE_ONLINE) {
683
+ this.time = new Date().getTime();
684
+ this.SendControlMsg(0);
685
+ }
686
+ })
687
+ }
688
+
689
+ sendInput(input = this.movement.input) {
690
+ if (this.State != States.STATE_ONLINE)
691
+ return;
692
+
693
+ let inputMsg = new MsgPacker(16, true);
694
+ inputMsg.AddInt(this.AckGameTick);
695
+ inputMsg.AddInt(this.PredGameTick);
696
+ inputMsg.AddInt(40);
697
+ // let playerflags = 2;
698
+ // playerflags |= 8; // scoreboard
699
+ // playerflags |= 16; // aimline
700
+
701
+ let input_data = [
702
+
703
+ input.m_Direction,
704
+ input.m_TargetX,
705
+ input.m_TargetY,
706
+ input.m_Jump,
707
+ input.m_Fire,
708
+ input.m_Hook,
709
+ input.m_PlayerFlags,
710
+ input.m_WantedWeapon,
711
+ input.m_NextWeapon,
712
+ input.m_PrevWeapon
713
+ ]
714
+ // console.log(this.player_infos, this.client_infos)
715
+ input_data.forEach(a => {
716
+ inputMsg.AddInt(a);
717
+ });
718
+ this.SendMsgEx(inputMsg, 0);
719
+ }
720
+ get input() {
721
+ return this.movement.input;
485
722
  }
486
723
 
487
724
  Disconnect() {
@@ -491,7 +728,7 @@ class Client extends EventEmitter {
491
728
  if (this.socket)
492
729
  this.socket.close();
493
730
  this.socket = undefined
494
- this.State = -1;
731
+ this.State = States.STATE_OFFLINE;
495
732
  })
496
733
  })
497
734
  }
@@ -533,6 +770,7 @@ class Client extends EventEmitter {
533
770
  this.SendMsgEx(packer, 1);
534
771
  }
535
772
 
773
+
536
774
  }
537
775
  export = Client;
538
776
  // module.exports = Client;