teeworlds 2.0.0 → 2.0.3

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/README.md CHANGED
@@ -3,7 +3,7 @@ Library to connect a bot to a Teeworlds server.
3
3
 
4
4
  # Usage
5
5
  Example file:
6
- ```const test = require('@swarfey/teeworlds');
6
+ ```const test = require('teeworlds');
7
7
  let client = new test.Client("127.0.0.1", 8303, "test");
8
8
 
9
9
  client.connect();
@@ -75,7 +75,7 @@ client.on("kill", info => {
75
75
  })
76
76
 
77
77
  process.on("SIGINT", () => {
78
- client.disconnect(); // disconnect on ctrl + c
78
+ client.Disconnect(); // disconnect on ctrl + c
79
79
  })
80
80
  process.stdin.on("data", data => {
81
81
  client.Say(data.toString()); // write input in chat
package/lib/client.js CHANGED
@@ -51,6 +51,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
51
51
  var __importDefault = (this && this.__importDefault) || function (mod) {
52
52
  return (mod && mod.__esModule) ? mod : { "default": mod };
53
53
  };
54
+ var crypto_1 = require("crypto");
54
55
  var dgram_1 = __importDefault(require("dgram"));
55
56
  var stream_1 = require("stream");
56
57
  var child_process_1 = require("child_process");
@@ -241,11 +242,13 @@ var Client = /** @class */ (function (_super) {
241
242
  var _this = this;
242
243
  if (ExtraMsg === void 0) { ExtraMsg = ""; }
243
244
  return new Promise(function (resolve, reject) {
244
- var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((_this.ack >> 8) & 0xf)), _this.ack & 0xff, 0x00, msg]);
245
- latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), _this.TKEN]); // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
246
- _this.socket.send(latestBuf, 0, latestBuf.length, _this.port, _this.host, function (err, bytes) {
247
- resolve(bytes);
248
- });
245
+ if (_this.socket) {
246
+ var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((_this.ack >> 8) & 0xf)), _this.ack & 0xff, 0x00, msg]);
247
+ latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), _this.TKEN]); // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
248
+ _this.socket.send(latestBuf, 0, latestBuf.length, _this.port, _this.host, function (err, bytes) {
249
+ resolve(bytes);
250
+ });
251
+ }
249
252
  setTimeout(function () { resolve("failed, rip"); }, 2000);
