teeworlds 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,684 @@
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 (b.hasOwnProperty(p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ extendStatics(d, b);
11
+ function __() { this.constructor = d; }
12
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
+ };
14
+ })();
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.Client = void 0;
20
+ var crypto_1 = require("crypto");
21
+ var dgram_1 = __importDefault(require("dgram"));
22
+ var stream_1 = require("stream");
23
+ var MsgUnpacker_1 = require("./MsgUnpacker");
24
+ var movement_1 = __importDefault(require("./movement"));
25
+ var MsgPacker_1 = require("./MsgPacker");
26
+ var snapshot_1 = require("./snapshot");
27
+ var huffman_1 = __importDefault(require("./huffman"));
28
+ var huff = new huffman_1.default();
29
+ var SnapUnpacker = new snapshot_1.Snapshot();
30
+ var States;
31
+ (function (States) {
32
+ States[States["STATE_OFFLINE"] = 0] = "STATE_OFFLINE";
33
+ States[States["STATE_CONNECTING"] = 1] = "STATE_CONNECTING";
34
+ States[States["STATE_LOADING"] = 2] = "STATE_LOADING";
35
+ States[States["STATE_ONLINE"] = 3] = "STATE_ONLINE";
36
+ States[States["STATE_DEMOPLAYBACK"] = 4] = "STATE_DEMOPLAYBACK";
37
+ States[States["STATE_QUITTING"] = 5] = "STATE_QUITTING";
38
+ States[States["STATE_RESTARTING"] = 6] = "STATE_RESTARTING";
39
+ })(States || (States = {}));
40
+ ;
41
+ var NETMSGTYPE;
42
+ (function (NETMSGTYPE) {
43
+ NETMSGTYPE[NETMSGTYPE["EX"] = 0] = "EX";
44
+ NETMSGTYPE[NETMSGTYPE["SV_MOTD"] = 1] = "SV_MOTD";
45
+ NETMSGTYPE[NETMSGTYPE["SV_BROADCAST"] = 2] = "SV_BROADCAST";
46
+ NETMSGTYPE[NETMSGTYPE["SV_CHAT"] = 3] = "SV_CHAT";
47
+ NETMSGTYPE[NETMSGTYPE["SV_KILLMSG"] = 4] = "SV_KILLMSG";
48
+ NETMSGTYPE[NETMSGTYPE["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
49
+ NETMSGTYPE[NETMSGTYPE["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
50
+ NETMSGTYPE[NETMSGTYPE["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
51
+ NETMSGTYPE[NETMSGTYPE["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
52
+ NETMSGTYPE[NETMSGTYPE["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
53
+ NETMSGTYPE[NETMSGTYPE["SV_EMOTICON"] = 10] = "SV_EMOTICON";
54
+ NETMSGTYPE[NETMSGTYPE["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
55
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
56
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
57
+ NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
58
+ NETMSGTYPE[NETMSGTYPE["SV_VOTESET"] = 15] = "SV_VOTESET";
59
+ NETMSGTYPE[NETMSGTYPE["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
60
+ NETMSGTYPE[NETMSGTYPE["CL_SAY"] = 17] = "CL_SAY";
61
+ NETMSGTYPE[NETMSGTYPE["CL_SETTEAM"] = 18] = "CL_SETTEAM";
62
+ NETMSGTYPE[NETMSGTYPE["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
63
+ NETMSGTYPE[NETMSGTYPE["CL_STARTINFO"] = 20] = "CL_STARTINFO";
64
+ NETMSGTYPE[NETMSGTYPE["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
65
+ NETMSGTYPE[NETMSGTYPE["CL_KILL"] = 22] = "CL_KILL";
66
+ NETMSGTYPE[NETMSGTYPE["CL_EMOTICON"] = 23] = "CL_EMOTICON";
67
+ NETMSGTYPE[NETMSGTYPE["CL_VOTE"] = 24] = "CL_VOTE";
68
+ NETMSGTYPE[NETMSGTYPE["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
69
+ NETMSGTYPE[NETMSGTYPE["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
70
+ NETMSGTYPE[NETMSGTYPE["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
71
+ NETMSGTYPE[NETMSGTYPE["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
72
+ NETMSGTYPE[NETMSGTYPE["UNUSED"] = 29] = "UNUSED";
73
+ NETMSGTYPE[NETMSGTYPE["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
74
+ NETMSGTYPE[NETMSGTYPE["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
75
+ NETMSGTYPE[NETMSGTYPE["NUM"] = 32] = "NUM";
76
+ })(NETMSGTYPE || (NETMSGTYPE = {}));
77
+ var items;
78
+ (function (items) {
79
+ items[items["OBJ_EX"] = 0] = "OBJ_EX";
80
+ items[items["OBJ_PLAYER_INPUT"] = 1] = "OBJ_PLAYER_INPUT";
81
+ items[items["OBJ_PROJECTILE"] = 2] = "OBJ_PROJECTILE";
82
+ items[items["OBJ_LASER"] = 3] = "OBJ_LASER";
83
+ items[items["OBJ_PICKUP"] = 4] = "OBJ_PICKUP";
84
+ items[items["OBJ_FLAG"] = 5] = "OBJ_FLAG";
85
+ items[items["OBJ_GAME_INFO"] = 6] = "OBJ_GAME_INFO";
86
+ items[items["OBJ_GAME_DATA"] = 7] = "OBJ_GAME_DATA";
87
+ items[items["OBJ_CHARACTER_CORE"] = 8] = "OBJ_CHARACTER_CORE";
88
+ items[items["OBJ_CHARACTER"] = 9] = "OBJ_CHARACTER";
89
+ items[items["OBJ_PLAYER_INFO"] = 10] = "OBJ_PLAYER_INFO";
90
+ items[items["OBJ_CLIENT_INFO"] = 11] = "OBJ_CLIENT_INFO";
91
+ items[items["OBJ_SPECTATOR_INFO"] = 12] = "OBJ_SPECTATOR_INFO";
92
+ items[items["EVENT_COMMON"] = 13] = "EVENT_COMMON";
93
+ items[items["EVENT_EXPLOSION"] = 14] = "EVENT_EXPLOSION";
94
+ items[items["EVENT_SPAWN"] = 15] = "EVENT_SPAWN";
95
+ items[items["EVENT_HAMMERHIT"] = 16] = "EVENT_HAMMERHIT";
96
+ items[items["EVENT_DEATH"] = 17] = "EVENT_DEATH";
97
+ items[items["EVENT_SOUND_GLOBAL"] = 18] = "EVENT_SOUND_GLOBAL";
98
+ items[items["EVENT_SOUND_WORLD"] = 19] = "EVENT_SOUND_WORLD";
99
+ items[items["EVENT_DAMAGE_INDICATOR"] = 20] = "EVENT_DAMAGE_INDICATOR";
100
+ })(items || (items = {}));
101
+ function toHexStream(buff) {
102
+ return buff.toJSON().data.map(function (a) { return ('0' + (a & 0xff).toString(16)).slice(-2); }).join("");
103
+ }
104
+ var messageTypes = [
105
+ ["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"],
106
+ ["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"]
107
+ ];
108
+ var messageUUIDs = {
109
+ "WHAT_IS": Buffer.from([0x24, 0x5e, 0x50, 0x97, 0x9f, 0xe0, 0x39, 0xd6, 0xbf, 0x7d, 0x9a, 0x29, 0xe1, 0x69, 0x1e, 0x4c]),
110
+ "IT_IS": Buffer.from([0x69, 0x54, 0x84, 0x7e, 0x2e, 0x87, 0x36, 0x03, 0xb5, 0x62, 0x36, 0xda, 0x29, 0xed, 0x1a, 0xca]),
111
+ "I_DONT_KNOW": Buffer.from([0x41, 0x69, 0x11, 0xb5, 0x79, 0x73, 0x33, 0xbf, 0x8d, 0x52, 0x7b, 0xf0, 0x1e, 0x51, 0x9c, 0xf0]),
112
+ "RCON_TYPE": Buffer.from([0x12, 0x81, 0x0e, 0x1f, 0xa1, 0xdb, 0x33, 0x78, 0xb4, 0xfb, 0x16, 0x4e, 0xd6, 0x50, 0x59, 0x26]),
113
+ "MAP_DETAILS": Buffer.from([0xf9, 0x11, 0x7b, 0x3c, 0x80, 0x39, 0x34, 0x16, 0x9f, 0xc0, 0xae, 0xf2, 0xbc, 0xb7, 0x5c, 0x03]),
114
+ "CLIENT_VERSION": Buffer.from([0x8c, 0x00, 0x13, 0x04, 0x84, 0x61, 0x3e, 0x47, 0x87, 0x87, 0xf6, 0x72, 0xb3, 0x83, 0x5b, 0xd4]),
115
+ "CAPABILITIES": Buffer.from([0xf6, 0x21, 0xa5, 0xa1, 0xf5, 0x85, 0x37, 0x75, 0x8e, 0x73, 0x41, 0xbe, 0xee, 0x79, 0xf2, 0xb2]),
116
+ };
117
+ function arrStartsWith(arr, arrStart, start) {
118
+ if (start === void 0) { start = 0; }
119
+ arr.splice(0, start);
120
+ for (var i = 0; i < arrStart.length; i++) {
121
+ if (arr[i] == arrStart[i])
122
+ continue;
123
+ else
124
+ return false;
125
+ }
126
+ return true;
127
+ }
128
+ var Client = /** @class */ (function (_super) {
129
+ __extends(Client, _super);
130
+ function Client(ip, port, nickname, options) {
131
+ var _this = _super.call(this) || this;
132
+ _this.host = ip;
133
+ _this.port = port;
134
+ _this.name = nickname;
135
+ _this.AckGameTick = 0;
136
+ _this.PredGameTick = 0;
137
+ if (options)
138
+ _this.options = options;
139
+ _this.timer = 0;
140
+ _this.movement = new movement_1.default();
141
+ _this.snaps = [];
142
+ _this.client_infos = [];
143
+ _this.player_infos = [];
144
+ _this.sentChunkQueue = [];
145
+ _this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
146
+ _this.ack = 0; // ack of messages the client has received
147
+ _this.clientAck = 0; // ack of messages the client has sent
148
+ _this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
149
+ _this.lastMsg = "";
150
+ _this.socket = dgram_1.default.createSocket("udp4");
151
+ _this.socket.bind();
152
+ _this.TKEN = Buffer.from([255, 255, 255, 255]);
153
+ _this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
154
+ _this.lastSendTime = new Date().getTime();
155
+ return _this;
156
+ }
157
+ Client.prototype.Unpack = function (packet) {
158
+ var unpacked = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
159
+ 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)
160
+ return unpacked;
161
+ packet = packet.slice(3);
162
+ if (unpacked.twprotocol.flags & 128) {
163
+ packet = huff.decompress(packet);
164
+ if (packet.length == 1 && packet[0] == -1)
165
+ return unpacked;
166
+ }
167
+ // return unpacked;
168
+ for (var i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
169
+ var chunk = {};
170
+ chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1)); // idk what this shit is but it works
171
+ chunk.flags = (packet[0] >> 6) & 3;
172
+ chunk.sequence = -1;
173
+ if (chunk.flags & 1) {
174
+ chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
175
+ packet = packet.slice(3); // remove flags & size
176
+ }
177
+ else
178
+ packet = packet.slice(2);
179
+ chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
180
+ chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
181
+ chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
182
+ chunk.raw = packet.slice(1, chunk.bytes);
183
+ Object.values(messageUUIDs).forEach(function (a, i) {
184
+ if (a.compare(packet.slice(0, 16)) == 0) {
185
+ chunk.extended_msgid = a;
186
+ // chunk.type = 'sys';
187
+ chunk.msg = Object.keys(messageUUIDs)[i];
188
+ }
189
+ });
190
+ packet = packet.slice(chunk.bytes); // +1 cuz it adds an extra \x00 for easier parsing i guess
191
+ unpacked.chunks.push(chunk);
192
+ }
193
+ return unpacked;
194
+ };
195
+ Client.prototype.SendControlMsg = function (msg, ExtraMsg) {
196
+ var _this = this;
197
+ if (ExtraMsg === void 0) { ExtraMsg = ""; }
198
+ this.lastSendTime = new Date().getTime();
199
+ return new Promise(function (resolve, reject) {
200
+ if (_this.socket) {
201
+ var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((_this.ack >> 8) & 0xf)), _this.ack & 0xff, 0x00, msg]);
202
+ latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), _this.TKEN]); // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
203
+ _this.socket.send(latestBuf, 0, latestBuf.length, _this.port, _this.host, function (err, bytes) {
204
+ resolve(bytes);
205
+ });
206
+ }
207
+ setTimeout(function () { resolve("failed, rip"); }, 2000);
208
+ /* after 2 seconds it was probably not able to send,
209
+ so when sending a quit message the user doesnt
210
+ stay stuck not being able to ctrl + c
211
+ */
212
+ });
213
+ };
214
+ // SendMsgEx(Msg: MsgPacker, Flags: number) {
215
+ // if (this.State == States.STATE_OFFLINE)
216
+ // throw new Error("Client is not connected");
217
+ // if (!this.socket)
218
+ // return;
219
+ // this.lastSendTime = new Date().getTime();
220
+ // var header = []
221
+ // header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
222
+ // header[1] = (Msg.size & 0xf);
223
+ // if (Flags & 1) {
224
+ // this.clientAck = (this.clientAck + 1) % (1 << 10);
225
+ // header[1] |= (this.clientAck >> 2) & 0xf0;
226
+ // header[2] = this.clientAck & 0xff;
227
+ // this.sentChunkQueue.push(Buffer.concat([Buffer.from(header), Msg.buffer]));
228
+ // }
229
+ // let latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1]]);
230
+ // if (Flags & 1)
231
+ // latestBuf = Buffer.concat([latestBuf, Buffer.from([this.clientAck])]);
232
+ // latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
233
+ // this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host);
234
+ // }
235
+ Client.prototype.SendMsgEx = function (Msgs, Flags) {
236
+ var _this = this;
237
+ if (this.State == States.STATE_OFFLINE)
238
+ return; //throw new Error("Client is not connected");
239
+ if (!this.socket)
240
+ return;
241
+ var _Msgs;
242
+ if (Msgs instanceof Array)
243
+ _Msgs = Msgs;
244
+ else
245
+ _Msgs = [Msgs];
246
+ this.lastSendTime = new Date().getTime();
247
+ var header = [];
248
+ _Msgs.forEach(function (Msg, index) {
249
+ header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
250
+ header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
251
+ header[index][1] = (Msg.size & 0xf);
252
+ if (Flags & 1) {
253
+ _this.clientAck = (_this.clientAck + 1) % (1 << 10);
254
+ header[index][1] |= (_this.clientAck >> 2) & 0xf0;
255
+ header[index][2] = _this.clientAck & 0xff;
256
+ header[index][0] = (((Flags | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
257
+ _this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
258
+ header[index][0] = (((Flags) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
259
+ }
260
+ });
261
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, _Msgs.length]);
262
+ var chunks = Buffer.from([]);
263
+ _Msgs.forEach(function (Msg, index) {
264
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
265
+ });
266
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
267
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
268
+ };
269
+ Client.prototype.SendMsgRaw = function (chunks) {
270
+ if (this.State == States.STATE_OFFLINE)
271
+ return console.log(chunks, "client not connected"); //throw new Error("Client is not connected");
272
+ if (!this.socket)
273
+ return;
274
+ this.lastSendTime = new Date().getTime();
275
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, chunks.length]);
276
+ var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
277
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
278
+ };
279
+ Client.prototype.MsgToChunk = function (packet) {
280
+ var chunk = {};
281
+ // let packet = Msg.buffer;
282
+ chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
283
+ chunk.flags = (packet[0] >> 6) & 3;
284
+ chunk.sequence = -1;
285
+ if (chunk.flags & 1) {
286
+ chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
287
+ packet = packet.slice(3); // remove flags & size
288
+ }
289
+ else
290
+ packet = packet.slice(2);
291
+ chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
292
+ chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
293
+ chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
294
+ // if (chunk.msg == undefined)
295
+ // console.log(packet)
296
+ chunk.raw = packet.slice(1, chunk.bytes);
297
+ Object.values(messageUUIDs).forEach(function (a, i) {
298
+ if (a.compare(packet.slice(0, 16)) === 0) {
299
+ chunk.extended_msgid = a;
300
+ // chunk.type = 'sys';
301
+ chunk.msg = Object.keys(messageUUIDs)[i];
302
+ }
303
+ });
304
+ return chunk;
305
+ };
306
+ Client.prototype.connect = function () {
307
+ var _this = this;
308
+ this.State = States.STATE_CONNECTING;
309
+ var predTimer = setInterval(function () {
310
+ if (_this.State == States.STATE_ONLINE) {
311
+ if (_this.AckGameTick > 0)
312
+ _this.PredGameTick++;
313
+ // console.log(this.PredGameTick, this.AckGameTick)
314
+ }
315
+ else if (_this.State == States.STATE_OFFLINE)
316
+ clearInterval(predTimer);
317
+ }, 20);
318
+ this.SendControlMsg(1, "TKEN");
319
+ var connectInterval = setInterval(function () {
320
+ if (_this.State == States.STATE_CONNECTING)
321
+ _this.SendControlMsg(1, "TKEN");
322
+ else
323
+ clearInterval(connectInterval);
324
+ }, 500);
325
+ var inputInterval = setInterval(function () {
326
+ // if (new Date().getTime() - this.time >= 1000) {
327
+ if (_this.State == States.STATE_OFFLINE)
328
+ clearInterval(inputInterval);
329
+ if (_this.State != States.STATE_ONLINE)
330
+ return;
331
+ _this.time = new Date().getTime();
332
+ // this.SendControlMsg(0);
333
+ // console.log("sending with " + this.AckGameTick)
334
+ _this.sendInput();
335
+ // }
336
+ }, 500);
337
+ var resendTimeout = setInterval(function () {
338
+ // this.sentChunkQueue.forEach((chunk) => {
339
+ // if (this.State == 0) // disconnected
340
+ // return;
341
+ if (_this.State != States.STATE_OFFLINE) {
342
+ if (((new Date().getTime()) - _this.lastSendTime) > 900 && _this.sentChunkQueue.length > 0) {
343
+ _this.SendMsgRaw([_this.sentChunkQueue[0]]);
344
+ console.log(_this.sentChunkQueue, _this.State);
345
+ }
346
+ }
347
+ else
348
+ clearInterval(resendTimeout);
349
+ // })
350
+ }, 1000);
351
+ this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
352
+ if (this.socket)
353
+ this.socket.on("message", function (a, rinfo) {
354
+ var _a, _b, _c, _d, _e, _f;
355
+ if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
356
+ return;
357
+ clearInterval(connectInterval);
358
+ if (a.toJSON().data[0] == 0x10) {
359
+ if (a.toString().includes("TKEN") || a.toJSON().data[3] == 0x2) {
360
+ clearInterval(connectInterval);
361
+ _this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length));
362
+ _this.SendControlMsg(3);
363
+ _this.State = States.STATE_LOADING; // loading state
364
+ _this.receivedSnaps = 0;
365
+ var info = new MsgPacker_1.MsgPacker(1, true);
366
+ info.AddString("0.6 626fce9a778df4d4");
367
+ info.AddString(((_a = _this.options) === null || _a === void 0 ? void 0 : _a.password) === undefined ? "" : (_b = _this.options) === null || _b === void 0 ? void 0 : _b.password); // password
368
+ var client_version = new MsgPacker_1.MsgPacker(0, true);
369
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
370
+ var randomUuid = Buffer.alloc(16);
371
+ crypto_1.randomBytes(16).copy(randomUuid);
372
+ client_version.AddBuffer(randomUuid);
373
+ if (((_c = _this.options) === null || _c === void 0 ? void 0 : _c.ddnet_version) !== undefined) {
374
+ client_version.AddInt((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version.version);
375
+ client_version.AddString("DDNet " + ((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.release_version));
376
+ }
377
+ else {
378
+ client_version.AddInt(16003);
379
+ client_version.AddString("DDNet 16.0.3");
380
+ }
381
+ _this.SendMsgEx([client_version, info], 1);
382
+ }
383
+ else if (a.toJSON().data[3] == 0x4) {
384
+ // disconnected
385
+ _this.State = States.STATE_OFFLINE;
386
+ var reason = (MsgUnpacker_1.unpackString(a.toJSON().data.slice(4)).result);
387
+ // this.State = -1;
388
+ _this.emit("disconnect", reason);
389
+ }
390
+ }
391
+ var unpacked = _this.Unpack(a);
392
+ unpacked.chunks.forEach(function (a) {
393
+ if (a.flags & 1) { // vital
394
+ if (a.seq != undefined && a.seq != -1)
395
+ _this.ack = a.seq;
396
+ else
397
+ console.log("no seq", a);
398
+ }
399
+ });
400
+ _this.sentChunkQueue.forEach(function (buff, i) {
401
+ var chunk = _this.MsgToChunk(buff);
402
+ if (chunk.flags & 1) {
403
+ if (chunk.seq && chunk.seq < _this.ack) {
404
+ _this.sentChunkQueue.splice(i, 1);
405
+ // this.ack = (this.ack + 1) % (1 << 10);
406
+ }
407
+ }
408
+ });
409
+ var snapChunks = unpacked.chunks.filter(function (a) { return a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY"; });
410
+ // console.log(unpacked.chunks.length, unpacked)
411
+ if (snapChunks.length > 0) {
412
+ var part_1 = 0;
413
+ var num_parts_1 = 1;
414
+ snapChunks.forEach(function (chunk) {
415
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
416
+ var AckGameTick = (MsgUnpacker_1.unpackInt(chunk.raw.toJSON().data).result);
417
+ // setImmediate(() => {
418
+ // console.log(AckGameTick, this.AckGameTick, chunk.msg)
419
+ if (AckGameTick > _this.AckGameTick) {
420
+ _this.AckGameTick = AckGameTick;
421
+ if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
422
+ _this.PredGameTick = AckGameTick + 1;
423
+ // console.log(this.AckGameTick)
424
+ }
425
+ // })
426
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_a = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _a === void 0 ? void 0 : _a.toJSON().data).remaining);
427
+ var DeltaTick = MsgUnpacker_1.unpackInt((_b = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _b === void 0 ? void 0 : _b.toJSON().data).result;
428
+ if (chunk.msg === "SNAP") {
429
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_c = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _c === void 0 ? void 0 : _c.toJSON().data).remaining); // delta tick
430
+ num_parts_1 = (MsgUnpacker_1.unpackInt((_d = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _d === void 0 ? void 0 : _d.toJSON().data).result);
431
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_e = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _e === void 0 ? void 0 : _e.toJSON().data).remaining); // num parts
432
+ part_1 = (MsgUnpacker_1.unpackInt((_f = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _f === void 0 ? void 0 : _f.toJSON().data).result);
433
+ }
434
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_g = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _g === void 0 ? void 0 : _g.toJSON().data).remaining); // part
435
+ if (chunk.msg != "SNAP_EMPTY")
436
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_h = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _h === void 0 ? void 0 : _h.toJSON().data).remaining); // crc
437
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_j = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _j === void 0 ? void 0 : _j.toJSON().data).remaining); // crc
438
+ if (part_1 === 0 || _this.snaps.length > 30) {
439
+ _this.snaps = [];
440
+ }
441
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_k = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _k === void 0 ? void 0 : _k.toJSON().data).remaining); // crc
442
+ _this.snaps.push(chunk.raw);
443
+ // console.log(this.PredGameTick - this.AckGameTick, this.PredGameTick, this.AckGameTick)
444
+ if ((num_parts_1 - 1) === part_1 && _this.snaps.length === num_parts_1) {
445
+ var mergedSnaps_1 = Buffer.concat(_this.snaps);
446
+ var snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps_1.toJSON().data, 1);
447
+ // console.log(snapUnpacked)
448
+ snapUnpacked.items.forEach(function (a, i) {
449
+ if (a.type_id === items.OBJ_CLIENT_INFO) {
450
+ // console.log(a.parsed, i)
451
+ _this.client_infos[a.id] = a.parsed;
452
+ if (a.parsed.name.includes("������")) {
453
+ console.log(_this.PredGameTick, _this.AckGameTick, mergedSnaps_1.toJSON().data.toString());
454
+ }
455
+ console.log(_this.client_infos[a.id].name, _this.client_infos[a.id].clan, [a.id]);
456
+ }
457
+ // else if (a.type_id === items.OBJ_PLAYER_INFO) {
458
+ // this.player_infos[a.id] = a.parsed as PlayerInfo;
459
+ // }
460
+ });
461
+ }
462
+ });
463
+ }
464
+ var chunkMessages = unpacked.chunks.map(function (a) { return a.msg; });
465
+ if (chunkMessages.includes("SV_CHAT")) {
466
+ var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_CHAT"; });
467
+ chat.forEach(function (a) {
468
+ if (a.msg == "SV_CHAT") {
469
+ var unpacked = {};
470
+ unpacked.team = MsgUnpacker_1.unpackInt(a.raw.toJSON().data).result;
471
+ var remaining = MsgUnpacker_1.unpackInt(a.raw.toJSON().data).remaining;
472
+ unpacked.client_id = MsgUnpacker_1.unpackInt(remaining).result;
473
+ remaining = MsgUnpacker_1.unpackInt(remaining).remaining;
474
+ unpacked.message = MsgUnpacker_1.unpackString(remaining).result;
475
+ if (unpacked.client_id != -1)
476
+ unpacked.author = { ClientInfo: _this.client_infos[unpacked.client_id], PlayerInfo: _this.player_infos[unpacked.client_id] };
477
+ // console.log(unpacked)
478
+ _this.emit("message", unpacked);
479
+ }
480
+ });
481
+ }
482
+ if (chunkMessages.includes("SV_KILL_MSG")) {
483
+ var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_KILL_MSG"; });
484
+ chat.forEach(function (a) {
485
+ if (a.msg == "SV_KILL_MSG") {
486
+ var unpacked = {};
487
+ var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
488
+ unpacked.killer_id = unpacker.unpackInt();
489
+ unpacked.victim_id = unpacker.unpackInt();
490
+ unpacked.weapon = unpacker.unpackInt();
491
+ unpacked.special_mode = unpacker.unpackInt();
492
+ if (unpacked.victim_id != -1)
493
+ unpacked.victim = { ClientInfo: _this.client_infos[unpacked.victim_id], PlayerInfo: _this.player_infos[unpacked.victim_id] };
494
+ if (unpacked.killer_id != -1)
495
+ unpacked.killer = { ClientInfo: _this.client_infos[unpacked.killer_id], PlayerInfo: _this.player_infos[unpacked.killer_id] };
496
+ // console.log(unpacked)
497
+ _this.emit("kill", unpacked);
498
+ }
499
+ });
500
+ }
501
+ if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
502
+ var Msg = new MsgPacker_1.MsgPacker(15, true); /* entergame */
503
+ _this.SendMsgEx(Msg, 1);
504
+ }
505
+ else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
506
+ // send ready
507
+ var Msg = new MsgPacker_1.MsgPacker(14, true); /* ready */
508
+ _this.SendMsgEx(Msg, 1);
509
+ }
510
+ else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
511
+ var info = new MsgPacker_1.MsgPacker(20, false);
512
+ if ((_f = _this.options) === null || _f === void 0 ? void 0 : _f.identity) {
513
+ info.AddString(_this.options.identity.name);
514
+ info.AddString(_this.options.identity.clan);
515
+ info.AddInt(_this.options.identity.country);
516
+ info.AddString(_this.options.identity.skin);
517
+ info.AddInt(_this.options.identity.use_custom_color);
518
+ info.AddInt(_this.options.identity.color_body);
519
+ info.AddInt(_this.options.identity.color_feet);
520
+ }
521
+ else {
522
+ info.AddString(_this.name); /* name */
523
+ info.AddString(""); /* clan */
524
+ info.AddInt(-1); /* country */
525
+ info.AddString("greyfox"); /* skin */
526
+ info.AddInt(1); /* use custom color */
527
+ info.AddInt(10346103); /* color body */
528
+ info.AddInt(65535); /* color feet */
529
+ }
530
+ _this.SendMsgEx(info, 1);
531
+ }
532
+ else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
533
+ var info = new MsgPacker_1.MsgPacker(23, true);
534
+ _this.SendMsgEx(info, 1);
535
+ }
536
+ if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
537
+ _this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
538
+ if (_this.receivedSnaps == 2) {
539
+ if (_this.State != States.STATE_ONLINE)
540
+ _this.emit('connected');
541
+ _this.State = States.STATE_ONLINE;
542
+ }
543
+ var chunks = unpacked.chunks.filter(function (a) { return a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY"; });
544
+ if (chunks.length > 0) {
545
+ var part_2 = 0;
546
+ var num_parts_2 = 1;
547
+ chunks.forEach(function (chunk) {
548
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
549
+ var AckGameTick = (MsgUnpacker_1.unpackInt(chunk.raw.toJSON().data).result);
550
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_a = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _a === void 0 ? void 0 : _a.toJSON().data).remaining);
551
+ var DeltaTick = MsgUnpacker_1.unpackInt((_b = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _b === void 0 ? void 0 : _b.toJSON().data).result;
552
+ if (chunk.msg == "SNAP") {
553
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_c = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _c === void 0 ? void 0 : _c.toJSON().data).remaining); // delta tick
554
+ num_parts_2 = (MsgUnpacker_1.unpackInt((_d = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _d === void 0 ? void 0 : _d.toJSON().data).result);
555
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_e = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _e === void 0 ? void 0 : _e.toJSON().data).remaining); // num parts
556
+ part_2 = (MsgUnpacker_1.unpackInt((_f = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _f === void 0 ? void 0 : _f.toJSON().data).result);
557
+ }
558
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_g = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _g === void 0 ? void 0 : _g.toJSON().data).remaining); // part
559
+ if (chunk.msg != "SNAP_EMPTY")
560
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_h = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _h === void 0 ? void 0 : _h.toJSON().data).remaining); // crc
561
+ chunk.raw = Buffer.from(MsgUnpacker_1.unpackInt((_j = chunk === null || chunk === void 0 ? void 0 : chunk.raw) === null || _j === void 0 ? void 0 : _j.toJSON().data).remaining); // crc
562
+ if (part_2 == 0 || _this.snaps.length > 30) {
563
+ _this.snaps = [];
564
+ }
565
+ _this.snaps.push(chunk.raw);
566
+ if ((num_parts_2 - 1) == part_2 && _this.snaps.length == num_parts_2) {
567
+ var mergedSnaps = Buffer.concat(_this.snaps);
568
+ var snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1);
569
+ snapUnpacked.items.forEach(function (a, i) {
570
+ if (a.type_id == items.OBJ_CLIENT_INFO) {
571
+ _this.client_infos[a.id] = a.parsed;
572
+ // console.log(a.parsed, i)
573
+ // console.log(this.client_infos[a.id])
574
+ }
575
+ else if (a.type_id == items.OBJ_PLAYER_INFO) {
576
+ _this.player_infos[i] = a.parsed;
577
+ }
578
+ else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
579
+ if (a.data.length == 5 && (a.parsed.freeze_end > 0 || a.parsed.freeze_end == -1)) {
580
+ // var packer = new MsgPacker(22, false)
581
+ // this.SendMsgEx(packer, 1)
582
+ }
583
+ }
584
+ });
585
+ }
586
+ });
587
+ }
588
+ }
589
+ if (new Date().getTime() - _this.time >= 1000 && _this.State == States.STATE_ONLINE) {
590
+ _this.time = new Date().getTime();
591
+ _this.SendControlMsg(0);
592
+ }
593
+ });
594
+ };
595
+ Client.prototype.sendInput = function (input) {
596
+ if (input === void 0) { input = this.movement.input; }
597
+ if (this.State != States.STATE_ONLINE)
598
+ return;
599
+ var inputMsg = new MsgPacker_1.MsgPacker(16, true);
600
+ inputMsg.AddInt(this.AckGameTick);
601
+ inputMsg.AddInt(this.PredGameTick);
602
+ inputMsg.AddInt(40);
603
+ // let playerflags = 2;
604
+ // playerflags |= 8; // scoreboard
605
+ // playerflags |= 16; // aimline
606
+ var input_data = [
607
+ input.m_Direction,
608
+ input.m_TargetX,
609
+ input.m_TargetY,
610
+ input.m_Jump,
611
+ input.m_Fire,
612
+ input.m_Hook,
613
+ input.m_PlayerFlags,
614
+ input.m_WantedWeapon,
615
+ input.m_NextWeapon,
616
+ input.m_PrevWeapon
617
+ ];
618
+ // console.log(this.player_infos, this.client_infos)
619
+ input_data.forEach(function (a) {
620
+ inputMsg.AddInt(a);
621
+ });
622
+ this.SendMsgEx(inputMsg, 0);
623
+ };
624
+ Object.defineProperty(Client.prototype, "input", {
625
+ get: function () {
626
+ return this.movement.input;
627
+ },
628
+ enumerable: false,
629
+ configurable: true
630
+ });
631
+ Client.prototype.Disconnect = function () {
632
+ var _this = this;
633
+ return new Promise(function (resolve) {
634
+ _this.SendControlMsg(4).then(function () {
635
+ resolve(true);
636
+ if (_this.socket)
637
+ _this.socket.close();
638
+ _this.socket = undefined;
639
+ _this.State = States.STATE_OFFLINE;
640
+ });
641
+ });
642
+ };
643
+ Client.prototype.Say = function (message, team) {
644
+ if (team === void 0) { team = false; }
645
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false);
646
+ packer.AddInt(team ? 1 : 0); // team
647
+ packer.AddString(message);
648
+ this.SendMsgEx(packer, 1);
649
+ };
650
+ Client.prototype.Vote = function (vote) {
651
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false);
652
+ packer.AddInt(vote ? 1 : 0);
653
+ this.SendMsgEx(packer, 1);
654
+ };
655
+ Client.prototype.ChangePlayerInfo = function (playerInfo) {
656
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false);
657
+ packer.AddString(playerInfo.name); //m_pName);
658
+ packer.AddString(playerInfo.clan); //m_pClan);
659
+ packer.AddInt(playerInfo.country); //m_Country);
660
+ packer.AddString(playerInfo.skin); //m_pSkin);
661
+ packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
662
+ packer.AddInt(playerInfo.color_body); //m_ColorBody);
663
+ packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
664
+ this.SendMsgEx(packer, 1);
665
+ };
666
+ Client.prototype.Kill = function () {
667
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false);
668
+ this.SendMsgEx(packer, 1);
669
+ };
670
+ Client.prototype.ChangeTeam = function (team) {
671
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false);
672
+ packer.AddInt(team);
673
+ this.SendMsgEx(packer, 1);
674
+ };
675
+ Client.prototype.Emote = function (emote) {
676
+ var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false);
677
+ packer.AddInt(emote);
678
+ this.SendMsgEx(packer, 1);
679
+ };
680
+ return Client;
681
+ }(stream_1.EventEmitter));
682
+ exports.Client = Client;
683
+ // export = Client;
684
+ // module.exports = Client;