teeworlds 2.4.0 → 2.4.2

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/index.js CHANGED
@@ -1,15 +1,20 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
8
12
  }));
9
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
15
  };
12
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.Client = void 0;
13
18
  var client_1 = require("./lib/client");
14
19
  Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } });
15
20
  __exportStar(require("./lib/MsgPacker"), exports);
package/lib/client.js CHANGED
@@ -3,10 +3,12 @@ var __extends = (this && this.__extends) || (function () {
3
3
  var extendStatics = function (d, b) {
4
4
  extendStatics = Object.setPrototypeOf ||
5
5
  ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
7
  return extendStatics(d, b);
8
8
  };
9
9
  return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
10
12
  extendStatics(d, b);
11
13
  function __() { this.constructor = d; }
12
14
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -37,42 +39,79 @@ var States;
37
39
  States[States["STATE_QUITTING"] = 5] = "STATE_QUITTING";
38
40
  States[States["STATE_RESTARTING"] = 6] = "STATE_RESTARTING";
39
41
  })(States || (States = {}));
40
- var NETMSGTYPE;
41
- (function (NETMSGTYPE) {
42
- NETMSGTYPE[NETMSGTYPE["EX"] = 0] = "EX";
43
- NETMSGTYPE[NETMSGTYPE["SV_MOTD"] = 1] = "SV_MOTD";
44
- NETMSGTYPE[NETMSGTYPE["SV_BROADCAST"] = 2] = "SV_BROADCAST";
45
- NETMSGTYPE[NETMSGTYPE["SV_CHAT"] = 3] = "SV_CHAT";
46
- NETMSGTYPE[NETMSGTYPE["SV_KILLMSG"] = 4] = "SV_KILLMSG";
47
- NETMSGTYPE[NETMSGTYPE["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
48
- NETMSGTYPE[NETMSGTYPE["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
49
- NETMSGTYPE[NETMSGTYPE["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
50
- NETMSGTYPE[NETMSGTYPE["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
51
- NETMSGTYPE[NETMSGTYPE["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
52
- NETMSGTYPE[NETMSGTYPE["SV_EMOTICON"] = 10] = "SV_EMOTICON";
53
- NETMSGTYPE[NETMSGTYPE["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
54
- NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
55
- NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
56
- NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
57
- NETMSGTYPE[NETMSGTYPE["SV_VOTESET"] = 15] = "SV_VOTESET";
58
- NETMSGTYPE[NETMSGTYPE["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
59
- NETMSGTYPE[NETMSGTYPE["CL_SAY"] = 17] = "CL_SAY";
60
- NETMSGTYPE[NETMSGTYPE["CL_SETTEAM"] = 18] = "CL_SETTEAM";
61
- NETMSGTYPE[NETMSGTYPE["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
62
- NETMSGTYPE[NETMSGTYPE["CL_STARTINFO"] = 20] = "CL_STARTINFO";
63
- NETMSGTYPE[NETMSGTYPE["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
64
- NETMSGTYPE[NETMSGTYPE["CL_KILL"] = 22] = "CL_KILL";
65
- NETMSGTYPE[NETMSGTYPE["CL_EMOTICON"] = 23] = "CL_EMOTICON";
66
- NETMSGTYPE[NETMSGTYPE["CL_VOTE"] = 24] = "CL_VOTE";
67
- NETMSGTYPE[NETMSGTYPE["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
68
- NETMSGTYPE[NETMSGTYPE["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
69
- NETMSGTYPE[NETMSGTYPE["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
70
- NETMSGTYPE[NETMSGTYPE["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
71
- NETMSGTYPE[NETMSGTYPE["UNUSED"] = 29] = "UNUSED";
72
- NETMSGTYPE[NETMSGTYPE["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
73
- NETMSGTYPE[NETMSGTYPE["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
74
- NETMSGTYPE[NETMSGTYPE["NUM"] = 32] = "NUM";
75
- })(NETMSGTYPE || (NETMSGTYPE = {}));
42
+ var NETMSG_Game;
43
+ (function (NETMSG_Game) {
44
+ NETMSG_Game[NETMSG_Game["EX"] = 0] = "EX";
45
+ NETMSG_Game[NETMSG_Game["SV_MOTD"] = 1] = "SV_MOTD";
46
+ NETMSG_Game[NETMSG_Game["SV_BROADCAST"] = 2] = "SV_BROADCAST";
47
+ NETMSG_Game[NETMSG_Game["SV_CHAT"] = 3] = "SV_CHAT";
48
+ NETMSG_Game[NETMSG_Game["SV_KILLMSG"] = 4] = "SV_KILLMSG";
49
+ NETMSG_Game[NETMSG_Game["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
50
+ NETMSG_Game[NETMSG_Game["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
51
+ NETMSG_Game[NETMSG_Game["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
52
+ NETMSG_Game[NETMSG_Game["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
53
+ NETMSG_Game[NETMSG_Game["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
54
+ NETMSG_Game[NETMSG_Game["SV_EMOTICON"] = 10] = "SV_EMOTICON";
55
+ NETMSG_Game[NETMSG_Game["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
56
+ NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
57
+ NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
58
+ NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
59
+ NETMSG_Game[NETMSG_Game["SV_VOTESET"] = 15] = "SV_VOTESET";
60
+ NETMSG_Game[NETMSG_Game["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
61
+ NETMSG_Game[NETMSG_Game["CL_SAY"] = 17] = "CL_SAY";
62
+ NETMSG_Game[NETMSG_Game["CL_SETTEAM"] = 18] = "CL_SETTEAM";
63
+ NETMSG_Game[NETMSG_Game["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
64
+ NETMSG_Game[NETMSG_Game["CL_STARTINFO"] = 20] = "CL_STARTINFO";
65
+ NETMSG_Game[NETMSG_Game["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
66
+ NETMSG_Game[NETMSG_Game["CL_KILL"] = 22] = "CL_KILL";
67
+ NETMSG_Game[NETMSG_Game["CL_EMOTICON"] = 23] = "CL_EMOTICON";
68
+ NETMSG_Game[NETMSG_Game["CL_VOTE"] = 24] = "CL_VOTE";
69
+ NETMSG_Game[NETMSG_Game["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
70
+ NETMSG_Game[NETMSG_Game["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
71
+ NETMSG_Game[NETMSG_Game["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
72
+ NETMSG_Game[NETMSG_Game["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
73
+ NETMSG_Game[NETMSG_Game["UNUSED"] = 29] = "UNUSED";
74
+ NETMSG_Game[NETMSG_Game["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
75
+ NETMSG_Game[NETMSG_Game["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
76
+ NETMSG_Game[NETMSG_Game["NUM"] = 32] = "NUM";
77
+ })(NETMSG_Game || (NETMSG_Game = {}));
78
+ var NETMSG_Sys;
79
+ (function (NETMSG_Sys) {
80
+ NETMSG_Sys[NETMSG_Sys["NETMSG_EX"] = 0] = "NETMSG_EX";
81
+ // the first thing sent by the client
82
+ // contains the version info for the client
83
+ NETMSG_Sys[NETMSG_Sys["NETMSG_INFO"] = 1] = "NETMSG_INFO";
84
+ // sent by server
85
+ NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_CHANGE"] = 2] = "NETMSG_MAP_CHANGE";
86
+ NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_DATA"] = 3] = "NETMSG_MAP_DATA";
87
+ NETMSG_Sys[NETMSG_Sys["NETMSG_CON_READY"] = 4] = "NETMSG_CON_READY";
88
+ NETMSG_Sys[NETMSG_Sys["NETMSG_SNAP"] = 5] = "NETMSG_SNAP";
89
+ NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPEMPTY"] = 6] = "NETMSG_SNAPEMPTY";
90
+ NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPSINGLE"] = 7] = "NETMSG_SNAPSINGLE";
91
+ NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPSMALL"] = 8] = "NETMSG_SNAPSMALL";
92
+ NETMSG_Sys[NETMSG_Sys["NETMSG_INPUTTIMING"] = 9] = "NETMSG_INPUTTIMING";
93
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_AUTH_STATUS"] = 10] = "NETMSG_RCON_AUTH_STATUS";
94
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_LINE"] = 11] = "NETMSG_RCON_LINE";
95
+ NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_CHALLANGE"] = 12] = "NETMSG_AUTH_CHALLANGE";
96
+ NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_RESULT"] = 13] = "NETMSG_AUTH_RESULT";
97
+ // sent by client
98
+ NETMSG_Sys[NETMSG_Sys["NETMSG_READY"] = 14] = "NETMSG_READY";
99
+ NETMSG_Sys[NETMSG_Sys["NETMSG_ENTERGAME"] = 15] = "NETMSG_ENTERGAME";
100
+ NETMSG_Sys[NETMSG_Sys["NETMSG_INPUT"] = 16] = "NETMSG_INPUT";
101
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD"] = 17] = "NETMSG_RCON_CMD";
102
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_AUTH"] = 18] = "NETMSG_RCON_AUTH";
103
+ NETMSG_Sys[NETMSG_Sys["NETMSG_REQUEST_MAP_DATA"] = 19] = "NETMSG_REQUEST_MAP_DATA";
104
+ NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_START"] = 20] = "NETMSG_AUTH_START";
105
+ NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_RESPONSE"] = 21] = "NETMSG_AUTH_RESPONSE";
106
+ // sent by both
107
+ NETMSG_Sys[NETMSG_Sys["NETMSG_PING"] = 22] = "NETMSG_PING";
108
+ NETMSG_Sys[NETMSG_Sys["NETMSG_PING_REPLY"] = 23] = "NETMSG_PING_REPLY";
109
+ NETMSG_Sys[NETMSG_Sys["NETMSG_ERROR"] = 24] = "NETMSG_ERROR";
110
+ // sent by server (todo: move it up)
111
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_ADD"] = 25] = "NETMSG_RCON_CMD_ADD";
112
+ NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_REM"] = 26] = "NETMSG_RCON_CMD_REM";
113
+ NETMSG_Sys[NETMSG_Sys["NUM_NETMSGS"] = 27] = "NUM_NETMSGS";
114
+ })(NETMSG_Sys || (NETMSG_Sys = {}));
76
115
  function toHexStream(buff) {
77
116
  return buff.toJSON().data.map(function (a) { return ('0' + (a & 0xff).toString(16)).slice(-2); }).join("");
78
117
  }
@@ -114,6 +153,7 @@ var Client = /** @class */ (function (_super) {
114
153
  _this.SnapUnpacker = new snapshot_1.Snapshot();
115
154
  // this.eSnapHolder = [];
116
155
  _this.requestResend = false;
156
+ _this.VoteList = [];
117
157
  if (options)
118
158
  _this.options = options;
119
159
  _this.snaps = [];
@@ -168,7 +208,8 @@ var Client = /** @class */ (function (_super) {
168
208
  }
169
209
  else
170
210
  packet = packet.slice(2);
171
- chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
211
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
212
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
172
213
  chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
173
214
  chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
174
215
  chunk.raw = packet.slice(1, chunk.bytes);
@@ -290,7 +331,8 @@ var Client = /** @class */ (function (_super) {
290
331
  }
291
332
  else
292
333
  packet = packet.slice(2);
293
- chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
334
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
335
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
294
336
  chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
295
337
  chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
296
338
  chunk.raw = packet.slice(1, chunk.bytes);
@@ -354,7 +396,7 @@ var Client = /** @class */ (function (_super) {
354
396
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
355
397
  if (this.socket)
356
398
  this.socket.on("message", function (packet, rinfo) {
357
- var _a, _b, _c, _d, _e, _f, _g, _h;
399
+ var _a, _b, _c, _d, _e, _f;
358
400
  if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
359
401
  return;
360
402
  clearInterval(connectInterval);
@@ -371,7 +413,7 @@ var Client = /** @class */ (function (_super) {
371
413
  var client_version = new MsgPacker_1.MsgPacker(0, true, 1);
372
414
  client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
373
415
  var randomUuid = Buffer.alloc(16);
374
- crypto_1.randomBytes(16).copy(randomUuid);
416
+ (0, crypto_1.randomBytes)(16).copy(randomUuid);
375
417
  client_version.AddBuffer(randomUuid);
376
418
  if (((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
377
419
  client_version.AddInt((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
@@ -386,7 +428,7 @@ var Client = /** @class */ (function (_super) {
386
428
  else if (packet.toJSON().data[3] == 0x4) {
387
429
  // disconnected
388
430
  _this.State = States.STATE_OFFLINE;
389
- var reason = (MsgUnpacker_1.unpackString(packet.toJSON().data.slice(4)).result);
431
+ var reason = ((0, MsgUnpacker_1.unpackString)(packet.toJSON().data.slice(4)).result);
390
432
  _this.emit("disconnect", reason);
391
433
  }
392
434
  if (packet.toJSON().data[3] !== 0x0) { // keepalive
@@ -418,10 +460,6 @@ var Client = /** @class */ (function (_super) {
418
460
  }
419
461
  }
420
462
  });
421
- unpacked.chunks.filter(function (chunk) { return chunk.msgid == NETMSGTYPE.SV_BROADCAST && chunk.type == 'game'; }).forEach(function (a) {
422
- var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
423
- _this.emit("broadcast", unpacker.unpackString());
424
- });
425
463
  _this.sentChunkQueue.forEach(function (buff, i) {
426
464
  var chunk = _this.MsgToChunk(buff);
427
465
  if (chunk.flags & 1) {
@@ -429,144 +467,186 @@ var Client = /** @class */ (function (_super) {
429
467
  _this.sentChunkQueue.splice(i, 1);
430
468
  }
431
469
  });
432
- var snapChunks = [];
433
- if (((_g = _this.options) === null || _g === void 0 ? void 0 : _g.lightweight) !== true)
434
- snapChunks = unpacked.chunks.filter(function (chunk) { return chunk.msg === "SNAP" || chunk.msg === "SNAP_SINGLE" || chunk.msg === "SNAP_EMPTY"; });
435
- if (snapChunks.length > 0) {
436
- if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
437
- _this.PredGameTick = _this.AckGameTick + 1;
438
- snapChunks.forEach(function (chunk) {
439
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
440
- var NumParts = 1;
441
- var Part = 0;
442
- var GameTick = unpacker.unpackInt();
443
- var DeltaTick = GameTick - unpacker.unpackInt();
444
- var PartSize = 0;
445
- var Crc = 0;
446
- var CompleteSize = 0;
447
- if (chunk.msg == "SNAP") {
448
- NumParts = unpacker.unpackInt();
449
- Part = unpacker.unpackInt();
470
+ unpacked.chunks.forEach(function (chunk, index) {
471
+ var _a;
472
+ var _b;
473
+ if (chunk.sys) {
474
+ // system messages
475
+ if (chunk.msgid == NETMSG_Sys.NETMSG_PING) { // ping
476
+ var packer = new MsgPacker_1.MsgPacker(NETMSG_Sys.NETMSG_PING_REPLY, true, 0);
477
+ _this.SendMsgEx(packer); // send ping reply
478
+ }
479
+ else if (chunk.msgid == NETMSG_Sys.NETMSG_PING_REPLY) { // Ping reply
480
+ _this.game._ping_resolve(new Date().getTime());
450
481
  }
451
- if (chunk.msg != "SNAP_EMPTY") {
452
- Crc = unpacker.unpackInt();
453
- PartSize = unpacker.unpackInt();
482
+ // packets neccessary for connection
483
+ // https://ddnet.org/docs/libtw2/connection/
484
+ if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_CHANGE) {
485
+ var Msg = new MsgPacker_1.MsgPacker(NETMSG_Sys.NETMSG_READY, true, 1); /* ready */
486
+ _this.SendMsgEx(Msg);
454
487
  }
455
- if (PartSize < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize <= 0 || PartSize > 900)
456
- return;
457
- if (GameTick >= _this.currentSnapshotGameTick) {
458
- if (GameTick != _this.currentSnapshotGameTick) {
459
- _this.snaps = [];
460
- _this.SnapshotParts = 0;
461
- _this.currentSnapshotGameTick = GameTick;
488
+ else if (chunk.msgid == NETMSG_Sys.NETMSG_CON_READY) {
489
+ var info = new MsgPacker_1.MsgPacker(NETMSG_Game.CL_STARTINFO, false, 1);
490
+ if ((_b = _this.options) === null || _b === void 0 ? void 0 : _b.identity) {
491
+ info.AddString(_this.options.identity.name);
492
+ info.AddString(_this.options.identity.clan);
493
+ info.AddInt(_this.options.identity.country);
494
+ info.AddString(_this.options.identity.skin);
495
+ info.AddInt(_this.options.identity.use_custom_color);
496
+ info.AddInt(_this.options.identity.color_body);
497
+ info.AddInt(_this.options.identity.color_feet);
462
498
  }
463
- // chunk.raw = Buffer.from(unpacker.remaining);
464
- _this.snaps[Part] = Buffer.from(unpacker.remaining);
465
- _this.SnapshotParts |= 1 << Part;
466
- if (_this.SnapshotParts == ((1 << NumParts) - 1)) {
467
- var mergedSnaps = Buffer.concat(_this.snaps);
468
- _this.SnapshotParts = 0;
469
- var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, GameTick);
470
- _this.emit("snapshot");
471
- _this.AckGameTick = snapUnpacked.recvTick;
472
- if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
473
- _this.PredGameTick = _this.AckGameTick + 1;
474
- _this.sendInput();
499
+ else {
500
+ info.AddString(_this.name); /* name */
501
+ info.AddString(""); /* clan */
502
+ info.AddInt(-1); /* country */
503
+ info.AddString("greyfox"); /* skin */
504
+ info.AddInt(1); /* use custom color */
505
+ info.AddInt(10346103); /* color body */
506
+ info.AddInt(65535); /* color feet */
475
507
  }
508
+ var crashmeplx = new MsgPacker_1.MsgPacker(17, true, 1); // rcon
509
+ crashmeplx.AddString("crashmeplx"); // 64 player support message
510
+ _this.SendMsgEx([info, crashmeplx]);
476
511
  }
477
- });
478
- }
479
- var chunkMessages = unpacked.chunks.map(function (a) { return a.msg; });
480
- if (unpacked.chunks.findIndex(function (chunk) { return chunk.msgid == 23 && chunk.type == "sys"; }) !== -1) {
481
- _this.game._ping_resolve(new Date().getTime());
482
- }
483
- if (chunkMessages.includes("SV_CHAT")) {
484
- var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_CHAT"; });
485
- chat.forEach(function (a) {
486
- if (a.msg == "SV_CHAT") {
487
- var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
488
- var unpacked = {
512
+ if (chunk.msgid >= NETMSG_Sys.NETMSG_SNAP && chunk.msgid <= NETMSG_Sys.NETMSG_SNAPSINGLE) {
513
+ _this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
514
+ if (_this.receivedSnaps == 2) {
515
+ if (_this.State != States.STATE_ONLINE)
516
+ _this.emit('connected');
517
+ _this.State = States.STATE_ONLINE;
518
+ }
519
+ if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
520
+ _this.PredGameTick = _this.AckGameTick + 1;
521
+ // snapChunks.forEach(chunk => {
522
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
523
+ var NumParts = 1;
524
+ var Part = 0;
525
+ var GameTick = unpacker.unpackInt();
526
+ var DeltaTick = GameTick - unpacker.unpackInt();
527
+ var PartSize = 0;
528
+ var Crc = 0;
529
+ var CompleteSize = 0;
530
+ if (chunk.msg == "SNAP") {
531
+ NumParts = unpacker.unpackInt();
532
+ Part = unpacker.unpackInt();
533
+ }
534
+ if (chunk.msg != "SNAP_EMPTY") {
535
+ Crc = unpacker.unpackInt();
536
+ PartSize = unpacker.unpackInt();
537
+ }
538
+ if (PartSize < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize <= 0 || PartSize > 900)
539
+ return;
540
+ if (GameTick >= _this.currentSnapshotGameTick) {
541
+ if (GameTick != _this.currentSnapshotGameTick) {
542
+ _this.snaps = [];
543
+ _this.SnapshotParts = 0;
544
+ _this.currentSnapshotGameTick = GameTick;
545
+ }
546
+ // chunk.raw = Buffer.from(unpacker.remaining);
547
+ _this.snaps[Part] = Buffer.from(unpacker.remaining);
548
+ _this.SnapshotParts |= 1 << Part;
549
+ if (_this.SnapshotParts == ((1 << NumParts) - 1)) {
550
+ var mergedSnaps = Buffer.concat(_this.snaps);
551
+ _this.SnapshotParts = 0;
552
+ var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, GameTick, Crc);
553
+ _this.emit("snapshot");
554
+ _this.AckGameTick = snapUnpacked.recvTick;
555
+ if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
556
+ _this.PredGameTick = _this.AckGameTick + 1;
557
+ _this.sendInput();
558
+ }
559
+ }
560
+ // })
561
+ }
562
+ }
563
+ else {
564
+ // game messages
565
+ // vote list:
566
+ if (chunk.msgid == NETMSG_Game.SV_VOTECLEAROPTIONS) {
567
+ _this.VoteList = [];
568
+ }
569
+ else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONLISTADD) {
570
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
571
+ var NumOptions = unpacker.unpackInt();
572
+ var list = [];
573
+ for (var i = 0; i < 15; i++) {
574
+ list.push(unpacker.unpackString());
575
+ }
576
+ list = list.slice(NumOptions);
577
+ (_a = _this.VoteList).push.apply(_a, list);
578
+ }
579
+ else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
580
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
581
+ _this.VoteList.push(unpacker.unpackString());
582
+ }
583
+ else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONREMOVE) {
584
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
585
+ var index_1 = _this.VoteList.indexOf(unpacker.unpackString());
586
+ if (index_1 > -1)
587
+ _this.VoteList = _this.VoteList.splice(index_1, 1);
588
+ }
589
+ // events
590
+ if (chunk.msgid == NETMSG_Game.SV_EMOTICON) {
591
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
592
+ var unpacked_1 = {
593
+ client_id: unpacker.unpackInt(),
594
+ emoticon: unpacker.unpackInt()
595
+ };
596
+ if (unpacked_1.client_id != -1) {
597
+ unpacked_1.author = {
598
+ ClientInfo: _this.client_info(unpacked_1.client_id),
599
+ PlayerInfo: _this.player_info(unpacked_1.client_id)
600
+ };
601
+ }
602
+ _this.emit("emote", unpacked_1);
603
+ }
604
+ else if (chunk.msgid == NETMSG_Game.SV_BROADCAST) {
605
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
606
+ _this.emit("broadcast", unpacker.unpackString());
607
+ }
608
+ if (chunk.msgid == NETMSG_Game.SV_CHAT) {
609
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
610
+ var unpacked_2 = {
489
611
  team: unpacker.unpackInt(),
490
612
  client_id: unpacker.unpackInt(),
491
613
  message: unpacker.unpackString()
492
614
  };
493
- if (unpacked.client_id != -1) {
494
- unpacked.author = {
495
- ClientInfo: _this.client_info(unpacked.client_id),
496
- PlayerInfo: _this.player_info(unpacked.client_id)
615
+ if (unpacked_2.client_id != -1) {
616
+ unpacked_2.author = {
617
+ ClientInfo: _this.client_info(unpacked_2.client_id),
618
+ PlayerInfo: _this.player_info(unpacked_2.client_id)
497
619
  };
498
620
  }
499
- _this.emit("message", unpacked);
621
+ _this.emit("message", unpacked_2);
500
622
  }
501
- });
502
- }
503
- var chat = unpacked.chunks.filter(function (chunk) { return chunk.msg == "SV_KILL_MSG" || chunk.msg == "SV_MOTD"; });
504
- chat.forEach(function (a) {
505
- if (a.msg == "SV_KILL_MSG") {
506
- var unpacked = {};
507
- var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
508
- unpacked.killer_id = unpacker.unpackInt();
509
- unpacked.victim_id = unpacker.unpackInt();
510
- unpacked.weapon = unpacker.unpackInt();
511
- unpacked.special_mode = unpacker.unpackInt();
512
- if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
513
- unpacked.victim = { ClientInfo: _this.client_info(unpacked.victim_id), PlayerInfo: _this.player_info(unpacked.victim_id) };
623
+ else if (chunk.msgid == NETMSG_Game.SV_KILLMSG) {
624
+ var unpacked_3 = {};
625
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
626
+ unpacked_3.killer_id = unpacker.unpackInt();
627
+ unpacked_3.victim_id = unpacker.unpackInt();
628
+ unpacked_3.weapon = unpacker.unpackInt();
629
+ unpacked_3.special_mode = unpacker.unpackInt();
630
+ if (unpacked_3.victim_id != -1 && unpacked_3.victim_id < 64) {
631
+ unpacked_3.victim = { ClientInfo: _this.client_info(unpacked_3.victim_id), PlayerInfo: _this.player_info(unpacked_3.victim_id) };
632
+ }
633
+ if (unpacked_3.killer_id != -1 && unpacked_3.killer_id < 64)
634
+ unpacked_3.killer = { ClientInfo: _this.client_info(unpacked_3.killer_id), PlayerInfo: _this.player_info(unpacked_3.killer_id) };
635
+ _this.emit("kill", unpacked_3);
636
+ }
637
+ else if (chunk.msgid == NETMSG_Game.SV_MOTD) {
638
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
639
+ var message = unpacker.unpackString();
640
+ _this.emit("motd", message);
641
+ }
642
+ // packets neccessary for connection
643
+ // https://ddnet.org/docs/libtw2/connection/
644
+ if (chunk.msgid == NETMSG_Game.SV_READYTOENTER) {
645
+ var Msg = new MsgPacker_1.MsgPacker(15, true, 1); /* entergame */
646
+ _this.SendMsgEx(Msg);
514
647
  }
515
- if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
516
- unpacked.killer = { ClientInfo: _this.client_info(unpacked.killer_id), PlayerInfo: _this.player_info(unpacked.killer_id) };
517
- _this.emit("kill", unpacked);
518
- }
519
- else if (a.msg == "SV_MOTD") {
520
- var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
521
- var message = unpacker.unpackString();
522
- _this.emit("motd", message);
523
648
  }
524
649
  });
525
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
526
- var Msg = new MsgPacker_1.MsgPacker(15, true, 1); /* entergame */
527
- _this.SendMsgEx(Msg);
528
- }
529
- else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
530
- // send ready
531
- var Msg = new MsgPacker_1.MsgPacker(14, true, 1); /* ready */
532
- _this.SendMsgEx(Msg);
533
- }
534
- else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY"))) {
535
- var info = new MsgPacker_1.MsgPacker(20, false, 1);
536
- if ((_h = _this.options) === null || _h === void 0 ? void 0 : _h.identity) {
537
- info.AddString(_this.options.identity.name);
538
- info.AddString(_this.options.identity.clan);
539
- info.AddInt(_this.options.identity.country);
540
- info.AddString(_this.options.identity.skin);
541
- info.AddInt(_this.options.identity.use_custom_color);
542
- info.AddInt(_this.options.identity.color_body);
543
- info.AddInt(_this.options.identity.color_feet);
544
- }
545
- else {
546
- info.AddString(_this.name); /* name */
547
- info.AddString(""); /* clan */
548
- info.AddInt(-1); /* country */
549
- info.AddString("greyfox"); /* skin */
550
- info.AddInt(1); /* use custom color */
551
- info.AddInt(10346103); /* color body */
552
- info.AddInt(65535); /* color feet */
553
- }
554
- var crashmeplx = new MsgPacker_1.MsgPacker(17, true, 1); // rcon
555
- crashmeplx.AddString("crashmeplx"); // 64 player support message
556
- _this.SendMsgEx([info, crashmeplx]);
557
- }
558
- else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
559
- var info = new MsgPacker_1.MsgPacker(23, true, 1);
560
- _this.SendMsgEx(info);
561
- }
562
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
563
- _this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
564
- if (_this.receivedSnaps == 2) {
565
- if (_this.State != States.STATE_ONLINE)
566
- _this.emit('connected');
567
- _this.State = States.STATE_ONLINE;
568
- }
569
- }
570
650
  if (new Date().getTime() - _this.time >= 1000 && _this.State == States.STATE_ONLINE) {
571
651
  _this.time = new Date().getTime();
572
652
  _this.SendControlMsg(0);
@@ -661,6 +741,13 @@ var Client = /** @class */ (function (_super) {
661
741
  enumerable: false,
662
742
  configurable: true
663
743
  });
744
+ Object.defineProperty(Client.prototype, "VoteOptionList", {
745
+ get: function () {
746
+ return this.VoteList;
747
+ },
748
+ enumerable: false,
749
+ configurable: true
750
+ });
664
751
  return Client;
665
752
  }(stream_1.EventEmitter));
666
753
  exports.Client = Client;
package/lib/client.ts CHANGED
@@ -25,7 +25,7 @@ enum States {
25
25
  }
26
26
 
27
27
 
28
- enum NETMSGTYPE {
28
+ enum NETMSG_Game {
29
29
  EX,
30
30
  SV_MOTD,
31
31
  SV_BROADCAST,
@@ -61,12 +61,58 @@ enum NETMSGTYPE {
61
61
  NUM
62
62
  }
63
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
+
64
109
  interface chunk {
65
110
  bytes: number,
66
111
  flags: number,
67
112
  sequence?: number,
68
113
  seq?: number,
69
- type: 'sys' | 'game',
114
+ // type: 'sys' | 'game',
115
+ sys: Boolean,
70
116
  msgid: number,
71
117
  msg: string,
72
118
  raw: Buffer,
@@ -128,6 +174,12 @@ declare interface iMessage {
128
174
  message: string
129
175
  }
130
176
 
177
+ declare interface iEmoticon {
178
+ client_id: number,
179
+ emoticon: number,
180
+ author?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo }
181
+ }
182
+
131
183
  declare interface iKillMsg {
132
184
  killer_id: number,
133
185
  killer?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
@@ -152,6 +204,7 @@ export declare interface Client {
152
204
  on(event: 'connected', listener: () => void): this;
153
205
  on(event: 'disconnect', listener: (reason: string) => void): this;
154
206
 
207
+ on(event: 'emote', listener: (message: iEmoticon) => void): this;
155
208
  on(event: 'message', listener: (message: iMessage) => void): this;
156
209
  on(event: 'broadcast', listener: (message: string) => void): this;
157
210
  on(event: 'kill', listener: (kill: iKillMsg) => void): this;
@@ -193,6 +246,7 @@ export class Client extends EventEmitter {
193
246
  public movement: Movement;
194
247
  public game: Game;
195
248
 
249
+ private VoteList: string[];
196
250
  // eSnapHolder: eSnap[];
197
251
 
198
252
 
@@ -215,6 +269,8 @@ export class Client extends EventEmitter {
215
269
  // this.eSnapHolder = [];
216
270
  this.requestResend = false;
217
271
 
272
+ this.VoteList = [];
273
+
218
274
  if (options)
219
275
  this.options = options;
220
276
 
@@ -286,7 +342,8 @@ export class Client extends EventEmitter {
286
342
  packet = packet.slice(3) // remove flags & size
287
343
  } else
288
344
  packet = packet.slice(2)
289
- 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
290
347
  chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
291
348
  chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
292
349
  chunk.raw = packet.slice(1, chunk.bytes)
@@ -414,7 +471,8 @@ export class Client extends EventEmitter {
414
471
  packet = packet.slice(3) // remove flags & size
415
472
  } else
416
473
  packet = packet.slice(2)
417
- 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
418
476
  chunk.msgid = (packet[0]-(packet[0]&1))/2;
419
477
  chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
420
478
  chunk.raw = packet.slice(1, chunk.bytes)
@@ -552,11 +610,6 @@ export class Client extends EventEmitter {
552
610
  }
553
611
 
554
612
  })
555
- unpacked.chunks.filter(chunk => chunk.msgid == NETMSGTYPE.SV_BROADCAST && chunk.type == 'game').forEach(a => {
556
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
557
-
558
- this.emit("broadcast", unpacker.unpackString());
559
- })
560
613
  this.sentChunkQueue.forEach((buff, i) => {
561
614
  let chunk = this.MsgToChunk(buff);
562
615
  if (chunk.flags & 1) {
@@ -564,14 +617,61 @@ export class Client extends EventEmitter {
564
617
  this.sentChunkQueue.splice(i, 1);
565
618
  }
566
619
  })
567
- let snapChunks: chunk[] = [];
568
- if (this.options?.lightweight !== true)
569
- snapChunks = unpacked.chunks.filter(chunk => chunk.msg === "SNAP" || chunk.msg === "SNAP_SINGLE" || chunk.msg === "SNAP_EMPTY");
570
- if (snapChunks.length > 0) {
571
- 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)
572
672
  this.PredGameTick = this.AckGameTick + 1;
573
673
 
574
- snapChunks.forEach(chunk => {
674
+ // snapChunks.forEach(chunk => {
575
675
  let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
576
676
 
577
677
  let NumParts = 1;
@@ -611,7 +711,7 @@ export class Client extends EventEmitter {
611
711
  let mergedSnaps = Buffer.concat(this.snaps);
612
712
  this.SnapshotParts = 0;
613
713
 
614
- let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, GameTick);
714
+ let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, GameTick, Crc);
615
715
 
616
716
  this.emit("snapshot");
617
717
  this.AckGameTick = snapUnpacked.recvTick;
@@ -625,19 +725,65 @@ export class Client extends EventEmitter {
625
725
  }
626
726
 
627
727
 
628
- })
629
- }
630
-
631
- var chunkMessages = unpacked.chunks.map(a => a.msg)
632
- if (unpacked.chunks.findIndex(chunk => chunk.msgid == 23 && chunk.type == "sys") !== -1) {
633
- this.game._ping_resolve(new Date().getTime())
728
+ // })
729
+
730
+
634
731
  }
635
- if (chunkMessages.includes("SV_CHAT")) {
636
- var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
637
- chat.forEach(a => {
638
- if (a.msg == "SV_CHAT") {
639
- let unpacker = new MsgUnpacker(a.raw.toJSON().data);
640
- 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 = {
641
787
  team: unpacker.unpackInt(),
642
788
  client_id: unpacker.unpackInt(),
643
789
  message: unpacker.unpackString()
@@ -650,14 +796,9 @@ export class Client extends EventEmitter {
650
796
  }
651
797
  }
652
798
  this.emit("message", unpacked)
653
- }
654
- })
655
- }
656
- var chat = unpacked.chunks.filter(chunk => chunk.msg == "SV_KILL_MSG" || chunk.msg == "SV_MOTD");
657
- chat.forEach(a => {
658
- if (a.msg == "SV_KILL_MSG") {
659
- var unpacked: iKillMsg = {} as iKillMsg;
660
- 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);
661
802
  unpacked.killer_id = unpacker.unpackInt();
662
803
  unpacked.victim_id = unpacker.unpackInt();
663
804
  unpacked.weapon = unpacker.unpackInt();
@@ -669,59 +810,22 @@ export class Client extends EventEmitter {
669
810
  if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
670
811
  unpacked.killer = { ClientInfo: this.client_info(unpacked.killer_id), PlayerInfo: this.player_info(unpacked.killer_id) }
671
812
  this.emit("kill", unpacked)
672
- } else if (a.msg == "SV_MOTD") {
673
- 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);
674
815
  let message = unpacker.unpackString();
675
816
  this.emit("motd", message);
676
817
  }
677
- })
678
-
679
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
680
- var Msg = new MsgPacker(15, true, 1); /* entergame */
681
- this.SendMsgEx(Msg);
682
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
683
- // send ready
684
- var Msg = new MsgPacker(14, true, 1); /* ready */
685
- this.SendMsgEx(Msg);
686
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY"))) {
687
- var info = new MsgPacker(20, false, 1);
688
- if (this.options?.identity) {
689
- info.AddString(this.options.identity.name);
690
- info.AddString(this.options.identity.clan);
691
- info.AddInt(this.options.identity.country);
692
- info.AddString(this.options.identity.skin);
693
- info.AddInt(this.options.identity.use_custom_color);
694
- info.AddInt(this.options.identity.color_body);
695
- info.AddInt(this.options.identity.color_feet);
696
- } else {
697
- info.AddString(this.name); /* name */
698
- info.AddString(""); /* clan */
699
- info.AddInt(-1); /* country */
700
- info.AddString("greyfox"); /* skin */
701
- info.AddInt(1); /* use custom color */
702
- info.AddInt(10346103); /* color body */
703
- info.AddInt(65535); /* color feet */
704
-
705
- }
706
- var crashmeplx = new MsgPacker(17, true, 1); // rcon
707
- crashmeplx.AddString("crashmeplx"); // 64 player support message
708
- this.SendMsgEx([info, crashmeplx]);
709
818
 
710
-
711
-
712
- } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
713
- var info = new MsgPacker(23, true, 1);
714
- this.SendMsgEx(info)
715
- }
716
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
717
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
718
- if (this.receivedSnaps == 2) {
719
- if (this.State != States.STATE_ONLINE)
720
- this.emit('connected')
721
- this.State = States.STATE_ONLINE;
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
+ }
722
825
  }
826
+ })
723
827
 
724
- }
828
+
725
829
  if (new Date().getTime() - this.time >= 1000 && this.State == States.STATE_ONLINE) {
726
830
  this.time = new Date().getTime();
727
831
  this.SendControlMsg(0);
@@ -812,4 +916,8 @@ export class Client extends EventEmitter {
812
916
  .sort((a, b) => a.id - b.id)
813
917
  .map(player => player.parsed as PlayerInfo);
814
918
  }
919
+
920
+ get VoteOptionList(): string[] {
921
+ return this.VoteList;
922
+ }
815
923
  }
package/lib/snapshot.js CHANGED
@@ -54,6 +54,7 @@ var Snapshot = /** @class */ (function () {
54
54
  function Snapshot() {
55
55
  this.deltas = [];
56
56
  this.eSnapHolder = [];
57
+ this.crc_errors = 0;
57
58
  }
58
59
  Snapshot.prototype.IntsToStr = function (pInts) {
59
60
  var pIntz = [];
@@ -291,7 +292,15 @@ var Snapshot = /** @class */ (function () {
291
292
  }
292
293
  return _item;
293
294
  };
294
- Snapshot.prototype.unpackSnapshot = function (snap, deltatick, recvTick) {
295
+ Snapshot.prototype.crc = function (tick) {
296
+ var checksum = 0;
297
+ this.eSnapHolder.forEach(function (snap) {
298
+ if (snap.ack == tick)
299
+ snap.Snapshot.Data.forEach(function (el) { return checksum += el; });
300
+ });
301
+ return checksum & 0xffffffff;
302
+ };
303
+ Snapshot.prototype.unpackSnapshot = function (snap, deltatick, recvTick, WantedCrc) {
295
304
  var _this = this;
296
305
  var unpacker = new MsgUnpacker_1.MsgUnpacker(snap);
297
306
  if (deltatick == -1) {
@@ -330,7 +339,6 @@ var Snapshot = /** @class */ (function () {
330
339
  var deleted_key = unpacker.unpackInt(); // removed_item_keys
331
340
  // let index = this.deltas.map(delta => delta.key).indexOf(deleted_key);
332
341
  var index = this_1.deltas.findIndex(function (delta) { return delta.key === deleted_key; });
333
- // console.log("deleting ", deleted_key, index)
334
342
  if (index > -1)
335
343
  this_1.deltas.splice(index, 1);
336
344
  deleted.push(deleted_key);
@@ -349,13 +357,8 @@ var Snapshot = /** @class */ (function () {
349
357
  var items = { items: [], /* client_infos: client_infos, player_infos: player_infos,*/ lost: 0 };
350
358
  var deltaSnaps = this.eSnapHolder.filter(function (a) { return a.ack === deltatick; });
351
359
  if (deltaSnaps.length == 0 && deltatick >= 0) {
352
- // console.log("RESET recvtick")
353
360
  return { items: [], recvTick: -1 };
354
361
  }
355
- var newSnaps = [];
356
- deltaSnaps.forEach(function (a) {
357
- newSnaps.push({ Snapshot: a.Snapshot, ack: recvTick });
358
- });
359
362
  var _loop_2 = function (i) {
360
363
  var type_id = unpacker.unpackInt();
361
364
  var id = unpacker.unpackInt();
@@ -370,10 +373,7 @@ var Snapshot = /** @class */ (function () {
370
373
  for (var j = 0; j < _size; j++) {
371
374
  if (unpacker.remaining.length > 0)
372
375
  data.push(unpacker.unpackInt());
373
- // else
374
- // console.log("wrong size")
375
376
  }
376
- // console.log(index, deltatick)
377
377
  if (deltatick >= 0) {
378
378
  // let index = deltaSnaps.map(delta => delta.Snapshot.Key).indexOf(key)
379
379
  var index = deltaSnaps.findIndex(function (delta) { return delta.Snapshot.Key === key; });
@@ -391,7 +391,7 @@ var Snapshot = /** @class */ (function () {
391
391
  _loop_2(i);
392
392
  }
393
393
  var _loop_3 = function (newSnap) {
394
- if (this_3.eSnapHolder.findIndex(function (a) { return a.ack == newSnap.ack && a.Snapshot.Key == newSnap.Snapshot.Key; }) === -1) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
394
+ if (this_3.eSnapHolder.findIndex(function (a) { return a.ack == recvTick && a.Snapshot.Key == newSnap.Snapshot.Key; }) === -1) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
395
395
  this_3.eSnapHolder.push({ Snapshot: { Data: newSnap.Snapshot.Data, Key: newSnap.Snapshot.Key }, ack: recvTick });
396
396
  }
397
397
  if (deltatick > -1) {
@@ -404,10 +404,24 @@ var Snapshot = /** @class */ (function () {
404
404
  this_3.deltas.push({ data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_3.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff)) });
405
405
  };
406
406
  var this_3 = this;
407
- for (var _i = 0, newSnaps_1 = newSnaps; _i < newSnaps_1.length; _i++) {
408
- var newSnap = newSnaps_1[_i];
407
+ for (var _i = 0, deltaSnaps_1 = deltaSnaps; _i < deltaSnaps_1.length; _i++) {
408
+ var newSnap = deltaSnaps_1[_i];
409
409
  _loop_3(newSnap);
410
410
  }
411
+ if (this.crc(recvTick) !== WantedCrc) {
412
+ this.crc_errors++;
413
+ if (this.crc_errors > 5) {
414
+ recvTick = -1;
415
+ this.crc_errors = 0;
416
+ this.eSnapHolder = [];
417
+ this.deltas = [];
418
+ }
419
+ else {
420
+ recvTick = deltatick;
421
+ }
422
+ }
423
+ else if (this.crc_errors > 0)
424
+ this.crc_errors--;
411
425
  return { items: items, recvTick: recvTick };
412
426
  };
413
427
  return Snapshot;
@@ -415,14 +429,15 @@ var Snapshot = /** @class */ (function () {
415
429
  exports.Snapshot = Snapshot;
416
430
  function UndiffItem(oldItem, newItem) {
417
431
  var out = newItem;
418
- if (JSON.stringify(newItem) === JSON.stringify(oldItem))
419
- return newItem;
432
+ // if (JSON.stringify(newItem) === JSON.stringify(oldItem))
433
+ // return newItem;
420
434
  oldItem.forEach(function (a, i) {
421
435
  if (a !== undefined && out[i] !== undefined) {
422
436
  out[i] += a;
423
437
  }
424
- else
438
+ else {
425
439
  out[i] = 0;
440
+ }
426
441
  });
427
442
  return out;
428
443
  }
package/lib/snapshot.ts CHANGED
@@ -57,6 +57,7 @@ interface eSnap {
57
57
  export class Snapshot {
58
58
  deltas: {'data': number[], 'parsed': Item, 'type_id': number, 'id': number, 'key': number}[] = [];
59
59
  eSnapHolder: eSnap[] = [];
60
+ crc_errors: number = 0;
60
61
 
61
62
  private IntsToStr(pInts: number[]): string {
62
63
  var pIntz: number[] = [];
@@ -297,7 +298,18 @@ export class Snapshot {
297
298
 
298
299
  return _item;
299
300
  }
300
- unpackSnapshot(snap: number[], deltatick: number, recvTick: number) {
301
+
302
+ crc(tick: number) {
303
+ var checksum = 0;
304
+ this.eSnapHolder.forEach(snap => {
305
+ if (snap.ack == tick)
306
+ snap.Snapshot.Data.forEach(el => checksum += el);
307
+ })
308
+
309
+ return checksum & 0xffffffff;
310
+ }
311
+
312
+ unpackSnapshot(snap: number[], deltatick: number, recvTick: number, WantedCrc: number) {
301
313
  let unpacker = new MsgUnpacker(snap);
302
314
  if (deltatick == -1) {
303
315
  this.eSnapHolder = [];
@@ -309,7 +321,7 @@ export class Snapshot {
309
321
  // empty snap, copy old one into new ack
310
322
  this.eSnapHolder.forEach(snap => {
311
323
  if (snap.ack == deltatick)
312
- this.eSnapHolder.push({Snapshot: snap.Snapshot, ack: recvTick});
324
+ this.eSnapHolder.push({Snapshot: snap.Snapshot, ack: recvTick});
313
325
 
314
326
  })
315
327
  return {items: [], recvTick: recvTick};
@@ -337,12 +349,11 @@ export class Snapshot {
337
349
  let deleted_key = unpacker.unpackInt(); // removed_item_keys
338
350
  // let index = this.deltas.map(delta => delta.key).indexOf(deleted_key);
339
351
  let index = this.deltas.findIndex(delta => delta.key === deleted_key);
340
- // console.log("deleting ", deleted_key, index)
341
352
  if (index > -1)
342
353
  this.deltas.splice(index, 1);
343
354
  deleted.push(deleted_key)
344
355
  }
345
- if (deleted.length)
356
+ if (deleted.length)
346
357
  this.eSnapHolder = this.eSnapHolder.filter(a => !deleted.includes(a.Snapshot.Key));
347
358
 
348
359
  /*item_delta:
@@ -355,14 +366,8 @@ export class Snapshot {
355
366
  let deltaSnaps = this.eSnapHolder.filter(a => a.ack === deltatick);
356
367
 
357
368
  if (deltaSnaps.length == 0 && deltatick >= 0) {
358
- // console.log("RESET recvtick")
359
369
  return {items: [], recvTick: -1};
360
370
  }
361
- let newSnaps: eSnap[] = [];
362
- deltaSnaps.forEach((a) => {
363
-
364
- newSnaps.push({Snapshot: a.Snapshot, ack: recvTick});
365
- })
366
371
 
367
372
 
368
373
  for (let i = 0; i < num_item_deltas; i++) {
@@ -380,10 +385,7 @@ export class Snapshot {
380
385
  for (let j = 0; j < _size; j++) {
381
386
  if (unpacker.remaining.length > 0)
382
387
  data.push(unpacker.unpackInt());
383
- // else
384
- // console.log("wrong size")
385
388
  }
386
- // console.log(index, deltatick)
387
389
  if (deltatick >= 0) {
388
390
  // let index = deltaSnaps.map(delta => delta.Snapshot.Key).indexOf(key)
389
391
  let index = deltaSnaps.findIndex(delta => delta.Snapshot.Key === key);
@@ -402,8 +404,8 @@ export class Snapshot {
402
404
 
403
405
 
404
406
  }
405
- for (let newSnap of newSnaps) {
406
- if (this.eSnapHolder.findIndex(a => a.ack == newSnap.ack && a.Snapshot.Key == newSnap.Snapshot.Key) === -1) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
407
+ for (let newSnap of deltaSnaps) {
408
+ if (this.eSnapHolder.findIndex(a => a.ack == recvTick && a.Snapshot.Key == newSnap.Snapshot.Key) === -1) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
407
409
  this.eSnapHolder.push({Snapshot: {Data: newSnap.Snapshot.Data, Key: newSnap.Snapshot.Key}, ack: recvTick});
408
410
  }
409
411
  if (deltatick > -1) {
@@ -417,6 +419,19 @@ export class Snapshot {
417
419
  this.deltas.push({data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff), ((newSnap.Snapshot.Key) & 0xffff))});
418
420
  }
419
421
 
422
+ if (this.crc(recvTick) !== WantedCrc) {
423
+ this.crc_errors++;
424
+ if (this.crc_errors > 5) {
425
+ recvTick = -1;
426
+ this.crc_errors = 0;
427
+ this.eSnapHolder = [];
428
+ this.deltas = [];
429
+ } else {
430
+ recvTick = deltatick;
431
+
432
+ }
433
+ } else if (this.crc_errors > 0)
434
+ this.crc_errors--;
420
435
 
421
436
  return {items, recvTick};
422
437
  }
@@ -424,13 +439,14 @@ export class Snapshot {
424
439
 
425
440
  function UndiffItem(oldItem: number[], newItem: number[]): number[] {
426
441
  let out: number[] = newItem;
427
- if (JSON.stringify(newItem) === JSON.stringify(oldItem))
428
- return newItem;
442
+ // if (JSON.stringify(newItem) === JSON.stringify(oldItem))
443
+ // return newItem;
429
444
  oldItem.forEach((a, i) => {
430
445
  if (a !== undefined && out[i] !== undefined) {
431
446
  out[i] += a;
432
- } else
447
+ } else {
433
448
  out[i] = 0;
449
+ }
434
450
  })
435
451
  return out;
436
452
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teeworlds",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "Library for (ingame) teeworlds bots.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
package/test.js ADDED
@@ -0,0 +1,32 @@
1
+ const { Snapshot } = require("./lib/snapshot")
2
+
3
+ let test_snap1 = [0,18,0,4,18,1744,1072,2,3,4,17,1840,912,1,0,4,16,880,880,0,0,4,15,1840,848,1,0,4,14,912,848,0,0,4,13,880,848,1,0,4,12,848,848,0,0,4,11,880,816,0,0,4,9,1264,656,0,0,4,8,1104,656,0,0,4,7,912,656,0,0,4,6,1712,624,2,2,4,5,1840,432,1,0,4,3,1840,336,1,0,9,0,292,1584,305,0,128,0,0,0,-1,0,0,1584,304,0,0,0,10,0,10,1,0,0,6,0,0,0,0,0,20,0,0,1,11,0,-287183387,-320474125,-1594563099,-2139062272,-2139062144,-2139062144,-2139062272,-1,-454695199,-169020288,-2139062144,-2139062144,-2139062144,-2139062272,0,65408,65408,10,0,1,0,0,0,0]
4
+ let first_crc = 0x5b96263a;
5
+ let test_snap2 = [0,1,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,];
6
+ let second_crc = 0x5b96263b;
7
+ let snapunpacker = new Snapshot();
8
+
9
+ let sum = (nums) => {
10
+ var sum = 0;
11
+ nums.forEach(a => {
12
+ // if (a < 0)
13
+ sum += a
14
+ })
15
+ console.log(sum, master_sum)
16
+ return sum;
17
+ }
18
+
19
+ let master_sum = 0;
20
+
21
+ // test_snap.forEach(a => master_sum += sum(...a));
22
+ // master_sum = sum(...test_snap)
23
+ // test_snap.forEach(a => {master_sum += a; console.log(master_sum)})
24
+ // console.log(snapunpacker.unpackSnapshot(test_snap).items.items);
25
+
26
+ snapunpacker = new Snapshot();
27
+ console.log(snapunpacker.unpackSnapshot(test_snap1, -1, 1, first_crc));
28
+ console.log(snapunpacker.unpackSnapshot(test_snap2, 1, 2, second_crc));
29
+ // console.log(snapunpacker.eSnapHolder)
30
+ // snapunpacker.eSnapHolder.forEach(a => console.log(a.Snapshot.Data))
31
+ snapunpacker.eSnapHolder.forEach(a => master_sum += sum(a.Snapshot.Data))
32
+ console.log(master_sum & 0xffffffff, 0x5b96263a)