250
253
  /* after 2 seconds it was probably not able to send,
251
254
  so when sending a quit message the user doesnt
@@ -256,6 +259,8 @@ var Client = /** @class */ (function (_super) {
256
259
  Client.prototype.SendMsgEx = function (Msg, Flags) {
257
260
  if (this.State == -1)
258
261
  throw new Error("Client is not connected");
262
+ if (!this.socket)
263
+ return;
259
264
  var header = [];
260
265
  header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
261
266
  header[1] = (Msg.size & 0xf);
@@ -266,15 +271,45 @@ var Client = /** @class */ (function (_super) {
266
271
  }
267
272
  var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
268
273
  var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
269
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, function (err, bytes) {
274
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host);
275
+ };
276
+ Client.prototype.SendMsgExWithChunks = function (Msgs, Flags) {
277
+ var _this = this;
278
+ if (this.State == -1)
279
+ throw new Error("Client is not connected");
280
+ if (!this.socket)
281
+ return;
282
+ var header = [];
283
+ Msgs.forEach(function (Msg, index) {
284
+ header[index] = new Array(2);
285
+ header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
286
+ header[index][1] = (Msg.size & 0xf);
287
+ if (Flags & 1) {
288
+ _this.clientAck = (_this.clientAck + 1) % (1 << 10);
289
+ header[index][1] |= (_this.clientAck >> 2) & 0xf0;
290
+ header[index][2] = _this.clientAck & 0xff;
291
+ }
270
292
  });
293
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
294
+ var chunks = Buffer.from([]);
295
+ Msgs.forEach(function (Msg, index) {
296
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
297
+ });
298
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
299
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
271
300
  };
272
301
  Client.prototype.connect = function () {
273
302
  var _this = this;
274
303
  this.SendControlMsg(1, "TKEN");
304
+ var connectInterval = setInterval(function () {
305
+ if (_this.State == 0)
306
+ _this.SendControlMsg(1, "TKEN");
307
+ else
308
+ clearInterval(connectInterval);
309
+ }, 500);
275
310
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
276
311
  this.socket.on("message", function (a) { return __awaiter(_this, void 0, void 0, function () {
277
- var unpacked, chunkMessages, chat, chat, packer, reason, Msg, Msg, packer, packer, chunks, part_1, num_parts_1;
312
+ var unpacked, chunkMessages, chat, chat, info, client_version, randomUuid, reason, Msg, Msg, info, info, chunks, part_1, num_parts_1;
278
313
  var _this = this;
279
314
  return __generator(this, function (_a) {
280
315
  switch (_a.label) {
@@ -330,13 +365,21 @@ var Client = /** @class */ (function (_super) {
330
365
  }
331
366
  if (a.toJSON().data[0] == 0x10) {
332
367
  if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
368
+ clearInterval(connectInterval);
333
369
  this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length));
334
370
  this.SendControlMsg(3);
335
371
  this.State = 2; // loading state
336
- packer = new MsgPacker_1.default(1, true);
337
- packer.AddString("0.6 626fce9a778df4d4");
338
- packer.AddString(""); // password
339
- this.SendMsgEx(packer, 1);
372
+ info = new MsgPacker_1.default(1, true);
373
+ info.AddString("0.6 626fce9a778df4d4");
374
+ info.AddString(""); // password
375
+ client_version = new MsgPacker_1.default(0, true);
376
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
377
+ randomUuid = new Uint8Array(16);
378
+ crypto_1.randomBytes(16).copy(randomUuid);
379
+ client_version.AddBuffer(Buffer.from(randomUuid));
380
+ client_version.AddInt(15091);
381
+ client_version.AddString("DDNet 15.9.1");
382
+ this.SendMsgExWithChunks([client_version, info], 1);
340
383
  }
341
384
  else if (a.toJSON().data[3] == 0x4) {
342
385
  // disconnected
@@ -355,15 +398,15 @@ var Client = /** @class */ (function (_super) {
355
398
  this.SendMsgEx(Msg, 1);
356
399
  }
357
400
  else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
358
- packer = new MsgPacker_1.default(20, false);
359
- packer.AddString(this.name); /* name */
360
- packer.AddString(""); /* clan */
361
- packer.AddInt(-1); /* country */
362
- packer.AddString("greyfox"); /* skin */
363
- packer.AddInt(1); /* use custom color */
364
- packer.AddInt(10346103); /* color body */
365
- packer.AddInt(65535); /* color feet */
366
- this.SendMsgEx(packer, 1);
401
+ info = new MsgPacker_1.default(20, false);
402
+ info.AddString(this.name); /* name */
403
+ info.AddString(""); /* clan */
404
+ info.AddInt(-1); /* country */
405
+ info.AddString("greyfox"); /* skin */
406
+ info.AddInt(1); /* use custom color */
407
+ info.AddInt(10346103); /* color body */
408
+ info.AddInt(65535); /* color feet */
409
+ this.SendMsgEx(info, 1);
367
410
  }
368
411
  else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
369
412
  if (this.State != 3) {
@@ -372,8 +415,8 @@ var Client = /** @class */ (function (_super) {
372
415
  this.State = 3;
373
416
  }
374
417
  else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
375
- packer = new MsgPacker_1.default(23, true);
376
- this.SendMsgEx(packer, 1);
418
+ info = new MsgPacker_1.default(23, true);
419
+ this.SendMsgEx(info, 1);
377
420
  }
378
421
  if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
379
422
  this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
@@ -439,9 +482,12 @@ var Client = /** @class */ (function (_super) {
439
482
  };
440
483
  Client.prototype.Disconnect = function () {
441
484
  var _this = this;
442
- this.SendControlMsg(4).then(function () {
443
- _this.State = -1;
444
- _this.socket.disconnect();
485
+ return new Promise(function (resolve) {
486
+ _this.SendControlMsg(4).then(function () {
487
+ resolve(true);
488
+ _this.socket.close();
489
+ _this.State = -1;
490
+ });
445
491
  });
446
492
  };
447
493
  Client.prototype.Say = function (message, team) {
package/lib/client.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { randomBytes } from "crypto";
2
+
1
3
  import net from 'dgram';
2
4
  import fs from 'fs';
3
5
  import { EventEmitter } from 'stream';
@@ -69,7 +71,7 @@ enum items {
69
71
  }
70
72
  interface chunk {
71
73
  bytes: number,
72
- flags: number,
74
+ flags: number,
73
75
  sequence?: number,
74
76
  seq?: number,
75
77
  type: 'sys' | 'game',
@@ -96,7 +98,7 @@ async function decompress(buff: Buffer): Promise<Buffer> {
96
98
 
97
99
  resolve(Buffer.from([]));
98
100
  }, 750)
99
-
101
+
100
102
  })
101
103
  }
102
104
  interface _packet {
@@ -105,7 +107,7 @@ interface _packet {
105
107
  }
106
108
 
107
109
  var messageTypes = [
108
- ["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"],
110
+ ["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"],
109
111
  ["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"]
110
112
  ]
111
113
 
@@ -119,28 +121,28 @@ var messageUUIDs = {
119
121
  "CAPABILITIES": Buffer.from([0xf6, 0x21, 0xa5, 0xa1, 0xf5, 0x85, 0x37, 0x75, 0x8e, 0x73, 0x41, 0xbe, 0xee, 0x79, 0xf2, 0xb2]),
120
122
  }
121
123
 
122
- function arrStartsWith(arr: number[], arrStart: number[], start=0) {
124
+ function arrStartsWith(arr: number[], arrStart: number[], start = 0) {
123
125
  arr.splice(0, start)
124
- for (let i = 0; i < arrStart.length; i++) {
126
+ for (let i = 0; i < arrStart.length; i++) {
125
127
  if (arr[i] == arrStart[i])
126
- continue;
127
- else return false;
128
- }
129
- return true;
128
+ continue;
129
+ else return false;
130
+ }
131
+ return true;
130
132
  }
131
133
  interface iMessage {
132
- team: number,
134
+ team: number,
133
135
  client_id: number,
134
- author?: {ClientInfo: ClientInfo, PlayerInfo: PlayerInfo},
136
+ author?: { ClientInfo: ClientInfo, PlayerInfo: PlayerInfo },
135
137
  message: string
136
138
  }
137
139
 
138
140
  interface iKillMsg {
139
- killer_id: number,
140
- killer?: {ClientInfo: ClientInfo, PlayerInfo: PlayerInfo},
141
- victim_id: number,
142
- victim?: {ClientInfo: ClientInfo, PlayerInfo: PlayerInfo},
143
- weapon: number,
141
+ killer_id: number,
142
+ killer?: { ClientInfo: ClientInfo, PlayerInfo: PlayerInfo },
143
+ victim_id: number,
144
+ victim?: { ClientInfo: ClientInfo, PlayerInfo: PlayerInfo },
145
+ weapon: number,
144
146
  special_mode: number
145
147
  }
146
148
 
@@ -162,12 +164,12 @@ declare interface Client {
162
164
  client_infos: ClientInfo[];
163
165
  player_infos: PlayerInfo[];
164
166
 
165
-
167
+
166
168
  on(event: 'connected'): this;
167
169
  on(event: 'disconnect', reason: string): this;
168
170
 
169
- on(event: 'message', message: iMessage, client_info: ClientInfo, player_info: PlayerInfo): this;
170
- on(event: 'kill', kill: {killer_id: number, victim_id: number, weapon: number, special_mode: number}, client_info: ClientInfo, player_info: PlayerInfo): this;
171
+ on(event: 'message', message: iMessage): this;
172
+ on(event: 'kill', kill: iKillMsg): this;
171
173
 
172
174
  }
173
175
  class Client extends EventEmitter {
@@ -189,23 +191,23 @@ class Client extends EventEmitter {
189
191
  this.clientAck = 0; // ack of messages the client has sent
190
192
  this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
191
193
  this.lastMsg = "";
192
- this._port = Math.floor(Math.random()*65535)
194
+ this._port = Math.floor(Math.random() * 65535)
193
195
  this.socket = net.createSocket("udp4")
194
196
  this.socket.bind();
195
197
 
196
198
  this.TKEN = Buffer.from([255, 255, 255, 255])
197
- this.time = new Date().getTime()+2000; // time (used for keepalives, start to send keepalives after 2 seconds)
199
+ this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
198
200
  this.State = 0;
199
201
  }
200
202
  async Unpack(packet: Buffer): Promise<_packet> {
201
- var unpacked: _packet = {twprotocol: {flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength-3}, chunks: []}
202
-
203
-
204
- 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)
203
+ var unpacked: _packet = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
204
+
205
+
206
+ 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)
205
207
  return unpacked;
206
208
  packet = packet.slice(3)
207
209
  if (unpacked.twprotocol.flags & 128) {
208
- packet = await decompress(packet)
210
+ packet = await decompress(packet)
209
211
  if (packet.length == 1 && packet[0] == -1)
210
212
  return unpacked
211
213
  }
@@ -215,15 +217,15 @@ class Client extends EventEmitter {
215
217
  chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1)); // idk what this shit is but it works
216
218
  chunk.flags = (packet[0] >> 6) & 3;
217
219
  chunk.sequence = -1;
218
-
220
+
219
221
  if (chunk.flags & 1) {
220
- chunk.seq = ((packet[1]&0xf0)<<2) | packet[2];
222
+ chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
221
223
  packet = packet.slice(3) // remove flags & size
222
224
  } else
223
225
  packet = packet.slice(2)
224
226
  chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
225
- chunk.msgid = (packet[0]-(packet[0]&1))/2;
226
- chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
227
+ chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
228
+ chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
227
229
  chunk.raw = packet.slice(1, chunk.bytes)
228
230
  Object.values(messageUUIDs).forEach((a, i) => {
229
231
  if (a.compare(packet.slice(0, 16)) == 0) {
@@ -232,209 +234,260 @@ class Client extends EventEmitter {
232
234
  chunk.msg = Object.keys(messageUUIDs)[i];
233
235
  }
234
236
  })
235
-
237
+
236
238
  packet = packet.slice(chunk.bytes) // +1 cuz it adds an extra \x00 for easier parsing i guess
237
239
  unpacked.chunks.push(chunk)
238
240
  }
239
241
  return unpacked
240
- }
242
+ }
241
243
  SendControlMsg(msg: number, ExtraMsg: string = "") {
242
244
  return new Promise((resolve, reject) => {
243
-
244
- var latestBuf = Buffer.from([0x10+(((16<<4)&0xf0)|((this.ack>>8)&0xf)), this.ack&0xff, 0x00, msg])
245
- latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), this.TKEN]) // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
245
+ if (this.socket) {
246
+ var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg])
247
+ latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), this.TKEN]) // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
246
248
  this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
247
249
  resolve(bytes)
248
250
  })
249
- setTimeout(() => { resolve("failed, rip") }, 2000)
251
+
252
+ }
253
+ setTimeout(() => { resolve("failed, rip") }, 2000)
250
254
  /* after 2 seconds it was probably not able to send,
251
255
  so when sending a quit message the user doesnt
252
256
  stay stuck not being able to ctrl + c
253
257
  */
254
- })
258
+ })
255
259
  }
