teeworlds 2.5.3 → 2.5.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.js CHANGED
@@ -1,845 +1,761 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
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");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __importDefault = (this && this.__importDefault) || function (mod) {
18
- return (mod && mod.__esModule) ? mod : { "default": mod };
19
- };
20
- Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.Client = void 0;
22
- var crypto_1 = require("crypto");
23
- var dgram_1 = __importDefault(require("dgram"));
24
- var stream_1 = require("stream");
25
- var MsgUnpacker_1 = require("./MsgUnpacker");
26
- var version = require('../package.json').version;
27
- var movement_1 = __importDefault(require("./components/movement"));
28
- var MsgPacker_1 = require("./MsgPacker");
29
- var snapshot_1 = require("./snapshot");
30
- var huffman_1 = require("./huffman");
31
- var game_1 = require("./components/game");
32
- var snapshot_2 = require("./components/snapshot");
33
- var UUIDManager_1 = require("./UUIDManager");
34
- var huff = new huffman_1.Huffman();
35
- var States;
36
- (function (States) {
37
- States[States["STATE_OFFLINE"] = 0] = "STATE_OFFLINE";
38
- States[States["STATE_CONNECTING"] = 1] = "STATE_CONNECTING";
39
- States[States["STATE_LOADING"] = 2] = "STATE_LOADING";
40
- States[States["STATE_ONLINE"] = 3] = "STATE_ONLINE";
41
- States[States["STATE_DEMOPLAYBACK"] = 4] = "STATE_DEMOPLAYBACK";
42
- States[States["STATE_QUITTING"] = 5] = "STATE_QUITTING";
43
- States[States["STATE_RESTARTING"] = 6] = "STATE_RESTARTING";
44
- })(States || (States = {}));
45
- var NETMSG_Game;
46
- (function (NETMSG_Game) {
47
- NETMSG_Game[NETMSG_Game["EX"] = 0] = "EX";
48
- NETMSG_Game[NETMSG_Game["SV_MOTD"] = 1] = "SV_MOTD";
49
- NETMSG_Game[NETMSG_Game["SV_BROADCAST"] = 2] = "SV_BROADCAST";
50
- NETMSG_Game[NETMSG_Game["SV_CHAT"] = 3] = "SV_CHAT";
51
- NETMSG_Game[NETMSG_Game["SV_KILLMSG"] = 4] = "SV_KILLMSG";
52
- NETMSG_Game[NETMSG_Game["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
53
- NETMSG_Game[NETMSG_Game["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
54
- NETMSG_Game[NETMSG_Game["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
55
- NETMSG_Game[NETMSG_Game["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
56
- NETMSG_Game[NETMSG_Game["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
57
- NETMSG_Game[NETMSG_Game["SV_EMOTICON"] = 10] = "SV_EMOTICON";
58
- NETMSG_Game[NETMSG_Game["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
59
- NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
60
- NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
61
- NETMSG_Game[NETMSG_Game["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
62
- NETMSG_Game[NETMSG_Game["SV_VOTESET"] = 15] = "SV_VOTESET";
63
- NETMSG_Game[NETMSG_Game["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
64
- NETMSG_Game[NETMSG_Game["CL_SAY"] = 17] = "CL_SAY";
65
- NETMSG_Game[NETMSG_Game["CL_SETTEAM"] = 18] = "CL_SETTEAM";
66
- NETMSG_Game[NETMSG_Game["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
67
- NETMSG_Game[NETMSG_Game["CL_STARTINFO"] = 20] = "CL_STARTINFO";
68
- NETMSG_Game[NETMSG_Game["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
69
- NETMSG_Game[NETMSG_Game["CL_KILL"] = 22] = "CL_KILL";
70
- NETMSG_Game[NETMSG_Game["CL_EMOTICON"] = 23] = "CL_EMOTICON";
71
- NETMSG_Game[NETMSG_Game["CL_VOTE"] = 24] = "CL_VOTE";
72
- NETMSG_Game[NETMSG_Game["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
73
- NETMSG_Game[NETMSG_Game["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
74
- NETMSG_Game[NETMSG_Game["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
75
- NETMSG_Game[NETMSG_Game["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
76
- NETMSG_Game[NETMSG_Game["UNUSED"] = 29] = "UNUSED";
77
- NETMSG_Game[NETMSG_Game["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
78
- NETMSG_Game[NETMSG_Game["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
79
- NETMSG_Game[NETMSG_Game["NUM"] = 32] = "NUM";
80
- })(NETMSG_Game || (NETMSG_Game = {}));
81
- var NETMSG_Sys;
82
- (function (NETMSG_Sys) {
83
- NETMSG_Sys[NETMSG_Sys["NETMSG_EX"] = 0] = "NETMSG_EX";
84
- // the first thing sent by the client
85
- // contains the version info for the client
86
- NETMSG_Sys[NETMSG_Sys["NETMSG_INFO"] = 1] = "NETMSG_INFO";
87
- // sent by server
88
- NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_CHANGE"] = 2] = "NETMSG_MAP_CHANGE";
89
- NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_DATA"] = 3] = "NETMSG_MAP_DATA";
90
- NETMSG_Sys[NETMSG_Sys["NETMSG_CON_READY"] = 4] = "NETMSG_CON_READY";
91
- NETMSG_Sys[NETMSG_Sys["NETMSG_SNAP"] = 5] = "NETMSG_SNAP";
92
- NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPEMPTY"] = 6] = "NETMSG_SNAPEMPTY";
93
- NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPSINGLE"] = 7] = "NETMSG_SNAPSINGLE";
94
- NETMSG_Sys[NETMSG_Sys["NETMSG_SNAPSMALL"] = 8] = "NETMSG_SNAPSMALL";
95
- NETMSG_Sys[NETMSG_Sys["NETMSG_INPUTTIMING"] = 9] = "NETMSG_INPUTTIMING";
96
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_AUTH_STATUS"] = 10] = "NETMSG_RCON_AUTH_STATUS";
97
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_LINE"] = 11] = "NETMSG_RCON_LINE";
98
- NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_CHALLANGE"] = 12] = "NETMSG_AUTH_CHALLANGE";
99
- NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_RESULT"] = 13] = "NETMSG_AUTH_RESULT";
100
- // sent by client
101
- NETMSG_Sys[NETMSG_Sys["NETMSG_READY"] = 14] = "NETMSG_READY";
102
- NETMSG_Sys[NETMSG_Sys["NETMSG_ENTERGAME"] = 15] = "NETMSG_ENTERGAME";
103
- NETMSG_Sys[NETMSG_Sys["NETMSG_INPUT"] = 16] = "NETMSG_INPUT";
104
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD"] = 17] = "NETMSG_RCON_CMD";
105
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_AUTH"] = 18] = "NETMSG_RCON_AUTH";
106
- NETMSG_Sys[NETMSG_Sys["NETMSG_REQUEST_MAP_DATA"] = 19] = "NETMSG_REQUEST_MAP_DATA";
107
- NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_START"] = 20] = "NETMSG_AUTH_START";
108
- NETMSG_Sys[NETMSG_Sys["NETMSG_AUTH_RESPONSE"] = 21] = "NETMSG_AUTH_RESPONSE";
109
- // sent by both
110
- NETMSG_Sys[NETMSG_Sys["NETMSG_PING"] = 22] = "NETMSG_PING";
111
- NETMSG_Sys[NETMSG_Sys["NETMSG_PING_REPLY"] = 23] = "NETMSG_PING_REPLY";
112
- NETMSG_Sys[NETMSG_Sys["NETMSG_ERROR"] = 24] = "NETMSG_ERROR";
113
- // sent by server (todo: move it up)
114
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_ADD"] = 25] = "NETMSG_RCON_CMD_ADD";
115
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_REM"] = 26] = "NETMSG_RCON_CMD_REM";
116
- NETMSG_Sys[NETMSG_Sys["NUM_NETMSGS"] = 27] = "NUM_NETMSGS";
117
- NETMSG_Sys[NETMSG_Sys["NETMSG_WHATIS"] = 65536] = "NETMSG_WHATIS";
118
- NETMSG_Sys[NETMSG_Sys["NETMSG_ITIS"] = 65537] = "NETMSG_ITIS";
119
- NETMSG_Sys[NETMSG_Sys["NETMSG_IDONTKNOW"] = 65538] = "NETMSG_IDONTKNOW";
120
- NETMSG_Sys[NETMSG_Sys["NETMSG_RCONTYPE"] = 65539] = "NETMSG_RCONTYPE";
121
- NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_DETAILS"] = 65540] = "NETMSG_MAP_DETAILS";
122
- NETMSG_Sys[NETMSG_Sys["NETMSG_CAPABILITIES"] = 65541] = "NETMSG_CAPABILITIES";
123
- NETMSG_Sys[NETMSG_Sys["NETMSG_CLIENTVER"] = 65542] = "NETMSG_CLIENTVER";
124
- NETMSG_Sys[NETMSG_Sys["NETMSG_PINGEX"] = 65543] = "NETMSG_PINGEX";
125
- NETMSG_Sys[NETMSG_Sys["NETMSG_PONGEX"] = 65544] = "NETMSG_PONGEX";
126
- NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_REQUEST"] = 65545] = "NETMSG_CHECKSUM_REQUEST";
127
- NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_RESPONSE"] = 65546] = "NETMSG_CHECKSUM_RESPONSE";
128
- NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_ERROR"] = 65547] = "NETMSG_CHECKSUM_ERROR";
129
- NETMSG_Sys[NETMSG_Sys["NETMSG_REDIRECT"] = 65548] = "NETMSG_REDIRECT";
130
- NETMSG_Sys[NETMSG_Sys["NETMSG_I_AM_NPM_PACKAGE"] = 65549] = "NETMSG_I_AM_NPM_PACKAGE";
131
- })(NETMSG_Sys || (NETMSG_Sys = {}));
132
- var messageTypes = [
133
- ["none, starts at 1", "SV_MOTD", "SV_BROADCAST", "SV_CHAT", "SV_KILL_MSG", "SV_SOUND_GLOBAL", "SV_TUNE_PARAMS", "SV_EXTRA_PROJECTILE", "SV_READY_TO_ENTER", "SV_WEAPON_PICKUP", "SV_EMOTICON", "SV_VOTE_CLEAR_OPTIONS", "SV_VOTE_OPTION_LIST_ADD", "SV_VOTE_OPTION_ADD", "SV_VOTE_OPTION_REMOVE", "SV_VOTE_SET", "SV_VOTE_STATUS", "CL_SAY", "CL_SET_TEAM", "CL_SET_SPECTATOR_MODE", "CL_START_INFO", "CL_CHANGE_INFO", "CL_KILL", "CL_EMOTICON", "CL_VOTE", "CL_CALL_VOTE", "CL_IS_DDNET", "SV_DDRACE_TIME", "SV_RECORD", "UNUSED", "SV_TEAMS_STATE", "CL_SHOW_OTHERS_LEGACY"],
134
- ["none, starts at 1", "INFO", "MAP_CHANGE", "MAP_DATA", "CON_READY", "SNAP", "SNAP_EMPTY", "SNAP_SINGLE", "INPUT_TIMING", "RCON_AUTH_STATUS", "RCON_LINE", "READY", "ENTER_GAME", "INPUT", "RCON_CMD", "RCON_AUTH", "REQUEST_MAP_DATA", "PING", "PING_REPLY", "RCON_CMD_ADD", "RCON_CMD_REMOVE"]
135
- ];
136
- var Client = /** @class */ (function (_super) {
137
- __extends(Client, _super);
138
- function Client(ip, port, nickname, options) {
139
- var _this = _super.call(this) || this;
140
- _this.host = ip;
141
- _this.port = port;
142
- _this.name = nickname;
143
- _this.AckGameTick = 0;
144
- _this.PredGameTick = 0;
145
- _this.currentSnapshotGameTick = 0;
146
- _this.SnapshotParts = 0;
147
- _this.SnapUnpacker = new snapshot_1.Snapshot(_this);
148
- // this.eSnapHolder = [];
149
- _this.requestResend = false;
150
- _this.VoteList = [];
151
- if (options)
152
- _this.options = options;
153
- _this.snaps = [];
154
- _this.sentChunkQueue = [];
155
- _this.queueChunkEx = [];
156
- _this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
157
- _this.ack = 0; // ack of messages the client has received
158
- _this.clientAck = 0; // ack of messages the client has sent
159
- _this.lastCheckedChunkAck = 0; // this.ack gets reset to this when flushing - used for resetting tick on e.g. map change
160
- _this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
161
- _this.socket = dgram_1.default.createSocket("udp4");
162
- _this.socket.bind();
163
- _this.TKEN = Buffer.from([255, 255, 255, 255]);
164
- _this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
165
- _this.lastSendTime = new Date().getTime();
166
- _this.lastRecvTime = new Date().getTime();
167
- _this.lastSentMessages = [];
168
- _this.movement = new movement_1.default();
169
- _this.game = new game_1.Game(_this);
170
- _this.SnapshotUnpacker = new snapshot_2.SnapshotWrapper(_this);
171
- _this.UUIDManager = new UUIDManager_1.UUIDManager();
172
- _this.UUIDManager.RegisterName("what-is@ddnet.tw", NETMSG_Sys.NETMSG_WHATIS);
173
- _this.UUIDManager.RegisterName("it-is@ddnet.tw", NETMSG_Sys.NETMSG_ITIS);
174
- _this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", NETMSG_Sys.NETMSG_IDONTKNOW);
175
- _this.UUIDManager.RegisterName("rcon-type@ddnet.tw", NETMSG_Sys.NETMSG_RCONTYPE);
176
- _this.UUIDManager.RegisterName("map-details@ddnet.tw", NETMSG_Sys.NETMSG_MAP_DETAILS);
177
- _this.UUIDManager.RegisterName("capabilities@ddnet.tw", NETMSG_Sys.NETMSG_CAPABILITIES);
178
- _this.UUIDManager.RegisterName("clientver@ddnet.tw", NETMSG_Sys.NETMSG_CLIENTVER);
179
- _this.UUIDManager.RegisterName("ping@ddnet.tw", NETMSG_Sys.NETMSG_PING);
180
- _this.UUIDManager.RegisterName("pong@ddnet.tw", NETMSG_Sys.NETMSG_PONGEX);
181
- _this.UUIDManager.RegisterName("checksum-request@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_REQUEST);
182
- _this.UUIDManager.RegisterName("checksum-response@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_RESPONSE);
183
- _this.UUIDManager.RegisterName("checksum-error@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_ERROR);
184
- _this.UUIDManager.RegisterName("redirect@ddnet.org", NETMSG_Sys.NETMSG_REDIRECT);
185
- _this.UUIDManager.RegisterName("i-am-npm-package@swarfey.gitlab.io", NETMSG_Sys.NETMSG_I_AM_NPM_PACKAGE);
186
- return _this;
187
- }
188
- Client.prototype.OnEnterGame = function () {
189
- this.snaps = [];
190
- this.SnapUnpacker = new snapshot_1.Snapshot(this);
191
- this.SnapshotParts = 0;
192
- this.receivedSnaps = 0;
193
- this.SnapshotUnpacker = new snapshot_2.SnapshotWrapper(this);
194
- this.currentSnapshotGameTick = 0;
195
- this.AckGameTick = -1;
196
- this.PredGameTick = 0;
197
- };
198
- Client.prototype.ResendAfter = function (lastAck) {
199
- this.clientAck = lastAck;
200
- var toResend = [];
201
- this.lastSentMessages.forEach(function (msg) {
202
- if (msg.ack > lastAck)
203
- toResend.push(msg.msg);
204
- });
205
- toResend.forEach(function (a) { return a.flag = 1 | 2; });
206
- this.SendMsgEx(toResend);
207
- };
208
- Client.prototype.Unpack = function (packet) {
209
- var unpacked = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0] & 0xf) << 8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
210
- if (packet.indexOf(Buffer.from([0xff, 0xff, 0xff, 0xff])) == 0) // !(unpacked.twprotocol.flags & 8) || unpacked.twprotocol.flags == 255) // flags == 255 is connectionless (used for sending usernames)
211
- return unpacked;
212
- if (unpacked.twprotocol.flags & 4) { // resend flag
213
- this.ResendAfter(unpacked.twprotocol.ack);
214
- }
215
- packet = packet.slice(3);
216
- if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
217
- packet = huff.decompress(packet);
218
- if (packet.length == 1 && packet[0] == -1)
219
- return unpacked;
220
- }
221
- for (var i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
222
- var chunk = {};
223
- chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
224
- chunk.flags = (packet[0] >> 6) & 3;
225
- if (chunk.flags & 1) {
226
- chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
227
- packet = packet.slice(3); // remove flags & size
228
- }
229
- else
230
- packet = packet.slice(2);
231
- // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
232
- chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
233
- chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
234
- chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
235
- chunk.raw = packet.slice(1, chunk.bytes);
236
- if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
237
- var uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
238
- if (uuid !== undefined) {
239
- chunk.extended_msgid = uuid.hash;
240
- chunk.msg = uuid.name;
241
- chunk.raw = chunk.raw.slice(16);
242
- chunk.msgid = uuid.type_id;
243
- }
244
- }
245
- packet = packet.slice(chunk.bytes);
246
- unpacked.chunks.push(chunk);
247
- }
248
- return unpacked;
249
- };
250
- /** Send a Control Msg to the server. (used for disconnect)*/
251
- Client.prototype.SendControlMsg = function (msg, ExtraMsg) {
252
- var _this = this;
253
- if (ExtraMsg === void 0) { ExtraMsg = ""; }
254
- this.lastSendTime = new Date().getTime();
255
- return new Promise(function (resolve, reject) {
256
- if (_this.socket) {
257
- var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((_this.ack >> 8) & 0xf)), _this.ack & 0xff, 0x00, msg]);
258
- latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), _this.TKEN]); // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
259
- _this.socket.send(latestBuf, 0, latestBuf.length, _this.port, _this.host, function (err, bytes) {
260
- resolve(bytes);
261
- });
262
- }
263
- setTimeout(function () { resolve("failed, rip"); }, 2000);
264
- /* after 2 seconds it was probably not able to send,
265
- so when sending a quit message the user doesnt
266
- stay stuck not being able to ctrl + c
267
- */
268
- });
269
- };
270
- /** Send a Msg (or Msg[]) to the server.*/
271
- Client.prototype.SendMsgEx = function (Msgs, flags) {
272
- var _this = this;
273
- if (flags === void 0) { flags = 0; }
274
- if (this.State == States.STATE_OFFLINE)
275
- return;
276
- if (!this.socket)
277
- return;
278
- var _Msgs;
279
- if (Msgs instanceof Array)
280
- _Msgs = Msgs;
281
- else
282
- _Msgs = [Msgs];
283
- if (this.queueChunkEx.length > 0) {
284
- _Msgs.push.apply(_Msgs, this.queueChunkEx);
285
- this.queueChunkEx = [];
286
- }
287
- this.lastSendTime = new Date().getTime();
288
- var header = [];
289
- if (this.clientAck == 0)
290
- this.lastSentMessages = [];
291
- _Msgs.forEach(function (Msg, index) {
292
- header[index] = Buffer.alloc((Msg.flag & 1 ? 3 : 2));
293
- header[index][0] = ((Msg.flag & 3) << 6) | ((Msg.size >> 4) & 0x3f);
294
- header[index][1] = (Msg.size & 0xf);
295
- if (Msg.flag & 1) {
296
- _this.clientAck = (_this.clientAck + 1) % (1 << 10);
297
- if (_this.clientAck == 0)
298
- _this.lastSentMessages = [];
299
- header[index][1] |= (_this.clientAck >> 2) & 0xf0;
300
- header[index][2] = _this.clientAck & 0xff;
301
- header[index][0] = (((Msg.flag | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
302
- if ((Msg.flag & 2) == 0)
303
- _this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
304
- header[index][0] = (((Msg.flag) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
305
- if ((Msg.flag & 2) == 0)
306
- _this.lastSentMessages.push({ msg: Msg, ack: _this.clientAck });
307
- }
308
- });
309
- // let flags = 0;
310
- if (this.requestResend)
311
- flags |= 4;
312
- var packetHeader = Buffer.from([((flags << 4) & 0xf0) | ((this.ack >> 8) & 0xf), this.ack & 0xff, _Msgs.length]);
313
- var chunks = Buffer.from([]);
314
- var skip = false;
315
- _Msgs.forEach(function (Msg, index) {
316
- if (skip)
317
- return;
318
- if (chunks.byteLength < 1300)
319
- chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
320
- else {
321
- skip = true;
322
- _this.SendMsgEx(_Msgs.slice(index));
323
- }
324
- });
325
- var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
326
- if (chunks.length < 0)
327
- return;
328
- this.socket.send(packet, 0, packet.length, this.port, this.host);
329
- };
330
- /** Queue a chunk (instantly sent if flush flag is set - otherwise it will be sent in the next packet). */
331
- Client.prototype.QueueChunkEx = function (Msg) {
332
- if (this.queueChunkEx.length > 0) {
333
- var total_size = 0;
334
- for (var _i = 0, _a = this.queueChunkEx; _i < _a.length; _i++) {
335
- var chunk = _a[_i];
336
- total_size += chunk.size;
337
- }
338
- if (total_size + Msg.size + 3 > 1394 - 4)
339
- this.Flush();
340
- }
341
- this.queueChunkEx.push(Msg);
342
- if (Msg.flag & 4)
343
- this.Flush();
344
- };
345
- /** Send a Raw Buffer (as chunk) to the server. */
346
- Client.prototype.SendMsgRaw = function (chunks) {
347
- if (this.State == States.STATE_OFFLINE)
348
- return;
349
- if (!this.socket)
350
- return;
351
- this.lastSendTime = new Date().getTime();
352
- var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, chunks.length]);
353
- var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
354
- if (chunks.length < 0)
355
- return;
356
- this.socket.send(packet, 0, packet.length, this.port, this.host);
357
- };
358
- Client.prototype.MsgToChunk = function (packet) {
359
- var chunk = {};
360
- chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
361
- chunk.flags = (packet[0] >> 6) & 3;
362
- if (chunk.flags & 1) {
363
- chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
364
- packet = packet.slice(3); // remove flags & size
365
- }
366
- else
367
- packet = packet.slice(2);
368
- // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
369
- chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
370
- chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
371
- chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
372
- chunk.raw = packet.slice(1, chunk.bytes);
373
- if (chunk.msgid == 0) {
374
- var uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
375
- if (uuid !== undefined) {
376
- chunk.extended_msgid = uuid.hash;
377
- chunk.msgid = uuid.type_id;
378
- chunk.msg = uuid.name;
379
- chunk.raw = chunk.raw.slice(16);
380
- }
381
- }
382
- return chunk;
383
- };
384
- Client.prototype.Flush = function () {
385
- // if (this.queueChunkEx.length == 0)
386
- console.log("flushing");
387
- this.SendMsgEx(this.queueChunkEx);
388
- this.queueChunkEx = [];
389
- this.ack = this.lastCheckedChunkAck;
390
- };
391
- /** Connect the client to the server. */
392
- Client.prototype.connect = function () {
393
- var _this = this;
394
- var _a;
395
- this.State = States.STATE_CONNECTING;
396
- var predTimer = setInterval(function () {
397
- if (_this.State == States.STATE_ONLINE) {
398
- if (_this.AckGameTick > 0)
399
- _this.PredGameTick++;
400
- }
401
- else if (_this.State == States.STATE_OFFLINE)
402
- clearInterval(predTimer);
403
- }, 1000 / 50); // 50 ticks per second
404
- this.SendControlMsg(1, "TKEN");
405
- var connectInterval = setInterval(function () {
406
- if (_this.State == States.STATE_CONNECTING)
407
- _this.SendControlMsg(1, "TKEN");
408
- else
409
- clearInterval(connectInterval);
410
- }, 500);
411
- if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight)) {
412
- var inputInterval_1 = setInterval(function () {
413
- if (_this.State == States.STATE_OFFLINE) {
414
- clearInterval(inputInterval_1);
415
- console.log("???");
416
- }
417
- if (_this.State != States.STATE_ONLINE)
418
- return;
419
- _this.time = new Date().getTime();
420
- _this.sendInput();
421
- }, 50);
422
- }
423
- var resendTimeout = setInterval(function () {
424
- if (_this.State != States.STATE_OFFLINE) {
425
- if (((new Date().getTime()) - _this.lastSendTime) > 900 && _this.sentChunkQueue.length > 0) {
426
- _this.SendMsgRaw([_this.sentChunkQueue[0]]);
427
- }
428
- }
429
- else
430
- clearInterval(resendTimeout);
431
- }, 1000);
432
- var Timeout = setInterval(function () {
433
- var _a;
434
- var timeoutTime = ((_a = _this.options) === null || _a === void 0 ? void 0 : _a.timeout) ? _this.options.timeout : 15000;
435
- if ((new Date().getTime() - _this.lastRecvTime) > timeoutTime) {
436
- _this.State = States.STATE_OFFLINE;
437
- _this.emit("timeout");
438
- _this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - _this.lastRecvTime) + "ms)");
439
- clearInterval(Timeout);
440
- }
441
- }, 5000);
442
- this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
443
- if (this.socket)
444
- this.socket.on("message", function (packet, rinfo) {
445
- var _a, _b, _c, _d, _e, _f;
446
- if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
447
- return;
448
- clearInterval(connectInterval);
449
- if (packet[0] == 0x10) {
450
- if (packet.toString().includes("TKEN") || packet[3] == 0x2) {
451
- clearInterval(connectInterval);
452
- _this.TKEN = packet.slice(-4);
453
- _this.SendControlMsg(3);
454
- _this.State = States.STATE_LOADING; // loading state
455
- _this.receivedSnaps = 0;
456
- var info = new MsgPacker_1.MsgPacker(1, true, 1);
457
- info.AddString(((_a = _this.options) === null || _a === void 0 ? void 0 : _a.NET_VERSION) ? _this.options.NET_VERSION : "0.6 626fce9a778df4d4");
458
- info.AddString(((_b = _this.options) === null || _b === void 0 ? void 0 : _b.password) === undefined ? "" : (_c = _this.options) === null || _c === void 0 ? void 0 : _c.password); // password
459
- var client_version = new MsgPacker_1.MsgPacker(0, true, 1);
460
- client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
461
- var randomUuid = (0, crypto_1.randomBytes)(16);
462
- client_version.AddBuffer(randomUuid);
463
- if (((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
464
- client_version.AddInt((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
465
- client_version.AddString("DDNet ".concat((_f = _this.options) === null || _f === void 0 ? void 0 : _f.ddnet_version.release_version, "; https://www.npmjs.com/package/teeworlds/v/").concat(version));
466
- }
467
- else {
468
- client_version.AddInt(16050);
469
- client_version.AddString("DDNet 16.5.0; https://www.npmjs.com/package/teeworlds/v/".concat(version));
470
- }
471
- var i_am_npm_package = new MsgPacker_1.MsgPacker(0, true, 1);
472
- i_am_npm_package.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_I_AM_NPM_PACKAGE).hash);
473
- i_am_npm_package.AddString("https://www.npmjs.com/package/teeworlds/v/".concat(version));
474
- _this.SendMsgEx([i_am_npm_package, client_version, info]);
475
- }
476
- else if (packet[3] == 0x4) {
477
- // disconnected
478
- _this.State = States.STATE_OFFLINE;
479
- var reason = ((0, MsgUnpacker_1.unpackString)(packet.slice(4)).result);
480
- _this.emit("disconnect", reason);
481
- }
482
- if (packet[3] !== 0x0) { // keepalive
483
- _this.lastRecvTime = new Date().getTime();
484
- }
485
- }
486
- else {
487
- _this.lastRecvTime = new Date().getTime();
488
- }
489
- var unpacked = _this.Unpack(packet);
490
- // unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
491
- _this.sentChunkQueue.forEach(function (buff, i) {
492
- var chunkFlags = (buff[0] >> 6) & 3;
493
- if (chunkFlags & 1) {
494
- var chunk = _this.MsgToChunk(buff);
495
- if (chunk.seq && chunk.seq >= _this.ack)
496
- _this.sentChunkQueue.splice(i, 1);
497
- }
498
- });
499
- unpacked.chunks.forEach(function (chunk) {
500
- var _a;
501
- var _b;
502
- if (!(((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq > _this.ack : true))
503
- return; // filter out already received chunks
504
- if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
505
- _this.lastCheckedChunkAck = chunk.seq;
506
- if (chunk.seq === (_this.ack + 1) % (1 << 10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
507
- _this.ack = chunk.seq;
508
- _this.requestResend = false;
509
- }
510
- else { //IsSeqInBackroom (old packet that we already got)
511
- var Bottom = (_this.ack - (1 << 10) / 2);
512
- if (Bottom < 0) {
513
- if ((chunk.seq <= _this.ack) || (chunk.seq >= (Bottom + (1 << 10)))) { }
514
- else
515
- _this.requestResend = true;
516
- }
517
- else {
518
- if (chunk.seq <= _this.ack && chunk.seq >= Bottom) { }
519
- else
520
- _this.requestResend = true;
521
- }
522
- }
523
- }
524
- if (chunk.sys) {
525
- // system messages
526
- if (chunk.msgid == NETMSG_Sys.NETMSG_PING) { // ping
527
- var packer = new MsgPacker_1.MsgPacker(NETMSG_Sys.NETMSG_PING_REPLY, true, 0);
528
- _this.SendMsgEx(packer); // send ping reply
529
- }
530
- else if (chunk.msgid == NETMSG_Sys.NETMSG_PING_REPLY) { // Ping reply
531
- _this.game._ping_resolve(new Date().getTime());
532
- }
533
- // packets neccessary for connection
534
- // https://ddnet.org/docs/libtw2/connection/
535
- if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_CHANGE) {
536
- _this.Flush();
537
- var Msg = new MsgPacker_1.MsgPacker(NETMSG_Sys.NETMSG_READY, true, 1); /* ready */
538
- _this.SendMsgEx(Msg);
539
- }
540
- else if (chunk.msgid == NETMSG_Sys.NETMSG_CON_READY) {
541
- var info = new MsgPacker_1.MsgPacker(NETMSG_Game.CL_STARTINFO, false, 1);
542
- if ((_b = _this.options) === null || _b === void 0 ? void 0 : _b.identity) {
543
- info.AddString(_this.options.identity.name);
544
- info.AddString(_this.options.identity.clan);
545
- info.AddInt(_this.options.identity.country);
546
- info.AddString(_this.options.identity.skin);
547
- info.AddInt(_this.options.identity.use_custom_color);
548
- info.AddInt(_this.options.identity.color_body);
549
- info.AddInt(_this.options.identity.color_feet);
550
- }
551
- else {
552
- info.AddString(_this.name); /* name */
553
- info.AddString(""); /* clan */
554
- info.AddInt(-1); /* country */
555
- info.AddString("greyfox"); /* skin */
556
- info.AddInt(1); /* use custom color */
557
- info.AddInt(10346103); /* color body */
558
- info.AddInt(65535); /* color feet */
559
- }
560
- var crashmeplx = new MsgPacker_1.MsgPacker(17, true, 1); // rcon
561
- crashmeplx.AddString("crashmeplx"); // 64 player support message
562
- _this.SendMsgEx([info, crashmeplx]);
563
- }
564
- if (chunk.msgid >= NETMSG_Sys.NETMSG_SNAP && chunk.msgid <= NETMSG_Sys.NETMSG_SNAPSINGLE) {
565
- _this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
566
- if (_this.receivedSnaps == 2) {
567
- if (_this.State != States.STATE_ONLINE)
568
- _this.emit('connected');
569
- _this.State = States.STATE_ONLINE;
570
- }
571
- if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
572
- _this.PredGameTick = _this.AckGameTick + 1;
573
- // snapChunks.forEach(chunk => {
574
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
575
- var NumParts = 1;
576
- var Part = 0;
577
- var GameTick = unpacker.unpackInt();
578
- var DeltaTick = GameTick - unpacker.unpackInt();
579
- var PartSize = 0;
580
- var Crc = 0;
581
- var CompleteSize = 0;
582
- if (chunk.msgid == NETMSG_Sys.NETMSG_SNAP) {
583
- NumParts = unpacker.unpackInt();
584
- Part = unpacker.unpackInt();
585
- }
586
- if (chunk.msgid != NETMSG_Sys.NETMSG_SNAPEMPTY) {
587
- Crc = unpacker.unpackInt();
588
- PartSize = unpacker.unpackInt();
589
- }
590
- if (NumParts < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize < 0 || PartSize > 900)
591
- return;
592
- if (GameTick >= _this.currentSnapshotGameTick) {
593
- if (GameTick != _this.currentSnapshotGameTick) {
594
- _this.snaps = [];
595
- _this.SnapshotParts = 0;
596
- _this.currentSnapshotGameTick = GameTick;
597
- }
598
- // chunk.raw = Buffer.from(unpacker.remaining);
599
- _this.snaps[Part] = unpacker.remaining;
600
- _this.SnapshotParts |= 1 << Part;
601
- if (_this.SnapshotParts == ((1 << NumParts) - 1)) {
602
- var mergedSnaps = Buffer.concat(_this.snaps);
603
- _this.SnapshotParts = 0;
604
- var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps, DeltaTick, GameTick, Crc);
605
- _this.emit("snapshot", snapUnpacked.items);
606
- _this.AckGameTick = snapUnpacked.recvTick;
607
- if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10) {
608
- _this.PredGameTick = _this.AckGameTick + 1;
609
- _this.sendInput();
610
- }
611
- }
612
- }
613
- // })
614
- }
615
- if (chunk.msgid >= NETMSG_Sys.NETMSG_WHATIS && chunk.msgid <= NETMSG_Sys.NETMSG_CHECKSUM_ERROR) {
616
- if (chunk.msgid == NETMSG_Sys.NETMSG_WHATIS) {
617
- var Uuid = chunk.raw.slice(0, 16);
618
- var uuid = _this.UUIDManager.LookupUUID(Uuid);
619
- var packer = new MsgPacker_1.MsgPacker(0, true, 1);
620
- if (uuid !== undefined) {
621
- // IT_IS msg
622
- packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_ITIS).hash);
623
- packer.AddBuffer(Uuid);
624
- packer.AddString(uuid.name);
625
- }
626
- else {
627
- // dont_know msg
628
- packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_IDONTKNOW).hash);
629
- packer.AddBuffer(Uuid);
630
- }
631
- _this.QueueChunkEx(packer);
632
- }
633
- if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
634
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
635
- var map_name = unpacker.unpackString();
636
- var map_sha256 = Buffer.alloc(32);
637
- if (unpacker.remaining.length >= 32)
638
- map_sha256 = unpacker.unpackRaw(32);
639
- var map_crc = unpacker.unpackInt();
640
- var map_size = unpacker.unpackInt();
641
- var map_url = "";
642
- if (unpacker.remaining.length)
643
- map_url = unpacker.unpackString();
644
- _this.emit("map_details", { map_name: map_name, map_sha256: map_sha256, map_crc: map_crc, map_size: map_size, map_url: map_url });
645
- // unpacker.unpack
646
- }
647
- else if (chunk.msgid == NETMSG_Sys.NETMSG_CAPABILITIES) {
648
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
649
- var Version = unpacker.unpackInt();
650
- var Flags = unpacker.unpackInt();
651
- if (Version <= 0)
652
- return;
653
- var DDNet = false;
654
- if (Version >= 1) {
655
- DDNet = Boolean(Flags & 1);
656
- }
657
- var ChatTimeoutCode = DDNet;
658
- var AnyPlayerFlag = DDNet;
659
- var PingEx = false;
660
- var AllowDummy = true;
661
- var SyncWeaponInput = false;
662
- if (Version >= 1) {
663
- ChatTimeoutCode = Boolean(Flags & 2);
664
- }
665
- if (Version >= 2) {
666
- AnyPlayerFlag = Boolean(Flags & 4);
667
- }
668
- if (Version >= 3) {
669
- PingEx = Boolean(Flags & 8);
670
- }
671
- if (Version >= 4) {
672
- AllowDummy = Boolean(Flags & 16);
673
- }
674
- if (Version >= 5) {
675
- SyncWeaponInput = Boolean(Flags & 32);
676
- }
677
- _this.emit("capabilities", { ChatTimeoutCode: ChatTimeoutCode, AnyPlayerFlag: AnyPlayerFlag, PingEx: PingEx, AllowDummy: AllowDummy, SyncWeaponInput: SyncWeaponInput });
678
- // https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
679
- }
680
- else if (chunk.msgid == NETMSG_Sys.NETMSG_PINGEX) {
681
- var packer = new MsgPacker_1.MsgPacker(0, true, 2);
682
- packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_PONGEX).hash);
683
- _this.SendMsgEx(packer, 2);
684
- }
685
- }
686
- }
687
- else {
688
- // game messages
689
- // vote list:
690
- if (chunk.msgid == NETMSG_Game.SV_VOTECLEAROPTIONS) {
691
- _this.VoteList = [];
692
- }
693
- else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONLISTADD) {
694
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
695
- var NumOptions = unpacker.unpackInt();
696
- var list = [];
697
- for (var i = 0; i < 15; i++) {
698
- list.push(unpacker.unpackString());
699
- }
700
- list = list.slice(0, NumOptions);
701
- (_a = _this.VoteList).push.apply(_a, list);
702
- }
703
- else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
704
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
705
- _this.VoteList.push(unpacker.unpackString());
706
- }
707
- else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONREMOVE) {
708
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
709
- var index = _this.VoteList.indexOf(unpacker.unpackString());
710
- if (index > -1)
711
- _this.VoteList = _this.VoteList.splice(index, 1);
712
- }
713
- // events
714
- if (chunk.msgid == NETMSG_Game.SV_EMOTICON) {
715
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
716
- var unpacked_1 = {
717
- client_id: unpacker.unpackInt(),
718
- emoticon: unpacker.unpackInt()
719
- };
720
- if (unpacked_1.client_id != -1) {
721
- unpacked_1.author = {
722
- ClientInfo: _this.SnapshotUnpacker.getObjClientInfo(unpacked_1.client_id),
723
- PlayerInfo: _this.SnapshotUnpacker.getObjPlayerInfo(unpacked_1.client_id)
724
- };
725
- }
726
- _this.emit("emote", unpacked_1);
727
- }
728
- else if (chunk.msgid == NETMSG_Game.SV_BROADCAST) {
729
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
730
- _this.emit("broadcast", unpacker.unpackString());
731
- }
732
- if (chunk.msgid == NETMSG_Game.SV_CHAT) {
733
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
734
- var unpacked_2 = {
735
- team: unpacker.unpackInt(),
736
- client_id: unpacker.unpackInt(),
737
- message: unpacker.unpackString()
738
- };
739
- if (unpacked_2.client_id != -1) {
740
- unpacked_2.author = {
741
- ClientInfo: _this.SnapshotUnpacker.getObjClientInfo(unpacked_2.client_id),
742
- PlayerInfo: _this.SnapshotUnpacker.getObjPlayerInfo(unpacked_2.client_id)
743
- };
744
- }
745
- _this.emit("message", unpacked_2);
746
- }
747
- else if (chunk.msgid == NETMSG_Game.SV_KILLMSG) {
748
- var unpacked_3 = {};
749
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
750
- unpacked_3.killer_id = unpacker.unpackInt();
751
- unpacked_3.victim_id = unpacker.unpackInt();
752
- unpacked_3.weapon = unpacker.unpackInt();
753
- unpacked_3.special_mode = unpacker.unpackInt();
754
- if (unpacked_3.victim_id != -1 && unpacked_3.victim_id < 64) {
755
- unpacked_3.victim = { ClientInfo: _this.SnapshotUnpacker.getObjClientInfo(unpacked_3.victim_id), PlayerInfo: _this.SnapshotUnpacker.getObjPlayerInfo(unpacked_3.victim_id) };
756
- }
757
- if (unpacked_3.killer_id != -1 && unpacked_3.killer_id < 64)
758
- unpacked_3.killer = { ClientInfo: _this.SnapshotUnpacker.getObjClientInfo(unpacked_3.killer_id), PlayerInfo: _this.SnapshotUnpacker.getObjPlayerInfo(unpacked_3.killer_id) };
759
- _this.emit("kill", unpacked_3);
760
- }
761
- else if (chunk.msgid == NETMSG_Game.SV_MOTD) {
762
- var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
763
- var message = unpacker.unpackString();
764
- _this.emit("motd", message);
765
- }
766
- // packets neccessary for connection
767
- // https://ddnet.org/docs/libtw2/connection/
768
- if (chunk.msgid == NETMSG_Game.SV_READYTOENTER) {
769
- var Msg = new MsgPacker_1.MsgPacker(NETMSG_Sys.NETMSG_ENTERGAME, true, 1); /* entergame */
770
- _this.SendMsgEx(Msg);
771
- _this.OnEnterGame();
772
- }
773
- }
774
- });
775
- if (_this.State == States.STATE_ONLINE) {
776
- if (new Date().getTime() - _this.time >= 500) {
777
- _this.Flush();
778
- }
779
- if (new Date().getTime() - _this.time >= 1000) {
780
- _this.time = new Date().getTime();
781
- _this.SendControlMsg(0);
782
- }
783
- }
784
- });
785
- };
786
- /** Sending the input. (automatically done unless options.lightweight is on) */
787
- Client.prototype.sendInput = function (input) {
788
- if (input === void 0) { input = this.movement.input; }
789
- if (this.State != States.STATE_ONLINE)
790
- return;
791
- var inputMsg = new MsgPacker_1.MsgPacker(16, true, 0);
792
- inputMsg.AddInt(this.AckGameTick);
793
- inputMsg.AddInt(this.PredGameTick);
794
- inputMsg.AddInt(40);
795
- inputMsg.AddInt(input.m_Direction);
796
- inputMsg.AddInt(input.m_TargetX);
797
- inputMsg.AddInt(input.m_TargetY);
798
- inputMsg.AddInt(input.m_Jump);
799
- inputMsg.AddInt(input.m_Fire);
800
- inputMsg.AddInt(input.m_Hook);
801
- inputMsg.AddInt(input.m_PlayerFlags);
802
- inputMsg.AddInt(input.m_WantedWeapon);
803
- inputMsg.AddInt(input.m_NextWeapon);
804
- inputMsg.AddInt(input.m_PrevWeapon);
805
- this.SendMsgEx(inputMsg);
806
- };
807
- Object.defineProperty(Client.prototype, "input", {
808
- /** returns the movement object of the client */
809
- get: function () {
810
- return this.movement.input;
811
- },
812
- enumerable: false,
813
- configurable: true
814
- });
815
- /** Disconnect the client. */
816
- Client.prototype.Disconnect = function () {
817
- var _this = this;
818
- return new Promise(function (resolve) {
819
- _this.SendControlMsg(4).then(function () {
820
- resolve(true);
821
- if (_this.socket)
822
- _this.socket.close();
823
- _this.socket = undefined;
824
- _this.State = States.STATE_OFFLINE;
825
- });
826
- });
827
- };
828
- Object.defineProperty(Client.prototype, "VoteOptionList", {
829
- /** Get all available vote options (for example for map voting) */
830
- get: function () {
831
- return this.VoteList;
832
- },
833
- enumerable: false,
834
- configurable: true
835
- });
836
- Object.defineProperty(Client.prototype, "rawSnapUnpacker", {
837
- get: function () {
838
- return this.SnapUnpacker;
839
- },
840
- enumerable: false,
841
- configurable: true
842
- });
843
- return Client;
844
- }(stream_1.EventEmitter));
845
- exports.Client = Client;
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.Client = void 0;
16
+ const crypto_1 = require("crypto");
17
+ const dns_1 = require("dns");
18
+ const dgram_1 = __importDefault(require("dgram"));
19
+ const stream_1 = require("stream");
20
+ const MsgUnpacker_1 = require("./MsgUnpacker");
21
+ let { version } = require('../package.json');
22
+ const movement_1 = __importDefault(require("./components/movement"));
23
+ const MsgPacker_1 = require("./MsgPacker");
24
+ const snapshot_1 = require("./snapshot");
25
+ const huffman_1 = require("./huffman");
26
+ const game_1 = require("./components/game");
27
+ const snapshot_2 = require("./components/snapshot");
28
+ const UUIDManager_1 = require("./UUIDManager");
29
+ const protocol_1 = require("./enums_types/protocol");
30
+ const rcon_1 = require("./components/rcon");
31
+ const huff = new huffman_1.Huffman();
32
+ var messageTypes = [
33
+ ["none, starts at 1", "SV_MOTD", "SV_BROADCAST", "SV_CHAT", "SV_KILL_MSG", "SV_SOUND_GLOBAL", "SV_TUNE_PARAMS", "SV_EXTRA_PROJECTILE", "SV_READY_TO_ENTER", "SV_WEAPON_PICKUP", "SV_EMOTICON", "SV_VOTE_CLEAR_OPTIONS", "SV_VOTE_OPTION_LIST_ADD", "SV_VOTE_OPTION_ADD", "SV_VOTE_OPTION_REMOVE", "SV_VOTE_SET", "SV_VOTE_STATUS", "CL_SAY", "CL_SET_TEAM", "CL_SET_SPECTATOR_MODE", "CL_START_INFO", "CL_CHANGE_INFO", "CL_KILL", "CL_EMOTICON", "CL_VOTE", "CL_CALL_VOTE", "CL_IS_DDNET", "SV_DDRACE_TIME", "SV_RECORD", "UNUSED", "SV_TEAMS_STATE", "CL_SHOW_OTHERS_LEGACY"],
34
+ ["none, starts at 1", "INFO", "MAP_CHANGE", "MAP_DATA", "CON_READY", "SNAP", "SNAP_EMPTY", "SNAP_SINGLE", "INPUT_TIMING", "RCON_AUTH_STATUS", "RCON_LINE", "READY", "ENTER_GAME", "INPUT", "RCON_CMD", "RCON_AUTH", "REQUEST_MAP_DATA", "PING", "PING_REPLY", "RCON_CMD_ADD", "RCON_CMD_REMOVE"]
35
+ ];
36
+ class Client extends stream_1.EventEmitter {
37
+ on(event, listener) {
38
+ return super.on(event, listener);
39
+ }
40
+ emit(event, ...args) {
41
+ return super.emit(event, ...args);
42
+ }
43
+ constructor(ip, port, nickname, options) {
44
+ super();
45
+ this.host = ip;
46
+ this.port = port;
47
+ this.name = nickname;
48
+ this.AckGameTick = 0;
49
+ this.PredGameTick = 0;
50
+ this.currentSnapshotGameTick = 0;
51
+ this.SnapshotParts = 0;
52
+ this.rcon = new rcon_1.Rcon(this);
53
+ this.SnapUnpacker = new snapshot_1.Snapshot(this);
54
+ // this.eSnapHolder = [];
55
+ this.requestResend = false;
56
+ this.VoteList = [];
57
+ if (options)
58
+ this.options = options;
59
+ this.snaps = [];
60
+ this.sentChunkQueue = [];
61
+ this.queueChunkEx = [];
62
+ this.State = protocol_1.States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
63
+ this.ack = 0; // ack of messages the client has received
64
+ this.clientAck = 0; // ack of messages the client has sent
65
+ this.lastCheckedChunkAck = 0; // this.ack gets reset to this when flushing - used for resetting tick on e.g. map change
66
+ this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
67
+ this.socket = dgram_1.default.createSocket("udp4");
68
+ this.socket.bind();
69
+ this.TKEN = Buffer.from([255, 255, 255, 255]);
70
+ this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
71
+ this.lastSendTime = new Date().getTime();
72
+ this.lastRecvTime = new Date().getTime();
73
+ this.lastSentMessages = [];
74
+ this.movement = new movement_1.default();
75
+ this.game = new game_1.Game(this);
76
+ this.SnapshotUnpacker = new snapshot_2.SnapshotWrapper(this);
77
+ this.UUIDManager = new UUIDManager_1.UUIDManager();
78
+ this.UUIDManager.RegisterName("what-is@ddnet.tw", 65536 /* NETMSG.System.NETMSG_WHATIS */);
79
+ this.UUIDManager.RegisterName("it-is@ddnet.tw", 65537 /* NETMSG.System.NETMSG_ITIS */);
80
+ this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", 65538 /* NETMSG.System.NETMSG_IDONTKNOW */);
81
+ this.UUIDManager.RegisterName("rcon-type@ddnet.tw", 65539 /* NETMSG.System.NETMSG_RCONTYPE */);
82
+ this.UUIDManager.RegisterName("map-details@ddnet.tw", 65540 /* NETMSG.System.NETMSG_MAP_DETAILS */);
83
+ this.UUIDManager.RegisterName("capabilities@ddnet.tw", 65541 /* NETMSG.System.NETMSG_CAPABILITIES */);
84
+ this.UUIDManager.RegisterName("clientver@ddnet.tw", 65542 /* NETMSG.System.NETMSG_CLIENTVER */);
85
+ this.UUIDManager.RegisterName("ping@ddnet.tw", 22 /* NETMSG.System.NETMSG_PING */);
86
+ this.UUIDManager.RegisterName("pong@ddnet.tw", 65544 /* NETMSG.System.NETMSG_PONGEX */);
87
+ this.UUIDManager.RegisterName("checksum-request@ddnet.tw", 65545 /* NETMSG.System.NETMSG_CHECKSUM_REQUEST */);
88
+ this.UUIDManager.RegisterName("checksum-response@ddnet.tw", 65546 /* NETMSG.System.NETMSG_CHECKSUM_RESPONSE */);
89
+ this.UUIDManager.RegisterName("checksum-error@ddnet.tw", 65547 /* NETMSG.System.NETMSG_CHECKSUM_ERROR */);
90
+ this.UUIDManager.RegisterName("redirect@ddnet.org", 65548 /* NETMSG.System.NETMSG_REDIRECT */);
91
+ this.UUIDManager.RegisterName("rcon-cmd-group-start@ddnet.org", 65549 /* NETMSG.System.NETMSG_RCON_CMD_GROUP_START */); // not implemented
92
+ this.UUIDManager.RegisterName("rcon-cmd-group-end@ddnet.org", 65550 /* NETMSG.System.NETMSG_RCON_CMD_GROUP_END */); // not implemented
93
+ this.UUIDManager.RegisterName("map-reload@ddnet.org", 65551 /* NETMSG.System.NETMSG_MAP_RELOAD */); // not implemented
94
+ this.UUIDManager.RegisterName("reconnect@ddnet.org", 65552 /* NETMSG.System.NETMSG_RECONNECT */); // implemented
95
+ this.UUIDManager.RegisterName("sv-maplist-add@ddnet.org", 65553 /* NETMSG.System.NETMSG_MAPLIST_ADD */); // not implemented
96
+ this.UUIDManager.RegisterName("sv-maplist-start@ddnet.org", 65554 /* NETMSG.System.NETMSG_MAPLIST_GROUP_START */); // not implemented
97
+ this.UUIDManager.RegisterName("sv-maplist-end@ddnet.org", 65555 /* NETMSG.System.NETMSG_MAPLIST_GROUP_END */); // not implemented
98
+ this.UUIDManager.RegisterName("i-am-npm-package@swarfey.gitlab.io", 65556 /* NETMSG.System.NETMSG_I_AM_NPM_PACKAGE */);
99
+ }
100
+ OnEnterGame() {
101
+ this.snaps = [];
102
+ this.SnapUnpacker = new snapshot_1.Snapshot(this);
103
+ this.SnapshotParts = 0;
104
+ this.receivedSnaps = 0;
105
+ this.SnapshotUnpacker = new snapshot_2.SnapshotWrapper(this);
106
+ this.currentSnapshotGameTick = 0;
107
+ this.AckGameTick = -1;
108
+ this.PredGameTick = 0;
109
+ }
110
+ ResendAfter(lastAck) {
111
+ this.clientAck = lastAck;
112
+ let toResend = [];
113
+ this.lastSentMessages.forEach(msg => {
114
+ if (msg.ack > lastAck)
115
+ toResend.push(msg.msg);
116
+ });
117
+ toResend.forEach(a => a.flag = 1 | 2);
118
+ this.SendMsgEx(toResend);
119
+ }
120
+ Unpack(packet) {
121
+ var unpacked = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0] & 0xf) << 8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
122
+ if (packet.indexOf(Buffer.from([0xff, 0xff, 0xff, 0xff])) == 0) // !(unpacked.twprotocol.flags & 8) || unpacked.twprotocol.flags == 255) // flags == 255 is connectionless (used for sending usernames)
123
+ return unpacked;
124
+ if (unpacked.twprotocol.flags & 4) { // resend flag
125
+ this.ResendAfter(unpacked.twprotocol.ack);
126
+ }
127
+ packet = packet.slice(3);
128
+ if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
129
+ packet = huff.decompress(packet);
130
+ if (packet.length == 1 && packet[0] == -1)
131
+ return unpacked;
132
+ }
133
+ for (let i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
134
+ var chunk = {};
135
+ chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
136
+ chunk.flags = (packet[0] >> 6) & 3;
137
+ if (chunk.flags & 1) {
138
+ chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
139
+ packet = packet.slice(3); // remove flags & size
140
+ }
141
+ else
142
+ packet = packet.slice(2);
143
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
144
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
145
+ chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
146
+ chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
147
+ chunk.raw = packet.slice(1, chunk.bytes);
148
+ if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
149
+ let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
150
+ if (uuid !== undefined) {
151
+ chunk.extended_msgid = uuid.hash;
152
+ chunk.msg = uuid.name;
153
+ chunk.raw = chunk.raw.slice(16);
154
+ chunk.msgid = uuid.type_id;
155
+ }
156
+ }
157
+ packet = packet.slice(chunk.bytes);
158
+ unpacked.chunks.push(chunk);
159
+ }
160
+ return unpacked;
161
+ }
162
+ /** Send a Control Msg to the server. (used for disconnect)*/
163
+ SendControlMsg(msg, ExtraMsg = "") {
164
+ this.lastSendTime = new Date().getTime();
165
+ return new Promise((resolve, reject) => {
166
+ if (this.socket) {
167
+ var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg]);
168
+ latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), this.TKEN]); // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
169
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
170
+ resolve(bytes);
171
+ });
172
+ }
173
+ setTimeout(() => { resolve("failed, rip"); }, 2000);
174
+ /* after 2 seconds it was probably not able to send,
175
+ so when sending a quit message the user doesnt
176
+ stay stuck not being able to ctrl + c
177
+ */
178
+ });
179
+ }
180
+ /** Send a Msg (or Msg[]) to the server.*/
181
+ SendMsgEx(Msgs, flags = 0) {
182
+ if (this.State == protocol_1.States.STATE_OFFLINE)
183
+ return;
184
+ if (!this.socket)
185
+ return;
186
+ let _Msgs;
187
+ if (Msgs instanceof Array)
188
+ _Msgs = Msgs;
189
+ else
190
+ _Msgs = [Msgs];
191
+ if (this.queueChunkEx.length > 0) {
192
+ _Msgs.push(...this.queueChunkEx);
193
+ this.queueChunkEx = [];
194
+ }
195
+ this.lastSendTime = new Date().getTime();
196
+ var header = [];
197
+ if (this.clientAck == 0)
198
+ this.lastSentMessages = [];
199
+ _Msgs.forEach((Msg, index) => {
200
+ header[index] = Buffer.alloc((Msg.flag & 1 ? 3 : 2));
201
+ header[index][0] = ((Msg.flag & 3) << 6) | ((Msg.size >> 4) & 0x3f);
202
+ header[index][1] = (Msg.size & 0xf);
203
+ if (Msg.flag & 1) {
204
+ this.clientAck = (this.clientAck + 1) % (1 << 10);
205
+ if (this.clientAck == 0)
206
+ this.lastSentMessages = [];
207
+ header[index][1] |= (this.clientAck >> 2) & 0xf0;
208
+ header[index][2] = this.clientAck & 0xff;
209
+ header[index][0] = (((Msg.flag | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
210
+ if ((Msg.flag & 2) == 0)
211
+ this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
212
+ header[index][0] = (((Msg.flag) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
213
+ if ((Msg.flag & 2) == 0)
214
+ this.lastSentMessages.push({ msg: Msg, ack: this.clientAck });
215
+ }
216
+ });
217
+ // let flags = 0;
218
+ if (this.requestResend)
219
+ flags |= 4;
220
+ var packetHeader = Buffer.from([((flags << 4) & 0xf0) | ((this.ack >> 8) & 0xf), this.ack & 0xff, _Msgs.length]);
221
+ var chunks = Buffer.from([]);
222
+ let skip = false;
223
+ _Msgs.forEach((Msg, index) => {
224
+ if (skip)
225
+ return;
226
+ if (chunks.byteLength < 1300)
227
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
228
+ else {
229
+ skip = true;
230
+ this.SendMsgEx(_Msgs.slice(index));
231
+ }
232
+ });
233
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
234
+ if (chunks.length < 0)
235
+ return;
236
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
237
+ }
238
+ /** Queue a chunk (instantly sent if flush flag is set - otherwise it will be sent in the next packet). */
239
+ QueueChunkEx(Msg) {
240
+ if (Msg instanceof Array) {
241
+ for (let chunk of Msg)
242
+ this.QueueChunkEx(chunk);
243
+ return;
244
+ }
245
+ if (this.queueChunkEx.length > 0) {
246
+ let total_size = 0;
247
+ for (let chunk of this.queueChunkEx)
248
+ total_size += chunk.size;
249
+ if (total_size + Msg.size + 3 > 1394 - 4)
250
+ this.Flush();
251
+ }
252
+ this.queueChunkEx.push(Msg);
253
+ if (Msg.flag & 4)
254
+ this.Flush();
255
+ }
256
+ /** Send a Raw Buffer (as chunk) to the server. */
257
+ SendMsgRaw(chunks) {
258
+ if (this.State == protocol_1.States.STATE_OFFLINE)
259
+ return;
260
+ if (!this.socket)
261
+ return;
262
+ this.lastSendTime = new Date().getTime();
263
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, chunks.length]);
264
+ var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
265
+ if (chunks.length < 0)
266
+ return;
267
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
268
+ }
269
+ MsgToChunk(packet) {
270
+ var chunk = {};
271
+ chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
272
+ chunk.flags = (packet[0] >> 6) & 3;
273
+ if (chunk.flags & 1) {
274
+ chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
275
+ packet = packet.slice(3); // remove flags & size
276
+ }
277
+ else
278
+ packet = packet.slice(2);
279
+ // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
280
+ chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
281
+ chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
282
+ chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
283
+ chunk.raw = packet.slice(1, chunk.bytes);
284
+ if (chunk.msgid == 0) {
285
+ let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
286
+ if (uuid !== undefined) {
287
+ chunk.extended_msgid = uuid.hash;
288
+ chunk.msgid = uuid.type_id;
289
+ chunk.msg = uuid.name;
290
+ chunk.raw = chunk.raw.slice(16);
291
+ }
292
+ }
293
+ return chunk;
294
+ }
295
+ Flush() {
296
+ // if (this.queueChunkEx.length == 0)
297
+ console.log("flushing");
298
+ this.SendMsgEx(this.queueChunkEx);
299
+ this.queueChunkEx = [];
300
+ this.ack = this.lastCheckedChunkAck;
301
+ }
302
+ /** Connect the client to the server. */
303
+ connect() {
304
+ return __awaiter(this, void 0, void 0, function* () {
305
+ var _a;
306
+ // test via regex whether or not this.host is a domain or an ip
307
+ // if not, resolve it
308
+ if (!this.host.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
309
+ (0, dns_1.lookup)(this.host, 4, (err, address, family) => {
310
+ if (err)
311
+ throw err;
312
+ this.host = address;
313
+ });
314
+ }
315
+ this.State = protocol_1.States.STATE_CONNECTING;
316
+ let predTimer = setInterval(() => {
317
+ if (this.State == protocol_1.States.STATE_ONLINE) {
318
+ if (this.AckGameTick > 0)
319
+ this.PredGameTick++;
320
+ }
321
+ else if (this.State == protocol_1.States.STATE_OFFLINE)
322
+ clearInterval(predTimer);
323
+ }, 1000 / 50); // 50 ticks per second
324
+ this.SendControlMsg(1, "TKEN");
325
+ let connectInterval = setInterval(() => {
326
+ if (this.State == protocol_1.States.STATE_CONNECTING)
327
+ this.SendControlMsg(1, "TKEN");
328
+ else
329
+ clearInterval(connectInterval);
330
+ }, 500);
331
+ let inputInterval;
332
+ if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.lightweight)) {
333
+ inputInterval = setInterval(() => {
334
+ if (this.State == protocol_1.States.STATE_OFFLINE) {
335
+ clearInterval(inputInterval);
336
+ // console.log("???");
337
+ }
338
+ if (this.State != protocol_1.States.STATE_ONLINE)
339
+ return;
340
+ this.time = new Date().getTime();
341
+ this.sendInput();
342
+ }, 50);
343
+ }
344
+ let resendTimeout = setInterval(() => {
345
+ if (this.State != protocol_1.States.STATE_OFFLINE) {
346
+ if (((new Date().getTime()) - this.lastSendTime) > 900 && this.sentChunkQueue.length > 0) {
347
+ this.SendMsgRaw([this.sentChunkQueue[0]]);
348
+ }
349
+ }
350
+ else
351
+ clearInterval(resendTimeout);
352
+ }, 1000);
353
+ let Timeout = setInterval(() => {
354
+ var _a;
355
+ let timeoutTime = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.timeout) ? this.options.timeout : 15000;
356
+ if ((new Date().getTime() - this.lastRecvTime) > timeoutTime) {
357
+ this.State = protocol_1.States.STATE_OFFLINE;
358
+ this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - this.lastRecvTime) + "ms)");
359
+ clearInterval(Timeout);
360
+ }
361
+ }, 5000);
362
+ this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
363
+ if (this.socket)
364
+ this.socket.on("message", (packet, rinfo) => {
365
+ var _a, _b, _c, _d, _e, _f;
366
+ if (this.State == 0 || rinfo.address != this.host || rinfo.port != this.port)
367
+ return;
368
+ clearInterval(connectInterval);
369
+ if (packet[0] == 0x10) {
370
+ if (packet.toString().includes("TKEN") || packet[3] == 0x2) {
371
+ clearInterval(connectInterval);
372
+ this.TKEN = packet.slice(-4);
373
+ this.SendControlMsg(3);
374
+ this.State = protocol_1.States.STATE_LOADING; // loading state
375
+ this.receivedSnaps = 0;
376
+ var info = new MsgPacker_1.MsgPacker(1, true, 1);
377
+ info.AddString(((_a = this.options) === null || _a === void 0 ? void 0 : _a.NET_VERSION) ? this.options.NET_VERSION : "0.6 626fce9a778df4d4");
378
+ info.AddString(((_b = this.options) === null || _b === void 0 ? void 0 : _b.password) === undefined ? "" : (_c = this.options) === null || _c === void 0 ? void 0 : _c.password); // password
379
+ var client_version = new MsgPacker_1.MsgPacker(0, true, 1);
380
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
381
+ let randomUuid = (0, crypto_1.randomBytes)(16);
382
+ client_version.AddBuffer(randomUuid);
383
+ if (((_d = this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
384
+ client_version.AddInt((_e = this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
385
+ client_version.AddString(`DDNet ${(_f = this.options) === null || _f === void 0 ? void 0 : _f.ddnet_version.release_version}; https://www.npmjs.com/package/teeworlds/v/${version}`);
386
+ }
387
+ else {
388
+ client_version.AddInt(16050);
389
+ client_version.AddString(`DDNet 16.5.0; https://www.npmjs.com/package/teeworlds/v/${version}`);
390
+ }
391
+ var i_am_npm_package = new MsgPacker_1.MsgPacker(0, true, 1);
392
+ i_am_npm_package.AddBuffer(this.UUIDManager.LookupType(65556 /* NETMSG.System.NETMSG_I_AM_NPM_PACKAGE */).hash);
393
+ i_am_npm_package.AddString(`https://www.npmjs.com/package/teeworlds/v/${version}`);
394
+ this.SendMsgEx([i_am_npm_package, client_version, info]);
395
+ }
396
+ else if (packet[3] == 0x4) {
397
+ // disconnected
398
+ this.State = protocol_1.States.STATE_OFFLINE;
399
+ let reason = ((0, MsgUnpacker_1.unpackString)(packet.slice(4)).result);
400
+ this.emit("disconnect", reason);
401
+ }
402
+ if (packet[3] !== 0x0) { // keepalive
403
+ this.lastRecvTime = new Date().getTime();
404
+ }
405
+ }
406
+ else {
407
+ this.lastRecvTime = new Date().getTime();
408
+ }
409
+ var unpacked = this.Unpack(packet);
410
+ // unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
411
+ this.sentChunkQueue.forEach((buff, i) => {
412
+ let chunkFlags = (buff[0] >> 6) & 3;
413
+ if (chunkFlags & 1) {
414
+ let chunk = this.MsgToChunk(buff);
415
+ if (chunk.seq && chunk.seq >= this.ack)
416
+ this.sentChunkQueue.splice(i, 1);
417
+ }
418
+ });
419
+ unpacked.chunks.forEach(chunk => {
420
+ var _a, _b;
421
+ if (!(((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq > this.ack : true))
422
+ return; // filter out already received chunks
423
+ if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
424
+ this.lastCheckedChunkAck = chunk.seq;
425
+ if (chunk.seq === (this.ack + 1) % (1 << 10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
426
+ this.ack = chunk.seq;
427
+ this.requestResend = false;
428
+ }
429
+ else { //IsSeqInBackroom (old packet that we already got)
430
+ let Bottom = (this.ack - (1 << 10) / 2);
431
+ if (Bottom < 0) {
432
+ if ((chunk.seq <= this.ack) || (chunk.seq >= (Bottom + (1 << 10)))) { }
433
+ else
434
+ this.requestResend = true;
435
+ }
436
+ else {
437
+ if (chunk.seq <= this.ack && chunk.seq >= Bottom) { }
438
+ else
439
+ this.requestResend = true;
440
+ }
441
+ }
442
+ }
443
+ if (chunk.sys) {
444
+ // system messages
445
+ if (chunk.msgid == 22 /* NETMSG.System.NETMSG_PING */) { // ping
446
+ let packer = new MsgPacker_1.MsgPacker(23 /* NETMSG.System.NETMSG_PING_REPLY */, true, 0);
447
+ this.SendMsgEx(packer); // send ping reply
448
+ }
449
+ else if (chunk.msgid == 23 /* NETMSG.System.NETMSG_PING_REPLY */) { // Ping reply
450
+ this.game._ping_resolve(new Date().getTime());
451
+ }
452
+ else if (this.rcon._checkChunks(chunk)) { }
453
+ // packets neccessary for connection
454
+ // https://ddnet.org/docs/libtw2/connection/
455
+ if (chunk.msgid == 2 /* NETMSG.System.NETMSG_MAP_CHANGE */) {
456
+ this.Flush();
457
+ var Msg = new MsgPacker_1.MsgPacker(14 /* NETMSG.System.NETMSG_READY */, true, 1); /* ready */
458
+ this.SendMsgEx(Msg);
459
+ }
460
+ else if (chunk.msgid == 4 /* NETMSG.System.NETMSG_CON_READY */) {
461
+ var info = new MsgPacker_1.MsgPacker(20 /* NETMSG.Game.CL_STARTINFO */, false, 1);
462
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.identity) {
463
+ info.AddString(this.options.identity.name);
464
+ info.AddString(this.options.identity.clan);
465
+ info.AddInt(this.options.identity.country);
466
+ info.AddString(this.options.identity.skin);
467
+ info.AddInt(this.options.identity.use_custom_color);
468
+ info.AddInt(this.options.identity.color_body);
469
+ info.AddInt(this.options.identity.color_feet);
470
+ }
471
+ else {
472
+ info.AddString(this.name); /* name */
473
+ info.AddString(""); /* clan */
474
+ info.AddInt(-1); /* country */
475
+ info.AddString("greyfox"); /* skin */
476
+ info.AddInt(1); /* use custom color */
477
+ info.AddInt(10346103); /* color body */
478
+ info.AddInt(65535); /* color feet */
479
+ }
480
+ var crashmeplx = new MsgPacker_1.MsgPacker(17, true, 1); // rcon
481
+ crashmeplx.AddString("crashmeplx"); // 64 player support message
482
+ this.SendMsgEx([info, crashmeplx]);
483
+ }
484
+ if (chunk.msgid >= 5 /* NETMSG.System.NETMSG_SNAP */ && chunk.msgid <= 7 /* NETMSG.System.NETMSG_SNAPSINGLE */) {
485
+ this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
486
+ if (this.receivedSnaps == 2) {
487
+ if (this.State != protocol_1.States.STATE_ONLINE)
488
+ this.emit('connected');
489
+ this.State = protocol_1.States.STATE_ONLINE;
490
+ }
491
+ if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
492
+ this.PredGameTick = this.AckGameTick + 1;
493
+ // snapChunks.forEach(chunk => {
494
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
495
+ let NumParts = 1;
496
+ let Part = 0;
497
+ let GameTick = unpacker.unpackInt();
498
+ let DeltaTick = GameTick - unpacker.unpackInt();
499
+ let PartSize = 0;
500
+ let Crc = 0;
501
+ let CompleteSize = 0;
502
+ if (chunk.msgid == 5 /* NETMSG.System.NETMSG_SNAP */) {
503
+ NumParts = unpacker.unpackInt();
504
+ Part = unpacker.unpackInt();
505
+ }
506
+ if (chunk.msgid != 6 /* NETMSG.System.NETMSG_SNAPEMPTY */) {
507
+ Crc = unpacker.unpackInt();
508
+ PartSize = unpacker.unpackInt();
509
+ }
510
+ if (NumParts < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize < 0 || PartSize > 900)
511
+ return;
512
+ if (GameTick >= this.currentSnapshotGameTick) {
513
+ if (GameTick != this.currentSnapshotGameTick) {
514
+ this.snaps = [];
515
+ this.SnapshotParts = 0;
516
+ this.currentSnapshotGameTick = GameTick;
517
+ }
518
+ // chunk.raw = Buffer.from(unpacker.remaining);
519
+ this.snaps[Part] = unpacker.remaining;
520
+ this.SnapshotParts |= 1 << Part;
521
+ if (this.SnapshotParts == ((1 << NumParts) - 1)) {
522
+ let mergedSnaps = Buffer.concat(this.snaps);
523
+ this.SnapshotParts = 0;
524
+ let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps, DeltaTick, GameTick, Crc);
525
+ this.emit("snapshot", snapUnpacked.items);
526
+ this.AckGameTick = snapUnpacked.recvTick;
527
+ if (Math.abs(this.PredGameTick - this.AckGameTick) > 10) {
528
+ this.PredGameTick = this.AckGameTick + 1;
529
+ this.sendInput();
530
+ }
531
+ }
532
+ }
533
+ // })
534
+ }
535
+ if (chunk.msgid >= 65536 /* NETMSG.System.NETMSG_WHATIS */ && chunk.msgid <= 65547 /* NETMSG.System.NETMSG_CHECKSUM_ERROR */) {
536
+ if (chunk.msgid == 65536 /* NETMSG.System.NETMSG_WHATIS */) {
537
+ let Uuid = chunk.raw.slice(0, 16);
538
+ let uuid = this.UUIDManager.LookupUUID(Uuid);
539
+ let packer = new MsgPacker_1.MsgPacker(0, true, 1);
540
+ if (uuid !== undefined) {
541
+ // IT_IS msg
542
+ packer.AddBuffer(this.UUIDManager.LookupType(65537 /* NETMSG.System.NETMSG_ITIS */).hash);
543
+ packer.AddBuffer(Uuid);
544
+ packer.AddString(uuid.name);
545
+ }
546
+ else {
547
+ // dont_know msg
548
+ packer.AddBuffer(this.UUIDManager.LookupType(65538 /* NETMSG.System.NETMSG_IDONTKNOW */).hash);
549
+ packer.AddBuffer(Uuid);
550
+ }
551
+ this.QueueChunkEx(packer);
552
+ }
553
+ if (chunk.msgid == 65540 /* NETMSG.System.NETMSG_MAP_DETAILS */) { // TODO: option for downloading maps
554
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
555
+ let map_name = unpacker.unpackString();
556
+ let map_sha256 = Buffer.alloc(32);
557
+ if (unpacker.remaining.length >= 32)
558
+ map_sha256 = unpacker.unpackRaw(32);
559
+ let map_crc = unpacker.unpackInt();
560
+ let map_size = unpacker.unpackInt();
561
+ let map_url = "";
562
+ if (unpacker.remaining.length)
563
+ map_url = unpacker.unpackString();
564
+ this.emit("map_details", { map_name, map_sha256, map_crc, map_size, map_url });
565
+ // unpacker.unpack
566
+ }
567
+ else if (chunk.msgid == 65541 /* NETMSG.System.NETMSG_CAPABILITIES */) {
568
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
569
+ let Version = unpacker.unpackInt();
570
+ let Flags = unpacker.unpackInt();
571
+ if (Version <= 0)
572
+ return;
573
+ let DDNet = false;
574
+ if (Version >= 1) {
575
+ DDNet = Boolean(Flags & 1);
576
+ }
577
+ let ChatTimeoutCode = DDNet;
578
+ let AnyPlayerFlag = DDNet;
579
+ let PingEx = false;
580
+ let AllowDummy = true;
581
+ let SyncWeaponInput = false;
582
+ if (Version >= 1) {
583
+ ChatTimeoutCode = Boolean(Flags & 2);
584
+ }
585
+ if (Version >= 2) {
586
+ AnyPlayerFlag = Boolean(Flags & 4);
587
+ }
588
+ if (Version >= 3) {
589
+ PingEx = Boolean(Flags & 8);
590
+ }
591
+ if (Version >= 4) {
592
+ AllowDummy = Boolean(Flags & 16);
593
+ }
594
+ if (Version >= 5) {
595
+ SyncWeaponInput = Boolean(Flags & 32);
596
+ }
597
+ this.emit("capabilities", { ChatTimeoutCode, AnyPlayerFlag, PingEx, AllowDummy, SyncWeaponInput });
598
+ // https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
599
+ }
600
+ else if (chunk.msgid == 65543 /* NETMSG.System.NETMSG_PINGEX */) {
601
+ let packer = new MsgPacker_1.MsgPacker(0, true, 2);
602
+ packer.AddBuffer(this.UUIDManager.LookupType(65544 /* NETMSG.System.NETMSG_PONGEX */).hash);
603
+ this.SendMsgEx(packer, 2);
604
+ }
605
+ else if (chunk.msgid == 65552 /* NETMSG.System.NETMSG_RECONNECT */) {
606
+ this.SendControlMsg(4); // sends disconnect packet
607
+ clearInterval(predTimer);
608
+ clearInterval(inputInterval);
609
+ clearInterval(resendTimeout);
610
+ clearInterval(Timeout);
611
+ (_b = this.socket) === null || _b === void 0 ? void 0 : _b.removeAllListeners("message");
612
+ this.connect();
613
+ return;
614
+ }
615
+ }
616
+ }
617
+ else {
618
+ // game messages
619
+ // vote list:
620
+ if (chunk.msgid == 11 /* NETMSG.Game.SV_VOTECLEAROPTIONS */) {
621
+ this.VoteList = [];
622
+ }
623
+ else if (chunk.msgid == 12 /* NETMSG.Game.SV_VOTEOPTIONLISTADD */) {
624
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
625
+ let NumOptions = unpacker.unpackInt();
626
+ let list = [];
627
+ for (let i = 0; i < 15; i++) {
628
+ list.push(unpacker.unpackString());
629
+ }
630
+ list = list.slice(0, NumOptions);
631
+ this.VoteList.push(...list);
632
+ }
633
+ else if (chunk.msgid == 13 /* NETMSG.Game.SV_VOTEOPTIONADD */) {
634
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
635
+ this.VoteList.push(unpacker.unpackString());
636
+ }
637
+ else if (chunk.msgid == 14 /* NETMSG.Game.SV_VOTEOPTIONREMOVE */) {
638
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
639
+ let index = this.VoteList.indexOf(unpacker.unpackString());
640
+ if (index > -1)
641
+ this.VoteList = this.VoteList.splice(index, 1);
642
+ }
643
+ // events
644
+ if (chunk.msgid == 10 /* NETMSG.Game.SV_EMOTICON */) {
645
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
646
+ let unpacked = {
647
+ client_id: unpacker.unpackInt(),
648
+ emoticon: unpacker.unpackInt()
649
+ };
650
+ if (unpacked.client_id != -1) {
651
+ unpacked.author = {
652
+ ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
653
+ PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
654
+ };
655
+ }
656
+ this.emit("emote", unpacked);
657
+ }
658
+ else if (chunk.msgid == 2 /* NETMSG.Game.SV_BROADCAST */) {
659
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
660
+ this.emit("broadcast", unpacker.unpackString());
661
+ }
662
+ if (chunk.msgid == 3 /* NETMSG.Game.SV_CHAT */) {
663
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
664
+ let unpacked = {
665
+ team: unpacker.unpackInt(),
666
+ client_id: unpacker.unpackInt(),
667
+ message: unpacker.unpackString()
668
+ };
669
+ if (unpacked.client_id != -1) {
670
+ unpacked.author = {
671
+ ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
672
+ PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
673
+ };
674
+ }
675
+ this.emit("message", unpacked);
676
+ }
677
+ else if (chunk.msgid == 4 /* NETMSG.Game.SV_KILLMSG */) {
678
+ let unpacked = {};
679
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
680
+ unpacked.killer_id = unpacker.unpackInt();
681
+ unpacked.victim_id = unpacker.unpackInt();
682
+ unpacked.weapon = unpacker.unpackInt();
683
+ unpacked.special_mode = unpacker.unpackInt();
684
+ if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
685
+ unpacked.victim = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.victim_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.victim_id) };
686
+ }
687
+ if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
688
+ unpacked.killer = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.killer_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.killer_id) };
689
+ this.emit("kill", unpacked);
690
+ }
691
+ else if (chunk.msgid == 1 /* NETMSG.Game.SV_MOTD */) {
692
+ let unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
693
+ let message = unpacker.unpackString();
694
+ this.emit("motd", message);
695
+ }
696
+ // packets neccessary for connection
697
+ // https://ddnet.org/docs/libtw2/connection/
698
+ if (chunk.msgid == 8 /* NETMSG.Game.SV_READYTOENTER */) {
699
+ var Msg = new MsgPacker_1.MsgPacker(15 /* NETMSG.System.NETMSG_ENTERGAME */, true, 1); /* entergame */
700
+ this.SendMsgEx(Msg);
701
+ this.OnEnterGame();
702
+ }
703
+ }
704
+ });
705
+ if (this.State == protocol_1.States.STATE_ONLINE) {
706
+ if (new Date().getTime() - this.time >= 500) {
707
+ this.Flush();
708
+ }
709
+ if (new Date().getTime() - this.time >= 1000) {
710
+ this.time = new Date().getTime();
711
+ this.SendControlMsg(0);
712
+ }
713
+ }
714
+ });
715
+ });
716
+ }
717
+ /** Sending the input. (automatically done unless options.lightweight is on) */
718
+ sendInput(input = this.movement.input) {
719
+ if (this.State != protocol_1.States.STATE_ONLINE)
720
+ return;
721
+ let inputMsg = new MsgPacker_1.MsgPacker(16, true, 0);
722
+ inputMsg.AddInt(this.AckGameTick);
723
+ inputMsg.AddInt(this.PredGameTick);
724
+ inputMsg.AddInt(40);
725
+ inputMsg.AddInt(input.m_Direction);
726
+ inputMsg.AddInt(input.m_TargetX);
727
+ inputMsg.AddInt(input.m_TargetY);
728
+ inputMsg.AddInt(input.m_Jump);
729
+ inputMsg.AddInt(input.m_Fire);
730
+ inputMsg.AddInt(input.m_Hook);
731
+ inputMsg.AddInt(input.m_PlayerFlags);
732
+ inputMsg.AddInt(input.m_WantedWeapon);
733
+ inputMsg.AddInt(input.m_NextWeapon);
734
+ inputMsg.AddInt(input.m_PrevWeapon);
735
+ this.SendMsgEx(inputMsg);
736
+ }
737
+ /** returns the movement object of the client */
738
+ get input() {
739
+ return this.movement.input;
740
+ }
741
+ /** Disconnect the client. */
742
+ Disconnect() {
743
+ return new Promise((resolve) => {
744
+ this.SendControlMsg(4).then(() => {
745
+ resolve(true);
746
+ if (this.socket)
747
+ this.socket.close();
748
+ this.socket = undefined;
749
+ this.State = protocol_1.States.STATE_OFFLINE;
750
+ });
751
+ });
752
+ }
753
+ /** Get all available vote options (for example for map voting) */
754
+ get VoteOptionList() {
755
+ return this.VoteList;
756
+ }
757
+ get rawSnapUnpacker() {
758
+ return this.SnapUnpacker;
759
+ }
760
+ }
761
+ exports.Client = Client;