teeworlds 2.1.1 → 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,
@@ -117,14 +141,31 @@ function arrStartsWith(arr: number[], arrStart: number[], start = 0) {
117
141
  }
118
142
  return true;
119
143
  }
120
- interface iMessage {
144
+ declare interface PlayerInfo {
145
+ local: number,
146
+ client_id: number,
147
+ team: number,
148
+ score: number,
149
+ latency: number,
150
+ }
151
+
152
+ declare interface ClientInfo {
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,
160
+ }
161
+ declare interface iMessage {
121
162
  team: number,
122
163
  client_id: number,
123
164
  author?: { ClientInfo: ClientInfo, PlayerInfo: PlayerInfo },
124
165
  message: string
125
166
  }
126
167
 
127
- interface iKillMsg {
168
+ declare interface iKillMsg {
128
169
  killer_id: number,
129
170
  killer?: { ClientInfo: ClientInfo, PlayerInfo: PlayerInfo },
130
171
  victim_id: number,
@@ -142,21 +183,30 @@ declare interface Client {
142
183
  clientAck: number;
143
184
  receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
144
185
  lastMsg: string;
145
- _port: number;
146
186
  socket: net.Socket | undefined;
147
187
  TKEN: Buffer;
148
188
  time: number;
149
189
 
190
+ timer: number;
191
+ PredGameTick: number;
192
+ AckGameTick: number;
193
+
194
+ movement: Movement;
195
+
150
196
  snaps: Buffer[];
151
197
  client_infos: ClientInfo[];
152
198
  player_infos: PlayerInfo[];
153
199
 
200
+ sentChunkQueue: Buffer[];
201
+
202
+ lastSendTime: number;
203
+
154
204
 
155
- on(event: 'connected'): this;
156
- on(event: 'disconnect', reason: string): this;
205
+ on(event: 'connected', listener: () => void): this;
206
+ on(event: 'disconnect', listener: (reason: string) => void): this;
157
207
 
158
- on(event: 'message', message: iMessage): this;
159
- on(event: 'kill', kill: iKillMsg): this;
208
+ on(event: 'message', listener: (message: iMessage) => void): this;
209
+ on(event: 'kill', listener: (kill: iKillMsg) => void): this;
160
210
 
161
211
  }
162
212
  class Client extends EventEmitter {
@@ -168,25 +218,34 @@ class Client extends EventEmitter {
168
218
  this.host = ip;
169
219
  this.port = port;
170
220
  this.name = nickname;
221
+ this.AckGameTick = 0;
222
+ this.PredGameTick = 0;
223
+
224
+ this.timer = 0;
225
+
226
+ this.movement = new Movement();
171
227
 
172
228
  this.snaps = [];
173
229
  this.client_infos = [];
174
230
  this.player_infos = [];
175
231
 
176
- 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
177
235
  this.ack = 0; // ack of messages the client has received
178
236
  this.clientAck = 0; // ack of messages the client has sent
179
237
  this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
180
238
  this.lastMsg = "";
181
- this._port = Math.floor(Math.random() * 65535)
182
239
  this.socket = net.createSocket("udp4")
183
240
  this.socket.bind();
184
241
 
185
242
  this.TKEN = Buffer.from([255, 255, 255, 255])
186
243
  this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
187
- this.State = 0;
244
+
245
+ this.lastSendTime = new Date().getTime();
246
+
188
247
  }
189
- async Unpack(packet: Buffer): Promise<_packet> {
248
+ Unpack(packet: Buffer): _packet {
190
249
  var unpacked: _packet = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
191
250
 
192
251
 
@@ -228,6 +287,7 @@ class Client extends EventEmitter {
228
287
  return unpacked
229
288
  }
230
289
  SendControlMsg(msg: number, ExtraMsg: string = "") {
290
+ this.lastSendTime = new Date().getTime();
231
291
  return new Promise((resolve, reject) => {
232
292
  if (this.socket) {
233
293
  var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg])
@@ -245,10 +305,11 @@ class Client extends EventEmitter {
245
305
  })
246
306
  }
247
307
  SendMsgEx(Msg: MsgPacker, Flags: number) {
248
- if (this.State == -1)
308
+ if (this.State == States.STATE_OFFLINE)
249
309
  throw new Error("Client is not connected");
250
310
  if (!this.socket)
251
311
  return;
312
+ this.lastSendTime = new Date().getTime();
252
313
  var header = []
253
314
  header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
254
315
  header[1] = (Msg.size & 0xf);
@@ -256,27 +317,41 @@ class Client extends EventEmitter {
256
317
  this.clientAck = (this.clientAck + 1) % (1 << 10);
257
318
  header[1] |= (this.clientAck >> 2) & 0xf0;
258
319
  header[2] = this.clientAck & 0xff;
320
+
321
+ this.sentChunkQueue.push(Buffer.concat([Buffer.from(header), Msg.buffer]));
259
322
  }
260
- var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
261
- var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
262
323
 
263
- 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
+
264
330
  }
265
331
  SendMsgExWithChunks(Msgs: MsgPacker[], Flags: number) {
266
- if (this.State == -1)
332
+ if (this.State == States.STATE_OFFLINE)
267
333
  throw new Error("Client is not connected");
268
334
  if (!this.socket)
269
335
  return;
270
- var header: any[][] = [];
336
+ this.lastSendTime = new Date().getTime();
337
+ var header: Buffer[] = [];
271
338
 
272
339
  Msgs.forEach((Msg: MsgPacker, index) => {
273
- header[index] = new Array(2);
340
+ header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
274
341
  header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
275
342
  header[index][1] = (Msg.size & 0xf);
276
343
  if (Flags & 1) {
277
344
  this.clientAck = (this.clientAck + 1) % (1 << 10);
278
345
  header[index][1] |= (this.clientAck >> 2) & 0xf0;
279
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
+
280
355
  }
281
356
  })
282
357
  var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
@@ -288,140 +363,167 @@ class Client extends EventEmitter {
288
363
 
289
364
  this.socket.send(packet, 0, packet.length, this.port, this.host)
290
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
+
291
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
+
292
420
  this.SendControlMsg(1, "TKEN")
293
421
  let connectInterval = setInterval(() => {
294
- if (this.State == 0)
422
+ if (this.State == States.STATE_CONNECTING)
295
423
  this.SendControlMsg(1, "TKEN")
296
424
  else
297
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
+ // }
298
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
+
299
453
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
454
+
300
455
  if (this.socket)
301
- this.socket.on("message", async (a) => {
302
- var unpacked: _packet = await this.Unpack(a)
303
- 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)
304
490
  unpacked.chunks.forEach(a => {
305
- if (a.msg && !a.msg.startsWith("SNAP")) {
491
+ if (a.flags & 1) { // vital
306
492
  if (a.seq != undefined && a.seq != -1)
307
493
  this.ack = a.seq
308
-
309
- }
310
- })
311
- }
312
- var chunkMessages = unpacked.chunks.map(a => a.msg)
313
- if (chunkMessages.includes("SV_CHAT")) {
314
- var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
315
- chat.forEach(a => {
316
- if (a.msg == "SV_CHAT") {
317
- var unpacked: iMessage = {} as iMessage;
318
- unpacked.team = unpackInt(a.raw.toJSON().data).result;
319
- var remaining: number[] = unpackInt(a.raw.toJSON().data).remaining;
320
- unpacked.client_id = unpackInt(remaining).result;
321
- remaining = unpackInt(remaining).remaining;
322
- unpacked.message = unpackString(remaining).result;
323
- if (unpacked.client_id != -1)
324
- unpacked.author = { ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id] }
325
- // console.log(unpacked)
326
- this.emit("message", unpacked)
494
+ else
495
+ console.log("no seq", a)
327
496
  }
328
497
  })
329
- }
330
- if (chunkMessages.includes("SV_KILL_MSG")) {
331
- var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
332
- chat.forEach(a => {
333
- if (a.msg == "SV_KILL_MSG") {
334
- var unpacked: iKillMsg = {} as iKillMsg;
335
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
336
- unpacked.killer_id = unpacker.unpackInt();
337
- unpacked.victim_id = unpacker.unpackInt();
338
- unpacked.weapon = unpacker.unpackInt();
339
- unpacked.special_mode = unpacker.unpackInt();
340
- if (unpacked.victim_id != -1)
341
- unpacked.victim = { ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id] }
342
- if (unpacked.killer_id != -1)
343
- unpacked.killer = { ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id] }
344
- // console.log(unpacked)
345
- this.emit("kill", unpacked)
346
- }
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
+ }
347
506
  })
348
- }
349
- if (a.toJSON().data[0] == 0x10) {
350
- if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
351
- clearInterval(connectInterval);
352
- this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
353
- this.SendControlMsg(3);
354
- this.State = 2; // loading state
355
- var info = new MsgPacker(1, true);
356
- info.AddString("0.6 626fce9a778df4d4");
357
- info.AddString(""); // password
358
-
359
- var client_version = new MsgPacker(0, true);
360
- client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
361
- let randomUuid = new Uint8Array(16);
362
-
363
- randomBytes(16).copy(randomUuid);
364
-
365
- client_version.AddBuffer(Buffer.from(randomUuid));
366
- client_version.AddInt(15091);
367
- client_version.AddString("DDNet 15.9.1");
368
-
369
- this.SendMsgExWithChunks([client_version, info], 1)
370
- } else if (a.toJSON().data[3] == 0x4) {
371
- // disconnected
372
- this.State = 0;
373
- let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
374
- this.State = -1;
375
- this.emit("disconnect", reason);
376
- }
377
-
378
- }
379
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
380
- var Msg = new MsgPacker(15, true); /* entergame */
381
- this.SendMsgEx(Msg, 1);
382
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
383
- // send ready
384
- var Msg = new MsgPacker(14, true); /* ready */
385
- this.SendMsgEx(Msg, 1);
386
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
387
- var info = new MsgPacker(20, false);
388
- info.AddString(this.name); /* name */
389
- info.AddString(""); /* clan */
390
- info.AddInt(-1); /* country */
391
- info.AddString("greyfox"); /* skin */
392
- info.AddInt(1); /* use custom color */
393
- info.AddInt(10346103); /* color body */
394
- info.AddInt(65535); /* color feet */
395
- this.SendMsgEx(info, 1);
396
-
397
-
398
- } else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
399
-
400
- if (this.State != 3) {
401
- this.emit('connected');
402
- }
403
- this.State = 3
404
- } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
405
- var info = new MsgPacker(23, true);
406
- this.SendMsgEx(info, 1)
407
- }
408
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
409
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
410
- if (this.receivedSnaps >= 2) {
411
- if (this.State != 3)
412
- this.emit('connected')
413
- this.State = 3
414
- }
415
-
416
- var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
417
- 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) {
418
510
  let part = 0;
419
511
  let num_parts = 1;
420
- chunks.forEach(chunk => {
512
+ snapChunks.forEach(chunk => {
421
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
+
422
524
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
423
525
  let DeltaTick = unpackInt(chunk?.raw?.toJSON().data).result
424
- if (chunk.msg == "SNAP") {
526
+ if (chunk.msg === "SNAP") {
425
527
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
426
528
  num_parts = (unpackInt(chunk?.raw?.toJSON().data).result)
427
529
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
@@ -431,40 +533,192 @@ class Client extends EventEmitter {
431
533
  if (chunk.msg != "SNAP_EMPTY")
432
534
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
433
535
  chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
434
- if (part == 0 || this.snaps.length > 30) {
536
+ if (part === 0 || this.snaps.length > 30) {
435
537
  this.snaps = [];
436
538
  }
539
+ chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
437
540
  this.snaps.push(chunk.raw)
541
+ // console.log(this.PredGameTick - this.AckGameTick, this.PredGameTick, this.AckGameTick)
438
542
 
439
- if ((num_parts - 1) == part && this.snaps.length == num_parts) {
543
+ if ((num_parts - 1) === part && this.snaps.length === num_parts) {
440
544
  let mergedSnaps = Buffer.concat(this.snaps);
441
545
  let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
546
+ // console.log(snapUnpacked)
442
547
 
443
548
  snapUnpacked.items.forEach((a, i) => {
444
- if (a.type_id == items.OBJ_CLIENT_INFO) {
445
- this.client_infos[a.id] = a.parsed as ClientInfo;
549
+ if (a.type_id === items.OBJ_CLIENT_INFO) {
446
550
  // console.log(a.parsed, i)
447
- // console.log(this.client_infos[a.id])
448
- } else if (a.type_id == items.OBJ_PLAYER_INFO) {
449
- this.player_infos[i] = a.parsed as PlayerInfo;
450
- } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
451
- if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
452
- // var packer = new MsgPacker(22, false)
453
- // 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())
454
554
  }
555
+ console.log(this.client_infos[a.id].name, this.client_infos[a.id].clan, [a.id])
455
556
  }
557
+ // else if (a.type_id === items.OBJ_PLAYER_INFO) {
558
+ // this.player_infos[a.id] = a.parsed as PlayerInfo;
559
+ // }
456
560
  })
457
561
  }
458
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
+ }
459
601
  })
460
602
  }
461
603
 
462
- }
463
- if (new Date().getTime() - this.time >= 1000) {
464
- this.time = new Date().getTime();
465
- this.SendControlMsg(0);
466
- }
467
- })
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;
468
722
  }
469
723
 
470
724
  Disconnect() {
@@ -474,7 +728,7 @@ class Client extends EventEmitter {
474
728
  if (this.socket)
475
729
  this.socket.close();
476
730
  this.socket = undefined
477
- this.State = -1;
731
+ this.State = States.STATE_OFFLINE;
478
732
  })
479
733
  })
480
734
  }
@@ -516,6 +770,7 @@ class Client extends EventEmitter {
516
770
  this.SendMsgEx(packer, 1);
517
771
  }
518
772
 
773
+
519
774
  }
520
775
  export = Client;
521
776
  // module.exports = Client;