256
260
  SendMsgEx(Msg: MsgPacker, Flags: number) {
257
261
  if (this.State == -1)
258
262
  throw new Error("Client is not connected");
259
- var header = []
260
- header[0] = ((Flags&3)<<6)|((Msg.size>>4)&0x3f);
261
- header[1] = (Msg.size&0xf);
262
- if(Flags&1) {
263
- this.clientAck = (this.clientAck+1)%(1<<10);
264
- header[1] |= (this.clientAck>>2)&0xf0;
265
- header[2] = this.clientAck&0xff;
266
- }
267
- var latestBuf = Buffer.from([0x0+(((16<<4)&0xf0)|((this.ack>>8)&0xf)), this.ack&0xff, 0x1, header[0], header[1], this.clientAck]);
263
+ if (!this.socket)
264
+ return;
265
+ var header = []
266
+ header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
267
+ header[1] = (Msg.size & 0xf);
268
+ if (Flags & 1) {
269
+ this.clientAck = (this.clientAck + 1) % (1 << 10);
270
+ header[1] |= (this.clientAck >> 2) & 0xf0;
271
+ header[2] = this.clientAck & 0xff;
272
+ }
273
+ var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
268
274
  var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
269
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
270
- })
275
+
276
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host)
277
+ }
278
+ SendMsgExWithChunks(Msgs: MsgPacker[], Flags: number) {
279
+ if (this.State == -1)
280
+ throw new Error("Client is not connected");
281
+ if (!this.socket)
282
+ return;
283
+ var header: any[][] = [];
284
+
285
+ Msgs.forEach((Msg: MsgPacker, index) => {
286
+ header[index] = new Array(2);
287
+ header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
288
+ header[index][1] = (Msg.size & 0xf);
289
+ if (Flags & 1) {
290
+ this.clientAck = (this.clientAck + 1) % (1 << 10);
291
+ header[index][1] |= (this.clientAck >> 2) & 0xf0;
292
+ header[index][2] = this.clientAck & 0xff;
293
+ }
294
+ })
295
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
296
+ var chunks = Buffer.from([]);
297
+ Msgs.forEach((Msg: MsgPacker, index) => {
298
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
299
+ })
300
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
301
+
302
+ this.socket.send(packet, 0, packet.length, this.port, this.host)
271
303
  }
