teeworlds 2.3.1 → 2.3.4
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/MsgPacker.js +2 -1
- package/lib/MsgPacker.ts +3 -1
- package/lib/client.js +75 -68
- package/lib/client.ts +71 -58
- package/package.json +1 -1
package/lib/MsgPacker.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MsgPacker = void 0;
|
|
4
4
|
var MsgPacker = /** @class */ (function () {
|
|
5
|
-
function MsgPacker(msg, sys) {
|
|
5
|
+
function MsgPacker(msg, sys, flag) {
|
|
6
6
|
this.result = Buffer.from([2 * msg + (sys ? 1 : 0)]);
|
|
7
7
|
this.sys = sys;
|
|
8
|
+
this.flag = flag;
|
|
8
9
|
}
|
|
9
10
|
MsgPacker.prototype.AddString = function (str) {
|
|
10
11
|
this.result = Buffer.concat([this.result, Buffer.from(str), Buffer.from([0x00])]);
|
package/lib/MsgPacker.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export class MsgPacker {
|
|
2
2
|
result: Buffer;
|
|
3
3
|
sys: boolean;
|
|
4
|
-
|
|
4
|
+
flag: number;
|
|
5
|
+
constructor(msg: number, sys: boolean, flag: number) {
|
|
5
6
|
this.result = Buffer.from([2*msg + (sys ? 1 : 0)]);
|
|
6
7
|
this.sys = sys;
|
|
8
|
+
this.flag = flag;
|
|
7
9
|
}
|
|
8
10
|
AddString(str: string) {
|
|
9
11
|
this.result = Buffer.concat([this.result, Buffer.from(str), Buffer.from([0x00])])
|
package/lib/client.js
CHANGED
|
@@ -111,7 +111,6 @@ var Client = /** @class */ (function (_super) {
|
|
|
111
111
|
_this.SnapUnpacker = new snapshot_1.Snapshot();
|
|
112
112
|
// this.eSnapHolder = [];
|
|
113
113
|
_this.requestResend = false;
|
|
114
|
-
_this.pingStart = 0;
|
|
115
114
|
if (options)
|
|
116
115
|
_this.options = options;
|
|
117
116
|
_this.timer = 0;
|
|
@@ -140,11 +139,12 @@ var Client = /** @class */ (function (_super) {
|
|
|
140
139
|
if (msg.ack > lastAck)
|
|
141
140
|
toResend.push(msg.msg);
|
|
142
141
|
});
|
|
143
|
-
|
|
142
|
+
toResend.forEach(function (a) { return a.flag = 1 | 2; });
|
|
143
|
+
this.SendMsgEx(toResend);
|
|
144
144
|
};
|
|
145
145
|
Client.prototype.Unpack = function (packet) {
|
|
146
146
|
var unpacked = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0] & 0xf) << 8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
|
|
147
|
-
if (packet.indexOf(Buffer.from([0xff, 0xff, 0xff, 0xff])) == 0
|
|
147
|
+
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)
|
|
148
148
|
return unpacked;
|
|
149
149
|
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
150
150
|
this.ResendAfter(unpacked.twprotocol.ack);
|
|
@@ -200,7 +200,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
200
200
|
*/
|
|
201
201
|
});
|
|
202
202
|
};
|
|
203
|
-
Client.prototype.SendMsgEx = function (Msgs
|
|
203
|
+
Client.prototype.SendMsgEx = function (Msgs) {
|
|
204
204
|
var _this = this;
|
|
205
205
|
if (this.State == States.STATE_OFFLINE)
|
|
206
206
|
return;
|
|
@@ -220,20 +220,20 @@ var Client = /** @class */ (function (_super) {
|
|
|
220
220
|
if (this.clientAck == 0)
|
|
221
221
|
this.lastSentMessages = [];
|
|
222
222
|
_Msgs.forEach(function (Msg, index) {
|
|
223
|
-
header[index] = Buffer.alloc((
|
|
224
|
-
header[index][0] = ((
|
|
223
|
+
header[index] = Buffer.alloc((Msg.flag & 1 ? 3 : 2));
|
|
224
|
+
header[index][0] = ((Msg.flag & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
225
225
|
header[index][1] = (Msg.size & 0xf);
|
|
226
|
-
if (
|
|
226
|
+
if (Msg.flag & 1) {
|
|
227
227
|
_this.clientAck = (_this.clientAck + 1) % (1 << 10);
|
|
228
228
|
if (_this.clientAck == 0)
|
|
229
229
|
_this.lastSentMessages = [];
|
|
230
230
|
header[index][1] |= (_this.clientAck >> 2) & 0xf0;
|
|
231
231
|
header[index][2] = _this.clientAck & 0xff;
|
|
232
|
-
header[index][0] = (((
|
|
233
|
-
if ((
|
|
232
|
+
header[index][0] = (((Msg.flag | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
|
|
233
|
+
if ((Msg.flag & 2) == 0)
|
|
234
234
|
_this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
235
|
-
header[index][0] = (((
|
|
236
|
-
if ((
|
|
235
|
+
header[index][0] = (((Msg.flag) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
236
|
+
if ((Msg.flag & 2) == 0)
|
|
237
237
|
_this.lastSentMessages.push({ msg: Msg, ack: _this.clientAck });
|
|
238
238
|
}
|
|
239
239
|
});
|
|
@@ -250,11 +250,12 @@ var Client = /** @class */ (function (_super) {
|
|
|
250
250
|
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
251
251
|
else {
|
|
252
252
|
skip = true;
|
|
253
|
-
_this.SendMsgEx(_Msgs.slice(index)
|
|
253
|
+
_this.SendMsgEx(_Msgs.slice(index));
|
|
254
254
|
}
|
|
255
255
|
});
|
|
256
256
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
257
|
-
|
|
257
|
+
if (chunks.length < 0)
|
|
258
|
+
return;
|
|
258
259
|
this.socket.send(packet, 0, packet.length, this.port, this.host);
|
|
259
260
|
};
|
|
260
261
|
Client.prototype.QueueChunkEx = function (Msg) {
|
|
@@ -268,6 +269,8 @@ var Client = /** @class */ (function (_super) {
|
|
|
268
269
|
this.lastSendTime = new Date().getTime();
|
|
269
270
|
var packetHeader = Buffer.from([0x0 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, chunks.length]);
|
|
270
271
|
var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
|
|
272
|
+
if (chunks.length < 0)
|
|
273
|
+
return;
|
|
271
274
|
this.socket.send(packet, 0, packet.length, this.port, this.host);
|
|
272
275
|
};
|
|
273
276
|
Client.prototype.MsgToChunk = function (packet) {
|
|
@@ -352,10 +355,10 @@ var Client = /** @class */ (function (_super) {
|
|
|
352
355
|
_this.SendControlMsg(3);
|
|
353
356
|
_this.State = States.STATE_LOADING; // loading state
|
|
354
357
|
_this.receivedSnaps = 0;
|
|
355
|
-
var info = new MsgPacker_1.MsgPacker(1, true);
|
|
358
|
+
var info = new MsgPacker_1.MsgPacker(1, true, 1);
|
|
356
359
|
info.AddString(((_a = _this.options) === null || _a === void 0 ? void 0 : _a.NET_VERSION) ? _this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
357
360
|
info.AddString(((_b = _this.options) === null || _b === void 0 ? void 0 : _b.password) === undefined ? "" : (_c = _this.options) === null || _c === void 0 ? void 0 : _c.password); // password
|
|
358
|
-
var client_version = new MsgPacker_1.MsgPacker(0, true);
|
|
361
|
+
var client_version = new MsgPacker_1.MsgPacker(0, true, 1);
|
|
359
362
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
360
363
|
var randomUuid = Buffer.alloc(16);
|
|
361
364
|
crypto_1.randomBytes(16).copy(randomUuid);
|
|
@@ -368,7 +371,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
368
371
|
client_version.AddInt(16003);
|
|
369
372
|
client_version.AddString("DDNet 16.0.3");
|
|
370
373
|
}
|
|
371
|
-
_this.SendMsgEx([client_version, info]
|
|
374
|
+
_this.SendMsgEx([client_version, info]);
|
|
372
375
|
}
|
|
373
376
|
else if (a.toJSON().data[3] == 0x4) {
|
|
374
377
|
// disconnected
|
|
@@ -380,12 +383,13 @@ var Client = /** @class */ (function (_super) {
|
|
|
380
383
|
_this.lastRecvTime = new Date().getTime();
|
|
381
384
|
}
|
|
382
385
|
}
|
|
383
|
-
else
|
|
386
|
+
else {
|
|
384
387
|
_this.lastRecvTime = new Date().getTime();
|
|
388
|
+
}
|
|
385
389
|
var unpacked = _this.Unpack(a);
|
|
386
390
|
unpacked.chunks.forEach(function (a) {
|
|
387
|
-
if (a.flags & 1) { // vital
|
|
388
|
-
if (a.seq === (_this.ack + 1) % (1 << 10)) {
|
|
391
|
+
if (a.flags & 1 && (a.flags !== 15)) { // vital and not connless
|
|
392
|
+
if (a.seq === (_this.ack + 1) % (1 << 10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
|
|
389
393
|
_this.ack = a.seq;
|
|
390
394
|
_this.requestResend = false;
|
|
391
395
|
}
|
|
@@ -400,11 +404,13 @@ var Client = /** @class */ (function (_super) {
|
|
|
400
404
|
return;
|
|
401
405
|
}
|
|
402
406
|
_this.requestResend = true;
|
|
403
|
-
// c_flags |= 4; /* resend flag */
|
|
404
|
-
// continue; // take the next chunk in the packet
|
|
405
407
|
}
|
|
406
408
|
}
|
|
407
409
|
});
|
|
410
|
+
unpacked.chunks.filter(function (a) { return a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game'; }).forEach(function (a) {
|
|
411
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
|
|
412
|
+
_this.emit("broadcast", unpacker.unpackString());
|
|
413
|
+
});
|
|
408
414
|
_this.sentChunkQueue.forEach(function (buff, i) {
|
|
409
415
|
var chunk = _this.MsgToChunk(buff);
|
|
410
416
|
if (chunk.flags & 1) {
|
|
@@ -433,9 +439,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
433
439
|
var num_parts_1 = 1;
|
|
434
440
|
var part_1 = 0;
|
|
435
441
|
if (chunk.msg === "SNAP") {
|
|
436
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
|
|
437
442
|
num_parts_1 = unpacker.unpackInt();
|
|
438
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
|
|
439
443
|
part_1 = unpacker.unpackInt();
|
|
440
444
|
}
|
|
441
445
|
var crc = 0;
|
|
@@ -479,36 +483,39 @@ var Client = /** @class */ (function (_super) {
|
|
|
479
483
|
}
|
|
480
484
|
});
|
|
481
485
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
unpacked.victim = { ClientInfo: _this.client_info(unpacked.victim_id), PlayerInfo: _this.player_info(unpacked.victim_id) };
|
|
494
|
-
}
|
|
495
|
-
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
496
|
-
unpacked.killer = { ClientInfo: _this.client_info(unpacked.killer_id), PlayerInfo: _this.player_info(unpacked.killer_id) };
|
|
497
|
-
_this.emit("kill", unpacked);
|
|
486
|
+
var chat = unpacked.chunks.filter(function (a) { return a.msg == "SV_KILL_MSG" || a.msg == "SV_MOTD"; });
|
|
487
|
+
chat.forEach(function (a) {
|
|
488
|
+
if (a.msg == "SV_KILL_MSG") {
|
|
489
|
+
var unpacked = {};
|
|
490
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
|
|
491
|
+
unpacked.killer_id = unpacker.unpackInt();
|
|
492
|
+
unpacked.victim_id = unpacker.unpackInt();
|
|
493
|
+
unpacked.weapon = unpacker.unpackInt();
|
|
494
|
+
unpacked.special_mode = unpacker.unpackInt();
|
|
495
|
+
if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
|
|
496
|
+
unpacked.victim = { ClientInfo: _this.client_info(unpacked.victim_id), PlayerInfo: _this.player_info(unpacked.victim_id) };
|
|
498
497
|
}
|
|
499
|
-
|
|
500
|
-
|
|
498
|
+
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
499
|
+
unpacked.killer = { ClientInfo: _this.client_info(unpacked.killer_id), PlayerInfo: _this.player_info(unpacked.killer_id) };
|
|
500
|
+
_this.emit("kill", unpacked);
|
|
501
|
+
}
|
|
502
|
+
else if (a.msg == "SV_MOTD") {
|
|
503
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
|
|
504
|
+
var message = unpacker.unpackString();
|
|
505
|
+
_this.emit("motd", message);
|
|
506
|
+
}
|
|
507
|
+
});
|
|
501
508
|
if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
|
|
502
|
-
var Msg = new MsgPacker_1.MsgPacker(15, true); /* entergame */
|
|
503
|
-
_this.SendMsgEx(Msg
|
|
509
|
+
var Msg = new MsgPacker_1.MsgPacker(15, true, 1); /* entergame */
|
|
510
|
+
_this.SendMsgEx(Msg);
|
|
504
511
|
}
|
|
505
512
|
else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
|
|
506
513
|
// send ready
|
|
507
|
-
var Msg = new MsgPacker_1.MsgPacker(14, true); /* ready */
|
|
508
|
-
_this.SendMsgEx(Msg
|
|
514
|
+
var Msg = new MsgPacker_1.MsgPacker(14, true, 1); /* ready */
|
|
515
|
+
_this.SendMsgEx(Msg);
|
|
509
516
|
}
|
|
510
|
-
else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY")
|
|
511
|
-
var info = new MsgPacker_1.MsgPacker(20, false);
|
|
517
|
+
else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY"))) {
|
|
518
|
+
var info = new MsgPacker_1.MsgPacker(20, false, 1);
|
|
512
519
|
if ((_g = _this.options) === null || _g === void 0 ? void 0 : _g.identity) {
|
|
513
520
|
info.AddString(_this.options.identity.name);
|
|
514
521
|
info.AddString(_this.options.identity.clan);
|
|
@@ -527,13 +534,13 @@ var Client = /** @class */ (function (_super) {
|
|
|
527
534
|
info.AddInt(10346103); /* color body */
|
|
528
535
|
info.AddInt(65535); /* color feet */
|
|
529
536
|
}
|
|
530
|
-
var crashmeplx = new MsgPacker_1.MsgPacker(17, true); // rcon
|
|
537
|
+
var crashmeplx = new MsgPacker_1.MsgPacker(17, true, 1); // rcon
|
|
531
538
|
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
532
|
-
_this.SendMsgEx([info, crashmeplx]
|
|
539
|
+
_this.SendMsgEx([info, crashmeplx]);
|
|
533
540
|
}
|
|
534
541
|
else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
535
|
-
var info = new MsgPacker_1.MsgPacker(23, true);
|
|
536
|
-
_this.SendMsgEx(info
|
|
542
|
+
var info = new MsgPacker_1.MsgPacker(23, true, 1);
|
|
543
|
+
_this.SendMsgEx(info);
|
|
537
544
|
}
|
|
538
545
|
if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
|
|
539
546
|
_this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
|
|
@@ -553,7 +560,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
553
560
|
if (input === void 0) { input = this.movement.input; }
|
|
554
561
|
if (this.State != States.STATE_ONLINE)
|
|
555
562
|
return;
|
|
556
|
-
var inputMsg = new MsgPacker_1.MsgPacker(16, true);
|
|
563
|
+
var inputMsg = new MsgPacker_1.MsgPacker(16, true, 0);
|
|
557
564
|
inputMsg.AddInt(this.AckGameTick);
|
|
558
565
|
inputMsg.AddInt(this.PredGameTick);
|
|
559
566
|
inputMsg.AddInt(40);
|
|
@@ -572,7 +579,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
572
579
|
input_data.forEach(function (a) {
|
|
573
580
|
inputMsg.AddInt(a);
|
|
574
581
|
});
|
|
575
|
-
this.SendMsgEx(inputMsg
|
|
582
|
+
this.SendMsgEx(inputMsg);
|
|
576
583
|
};
|
|
577
584
|
Object.defineProperty(Client.prototype, "input", {
|
|
578
585
|
get: function () {
|
|
@@ -595,18 +602,18 @@ var Client = /** @class */ (function (_super) {
|
|
|
595
602
|
};
|
|
596
603
|
Client.prototype.Say = function (message, team) {
|
|
597
604
|
if (team === void 0) { team = false; }
|
|
598
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false);
|
|
605
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
|
|
599
606
|
packer.AddInt(team ? 1 : 0); // team
|
|
600
607
|
packer.AddString(message);
|
|
601
|
-
this.
|
|
608
|
+
this.QueueChunkEx(packer);
|
|
602
609
|
};
|
|
603
610
|
Client.prototype.Vote = function (vote) {
|
|
604
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false);
|
|
605
|
-
packer.AddInt(vote ? 1 :
|
|
606
|
-
this.
|
|
611
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
|
|
612
|
+
packer.AddInt(vote ? 1 : -1);
|
|
613
|
+
this.QueueChunkEx(packer);
|
|
607
614
|
};
|
|
608
615
|
Client.prototype.ChangePlayerInfo = function (playerInfo) {
|
|
609
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false);
|
|
616
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
|
|
610
617
|
packer.AddString(playerInfo.name); //m_pName);
|
|
611
618
|
packer.AddString(playerInfo.clan); //m_pClan);
|
|
612
619
|
packer.AddInt(playerInfo.country); //m_Country);
|
|
@@ -614,21 +621,21 @@ var Client = /** @class */ (function (_super) {
|
|
|
614
621
|
packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
|
|
615
622
|
packer.AddInt(playerInfo.color_body); //m_ColorBody);
|
|
616
623
|
packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
|
|
617
|
-
this.
|
|
624
|
+
this.QueueChunkEx(packer);
|
|
618
625
|
};
|
|
619
626
|
Client.prototype.Kill = function () {
|
|
620
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false);
|
|
621
|
-
this.
|
|
627
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
|
|
628
|
+
this.QueueChunkEx(packer);
|
|
622
629
|
};
|
|
623
630
|
Client.prototype.ChangeTeam = function (team) {
|
|
624
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false);
|
|
631
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
|
|
625
632
|
packer.AddInt(team);
|
|
626
|
-
this.
|
|
633
|
+
this.QueueChunkEx(packer);
|
|
627
634
|
};
|
|
628
635
|
Client.prototype.Emote = function (emote) {
|
|
629
|
-
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false);
|
|
636
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
|
|
630
637
|
packer.AddInt(emote);
|
|
631
|
-
this.
|
|
638
|
+
this.QueueChunkEx(packer);
|
|
632
639
|
};
|
|
633
640
|
Client.prototype.client_info = function (id) {
|
|
634
641
|
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
@@ -652,7 +659,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
652
659
|
});
|
|
653
660
|
Client.prototype.player_info = function (id) {
|
|
654
661
|
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
655
|
-
return a.type_id ==
|
|
662
|
+
return a.type_id == 10
|
|
656
663
|
&& a.id == id;
|
|
657
664
|
});
|
|
658
665
|
if (delta.length == 0)
|
|
@@ -661,7 +668,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
661
668
|
};
|
|
662
669
|
Object.defineProperty(Client.prototype, "player_infos", {
|
|
663
670
|
get: function () {
|
|
664
|
-
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id ==
|
|
671
|
+
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 10; })
|
|
665
672
|
.sort(function (a, b) { return a.id - b.id; })
|
|
666
673
|
.map(function (a) { return a.parsed; });
|
|
667
674
|
},
|
package/lib/client.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { unpackString, MsgUnpacker } from "./MsgUnpacker";
|
|
|
8
8
|
import Movement from './movement';
|
|
9
9
|
|
|
10
10
|
import { MsgPacker } from './MsgPacker';
|
|
11
|
-
import {
|
|
11
|
+
import { Snapshot } from './snapshot';
|
|
12
12
|
import Huffman from "./huffman";
|
|
13
13
|
|
|
14
14
|
const huff = new Huffman();
|
|
@@ -175,7 +175,6 @@ export declare interface Client {
|
|
|
175
175
|
|
|
176
176
|
// eSnapHolder: eSnap[];
|
|
177
177
|
|
|
178
|
-
pingStart: number;
|
|
179
178
|
|
|
180
179
|
options?: iOptions;
|
|
181
180
|
|
|
@@ -183,7 +182,10 @@ export declare interface Client {
|
|
|
183
182
|
on(event: 'disconnect', listener: (reason: string) => void): this;
|
|
184
183
|
|
|
185
184
|
on(event: 'message', listener: (message: iMessage) => void): this;
|
|
185
|
+
on(event: 'broadcast', listener: (message: string) => void): this;
|
|
186
186
|
on(event: 'kill', listener: (kill: iKillMsg) => void): this;
|
|
187
|
+
on(event: 'motd', listener: (message: string) => void): this;
|
|
188
|
+
|
|
187
189
|
requestResend: boolean;
|
|
188
190
|
}
|
|
189
191
|
|
|
@@ -203,7 +205,6 @@ export class Client extends EventEmitter {
|
|
|
203
205
|
this.SnapUnpacker = new Snapshot();
|
|
204
206
|
// this.eSnapHolder = [];
|
|
205
207
|
this.requestResend = false;
|
|
206
|
-
this.pingStart = 0;
|
|
207
208
|
|
|
208
209
|
if (options)
|
|
209
210
|
this.options = options;
|
|
@@ -236,19 +237,22 @@ export class Client extends EventEmitter {
|
|
|
236
237
|
|
|
237
238
|
ResendAfter(lastAck: number) {
|
|
238
239
|
this.clientAck = lastAck;
|
|
240
|
+
|
|
241
|
+
|
|
239
242
|
let toResend: MsgPacker[] = [];
|
|
240
243
|
this.lastSentMessages.forEach(msg => {
|
|
241
244
|
if (msg.ack > lastAck)
|
|
242
245
|
toResend.push(msg.msg);
|
|
243
246
|
});
|
|
244
|
-
|
|
247
|
+
toResend.forEach(a => a.flag = 1|2);
|
|
248
|
+
this.SendMsgEx(toResend);
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
Unpack(packet: Buffer): _packet {
|
|
248
252
|
var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
249
253
|
|
|
250
254
|
|
|
251
|
-
if (packet.indexOf(Buffer.from([0xff, 0xff, 0xff, 0xff])) == 0
|
|
255
|
+
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)
|
|
252
256
|
return unpacked;
|
|
253
257
|
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
254
258
|
this.ResendAfter(unpacked.twprotocol.ack);
|
|
@@ -308,7 +312,7 @@ export class Client extends EventEmitter {
|
|
|
308
312
|
})
|
|
309
313
|
}
|
|
310
314
|
|
|
311
|
-
SendMsgEx(Msgs: MsgPacker[] | MsgPacker
|
|
315
|
+
SendMsgEx(Msgs: MsgPacker[] | MsgPacker) {
|
|
312
316
|
if (this.State == States.STATE_OFFLINE)
|
|
313
317
|
return;
|
|
314
318
|
if (!this.socket)
|
|
@@ -327,20 +331,20 @@ export class Client extends EventEmitter {
|
|
|
327
331
|
if (this.clientAck == 0)
|
|
328
332
|
this.lastSentMessages = [];
|
|
329
333
|
_Msgs.forEach((Msg: MsgPacker, index) => {
|
|
330
|
-
header[index] = Buffer.alloc((
|
|
331
|
-
header[index][0] = ((
|
|
334
|
+
header[index] = Buffer.alloc((Msg.flag & 1 ? 3 : 2));
|
|
335
|
+
header[index][0] = ((Msg.flag & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
332
336
|
header[index][1] = (Msg.size & 0xf);
|
|
333
|
-
if (
|
|
337
|
+
if (Msg.flag & 1) {
|
|
334
338
|
this.clientAck = (this.clientAck + 1) % (1 << 10);
|
|
335
339
|
if (this.clientAck == 0)
|
|
336
340
|
this.lastSentMessages = [];
|
|
337
341
|
header[index][1] |= (this.clientAck >> 2) & 0xf0;
|
|
338
342
|
header[index][2] = this.clientAck & 0xff;
|
|
339
|
-
header[index][0] = (((
|
|
340
|
-
if ((
|
|
343
|
+
header[index][0] = (((Msg.flag | 2)&3)<<6)|((Msg.size>>4)&0x3f); // 2 is resend flag (ugly hack for queue)
|
|
344
|
+
if ((Msg.flag & 2) == 0)
|
|
341
345
|
this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
342
|
-
header[index][0] = (((
|
|
343
|
-
if ((
|
|
346
|
+
header[index][0] = (((Msg.flag)&3)<<6)|((Msg.size>>4)&0x3f);
|
|
347
|
+
if ((Msg.flag & 2) == 0)
|
|
344
348
|
this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
|
|
345
349
|
}
|
|
346
350
|
})
|
|
@@ -358,11 +362,12 @@ export class Client extends EventEmitter {
|
|
|
358
362
|
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
359
363
|
else {
|
|
360
364
|
skip = true;
|
|
361
|
-
this.SendMsgEx(_Msgs.slice(index)
|
|
365
|
+
this.SendMsgEx(_Msgs.slice(index));
|
|
362
366
|
}
|
|
363
367
|
})
|
|
364
368
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
365
|
-
|
|
369
|
+
if (chunks.length < 0)
|
|
370
|
+
return;
|
|
366
371
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
367
372
|
}
|
|
368
373
|
|
|
@@ -381,7 +386,8 @@ export class Client extends EventEmitter {
|
|
|
381
386
|
var packetHeader = Buffer.from([0x0+(((16<<4)&0xf0)|((this.ack>>8)&0xf)), this.ack&0xff, chunks.length]);
|
|
382
387
|
|
|
383
388
|
var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
|
|
384
|
-
|
|
389
|
+
if (chunks.length < 0)
|
|
390
|
+
return;
|
|
385
391
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
386
392
|
}
|
|
387
393
|
|
|
@@ -472,11 +478,11 @@ export class Client extends EventEmitter {
|
|
|
472
478
|
this.State = States.STATE_LOADING; // loading state
|
|
473
479
|
this.receivedSnaps = 0;
|
|
474
480
|
|
|
475
|
-
var info = new MsgPacker(1, true);
|
|
481
|
+
var info = new MsgPacker(1, true, 1);
|
|
476
482
|
info.AddString(this.options?.NET_VERSION ? this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
477
483
|
info.AddString(this.options?.password === undefined ? "" : this.options?.password); // password
|
|
478
484
|
|
|
479
|
-
var client_version = new MsgPacker(0, true);
|
|
485
|
+
var client_version = new MsgPacker(0, true, 1);
|
|
480
486
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
481
487
|
let randomUuid = Buffer.alloc(16);
|
|
482
488
|
|
|
@@ -491,7 +497,7 @@ export class Client extends EventEmitter {
|
|
|
491
497
|
client_version.AddString("DDNet 16.0.3");
|
|
492
498
|
}
|
|
493
499
|
|
|
494
|
-
this.SendMsgEx([client_version, info]
|
|
500
|
+
this.SendMsgEx([client_version, info])
|
|
495
501
|
} else if (a.toJSON().data[3] == 0x4) {
|
|
496
502
|
// disconnected
|
|
497
503
|
this.State = States.STATE_OFFLINE;
|
|
@@ -501,15 +507,18 @@ export class Client extends EventEmitter {
|
|
|
501
507
|
if (a.toJSON().data[3] !== 0x0) { // keepalive
|
|
502
508
|
this.lastRecvTime = new Date().getTime();
|
|
503
509
|
}
|
|
504
|
-
} else
|
|
510
|
+
} else {
|
|
505
511
|
this.lastRecvTime = new Date().getTime();
|
|
506
512
|
|
|
513
|
+
}
|
|
514
|
+
|
|
507
515
|
|
|
508
|
-
var unpacked: _packet = this.Unpack(a)
|
|
516
|
+
var unpacked: _packet = this.Unpack(a);
|
|
509
517
|
unpacked.chunks.forEach(a => {
|
|
510
|
-
if (a.flags & 1) { // vital
|
|
511
|
-
if (a.seq === (this.ack+1)%(1<<10)) {
|
|
512
|
-
this.ack = a.seq
|
|
518
|
+
if (a.flags & 1 && (a.flags !== 15)) { // vital and not connless
|
|
519
|
+
if (a.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
|
|
520
|
+
this.ack = a.seq!;
|
|
521
|
+
|
|
513
522
|
this.requestResend = false;
|
|
514
523
|
}
|
|
515
524
|
else { //IsSeqInBackroom (old packet that we already got)
|
|
@@ -523,11 +532,15 @@ export class Client extends EventEmitter {
|
|
|
523
532
|
return;
|
|
524
533
|
}
|
|
525
534
|
this.requestResend = true;
|
|
526
|
-
// c_flags |= 4; /* resend flag */
|
|
527
|
-
// continue; // take the next chunk in the packet
|
|
528
535
|
|
|
529
536
|
}
|
|
530
537
|
}
|
|
538
|
+
|
|
539
|
+
})
|
|
540
|
+
unpacked.chunks.filter(a => a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game').forEach(a => {
|
|
541
|
+
let unpacker = new MsgUnpacker(a.raw.toJSON().data);
|
|
542
|
+
|
|
543
|
+
this.emit("broadcast", unpacker.unpackString());
|
|
531
544
|
})
|
|
532
545
|
this.sentChunkQueue.forEach((buff, i) => {
|
|
533
546
|
let chunk = this.MsgToChunk(buff);
|
|
@@ -560,9 +573,7 @@ export class Client extends EventEmitter {
|
|
|
560
573
|
let part = 0;
|
|
561
574
|
|
|
562
575
|
if (chunk.msg === "SNAP") {
|
|
563
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
|
|
564
576
|
num_parts = unpacker.unpackInt();
|
|
565
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
|
|
566
577
|
part = unpacker.unpackInt();
|
|
567
578
|
}
|
|
568
579
|
|
|
@@ -613,8 +624,7 @@ export class Client extends EventEmitter {
|
|
|
613
624
|
}
|
|
614
625
|
})
|
|
615
626
|
}
|
|
616
|
-
|
|
617
|
-
var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG");
|
|
627
|
+
var chat = unpacked.chunks.filter(a => a.msg == "SV_KILL_MSG" || a.msg == "SV_MOTD");
|
|
618
628
|
chat.forEach(a => {
|
|
619
629
|
if (a.msg == "SV_KILL_MSG") {
|
|
620
630
|
var unpacked: iKillMsg = {} as iKillMsg;
|
|
@@ -630,19 +640,22 @@ export class Client extends EventEmitter {
|
|
|
630
640
|
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
631
641
|
unpacked.killer = { ClientInfo: this.client_info(unpacked.killer_id), PlayerInfo: this.player_info(unpacked.killer_id) }
|
|
632
642
|
this.emit("kill", unpacked)
|
|
643
|
+
} else if (a.msg == "SV_MOTD") {
|
|
644
|
+
let unpacker = new MsgUnpacker(a.raw.toJSON().data);
|
|
645
|
+
let message = unpacker.unpackString();
|
|
646
|
+
this.emit("motd", message);
|
|
633
647
|
}
|
|
634
648
|
})
|
|
635
|
-
}
|
|
636
649
|
|
|
637
650
|
if (unpacked.chunks[0] && chunkMessages.includes("SV_READY_TO_ENTER")) {
|
|
638
|
-
var Msg = new MsgPacker(15, true); /* entergame */
|
|
639
|
-
this.SendMsgEx(Msg
|
|
651
|
+
var Msg = new MsgPacker(15, true, 1); /* entergame */
|
|
652
|
+
this.SendMsgEx(Msg);
|
|
640
653
|
} else if ((unpacked.chunks[0] && chunkMessages.includes("CAPABILITIES") || unpacked.chunks[0] && chunkMessages.includes("MAP_CHANGE"))) {
|
|
641
654
|
// send ready
|
|
642
|
-
var Msg = new MsgPacker(14, true); /* ready */
|
|
643
|
-
this.SendMsgEx(Msg
|
|
644
|
-
} else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY")
|
|
645
|
-
var info = new MsgPacker(20, false);
|
|
655
|
+
var Msg = new MsgPacker(14, true, 1); /* ready */
|
|
656
|
+
this.SendMsgEx(Msg);
|
|
657
|
+
} else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY"))) {
|
|
658
|
+
var info = new MsgPacker(20, false, 1);
|
|
646
659
|
if (this.options?.identity) {
|
|
647
660
|
info.AddString(this.options.identity.name);
|
|
648
661
|
info.AddString(this.options.identity.clan);
|
|
@@ -661,15 +674,15 @@ export class Client extends EventEmitter {
|
|
|
661
674
|
info.AddInt(65535); /* color feet */
|
|
662
675
|
|
|
663
676
|
}
|
|
664
|
-
var crashmeplx = new MsgPacker(17, true); // rcon
|
|
677
|
+
var crashmeplx = new MsgPacker(17, true, 1); // rcon
|
|
665
678
|
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
666
|
-
this.SendMsgEx([info, crashmeplx]
|
|
679
|
+
this.SendMsgEx([info, crashmeplx]);
|
|
667
680
|
|
|
668
681
|
|
|
669
682
|
|
|
670
683
|
} else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
671
|
-
var info = new MsgPacker(23, true);
|
|
672
|
-
this.SendMsgEx(info
|
|
684
|
+
var info = new MsgPacker(23, true, 1);
|
|
685
|
+
this.SendMsgEx(info)
|
|
673
686
|
}
|
|
674
687
|
if (chunkMessages.includes("SNAP") || chunkMessages.includes("SNAP_EMPTY") || chunkMessages.includes("SNAP_SINGLE")) {
|
|
675
688
|
this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
|
|
@@ -691,7 +704,7 @@ export class Client extends EventEmitter {
|
|
|
691
704
|
if (this.State != States.STATE_ONLINE)
|
|
692
705
|
return;
|
|
693
706
|
|
|
694
|
-
let inputMsg = new MsgPacker(16, true);
|
|
707
|
+
let inputMsg = new MsgPacker(16, true, 0);
|
|
695
708
|
inputMsg.AddInt(this.AckGameTick);
|
|
696
709
|
inputMsg.AddInt(this.PredGameTick);
|
|
697
710
|
inputMsg.AddInt(40);
|
|
@@ -712,7 +725,7 @@ export class Client extends EventEmitter {
|
|
|
712
725
|
input_data.forEach(a => {
|
|
713
726
|
inputMsg.AddInt(a);
|
|
714
727
|
});
|
|
715
|
-
this.SendMsgEx(inputMsg
|
|
728
|
+
this.SendMsgEx(inputMsg);
|
|
716
729
|
}
|
|
717
730
|
get input() {
|
|
718
731
|
return this.movement.input;
|
|
@@ -731,18 +744,18 @@ export class Client extends EventEmitter {
|
|
|
731
744
|
}
|
|
732
745
|
|
|
733
746
|
Say(message: string, team = false) {
|
|
734
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_SAY, false);
|
|
747
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
|
|
735
748
|
packer.AddInt(team ? 1 : 0); // team
|
|
736
749
|
packer.AddString(message);
|
|
737
|
-
this.
|
|
750
|
+
this.QueueChunkEx(packer);
|
|
738
751
|
}
|
|
739
752
|
Vote(vote: boolean) {
|
|
740
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_VOTE, false);
|
|
741
|
-
packer.AddInt(vote ? 1 :
|
|
742
|
-
this.
|
|
753
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
|
|
754
|
+
packer.AddInt(vote ? 1 : -1);
|
|
755
|
+
this.QueueChunkEx(packer);
|
|
743
756
|
}
|
|
744
757
|
ChangePlayerInfo(playerInfo: ClientInfo) {
|
|
745
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false);
|
|
758
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
|
|
746
759
|
packer.AddString(playerInfo.name); //m_pName);
|
|
747
760
|
packer.AddString(playerInfo.clan); //m_pClan);
|
|
748
761
|
packer.AddInt(playerInfo.country); //m_Country);
|
|
@@ -750,21 +763,21 @@ export class Client extends EventEmitter {
|
|
|
750
763
|
packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
|
|
751
764
|
packer.AddInt(playerInfo.color_body); //m_ColorBody);
|
|
752
765
|
packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
|
|
753
|
-
this.
|
|
766
|
+
this.QueueChunkEx(packer);
|
|
754
767
|
}
|
|
755
768
|
Kill() {
|
|
756
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_KILL, false);
|
|
757
|
-
this.
|
|
769
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
|
|
770
|
+
this.QueueChunkEx(packer);
|
|
758
771
|
}
|
|
759
772
|
ChangeTeam(team: number) {
|
|
760
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_SETTEAM, false);
|
|
773
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
|
|
761
774
|
packer.AddInt(team);
|
|
762
|
-
this.
|
|
775
|
+
this.QueueChunkEx(packer);
|
|
763
776
|
}
|
|
764
777
|
Emote(emote: number) {
|
|
765
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_EMOTICON, false);
|
|
778
|
+
var packer = new MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
|
|
766
779
|
packer.AddInt(emote);
|
|
767
|
-
this.
|
|
780
|
+
this.QueueChunkEx(packer);
|
|
768
781
|
}
|
|
769
782
|
client_info(id: number) {
|
|
770
783
|
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
@@ -786,7 +799,7 @@ export class Client extends EventEmitter {
|
|
|
786
799
|
}
|
|
787
800
|
player_info(id: number) {
|
|
788
801
|
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
789
|
-
a.type_id ==
|
|
802
|
+
a.type_id == 10
|
|
790
803
|
&& a.id == id
|
|
791
804
|
);
|
|
792
805
|
|
|
@@ -795,7 +808,7 @@ export class Client extends EventEmitter {
|
|
|
795
808
|
return delta[0].parsed as PlayerInfo;
|
|
796
809
|
}
|
|
797
810
|
get player_infos(): PlayerInfo[] {
|
|
798
|
-
return this.SnapUnpacker.deltas.filter(a => a.type_id ==
|
|
811
|
+
return this.SnapUnpacker.deltas.filter(a => a.type_id == 10)
|
|
799
812
|
.sort((a, b) => a.id - b.id)
|
|
800
813
|
.map(a => a.parsed as PlayerInfo);
|
|
801
814
|
}
|