teeworlds 2.1.2 → 2.1.7

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