272
304
  connect() {
273
305
  this.SendControlMsg(1, "TKEN")
306
+ let connectInterval = setInterval(() => {
307
+ if (this.State == 0)
308
+ this.SendControlMsg(1, "TKEN")
309
+ else
310
+ clearInterval(connectInterval)
311
+ }, 500)
274
312
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
275
- this.socket.on("message", async (a) => {
276
- var unpacked: _packet = await this.Unpack(a)
277
- if (unpacked.twprotocol.flags != 128 && unpacked.twprotocol.ack) {
278
- unpacked.chunks.forEach(a => {
279
- if (a.msg && !a.msg.startsWith("SNAP")) {
280
- if (a.seq != undefined && a.seq != -1)
281
- this.ack = a.seq
282
-
283
- }
284
- })
285
- }
286
- var chunkMessages = unpacked.chunks.map(a => a.msg)
287
- if (chunkMessages.includes("SV_CHAT")) {
288
- var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
289
- chat.forEach(a => {
290
- if (a.msg == "SV_CHAT") {
291
- var unpacked: iMessage = {} as iMessage;
292
- unpacked.team = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
293
- var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
294
- unpacked.client_id = MsgUnpacker.unpackInt(remaining).result;
295
- remaining = MsgUnpacker.unpackInt(remaining).remaining;
296
- unpacked.message = MsgUnpacker.unpackString(remaining).result;
297
- if (unpacked.client_id != -1)
298
- unpacked.author = {ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id]}
299
- // console.log(unpacked)
300
- this.emit("message", unpacked)
301
- }
302
- })
303
- }
304
- if (chunkMessages.includes("SV_KILL_MSG")) {
305
- var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
306
- chat.forEach(a => {
307
- if (a.msg == "SV_KILL_MSG") {
308
- var unpacked: iKillMsg = {} as iKillMsg;
309
- unpacked.killer_id = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
310
- var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
311
- unpacked.victim_id = MsgUnpacker.unpackInt(remaining).result;
312
- remaining = MsgUnpacker.unpackInt(remaining).remaining;
313
- unpacked.weapon = MsgUnpacker.unpackInt(remaining).result;
314
- remaining = MsgUnpacker.unpackInt(remaining).remaining;
315
- unpacked.special_mode = MsgUnpacker.unpackInt(remaining).result;
316
- if (unpacked.victim_id != -1)
317
- unpacked.victim = {ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id]}
318
- if (unpacked.killer_id != -1)
319
- unpacked.killer = {ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id]}
320
- // console.log(unpacked)
321
- this.emit("kill", unpacked)
313
+ this.socket.on("message", async (a) => {
314
+ var unpacked: _packet = await this.Unpack(a)
315
+ if (unpacked.twprotocol.flags != 128 && unpacked.twprotocol.ack) {
316
+ unpacked.chunks.forEach(a => {
317
+ if (a.msg && !a.msg.startsWith("SNAP")) {
318
+ if (a.seq != undefined && a.seq != -1)
319
+ this.ack = a.seq
320
+
321
+ }
322
+ })
323
+ }
324
+ var chunkMessages = unpacked.chunks.map(a => a.msg)
325
+ if (chunkMessages.includes("SV_CHAT")) {
326
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
327
+ chat.forEach(a => {
328
+ if (a.msg == "SV_CHAT") {
329
+ var unpacked: iMessage = {} as iMessage;
330
+ unpacked.team = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
331
+ var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
332
+ unpacked.client_id = MsgUnpacker.unpackInt(remaining).result;
333
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
334
+ unpacked.message = MsgUnpacker.unpackString(remaining).result;
335
+ if (unpacked.client_id != -1)
336
+ unpacked.author = { ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id] }
337
+ // console.log(unpacked)
338
+ this.emit("message", unpacked)
339
+ }
340
+ })
341
+ }
342
+ if (chunkMessages.includes("SV_KILL_MSG")) {
343
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
344
+ chat.forEach(a => {
345
+ if (a.msg == "SV_KILL_MSG") {
346
+ var unpacked: iKillMsg = {} as iKillMsg;
347
+ unpacked.killer_id = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
348
+ var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
349
+ unpacked.victim_id = MsgUnpacker.unpackInt(remaining).result;
350
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
351
+ unpacked.weapon = MsgUnpacker.unpackInt(remaining).result;
352
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
353
+ unpacked.special_mode = MsgUnpacker.unpackInt(remaining).result;
354
+ if (unpacked.victim_id != -1)
355
+ unpacked.victim = { ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id] }
356
+ if (unpacked.killer_id != -1)
357
+ unpacked.killer = { ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id] }
358
+ // console.log(unpacked)
359
+ this.emit("kill", unpacked)
360
+ }
361
+ })
362
+ }
363
+ if (a.toJSON().data[0] == 0x10) {
364
+ if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
365
+ clearInterval(connectInterval);
366
+ this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
367
+ this.SendControlMsg(3);
368
+ this.State = 2; // loading state
369
+ var info = new MsgPacker(1, true);
370
+ info.AddString("0.6 626fce9a778df4d4");
371
+ info.AddString(""); // password
372
+
373
+ var client_version = new MsgPacker(0, true);
374
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
375
+ let randomUuid = new Uint8Array(16);
376
+
377
+ randomBytes(16).copy(randomUuid);
378
+
379
+ client_version.AddBuffer(Buffer.from(randomUuid));
380
+ client_version.AddInt(15091);
381
+ client_version.AddString("DDNet 15.9.1");
382
+
383
+ this.SendMsgExWithChunks([client_version, info], 1)
384
+ } else if (a.toJSON().data[3] == 0x4) {
385
+ // disconnected
386
+ this.State = 0;
387
+ let reason: string = (MsgUnpacker.unpackString(a.toJSON().data.slice(4)).result);
388
+ this.State = -1;
389
+ this.emit("disconnect", reason);
322
390
  }
323
- })
324
- }
325
- if (a.toJSON().data[0] == 0x10) {
326
- if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
327
- this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length-4, a.toJSON().data.length))
328
- this.SendControlMsg(3);
329
- this.State = 2; // loading state
330
- var packer = new MsgPacker(1, true);
331
- packer.AddString("0.6 626fce9a778df4d4");
332
- packer.AddString(""); // password
333
- this.SendMsgEx(packer, 1)
334
- } else if (a.toJSON().data[3] == 0x4) {
335
- // disconnected
336
- this.State = 0;
337
- let reason: string = (MsgUnpacker.unpackString(a.toJSON().data.slice(4)).result);
338
- this.State = -1;
339
- this.emit("disconnect", reason);
340
- }
341
391
 
