teeworlds 2.0.0 → 2.0.1

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");
@@ -266,15 +267,43 @@ var Client = /** @class */ (function (_super) {
266
267
  }
267
268
  var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
268
269
  var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
269
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, function (err, bytes) {
270
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host);
271
+ };
272
+ Client.prototype.SendMsgExWithChunks = function (Msgs, Flags) {
273
+ var _this = this;
274
+ if (this.State == -1)
275
+ throw new Error("Client is not connected");
276
+ var header = [];
277
+ Msgs.forEach(function (Msg, index) {
278
+ header[index] = new Array(2);
279
+ header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
280
+ header[index][1] = (Msg.size & 0xf);
281
+ if (Flags & 1) {
282
+ _this.clientAck = (_this.clientAck + 1) % (1 << 10);
283
+ header[index][1] |= (_this.clientAck >> 2) & 0xf0;
284
+ header[index][2] = _this.clientAck & 0xff;
285
+ }
270
286
  });
287
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
288
+ var chunks = Buffer.from([]);
289
+ Msgs.forEach(function (Msg, index) {
290
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
291
+ });
292
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
293
+ this.socket.send(packet, 0, packet.length, this.port, this.host);
271
294
  };
272
295
  Client.prototype.connect = function () {
273
296
  var _this = this;
274
297
  this.SendControlMsg(1, "TKEN");
298
+ var connectInterval = setInterval(function () {
299
+ if (_this.State == 0)
300
+ _this.SendControlMsg(1, "TKEN");
301
+ else
302
+ clearInterval(connectInterval);
303
+ }, 500);
275
304
  this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
276
305
  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;
306
+ var unpacked, chunkMessages, chat, chat, info, client_version, randomUuid, reason, Msg, Msg, info, info, chunks, part_1, num_parts_1;
278
307
  var _this = this;
279
308
  return __generator(this, function (_a) {
280
309
  switch (_a.label) {
@@ -330,13 +359,21 @@ var Client = /** @class */ (function (_super) {
330
359
  }
331
360
  if (a.toJSON().data[0] == 0x10) {
332
361
  if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
362
+ clearInterval(connectInterval);
333
363
  this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length));
334
364
  this.SendControlMsg(3);
335
365
  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);
366
+ info = new MsgPacker_1.default(1, true);
367
+ info.AddString("0.6 626fce9a778df4d4");
368
+ info.AddString(""); // password
369
+ client_version = new MsgPacker_1.default(0, true);
370
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
371
+ randomUuid = new Uint8Array(16);
372
+ crypto_1.randomBytes(16).copy(randomUuid);
373
+ client_version.AddBuffer(Buffer.from(randomUuid));
374
+ client_version.AddInt(15091);
375
+ client_version.AddString("DDNet 15.9.1");
376
+ this.SendMsgExWithChunks([client_version, info], 1);
340
377
  }
341
378
  else if (a.toJSON().data[3] == 0x4) {
342
379
  // disconnected
@@ -355,15 +392,15 @@ var Client = /** @class */ (function (_super) {
355
392
  this.SendMsgEx(Msg, 1);
356
393
  }
357
394
  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);
395
+ info = new MsgPacker_1.default(20, false);
396
+ info.AddString(this.name); /* name */
397
+ info.AddString(""); /* clan */
398
+ info.AddInt(-1); /* country */
399
+ info.AddString("greyfox"); /* skin */
400
+ info.AddInt(1); /* use custom color */
401
+ info.AddInt(10346103); /* color body */
402
+ info.AddInt(65535); /* color feet */
403
+ this.SendMsgEx(info, 1);
367
404
  }
368
405
  else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
369
406
  if (this.State != 3) {
@@ -372,8 +409,8 @@ var Client = /** @class */ (function (_super) {
372
409
  this.State = 3;
373
410
  }
374
411
  else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
375
- packer = new MsgPacker_1.default(23, true);
376
- this.SendMsgEx(packer, 1);
412
+ info = new MsgPacker_1.default(23, true);
413
+ this.SendMsgEx(info, 1);
377
414
  }
378
415
  if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
379
416
  this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
@@ -439,9 +476,12 @@ var Client = /** @class */ (function (_super) {
439
476
  };
440
477
  Client.prototype.Disconnect = function () {
441
478
  var _this = this;
442
- this.SendControlMsg(4).then(function () {
443
- _this.State = -1;
444
- _this.socket.disconnect();
479
+ return new Promise(function (resolve) {
480
+ _this.SendControlMsg(4).then(function () {
481
+ resolve(true);
482
+ _this.socket.close();
483
+ _this.State = -1;
484
+ });
445
485
  });
446
486
  };
447
487
  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,254 @@ 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
+
246
+ var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg])
245
247
  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, (err, bytes) => {
247
- resolve(bytes)
248
- })
249
- setTimeout(() => { resolve("failed, rip") }, 2000)
248
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
249
+ resolve(bytes)
250
+ })
251
+ setTimeout(() => { resolve("failed, rip") }, 2000)
250
252
  /* after 2 seconds it was probably not able to send,
251
253
  so when sending a quit message the user doesnt
252
254
  stay stuck not being able to ctrl + c
253
255
  */
254
- })
256
+ })
255
257
  }