342
- }
343
- if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
344
- var Msg = new MsgPacker(15, true); /* entergame */
345
- this.SendMsgEx(Msg, 1);
346
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
347
- // send ready
348
- var Msg = new MsgPacker(14, true); /* ready */
349
- this.SendMsgEx(Msg, 1);
350
- } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
351
- var packer = new MsgPacker(20, false);
352
- packer.AddString(this.name); /* name */
353
- packer.AddString(""); /* clan */
354
- packer.AddInt(-1); /* country */
355
- packer.AddString("greyfox"); /* skin */
356
- packer.AddInt(1); /* use custom color */
357
- packer.AddInt(10346103); /* color body */
358
- packer.AddInt(65535); /* color feet */
359
- this.SendMsgEx(packer, 1);
360
-
361
-
362
- } else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
363
-
364
- if (this.State != 3) {
365
- this.emit('connected');
366
392
  }
367
- this.State = 3
368
- } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
369
- var packer = new MsgPacker(23, true);
370
- this.SendMsgEx(packer, 1)
371
- }
372
- if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
373
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
374
- if (this.receivedSnaps >= 2) {
375
- if (this.State != 3)
376
- this.emit('connected')
393
+ if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
394
+ var Msg = new MsgPacker(15, true); /* entergame */
395
+ this.SendMsgEx(Msg, 1);
396
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
397
+ // send ready
398
+ var Msg = new MsgPacker(14, true); /* ready */
399
+ this.SendMsgEx(Msg, 1);
400
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
401
+ var info = new MsgPacker(20, false);
402
+ info.AddString(this.name); /* name */
403
+ info.AddString(""); /* clan */
404
+ info.AddInt(-1); /* country */
405
+ info.AddString("greyfox"); /* skin */
406
+ info.AddInt(1); /* use custom color */
407
+ info.AddInt(10346103); /* color body */
408
+ info.AddInt(65535); /* color feet */
409
+ this.SendMsgEx(info, 1);
410
+
411
+
412
+ } else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
413
+
414
+ if (this.State != 3) {
415
+ this.emit('connected');
416
+ }
377
417
  this.State = 3
418
+ } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
419
+ var info = new MsgPacker(23, true);
420
+ this.SendMsgEx(info, 1)
378
421
  }
422
+ if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
423
+ this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
424
+ if (this.receivedSnaps >= 2) {
425
+ if (this.State != 3)
426
+ this.emit('connected')
427
+ this.State = 3
428
+ }
379
429
 
380
- var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
381
- if (chunks.length > 0) {
382
- let part = 0;
383
- let num_parts = 1;
384
- chunks.forEach(chunk => {
385
- let AckGameTick = (MsgUnpacker.unpackInt(chunk.raw.toJSON().data).result);
386
- chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining);
387
- let DeltaTick = MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result
388
- if (chunk.msg == "SNAP") {
389
- chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
390
- num_parts = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
391
- chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
392
- part = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
393
- }
394
- chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // part
395
- if (chunk.msg != "SNAP_EMPTY")
430
+ var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
431
+ if (chunks.length > 0) {
432
+ let part = 0;
433
+ let num_parts = 1;
434
+ chunks.forEach(chunk => {
435
+ let AckGameTick = (MsgUnpacker.unpackInt(chunk.raw.toJSON().data).result);
436
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining);
437
+ let DeltaTick = MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result
438
+ if (chunk.msg == "SNAP") {
439
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
440
+ num_parts = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
441
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
442
+ part = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
443
+ }
444
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // part
445
+ if (chunk.msg != "SNAP_EMPTY")
446
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
396
447
  chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
397
- chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
398
- if (part == 0 || this.snaps.length > 30) {
399
- this.snaps = [];
400
- }
401
- this.snaps.push(chunk.raw)
402
-
403
- if ((num_parts-1) == part && this.snaps.length == num_parts) {
404
- let mergedSnaps = Buffer.concat(this.snaps);
405
- let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
406
-
407
- snapUnpacked.items.forEach((a, i) => {
408
- if (a.type_id == items.OBJ_CLIENT_INFO) {
409
- this.client_infos[a.id] = a.parsed as ClientInfo;
410
- // console.log(a.parsed, i)
411
- // console.log(this.client_infos[a.id])
412
- } else if (a.type_id == items.OBJ_PLAYER_INFO) {
413
- this.player_infos[i] = a.parsed as PlayerInfo;
414
- } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
415
- if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
416
- // var packer = new MsgPacker(22, false)
417
- // this.SendMsgEx(packer, 1)
448
+ if (part == 0 || this.snaps.length > 30) {
449
+ this.snaps = [];
450
+ }
451
+ this.snaps.push(chunk.raw)
452
+
453
+ if ((num_parts - 1) == part && this.snaps.length == num_parts) {
454
+ let mergedSnaps = Buffer.concat(this.snaps);
455
+ let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
456
+
457
+ snapUnpacked.items.forEach((a, i) => {
458
+ if (a.type_id == items.OBJ_CLIENT_INFO) {
459
+ this.client_infos[a.id] = a.parsed as ClientInfo;
460
+ // console.log(a.parsed, i)
461
+ // console.log(this.client_infos[a.id])
462
+ } else if (a.type_id == items.OBJ_PLAYER_INFO) {
463
+ this.player_infos[i] = a.parsed as PlayerInfo;
464
+ } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
465
+ if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
466
+ // var packer = new MsgPacker(22, false)
467
+ // this.SendMsgEx(packer, 1)
468
+ }
418
469
  }
419
- }
420
- })
421
- }
470
+ })
471
+ }
422
472
 
423
- })
424
- }
473
+ })
474
+ }
425
475
 
426
- }
427
- if (new Date().getTime() - this.time >= 1000) {
428
- this.time = new Date().getTime();
429
- this.SendControlMsg(0);
430
- }
431
- })
476
+ }
477
+ if (new Date().getTime() - this.time >= 1000) {
478
+ this.time = new Date().getTime();
479
+ this.SendControlMsg(0);
480
+ }
481
+ })
432
482
  }
433
483
 
434
484
  Disconnect() {
435
- this.SendControlMsg(4).then(() => {
436
- this.State = -1;
437
- this.socket.disconnect();
485
+ return new Promise((resolve) => {
486
+ this.SendControlMsg(4).then(() => {
487
+ resolve(true);
488
+ this.socket.close();
489
+ this.State = -1;
490
+ })
438
491
  })
439
492
  }
440
493
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teeworlds",
3
- "version": "2.0.0",
3
+ "version": "2.0.3",
4
4
  "description": "Library for (ingame) teeworlds bots.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",