256
258
  SendMsgEx(Msg: MsgPacker, Flags: number) {
257
259
  if (this.State == -1)
258
260
  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]);
261
+ var header = []
262
+ header[0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
263
+ header[1] = (Msg.size & 0xf);
264
+ if (Flags & 1) {
265
+ this.clientAck = (this.clientAck + 1) % (1 << 10);
266
+ header[1] |= (this.clientAck >> 2) & 0xf0;
267
+ header[2] = this.clientAck & 0xff;
268
+ }
269
+ var latestBuf = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x1, header[0], header[1], this.clientAck]);
268
270
  var latestBuf = Buffer.concat([latestBuf, Msg.buffer, this.TKEN]);
269
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
270
- })
271
+
272
+ this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host)
273
+ }
274
+ SendMsgExWithChunks(Msgs: MsgPacker[], Flags: number) {
275
+ if (this.State == -1)
276
+ throw new Error("Client is not connected");
277
+ var header: any[][] = [];
278
+
279
+ Msgs.forEach((Msg: MsgPacker, index) => {
280
+ header[index] = new Array(2);
281
+ header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
282
+ header[index][1] = (Msg.size & 0xf);
283
+ if (Flags & 1) {
284
+ this.clientAck = (this.clientAck + 1) % (1 << 10);
285
+ header[index][1] |= (this.clientAck >> 2) & 0xf0;
286
+ header[index][2] = this.clientAck & 0xff;
287
+ }
288
+ })
289
+ var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, Msgs.length]);
290
+ var chunks = Buffer.from([]);
291
+ Msgs.forEach((Msg: MsgPacker, index) => {
292
+ chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
293
+ })
294
+ var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
295
+
296
+ this.socket.send(packet, 0, packet.length, this.port, this.host)
271
297
  }
272
298
  connect() {
273
299
  this.SendControlMsg(1, "TKEN")
300
+ let connectInterval = setInterval(() => {
301
+ if (this.State == 0)
302
+ this.SendControlMsg(1, "TKEN")
303
+ else
304
+ clearInterval(connectInterval)
305
+ }, 500)
274
306
  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)
307
+ this.socket.on("message", async (a) => {
308
+ var unpacked: _packet = await this.Unpack(a)
309
+ if (unpacked.twprotocol.flags != 128 && unpacked.twprotocol.ack) {
310
+ unpacked.chunks.forEach(a => {
311
+ if (a.msg && !a.msg.startsWith("SNAP")) {
312
+ if (a.seq != undefined && a.seq != -1)
313
+ this.ack = a.seq
314
+
315
+ }
316
+ })
317
+ }
318
+ var chunkMessages = unpacked.chunks.map(a => a.msg)
319
+ if (chunkMessages.includes("SV_CHAT")) {
320
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
321
+ chat.forEach(a => {
322
+ if (a.msg == "SV_CHAT") {
323
+ var unpacked: iMessage = {} as iMessage;
324
+ unpacked.team = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
325
+ var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
326
+ unpacked.client_id = MsgUnpacker.unpackInt(remaining).result;
327
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
328
+ unpacked.message = MsgUnpacker.unpackString(remaining).result;
329
+ if (unpacked.client_id != -1)
330
+ unpacked.author = { ClientInfo: this.client_infos[unpacked.client_id], PlayerInfo: this.player_infos[unpacked.client_id] }
331
+ // console.log(unpacked)
332
+ this.emit("message", unpacked)
333
+ }
334
+ })
335
+ }
336
+ if (chunkMessages.includes("SV_KILL_MSG")) {
337
+ var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
338
+ chat.forEach(a => {
339
+ if (a.msg == "SV_KILL_MSG") {
340
+ var unpacked: iKillMsg = {} as iKillMsg;
341
+ unpacked.killer_id = MsgUnpacker.unpackInt(a.raw.toJSON().data).result;
342
+ var remaining: number[] = MsgUnpacker.unpackInt(a.raw.toJSON().data).remaining;
343
+ unpacked.victim_id = MsgUnpacker.unpackInt(remaining).result;
344
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
345
+ unpacked.weapon = MsgUnpacker.unpackInt(remaining).result;
346
+ remaining = MsgUnpacker.unpackInt(remaining).remaining;
347
+ unpacked.special_mode = MsgUnpacker.unpackInt(remaining).result;
348
+ if (unpacked.victim_id != -1)
349
+ unpacked.victim = { ClientInfo: this.client_infos[unpacked.victim_id], PlayerInfo: this.player_infos[unpacked.victim_id] }
350
+ if (unpacked.killer_id != -1)
351
+ unpacked.killer = { ClientInfo: this.client_infos[unpacked.killer_id], PlayerInfo: this.player_infos[unpacked.killer_id] }
352
+ // console.log(unpacked)
353
+ this.emit("kill", unpacked)
354
+ }
355
+ })
356
+ }
357
+ if (a.toJSON().data[0] == 0x10) {
358
+ if (a.toString().includes("TKEN") || arrStartsWith(a.toJSON().data, [0x10, 0x0, 0x0, 0x0])) {
359
+ clearInterval(connectInterval);
360
+ this.TKEN = Buffer.from(a.toJSON().data.slice(a.toJSON().data.length - 4, a.toJSON().data.length))
361
+ this.SendControlMsg(3);
362
+ this.State = 2; // loading state
363
+ var info = new MsgPacker(1, true);
364
+ info.AddString("0.6 626fce9a778df4d4");
365
+ info.AddString(""); // password
366
+
367
+ var client_version = new MsgPacker(0, true);
368
+ client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
369
+ let randomUuid = new Uint8Array(16);
370
+
371
+ randomBytes(16).copy(randomUuid);
372
+
373
+ client_version.AddBuffer(Buffer.from(randomUuid));
374
+ client_version.AddInt(15091);
375
+ client_version.AddString("DDNet 15.9.1");
376
+
377
+ this.SendMsgExWithChunks([client_version, info], 1)
378
+ } else if (a.toJSON().data[3] == 0x4) {
379
+ // disconnected
380
+ this.State = 0;
381
+ let reason: string = (MsgUnpacker.unpackString(a.toJSON().data.slice(4)).result);
382
+ this.State = -1;
383
+ this.emit("disconnect", reason);
322
384
  }
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
385
 
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
386
  }
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')
387
+ if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
388
+ var Msg = new MsgPacker(15, true); /* entergame */
389
+ this.SendMsgEx(Msg, 1);
390
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
391
+ // send ready
392
+ var Msg = new MsgPacker(14, true); /* ready */
393
+ this.SendMsgEx(Msg, 1);
394
+ } else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
395
+ var info = new MsgPacker(20, false);
396
+ info.AddString(this.name); /* name */
397
+ info.AddString(""); /* clan */
398
+ info.AddInt(-1); /* country */
399
+ info.AddString("greyfox"); /* skin */
400
+ info.AddInt(1); /* use custom color */
401
+ info.AddInt(10346103); /* color body */
402
+ info.AddInt(65535); /* color feet */
403
+ this.SendMsgEx(info, 1);
404
+
405
+
406
+ } else if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
407
+
408
+ if (this.State != 3) {
409
+ this.emit('connected');
410
+ }
377
411
  this.State = 3
412
+ } else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
413
+ var info = new MsgPacker(23, true);
414
+ this.SendMsgEx(info, 1)
378
415
  }
416
+ if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
417
+ this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
418
+ if (this.receivedSnaps >= 2) {
419
+ if (this.State != 3)
420
+ this.emit('connected')
421
+ this.State = 3
422
+ }
379
423
 
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")
424
+ var chunks = unpacked.chunks.filter(a => a.msg == "SNAP" || a.msg == "SNAP_SINGLE" || a.msg == "SNAP_EMPTY");
425
+ if (chunks.length > 0) {
426
+ let part = 0;
427
+ let num_parts = 1;
428
+ chunks.forEach(chunk => {
429
+ let AckGameTick = (MsgUnpacker.unpackInt(chunk.raw.toJSON().data).result);
430
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining);
431
+ let DeltaTick = MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result
432
+ if (chunk.msg == "SNAP") {
433
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
434
+ num_parts = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
435
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
436
+ part = (MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).result)
437
+ }
438
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // part
439
+ if (chunk.msg != "SNAP_EMPTY")
440
+ chunk.raw = Buffer.from(MsgUnpacker.unpackInt(chunk?.raw?.toJSON().data).remaining); // crc
396
441
  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)
442
+ if (part == 0 || this.snaps.length > 30) {
443
+ this.snaps = [];
444
+ }
445
+ this.snaps.push(chunk.raw)
446
+
447
+ if ((num_parts - 1) == part && this.snaps.length == num_parts) {
448
+ let mergedSnaps = Buffer.concat(this.snaps);
449
+ let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
450
+
451
+ snapUnpacked.items.forEach((a, i) => {
452
+ if (a.type_id == items.OBJ_CLIENT_INFO) {
453
+ this.client_infos[a.id] = a.parsed as ClientInfo;
454
+ // console.log(a.parsed, i)
455
+ // console.log(this.client_infos[a.id])
456
+ } else if (a.type_id == items.OBJ_PLAYER_INFO) {
457
+ this.player_infos[i] = a.parsed as PlayerInfo;
458
+ } else if (a.type_id == items.OBJ_EX || a.type_id > 0x4000) {
459
+ if (a.data.length == 5 && ((a.parsed as DdnetCharacter).freeze_end > 0 || (a.parsed as DdnetCharacter).freeze_end == -1)) {
460
+ // var packer = new MsgPacker(22, false)
461
+ // this.SendMsgEx(packer, 1)
462
+ }
418
463
  }
419
- }
420
- })
421
- }
464
+ })
465
+ }
422
466
 
423
- })
424
- }
467
+ })
468
+ }
425
469
 
426
- }
427
- if (new Date().getTime() - this.time >= 1000) {
428
- this.time = new Date().getTime();
429
- this.SendControlMsg(0);
430
- }
431
- })
470
+ }
471
+ if (new Date().getTime() - this.time >= 1000) {
472
+ this.time = new Date().getTime();
473
+ this.SendControlMsg(0);
474
+ }
475
+ })
432
476
  }
433
477
 
434
478
  Disconnect() {
435
- this.SendControlMsg(4).then(() => {
436
- this.State = -1;
437
- this.socket.disconnect();
479
+ return new Promise((resolve) => {
480
+ this.SendControlMsg(4).then(() => {
481
+ resolve(true);
482
+ this.socket.close();
483
+ this.State = -1;
484
+ })
438
485
  })
439
486
  }
440
487
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teeworlds",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Library for (ingame) teeworlds bots.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",