teeworlds 2.1.9 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +177 -89
- package/lib/client.ts +179 -92
- package/lib/snapshot.js +78 -4
- package/lib/snapshot.ts +82 -7
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -26,7 +26,6 @@ var MsgPacker_1 = require("./MsgPacker");
|
|
|
26
26
|
var snapshot_1 = require("./snapshot");
|
|
27
27
|
var huffman_1 = __importDefault(require("./huffman"));
|
|
28
28
|
var huff = new huffman_1.default();
|
|
29
|
-
var SnapUnpacker = new snapshot_1.Snapshot();
|
|
30
29
|
var States;
|
|
31
30
|
(function (States) {
|
|
32
31
|
States[States["STATE_OFFLINE"] = 0] = "STATE_OFFLINE";
|
|
@@ -37,7 +36,6 @@ var States;
|
|
|
37
36
|
States[States["STATE_QUITTING"] = 5] = "STATE_QUITTING";
|
|
38
37
|
States[States["STATE_RESTARTING"] = 6] = "STATE_RESTARTING";
|
|
39
38
|
})(States || (States = {}));
|
|
40
|
-
;
|
|
41
39
|
var NETMSGTYPE;
|
|
42
40
|
(function (NETMSGTYPE) {
|
|
43
41
|
NETMSGTYPE[NETMSGTYPE["EX"] = 0] = "EX";
|
|
@@ -74,30 +72,6 @@ var NETMSGTYPE;
|
|
|
74
72
|
NETMSGTYPE[NETMSGTYPE["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
|
|
75
73
|
NETMSGTYPE[NETMSGTYPE["NUM"] = 32] = "NUM";
|
|
76
74
|
})(NETMSGTYPE || (NETMSGTYPE = {}));
|
|
77
|
-
var items;
|
|
78
|
-
(function (items) {
|
|
79
|
-
items[items["OBJ_EX"] = 0] = "OBJ_EX";
|
|
80
|
-
items[items["OBJ_PLAYER_INPUT"] = 1] = "OBJ_PLAYER_INPUT";
|
|
81
|
-
items[items["OBJ_PROJECTILE"] = 2] = "OBJ_PROJECTILE";
|
|
82
|
-
items[items["OBJ_LASER"] = 3] = "OBJ_LASER";
|
|
83
|
-
items[items["OBJ_PICKUP"] = 4] = "OBJ_PICKUP";
|
|
84
|
-
items[items["OBJ_FLAG"] = 5] = "OBJ_FLAG";
|
|
85
|
-
items[items["OBJ_GAME_INFO"] = 6] = "OBJ_GAME_INFO";
|
|
86
|
-
items[items["OBJ_GAME_DATA"] = 7] = "OBJ_GAME_DATA";
|
|
87
|
-
items[items["OBJ_CHARACTER_CORE"] = 8] = "OBJ_CHARACTER_CORE";
|
|
88
|
-
items[items["OBJ_CHARACTER"] = 9] = "OBJ_CHARACTER";
|
|
89
|
-
items[items["OBJ_PLAYER_INFO"] = 10] = "OBJ_PLAYER_INFO";
|
|
90
|
-
items[items["OBJ_CLIENT_INFO"] = 11] = "OBJ_CLIENT_INFO";
|
|
91
|
-
items[items["OBJ_SPECTATOR_INFO"] = 12] = "OBJ_SPECTATOR_INFO";
|
|
92
|
-
items[items["EVENT_COMMON"] = 13] = "EVENT_COMMON";
|
|
93
|
-
items[items["EVENT_EXPLOSION"] = 14] = "EVENT_EXPLOSION";
|
|
94
|
-
items[items["EVENT_SPAWN"] = 15] = "EVENT_SPAWN";
|
|
95
|
-
items[items["EVENT_HAMMERHIT"] = 16] = "EVENT_HAMMERHIT";
|
|
96
|
-
items[items["EVENT_DEATH"] = 17] = "EVENT_DEATH";
|
|
97
|
-
items[items["EVENT_SOUND_GLOBAL"] = 18] = "EVENT_SOUND_GLOBAL";
|
|
98
|
-
items[items["EVENT_SOUND_WORLD"] = 19] = "EVENT_SOUND_WORLD";
|
|
99
|
-
items[items["EVENT_DAMAGE_INDICATOR"] = 20] = "EVENT_DAMAGE_INDICATOR";
|
|
100
|
-
})(items || (items = {}));
|
|
101
75
|
function toHexStream(buff) {
|
|
102
76
|
return buff.toJSON().data.map(function (a) { return ('0' + (a & 0xff).toString(16)).slice(-2); }).join("");
|
|
103
77
|
}
|
|
@@ -134,14 +108,16 @@ var Client = /** @class */ (function (_super) {
|
|
|
134
108
|
_this.name = nickname;
|
|
135
109
|
_this.AckGameTick = 0;
|
|
136
110
|
_this.PredGameTick = 0;
|
|
111
|
+
_this.SnapUnpacker = new snapshot_1.Snapshot();
|
|
112
|
+
// this.eSnapHolder = [];
|
|
113
|
+
_this.requestResend = false;
|
|
137
114
|
if (options)
|
|
138
115
|
_this.options = options;
|
|
139
116
|
_this.timer = 0;
|
|
140
117
|
_this.movement = new movement_1.default();
|
|
141
118
|
_this.snaps = [];
|
|
142
|
-
_this.client_infos = [];
|
|
143
|
-
_this.player_infos = [];
|
|
144
119
|
_this.sentChunkQueue = [];
|
|
120
|
+
_this.queueChunkEx = [];
|
|
145
121
|
_this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
146
122
|
_this.ack = 0; // ack of messages the client has received
|
|
147
123
|
_this.clientAck = 0; // ack of messages the client has sent
|
|
@@ -152,14 +128,28 @@ var Client = /** @class */ (function (_super) {
|
|
|
152
128
|
_this.TKEN = Buffer.from([255, 255, 255, 255]);
|
|
153
129
|
_this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
|
|
154
130
|
_this.lastSendTime = new Date().getTime();
|
|
131
|
+
_this.lastRecvTime = new Date().getTime();
|
|
132
|
+
_this.lastSentMessages = [];
|
|
155
133
|
return _this;
|
|
156
134
|
}
|
|
135
|
+
Client.prototype.ResendAfter = function (lastAck) {
|
|
136
|
+
this.clientAck = lastAck;
|
|
137
|
+
var toResend = [];
|
|
138
|
+
this.lastSentMessages.forEach(function (msg) {
|
|
139
|
+
if (msg.ack > lastAck)
|
|
140
|
+
toResend.push(msg.msg);
|
|
141
|
+
});
|
|
142
|
+
this.SendMsgEx(toResend, 1 | 2);
|
|
143
|
+
};
|
|
157
144
|
Client.prototype.Unpack = function (packet) {
|
|
158
|
-
var unpacked = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
|
|
145
|
+
var unpacked = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0] & 0xf) << 8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
|
|
159
146
|
if (packet.indexOf(Buffer.from([0xff, 0xff, 0xff, 0xff])) == 0 && !(unpacked.twprotocol.flags & 8) || unpacked.twprotocol.flags == 255) // flags == 255 is connectionless (used for sending usernames)
|
|
160
147
|
return unpacked;
|
|
148
|
+
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
149
|
+
this.ResendAfter(unpacked.twprotocol.ack);
|
|
150
|
+
}
|
|
161
151
|
packet = packet.slice(3);
|
|
162
|
-
if (unpacked.twprotocol.flags &
|
|
152
|
+
if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
|
|
163
153
|
packet = huff.decompress(packet);
|
|
164
154
|
if (packet.length == 1 && packet[0] == -1)
|
|
165
155
|
return unpacked;
|
|
@@ -220,29 +210,55 @@ var Client = /** @class */ (function (_super) {
|
|
|
220
210
|
_Msgs = Msgs;
|
|
221
211
|
else
|
|
222
212
|
_Msgs = [Msgs];
|
|
213
|
+
if (this.queueChunkEx.length > 0) {
|
|
214
|
+
_Msgs.push.apply(_Msgs, this.queueChunkEx);
|
|
215
|
+
this.queueChunkEx = [];
|
|
216
|
+
}
|
|
223
217
|
this.lastSendTime = new Date().getTime();
|
|
224
218
|
var header = [];
|
|
219
|
+
if (this.clientAck == 0)
|
|
220
|
+
this.lastSentMessages = [];
|
|
225
221
|
_Msgs.forEach(function (Msg, index) {
|
|
226
222
|
header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
|
|
227
223
|
header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
228
224
|
header[index][1] = (Msg.size & 0xf);
|
|
229
225
|
if (Flags & 1) {
|
|
230
226
|
_this.clientAck = (_this.clientAck + 1) % (1 << 10);
|
|
227
|
+
if (_this.clientAck == 0)
|
|
228
|
+
_this.lastSentMessages = [];
|
|
231
229
|
header[index][1] |= (_this.clientAck >> 2) & 0xf0;
|
|
232
230
|
header[index][2] = _this.clientAck & 0xff;
|
|
233
231
|
header[index][0] = (((Flags | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
|
|
234
|
-
|
|
232
|
+
if ((Flags & 2) == 0)
|
|
233
|
+
_this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
235
234
|
header[index][0] = (((Flags) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
235
|
+
if ((Flags & 2) == 0)
|
|
236
|
+
_this.lastSentMessages.push({ msg: Msg, ack: _this.clientAck });
|
|
236
237
|
}
|
|
237
238
|
});
|
|
238
|
-
var
|
|
239
|
+
var flags = 0;
|
|
240
|
+
if (this.requestResend)
|
|
241
|
+
flags |= 4;
|
|
242
|
+
var packetHeader = Buffer.from([((flags << 4) & 0xf0) | ((this.ack >> 8) & 0xf), this.ack & 0xff, _Msgs.length]);
|
|
239
243
|
var chunks = Buffer.from([]);
|
|
244
|
+
var skip = false;
|
|
240
245
|
_Msgs.forEach(function (Msg, index) {
|
|
241
|
-
|
|
246
|
+
if (skip)
|
|
247
|
+
return;
|
|
248
|
+
if (chunks.byteLength < 1300)
|
|
249
|
+
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
250
|
+
else {
|
|
251
|
+
skip = true;
|
|
252
|
+
_this.SendMsgEx(_Msgs.slice(index), Flags);
|
|
253
|
+
}
|
|
242
254
|
});
|
|
243
255
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
256
|
+
// packet[0] |= 4;
|
|
244
257
|
this.socket.send(packet, 0, packet.length, this.port, this.host);
|
|
245
258
|
};
|
|
259
|
+
Client.prototype.QueueChunkEx = function (Msg) {
|
|
260
|
+
this.queueChunkEx.push(Msg);
|
|
261
|
+
};
|
|
246
262
|
Client.prototype.SendMsgRaw = function (chunks) {
|
|
247
263
|
if (this.State == States.STATE_OFFLINE)
|
|
248
264
|
return;
|
|
@@ -311,10 +327,20 @@ var Client = /** @class */ (function (_super) {
|
|
|
311
327
|
else
|
|
312
328
|
clearInterval(resendTimeout);
|
|
313
329
|
}, 1000);
|
|
330
|
+
var Timeout = setInterval(function () {
|
|
331
|
+
var _a;
|
|
332
|
+
var timeoutTime = ((_a = _this.options) === null || _a === void 0 ? void 0 : _a.timeout) ? _this.options.timeout : 15000;
|
|
333
|
+
if ((new Date().getTime() - _this.lastRecvTime) > timeoutTime) {
|
|
334
|
+
_this.State = States.STATE_OFFLINE;
|
|
335
|
+
_this.emit("timeout");
|
|
336
|
+
_this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - _this.lastRecvTime) + "ms)");
|
|
337
|
+
clearInterval(Timeout);
|
|
338
|
+
}
|
|
339
|
+
}, 5000);
|
|
314
340
|
this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
|
|
315
341
|
if (this.socket)
|
|
316
342
|
this.socket.on("message", function (a, rinfo) {
|
|
317
|
-
var _a, _b, _c, _d, _e, _f;
|
|
343
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
318
344
|
if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
|
|
319
345
|
return;
|
|
320
346
|
clearInterval(connectInterval);
|
|
@@ -326,16 +352,16 @@ var Client = /** @class */ (function (_super) {
|
|
|
326
352
|
_this.State = States.STATE_LOADING; // loading state
|
|
327
353
|
_this.receivedSnaps = 0;
|
|
328
354
|
var info = new MsgPacker_1.MsgPacker(1, true);
|
|
329
|
-
info.AddString("0.6 626fce9a778df4d4");
|
|
330
|
-
info.AddString(((
|
|
355
|
+
info.AddString(((_a = _this.options) === null || _a === void 0 ? void 0 : _a.NET_VERSION) ? _this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
356
|
+
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
|
|
331
357
|
var client_version = new MsgPacker_1.MsgPacker(0, true);
|
|
332
358
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
333
359
|
var randomUuid = Buffer.alloc(16);
|
|
334
360
|
crypto_1.randomBytes(16).copy(randomUuid);
|
|
335
361
|
client_version.AddBuffer(randomUuid);
|
|
336
|
-
if (((
|
|
337
|
-
client_version.AddInt((
|
|
338
|
-
client_version.AddString("DDNet " + ((
|
|
362
|
+
if (((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
|
|
363
|
+
client_version.AddInt((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
|
|
364
|
+
client_version.AddString("DDNet " + ((_f = _this.options) === null || _f === void 0 ? void 0 : _f.ddnet_version.release_version));
|
|
339
365
|
}
|
|
340
366
|
else {
|
|
341
367
|
client_version.AddInt(16003);
|
|
@@ -349,20 +375,44 @@ var Client = /** @class */ (function (_super) {
|
|
|
349
375
|
var reason = (MsgUnpacker_1.unpackString(a.toJSON().data.slice(4)).result);
|
|
350
376
|
_this.emit("disconnect", reason);
|
|
351
377
|
}
|
|
378
|
+
if (a.toJSON().data[3] !== 0x0) { // keepalive
|
|
379
|
+
_this.lastRecvTime = new Date().getTime();
|
|
380
|
+
}
|
|
352
381
|
}
|
|
382
|
+
else
|
|
383
|
+
_this.lastRecvTime = new Date().getTime();
|
|
353
384
|
var unpacked = _this.Unpack(a);
|
|
354
385
|
unpacked.chunks.forEach(function (a) {
|
|
355
386
|
if (a.flags & 1) { // vital
|
|
356
|
-
if (a.seq
|
|
387
|
+
if (a.seq === (_this.ack + 1) % (1 << 10)) {
|
|
357
388
|
_this.ack = a.seq;
|
|
389
|
+
_this.requestResend = false;
|
|
390
|
+
}
|
|
391
|
+
else { //IsSeqInBackroom (old packet that we already got)
|
|
392
|
+
var Bottom = (_this.ack - (1 << 10) / 2);
|
|
393
|
+
if (Bottom < 0) {
|
|
394
|
+
if ((a.seq <= _this.ack) || (a.seq >= (Bottom + (1 << 10))))
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
if (a.seq <= _this.ack && a.seq >= Bottom)
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
_this.requestResend = true;
|
|
402
|
+
// c_flags |= 4; /* resend flag */
|
|
403
|
+
// continue; // take the next chunk in the packet
|
|
404
|
+
}
|
|
358
405
|
}
|
|
359
406
|
});
|
|
407
|
+
unpacked.chunks.filter(function (a) { return a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game'; }).forEach(function (a) {
|
|
408
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(a.raw.toJSON().data);
|
|
409
|
+
_this.emit("broadcast", unpacker.unpackString());
|
|
410
|
+
});
|
|
360
411
|
_this.sentChunkQueue.forEach(function (buff, i) {
|
|
361
412
|
var chunk = _this.MsgToChunk(buff);
|
|
362
413
|
if (chunk.flags & 1) {
|
|
363
|
-
if (chunk.seq && chunk.seq
|
|
414
|
+
if (chunk.seq && chunk.seq >= _this.ack)
|
|
364
415
|
_this.sentChunkQueue.splice(i, 1);
|
|
365
|
-
}
|
|
366
416
|
}
|
|
367
417
|
});
|
|
368
418
|
var snapChunks = unpacked.chunks.filter(function (a) { return a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY"; });
|
|
@@ -372,46 +422,42 @@ var Client = /** @class */ (function (_super) {
|
|
|
372
422
|
snapChunks.forEach(function (chunk) {
|
|
373
423
|
var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
|
|
374
424
|
var AckGameTick = unpacker.unpackInt();
|
|
375
|
-
if (AckGameTick > _this.AckGameTick) {
|
|
376
|
-
_this.AckGameTick = AckGameTick;
|
|
377
|
-
if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
|
|
378
|
-
_this.PredGameTick = AckGameTick + 1;
|
|
379
|
-
}
|
|
380
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
|
|
381
425
|
var DeltaTick = AckGameTick - unpacker.unpackInt();
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
num_parts = unpacker.unpackInt();
|
|
387
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
|
|
388
|
-
part = unpacker.unpackInt();
|
|
389
|
-
}
|
|
390
|
-
var crc = 0;
|
|
391
|
-
var part_size = 0;
|
|
392
|
-
if (chunk.msg != "SNAP_EMPTY") {
|
|
393
|
-
crc = unpacker.unpackInt(); // crc
|
|
394
|
-
part_size = unpacker.unpackInt();
|
|
395
|
-
}
|
|
396
|
-
if (part === 0 || _this.snaps.length > 30) {
|
|
397
|
-
_this.snaps = [];
|
|
398
|
-
}
|
|
399
|
-
chunk.raw = Buffer.from(unpacker.remaining);
|
|
400
|
-
_this.snaps.push(chunk.raw);
|
|
401
|
-
if ((num_parts - 1) === part && _this.snaps.length === num_parts) {
|
|
402
|
-
var mergedSnaps = Buffer.concat(_this.snaps);
|
|
403
|
-
// mergedSnaps = Buffer.from(unpackInt(mergedSnaps.toJSON().data).remaining);
|
|
404
|
-
var snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1);
|
|
405
|
-
// console.log(snapUnpacked.items, toHexStream(mergedSnaps));
|
|
406
|
-
snapUnpacked.items.forEach(function (a, i) {
|
|
407
|
-
if (a.type_id === items.OBJ_CLIENT_INFO) {
|
|
408
|
-
_this.client_infos[a.id] = a.parsed;
|
|
409
|
-
// if (this.client_infos[a.id].name.includes("�") || this.client_infos[a.id].clan.includes("�")) {
|
|
410
|
-
// console.log("bad name", this.client_infos[a.id], toHexStream(mergedSnaps), chunk, AckGameTick, DeltaTick, crc, part_size);
|
|
411
|
-
// }
|
|
412
|
-
// console.log(this.client_infos[a.id])
|
|
426
|
+
if (AckGameTick >= _this.AckGameTick) {
|
|
427
|
+
if (_this.AckGameTick == -1) { // reset ack
|
|
428
|
+
if (DeltaTick == -1) { // acked reset
|
|
429
|
+
_this.AckGameTick = AckGameTick;
|
|
413
430
|
}
|
|
414
|
-
}
|
|
431
|
+
}
|
|
432
|
+
else
|
|
433
|
+
_this.AckGameTick = AckGameTick;
|
|
434
|
+
if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
|
|
435
|
+
_this.PredGameTick = AckGameTick + 1;
|
|
436
|
+
var num_parts_1 = 1;
|
|
437
|
+
var part_1 = 0;
|
|
438
|
+
if (chunk.msg === "SNAP") {
|
|
439
|
+
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
|
|
440
|
+
num_parts_1 = unpacker.unpackInt();
|
|
441
|
+
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
|
|
442
|
+
part_1 = unpacker.unpackInt();
|
|
443
|
+
}
|
|
444
|
+
var crc = 0;
|
|
445
|
+
var part_size = 0;
|
|
446
|
+
if (chunk.msg != "SNAP_EMPTY") {
|
|
447
|
+
crc = unpacker.unpackInt(); // crc
|
|
448
|
+
part_size = unpacker.unpackInt();
|
|
449
|
+
}
|
|
450
|
+
if (part_1 === 0 || _this.snaps.length > 30) {
|
|
451
|
+
_this.snaps = [];
|
|
452
|
+
}
|
|
453
|
+
chunk.raw = Buffer.from(unpacker.remaining);
|
|
454
|
+
_this.snaps.push(chunk.raw);
|
|
455
|
+
if ((num_parts_1 - 1) === part_1 && _this.snaps.length === num_parts_1) {
|
|
456
|
+
var mergedSnaps = Buffer.concat(_this.snaps);
|
|
457
|
+
var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, AckGameTick);
|
|
458
|
+
_this.AckGameTick = snapUnpacked.recvTick;
|
|
459
|
+
_this.sendInput();
|
|
460
|
+
}
|
|
415
461
|
}
|
|
416
462
|
});
|
|
417
463
|
}
|
|
@@ -426,11 +472,12 @@ var Client = /** @class */ (function (_super) {
|
|
|
426
472
|
client_id: unpacker.unpackInt(),
|
|
427
473
|
message: unpacker.unpackString()
|
|
428
474
|
};
|
|
429
|
-
if (unpacked.client_id != -1)
|
|
475
|
+
if (unpacked.client_id != -1) {
|
|
430
476
|
unpacked.author = {
|
|
431
|
-
ClientInfo: _this.
|
|
432
|
-
PlayerInfo: _this.
|
|
477
|
+
ClientInfo: _this.client_info(unpacked.client_id),
|
|
478
|
+
PlayerInfo: _this.player_info(unpacked.client_id)
|
|
433
479
|
};
|
|
480
|
+
}
|
|
434
481
|
_this.emit("message", unpacked);
|
|
435
482
|
}
|
|
436
483
|
});
|
|
@@ -445,10 +492,11 @@ var Client = /** @class */ (function (_super) {
|
|
|
445
492
|
unpacked.victim_id = unpacker.unpackInt();
|
|
446
493
|
unpacked.weapon = unpacker.unpackInt();
|
|
447
494
|
unpacked.special_mode = unpacker.unpackInt();
|
|
448
|
-
if (unpacked.victim_id != -1)
|
|
449
|
-
unpacked.victim = { ClientInfo: _this.
|
|
450
|
-
|
|
451
|
-
|
|
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) };
|
|
497
|
+
}
|
|
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) };
|
|
452
500
|
_this.emit("kill", unpacked);
|
|
453
501
|
}
|
|
454
502
|
});
|
|
@@ -464,7 +512,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
464
512
|
}
|
|
465
513
|
else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
|
|
466
514
|
var info = new MsgPacker_1.MsgPacker(20, false);
|
|
467
|
-
if ((
|
|
515
|
+
if ((_g = _this.options) === null || _g === void 0 ? void 0 : _g.identity) {
|
|
468
516
|
info.AddString(_this.options.identity.name);
|
|
469
517
|
info.AddString(_this.options.identity.clan);
|
|
470
518
|
info.AddInt(_this.options.identity.country);
|
|
@@ -482,7 +530,9 @@ var Client = /** @class */ (function (_super) {
|
|
|
482
530
|
info.AddInt(10346103); /* color body */
|
|
483
531
|
info.AddInt(65535); /* color feet */
|
|
484
532
|
}
|
|
485
|
-
|
|
533
|
+
var crashmeplx = new MsgPacker_1.MsgPacker(17, true); // rcon
|
|
534
|
+
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
535
|
+
_this.SendMsgEx([info, crashmeplx], 1);
|
|
486
536
|
}
|
|
487
537
|
else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
488
538
|
var info = new MsgPacker_1.MsgPacker(23, true);
|
|
@@ -583,6 +633,44 @@ var Client = /** @class */ (function (_super) {
|
|
|
583
633
|
packer.AddInt(emote);
|
|
584
634
|
this.SendMsgEx(packer, 1);
|
|
585
635
|
};
|
|
636
|
+
Client.prototype.client_info = function (id) {
|
|
637
|
+
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
638
|
+
return a.type_id == 11
|
|
639
|
+
&& a.id == id;
|
|
640
|
+
});
|
|
641
|
+
if (delta.length == 0)
|
|
642
|
+
return undefined;
|
|
643
|
+
return delta[0].parsed;
|
|
644
|
+
// .sort((a, b) => a.id - b.id)
|
|
645
|
+
// .map(a => a.parsed as ClientInfo);
|
|
646
|
+
};
|
|
647
|
+
Object.defineProperty(Client.prototype, "client_infos", {
|
|
648
|
+
get: function () {
|
|
649
|
+
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 11; })
|
|
650
|
+
.sort(function (a, b) { return a.id - b.id; })
|
|
651
|
+
.map(function (a) { return a.parsed; });
|
|
652
|
+
},
|
|
653
|
+
enumerable: false,
|
|
654
|
+
configurable: true
|
|
655
|
+
});
|
|
656
|
+
Client.prototype.player_info = function (id) {
|
|
657
|
+
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
658
|
+
return a.type_id == 11
|
|
659
|
+
&& a.id == id;
|
|
660
|
+
});
|
|
661
|
+
if (delta.length == 0)
|
|
662
|
+
return undefined;
|
|
663
|
+
return delta[0].parsed;
|
|
664
|
+
};
|
|
665
|
+
Object.defineProperty(Client.prototype, "player_infos", {
|
|
666
|
+
get: function () {
|
|
667
|
+
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 11; })
|
|
668
|
+
.sort(function (a, b) { return a.id - b.id; })
|
|
669
|
+
.map(function (a) { return a.parsed; });
|
|
670
|
+
},
|
|
671
|
+
enumerable: false,
|
|
672
|
+
configurable: true
|
|
673
|
+
});
|
|
586
674
|
return Client;
|
|
587
675
|
}(stream_1.EventEmitter));
|
|
588
676
|
exports.Client = Client;
|
package/lib/client.ts
CHANGED
|
@@ -12,7 +12,6 @@ import { Snapshot } from './snapshot';
|
|
|
12
12
|
import Huffman from "./huffman";
|
|
13
13
|
|
|
14
14
|
const huff = new Huffman();
|
|
15
|
-
const SnapUnpacker = new Snapshot();
|
|
16
15
|
|
|
17
16
|
enum States {
|
|
18
17
|
STATE_OFFLINE = 0,
|
|
@@ -24,18 +23,6 @@ enum States {
|
|
|
24
23
|
STATE_RESTARTING
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
interface NetObj_PlayerInput {
|
|
28
|
-
m_Direction: number,
|
|
29
|
-
m_TargetX: number,
|
|
30
|
-
m_TargetY: number,
|
|
31
|
-
m_Jump: number,
|
|
32
|
-
m_Fire: number,
|
|
33
|
-
m_Hook: number,
|
|
34
|
-
m_PlayerFlags: number,
|
|
35
|
-
m_WantedWeapon: number,
|
|
36
|
-
m_NextWeapon: number,
|
|
37
|
-
m_PrevWeapon: number
|
|
38
|
-
};
|
|
39
26
|
|
|
40
27
|
enum NETMSGTYPE {
|
|
41
28
|
EX,
|
|
@@ -73,29 +60,6 @@ enum NETMSGTYPE {
|
|
|
73
60
|
NUM
|
|
74
61
|
}
|
|
75
62
|
|
|
76
|
-
enum items {
|
|
77
|
-
OBJ_EX,
|
|
78
|
-
OBJ_PLAYER_INPUT,
|
|
79
|
-
OBJ_PROJECTILE,
|
|
80
|
-
OBJ_LASER,
|
|
81
|
-
OBJ_PICKUP,
|
|
82
|
-
OBJ_FLAG,
|
|
83
|
-
OBJ_GAME_INFO,
|
|
84
|
-
OBJ_GAME_DATA,
|
|
85
|
-
OBJ_CHARACTER_CORE,
|
|
86
|
-
OBJ_CHARACTER,
|
|
87
|
-
OBJ_PLAYER_INFO,
|
|
88
|
-
OBJ_CLIENT_INFO,
|
|
89
|
-
OBJ_SPECTATOR_INFO,
|
|
90
|
-
EVENT_COMMON,
|
|
91
|
-
EVENT_EXPLOSION,
|
|
92
|
-
EVENT_SPAWN,
|
|
93
|
-
EVENT_HAMMERHIT,
|
|
94
|
-
EVENT_DEATH,
|
|
95
|
-
EVENT_SOUND_GLOBAL,
|
|
96
|
-
EVENT_SOUND_WORLD,
|
|
97
|
-
EVENT_DAMAGE_INDICATOR
|
|
98
|
-
}
|
|
99
63
|
interface chunk {
|
|
100
64
|
bytes: number,
|
|
101
65
|
flags: number,
|
|
@@ -159,15 +123,15 @@ declare interface ClientInfo {
|
|
|
159
123
|
declare interface iMessage {
|
|
160
124
|
team: number,
|
|
161
125
|
client_id: number,
|
|
162
|
-
author?: { ClientInfo
|
|
126
|
+
author?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
163
127
|
message: string
|
|
164
128
|
}
|
|
165
129
|
|
|
166
130
|
declare interface iKillMsg {
|
|
167
131
|
killer_id: number,
|
|
168
|
-
killer?: { ClientInfo
|
|
132
|
+
killer?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
169
133
|
victim_id: number,
|
|
170
|
-
victim?: { ClientInfo
|
|
134
|
+
victim?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
171
135
|
weapon: number,
|
|
172
136
|
special_mode: number
|
|
173
137
|
}
|
|
@@ -176,6 +140,8 @@ declare interface iOptions {
|
|
|
176
140
|
identity?: ClientInfo,
|
|
177
141
|
password?: string,
|
|
178
142
|
ddnet_version?: {version: number, release_version: string},
|
|
143
|
+
timeout?: number, // in ms
|
|
144
|
+
NET_VERSION?: string
|
|
179
145
|
}
|
|
180
146
|
|
|
181
147
|
export declare interface Client {
|
|
@@ -190,6 +156,7 @@ export declare interface Client {
|
|
|
190
156
|
socket: net.Socket | undefined;
|
|
191
157
|
TKEN: Buffer;
|
|
192
158
|
time: number;
|
|
159
|
+
SnapUnpacker: Snapshot;
|
|
193
160
|
|
|
194
161
|
timer: number;
|
|
195
162
|
PredGameTick: number;
|
|
@@ -198,12 +165,16 @@ export declare interface Client {
|
|
|
198
165
|
movement: Movement;
|
|
199
166
|
|
|
200
167
|
snaps: Buffer[];
|
|
201
|
-
client_infos: ClientInfo[];
|
|
202
|
-
player_infos: PlayerInfo[];
|
|
203
168
|
|
|
204
169
|
sentChunkQueue: Buffer[];
|
|
205
|
-
|
|
170
|
+
queueChunkEx: MsgPacker[];
|
|
206
171
|
lastSendTime: number;
|
|
172
|
+
lastRecvTime: number;
|
|
173
|
+
|
|
174
|
+
lastSentMessages: {msg: MsgPacker, ack: number}[];
|
|
175
|
+
|
|
176
|
+
// eSnapHolder: eSnap[];
|
|
177
|
+
|
|
207
178
|
|
|
208
179
|
options?: iOptions;
|
|
209
180
|
|
|
@@ -211,8 +182,9 @@ export declare interface Client {
|
|
|
211
182
|
on(event: 'disconnect', listener: (reason: string) => void): this;
|
|
212
183
|
|
|
213
184
|
on(event: 'message', listener: (message: iMessage) => void): this;
|
|
185
|
+
on(event: 'broadcast', listener: (message: string) => void): this;
|
|
214
186
|
on(event: 'kill', listener: (kill: iKillMsg) => void): this;
|
|
215
|
-
|
|
187
|
+
requestResend: boolean;
|
|
216
188
|
}
|
|
217
189
|
|
|
218
190
|
|
|
@@ -228,6 +200,9 @@ export class Client extends EventEmitter {
|
|
|
228
200
|
this.name = nickname;
|
|
229
201
|
this.AckGameTick = 0;
|
|
230
202
|
this.PredGameTick = 0;
|
|
203
|
+
this.SnapUnpacker = new Snapshot();
|
|
204
|
+
// this.eSnapHolder = [];
|
|
205
|
+
this.requestResend = false;
|
|
231
206
|
|
|
232
207
|
if (options)
|
|
233
208
|
this.options = options;
|
|
@@ -237,10 +212,9 @@ export class Client extends EventEmitter {
|
|
|
237
212
|
this.movement = new Movement();
|
|
238
213
|
|
|
239
214
|
this.snaps = [];
|
|
240
|
-
this.client_infos = [];
|
|
241
|
-
this.player_infos = [];
|
|
242
215
|
|
|
243
216
|
this.sentChunkQueue = [];
|
|
217
|
+
this.queueChunkEx = [];
|
|
244
218
|
|
|
245
219
|
this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
246
220
|
this.ack = 0; // ack of messages the client has received
|
|
@@ -252,22 +226,40 @@ export class Client extends EventEmitter {
|
|
|
252
226
|
|
|
253
227
|
this.TKEN = Buffer.from([255, 255, 255, 255])
|
|
254
228
|
this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
|
|
255
|
-
|
|
256
229
|
this.lastSendTime = new Date().getTime();
|
|
230
|
+
this.lastRecvTime = new Date().getTime();
|
|
231
|
+
|
|
232
|
+
this.lastSentMessages = [];
|
|
257
233
|
|
|
258
234
|
}
|
|
235
|
+
|
|
236
|
+
ResendAfter(lastAck: number) {
|
|
237
|
+
this.clientAck = lastAck;
|
|
238
|
+
let toResend: MsgPacker[] = [];
|
|
239
|
+
this.lastSentMessages.forEach(msg => {
|
|
240
|
+
if (msg.ack > lastAck)
|
|
241
|
+
toResend.push(msg.msg);
|
|
242
|
+
});
|
|
243
|
+
this.SendMsgEx(toResend, 1|2);
|
|
244
|
+
}
|
|
245
|
+
|
|
259
246
|
Unpack(packet: Buffer): _packet {
|
|
260
|
-
var unpacked: _packet = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
247
|
+
var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
261
248
|
|
|
262
249
|
|
|
263
250
|
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)
|
|
264
251
|
return unpacked;
|
|
252
|
+
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
253
|
+
this.ResendAfter(unpacked.twprotocol.ack);
|
|
254
|
+
}
|
|
265
255
|
packet = packet.slice(3)
|
|
266
|
-
|
|
256
|
+
|
|
257
|
+
if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
|
|
267
258
|
packet = huff.decompress(packet)
|
|
268
259
|
if (packet.length == 1 && packet[0] == -1)
|
|
269
260
|
return unpacked
|
|
270
|
-
}
|
|
261
|
+
}
|
|
262
|
+
|
|
271
263
|
|
|
272
264
|
for (let i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
|
|
273
265
|
var chunk: chunk = {} as chunk;
|
|
@@ -325,31 +317,58 @@ export class Client extends EventEmitter {
|
|
|
325
317
|
_Msgs = Msgs;
|
|
326
318
|
else
|
|
327
319
|
_Msgs = [Msgs];
|
|
320
|
+
if (this.queueChunkEx.length > 0) {
|
|
321
|
+
_Msgs.push(...this.queueChunkEx);
|
|
322
|
+
this.queueChunkEx = [];
|
|
323
|
+
}
|
|
328
324
|
this.lastSendTime = new Date().getTime();
|
|
329
325
|
var header: Buffer[] = [];
|
|
326
|
+
if (this.clientAck == 0)
|
|
327
|
+
this.lastSentMessages = [];
|
|
330
328
|
_Msgs.forEach((Msg: MsgPacker, index) => {
|
|
331
329
|
header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
|
|
332
330
|
header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
333
331
|
header[index][1] = (Msg.size & 0xf);
|
|
334
332
|
if (Flags & 1) {
|
|
335
333
|
this.clientAck = (this.clientAck + 1) % (1 << 10);
|
|
334
|
+
if (this.clientAck == 0)
|
|
335
|
+
this.lastSentMessages = [];
|
|
336
336
|
header[index][1] |= (this.clientAck >> 2) & 0xf0;
|
|
337
337
|
header[index][2] = this.clientAck & 0xff;
|
|
338
338
|
header[index][0] = (((Flags | 2)&3)<<6)|((Msg.size>>4)&0x3f); // 2 is resend flag (ugly hack for queue)
|
|
339
|
-
|
|
340
|
-
|
|
339
|
+
if ((Flags & 2) == 0)
|
|
340
|
+
this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
341
341
|
header[index][0] = (((Flags)&3)<<6)|((Msg.size>>4)&0x3f);
|
|
342
|
+
if ((Flags & 2) == 0)
|
|
343
|
+
this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
|
|
342
344
|
}
|
|
343
345
|
})
|
|
344
|
-
|
|
346
|
+
let flags = 0;
|
|
347
|
+
if (this.requestResend)
|
|
348
|
+
flags |= 4;
|
|
349
|
+
|
|
350
|
+
var packetHeader = Buffer.from([((flags<<4)&0xf0)|((this.ack>>8)&0xf), this.ack & 0xff, _Msgs.length]);
|
|
345
351
|
var chunks = Buffer.from([]);
|
|
352
|
+
let skip = false;
|
|
346
353
|
_Msgs.forEach((Msg: MsgPacker, index) => {
|
|
347
|
-
|
|
354
|
+
if (skip)
|
|
355
|
+
return;
|
|
356
|
+
if (chunks.byteLength < 1300)
|
|
357
|
+
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
358
|
+
else {
|
|
359
|
+
skip = true;
|
|
360
|
+
this.SendMsgEx(_Msgs.slice(index), Flags);
|
|
361
|
+
}
|
|
348
362
|
})
|
|
349
363
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
350
|
-
|
|
364
|
+
// packet[0] |= 4;
|
|
351
365
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
352
366
|
}
|
|
367
|
+
|
|
368
|
+
QueueChunkEx(Msg: MsgPacker) {
|
|
369
|
+
this.queueChunkEx.push(Msg);
|
|
370
|
+
}
|
|
371
|
+
|
|
353
372
|
SendMsgRaw(chunks: Buffer[]) {
|
|
354
373
|
if (this.State == States.STATE_OFFLINE)
|
|
355
374
|
return;
|
|
@@ -390,7 +409,6 @@ export class Client extends EventEmitter {
|
|
|
390
409
|
}
|
|
391
410
|
|
|
392
411
|
connect() {
|
|
393
|
-
|
|
394
412
|
this.State = States.STATE_CONNECTING;
|
|
395
413
|
|
|
396
414
|
let predTimer = setInterval(() => {
|
|
@@ -427,7 +445,16 @@ export class Client extends EventEmitter {
|
|
|
427
445
|
} else
|
|
428
446
|
clearInterval(resendTimeout)
|
|
429
447
|
}, 1000)
|
|
430
|
-
|
|
448
|
+
|
|
449
|
+
let Timeout = setInterval(() => {
|
|
450
|
+
let timeoutTime = this.options?.timeout ? this.options.timeout : 15000;
|
|
451
|
+
if ((new Date().getTime() - this.lastRecvTime) > timeoutTime) {
|
|
452
|
+
this.State = States.STATE_OFFLINE;
|
|
453
|
+
this.emit("timeout");
|
|
454
|
+
this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - this.lastRecvTime) + "ms)");
|
|
455
|
+
clearInterval(Timeout);
|
|
456
|
+
}
|
|
457
|
+
}, 5000)
|
|
431
458
|
|
|
432
459
|
this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
|
|
433
460
|
|
|
@@ -445,7 +472,7 @@ export class Client extends EventEmitter {
|
|
|
445
472
|
this.receivedSnaps = 0;
|
|
446
473
|
|
|
447
474
|
var info = new MsgPacker(1, true);
|
|
448
|
-
info.AddString("0.6 626fce9a778df4d4");
|
|
475
|
+
info.AddString(this.options?.NET_VERSION ? this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
449
476
|
info.AddString(this.options?.password === undefined ? "" : this.options?.password); // password
|
|
450
477
|
|
|
451
478
|
var client_version = new MsgPacker(0, true);
|
|
@@ -469,23 +496,48 @@ export class Client extends EventEmitter {
|
|
|
469
496
|
this.State = States.STATE_OFFLINE;
|
|
470
497
|
let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
|
|
471
498
|
this.emit("disconnect", reason);
|
|
499
|
+
}
|
|
500
|
+
if (a.toJSON().data[3] !== 0x0) { // keepalive
|
|
501
|
+
this.lastRecvTime = new Date().getTime();
|
|
472
502
|
}
|
|
503
|
+
} else
|
|
504
|
+
this.lastRecvTime = new Date().getTime();
|
|
473
505
|
|
|
474
|
-
}
|
|
475
506
|
|
|
476
507
|
var unpacked: _packet = this.Unpack(a)
|
|
477
508
|
unpacked.chunks.forEach(a => {
|
|
478
509
|
if (a.flags & 1) { // vital
|
|
479
|
-
if (a.seq
|
|
480
|
-
this.ack = a.seq
|
|
510
|
+
if (a.seq === (this.ack+1)%(1<<10)) {
|
|
511
|
+
this.ack = a.seq;
|
|
512
|
+
this.requestResend = false;
|
|
513
|
+
}
|
|
514
|
+
else { //IsSeqInBackroom (old packet that we already got)
|
|
515
|
+
let Bottom = (this.ack - (1<<10)/2);
|
|
516
|
+
|
|
517
|
+
if(Bottom < 0) {
|
|
518
|
+
if((a.seq! <= this.ack) || (a.seq! >= (Bottom + (1<<10))))
|
|
519
|
+
return;
|
|
520
|
+
} else {
|
|
521
|
+
if(a.seq! <= this.ack && a.seq! >= Bottom)
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
this.requestResend = true;
|
|
525
|
+
// c_flags |= 4; /* resend flag */
|
|
526
|
+
// continue; // take the next chunk in the packet
|
|
527
|
+
|
|
528
|
+
}
|
|
481
529
|
}
|
|
482
530
|
})
|
|
531
|
+
unpacked.chunks.filter(a => a.msgid == NETMSGTYPE.SV_BROADCAST && a.type == 'game').forEach(a => {
|
|
532
|
+
let unpacker = new MsgUnpacker(a.raw.toJSON().data);
|
|
533
|
+
|
|
534
|
+
this.emit("broadcast", unpacker.unpackString());
|
|
535
|
+
})
|
|
483
536
|
this.sentChunkQueue.forEach((buff, i) => {
|
|
484
537
|
let chunk = this.MsgToChunk(buff);
|
|
485
538
|
if (chunk.flags & 1) {
|
|
486
|
-
if (chunk.seq && chunk.seq
|
|
539
|
+
if (chunk.seq && chunk.seq >= this.ack)
|
|
487
540
|
this.sentChunkQueue.splice(i, 1);
|
|
488
|
-
}
|
|
489
541
|
}
|
|
490
542
|
})
|
|
491
543
|
var snapChunks = unpacked.chunks.filter(a => a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY");
|
|
@@ -496,14 +548,18 @@ export class Client extends EventEmitter {
|
|
|
496
548
|
let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
|
|
497
549
|
|
|
498
550
|
let AckGameTick = unpacker.unpackInt();
|
|
499
|
-
|
|
500
|
-
|
|
551
|
+
|
|
552
|
+
let DeltaTick = AckGameTick - unpacker.unpackInt();
|
|
553
|
+
if (AckGameTick >= this.AckGameTick) {
|
|
554
|
+
if (this.AckGameTick == -1) {// reset ack
|
|
555
|
+
if (DeltaTick == -1) {// acked reset
|
|
556
|
+
this.AckGameTick = AckGameTick;
|
|
557
|
+
}
|
|
558
|
+
} else
|
|
559
|
+
this.AckGameTick = AckGameTick;
|
|
501
560
|
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
502
561
|
this.PredGameTick = AckGameTick + 1;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
|
|
506
|
-
let DeltaTick = AckGameTick - unpacker.unpackInt();
|
|
562
|
+
|
|
507
563
|
let num_parts = 1;
|
|
508
564
|
let part = 0;
|
|
509
565
|
|
|
@@ -525,24 +581,18 @@ export class Client extends EventEmitter {
|
|
|
525
581
|
}
|
|
526
582
|
chunk.raw = Buffer.from(unpacker.remaining);
|
|
527
583
|
this.snaps.push(chunk.raw)
|
|
528
|
-
|
|
584
|
+
|
|
529
585
|
if ((num_parts - 1) === part && this.snaps.length === num_parts) {
|
|
586
|
+
|
|
530
587
|
let mergedSnaps = Buffer.concat(this.snaps);
|
|
531
|
-
// mergedSnaps = Buffer.from(unpackInt(mergedSnaps.toJSON().data).remaining);
|
|
532
|
-
let snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, 1)
|
|
533
|
-
// console.log(snapUnpacked.items, toHexStream(mergedSnaps));
|
|
534
|
-
snapUnpacked.items.forEach((a, i) => {
|
|
535
|
-
if (a.type_id === items.OBJ_CLIENT_INFO) {
|
|
536
|
-
this.client_infos[a.id] = a.parsed as ClientInfo;
|
|
537
|
-
// if (this.client_infos[a.id].name.includes("�") || this.client_infos[a.id].clan.includes("�")) {
|
|
538
|
-
// console.log("bad name", this.client_infos[a.id], toHexStream(mergedSnaps), chunk, AckGameTick, DeltaTick, crc, part_size);
|
|
539
|
-
// }
|
|
540
|
-
// console.log(this.client_infos[a.id])
|
|
541
|
-
}
|
|
542
|
-
})
|
|
543
|
-
}
|
|
544
588
|
|
|
589
|
+
let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, AckGameTick);
|
|
590
|
+
this.AckGameTick = snapUnpacked.recvTick;
|
|
591
|
+
|
|
592
|
+
this.sendInput();
|
|
593
|
+
}
|
|
545
594
|
|
|
595
|
+
}
|
|
546
596
|
})
|
|
547
597
|
}
|
|
548
598
|
var chunkMessages = unpacked.chunks.map(a => a.msg)
|
|
@@ -557,11 +607,12 @@ export class Client extends EventEmitter {
|
|
|
557
607
|
message: unpacker.unpackString()
|
|
558
608
|
} as iMessage;
|
|
559
609
|
|
|
560
|
-
if (unpacked.client_id != -1)
|
|
610
|
+
if (unpacked.client_id != -1) {
|
|
561
611
|
unpacked.author = {
|
|
562
|
-
ClientInfo: this.
|
|
563
|
-
PlayerInfo: this.
|
|
612
|
+
ClientInfo: this.client_info(unpacked.client_id),
|
|
613
|
+
PlayerInfo: this.player_info(unpacked.client_id)
|
|
564
614
|
}
|
|
615
|
+
}
|
|
565
616
|
this.emit("message", unpacked)
|
|
566
617
|
}
|
|
567
618
|
})
|
|
@@ -576,10 +627,12 @@ export class Client extends EventEmitter {
|
|
|
576
627
|
unpacked.victim_id = unpacker.unpackInt();
|
|
577
628
|
unpacked.weapon = unpacker.unpackInt();
|
|
578
629
|
unpacked.special_mode = unpacker.unpackInt();
|
|
579
|
-
if (unpacked.victim_id != -1)
|
|
580
|
-
unpacked.victim = { ClientInfo: this.
|
|
581
|
-
|
|
582
|
-
|
|
630
|
+
if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
|
|
631
|
+
unpacked.victim = { ClientInfo: this.client_info(unpacked.victim_id), PlayerInfo: this.player_info(unpacked.victim_id) }
|
|
632
|
+
|
|
633
|
+
}
|
|
634
|
+
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
635
|
+
unpacked.killer = { ClientInfo: this.client_info(unpacked.killer_id), PlayerInfo: this.player_info(unpacked.killer_id) }
|
|
583
636
|
this.emit("kill", unpacked)
|
|
584
637
|
}
|
|
585
638
|
})
|
|
@@ -612,7 +665,10 @@ export class Client extends EventEmitter {
|
|
|
612
665
|
info.AddInt(65535); /* color feet */
|
|
613
666
|
|
|
614
667
|
}
|
|
615
|
-
|
|
668
|
+
var crashmeplx = new MsgPacker(17, true); // rcon
|
|
669
|
+
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
670
|
+
this.SendMsgEx([info, crashmeplx], 1);
|
|
671
|
+
|
|
616
672
|
|
|
617
673
|
|
|
618
674
|
} else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
@@ -714,6 +770,37 @@ export class Client extends EventEmitter {
|
|
|
714
770
|
packer.AddInt(emote);
|
|
715
771
|
this.SendMsgEx(packer, 1);
|
|
716
772
|
}
|
|
717
|
-
|
|
718
|
-
|
|
773
|
+
client_info(id: number) {
|
|
774
|
+
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
775
|
+
a.type_id == 11
|
|
776
|
+
&& a.id == id
|
|
777
|
+
);
|
|
778
|
+
|
|
779
|
+
if (delta.length == 0)
|
|
780
|
+
return undefined;
|
|
781
|
+
return delta[0].parsed as ClientInfo;
|
|
782
|
+
// .sort((a, b) => a.id - b.id)
|
|
783
|
+
// .map(a => a.parsed as ClientInfo);
|
|
784
|
+
}
|
|
785
|
+
get client_infos(): ClientInfo[] {
|
|
786
|
+
|
|
787
|
+
return this.SnapUnpacker.deltas.filter(a => a.type_id == 11)
|
|
788
|
+
.sort((a, b) => a.id - b.id)
|
|
789
|
+
.map(a => a.parsed as ClientInfo) ;
|
|
790
|
+
}
|
|
791
|
+
player_info(id: number) {
|
|
792
|
+
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
793
|
+
a.type_id == 11
|
|
794
|
+
&& a.id == id
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
if (delta.length == 0)
|
|
798
|
+
return undefined;
|
|
799
|
+
return delta[0].parsed as PlayerInfo;
|
|
800
|
+
}
|
|
801
|
+
get player_infos(): PlayerInfo[] {
|
|
802
|
+
return this.SnapUnpacker.deltas.filter(a => a.type_id == 11)
|
|
803
|
+
.sort((a, b) => a.id - b.id)
|
|
804
|
+
.map(a => a.parsed as PlayerInfo);
|
|
805
|
+
}
|
|
719
806
|
}
|
package/lib/snapshot.js
CHANGED
|
@@ -52,6 +52,8 @@ var items;
|
|
|
52
52
|
})(items = exports.items || (exports.items = {}));
|
|
53
53
|
var Snapshot = /** @class */ (function () {
|
|
54
54
|
function Snapshot() {
|
|
55
|
+
this.deltas = [];
|
|
56
|
+
this.eSnapHolder = [];
|
|
55
57
|
}
|
|
56
58
|
Snapshot.prototype.IntsToStr = function (pInts) {
|
|
57
59
|
var pIntz = [];
|
|
@@ -288,9 +290,23 @@ var Snapshot = /** @class */ (function () {
|
|
|
288
290
|
}
|
|
289
291
|
return _item;
|
|
290
292
|
};
|
|
291
|
-
Snapshot.prototype.unpackSnapshot = function (snap,
|
|
292
|
-
|
|
293
|
+
Snapshot.prototype.unpackSnapshot = function (snap, deltatick, recvTick) {
|
|
294
|
+
var _this = this;
|
|
293
295
|
var unpacker = new MsgUnpacker_1.MsgUnpacker(snap);
|
|
296
|
+
if (deltatick == -1) {
|
|
297
|
+
this.eSnapHolder = [];
|
|
298
|
+
this.deltas = [];
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
this.eSnapHolder = this.eSnapHolder.filter(function (a) { return a.ack >= deltatick; });
|
|
302
|
+
}
|
|
303
|
+
if (snap.length == 0) {
|
|
304
|
+
// empty snap, copy old one into new ack
|
|
305
|
+
this.eSnapHolder.filter(function (a) { return a.ack == deltatick; }).forEach(function (snap) {
|
|
306
|
+
_this.eSnapHolder.push({ Snapshot: snap.Snapshot, ack: recvTick });
|
|
307
|
+
});
|
|
308
|
+
return { items: [], recvTick: recvTick };
|
|
309
|
+
}
|
|
294
310
|
/* key = (((type_id) << 16) | (id))
|
|
295
311
|
* key_to_id = ((key) & 0xffff)
|
|
296
312
|
* key_to_type_id = ((key >> 16) & 0xffff)
|
|
@@ -300,6 +316,15 @@ var Snapshot = /** @class */ (function () {
|
|
|
300
316
|
var num_removed_items = unpacker.unpackInt();
|
|
301
317
|
var num_item_deltas = unpacker.unpackInt();
|
|
302
318
|
unpacker.unpackInt(); // _zero padding
|
|
319
|
+
var _loop_1 = function (i) {
|
|
320
|
+
var deleted_key = unpacker.unpackInt(); // removed_item_keys
|
|
321
|
+
var index = this_1.deltas.map(function (delta) { return delta.key; }).indexOf(deleted_key);
|
|
322
|
+
// console.log("deleting ", deleted_key, index)
|
|
323
|
+
if (index > -1)
|
|
324
|
+
this_1.deltas.splice(index, 1);
|
|
325
|
+
this_1.eSnapHolder = this_1.eSnapHolder.filter(function (a) { return a.Snapshot.Key !== deleted_key; });
|
|
326
|
+
};
|
|
327
|
+
var this_1 = this;
|
|
303
328
|
/*snapshot_delta:
|
|
304
329
|
[ 4] num_removed_items
|
|
305
330
|
[ 4] num_item_deltas
|
|
@@ -308,7 +333,7 @@ var Snapshot = /** @class */ (function () {
|
|
|
308
333
|
[ ] item_deltas
|
|
309
334
|
*/
|
|
310
335
|
for (var i = 0; i < num_removed_items; i++) {
|
|
311
|
-
|
|
336
|
+
_loop_1(i);
|
|
312
337
|
}
|
|
313
338
|
/*item_delta:
|
|
314
339
|
[ 4] type_id
|
|
@@ -316,6 +341,15 @@ var Snapshot = /** @class */ (function () {
|
|
|
316
341
|
[ 4] _size
|
|
317
342
|
[*4] data_delta*/
|
|
318
343
|
var items = { items: [], /* client_infos: client_infos, player_infos: player_infos,*/ lost: 0 };
|
|
344
|
+
var deltaSnaps = this.eSnapHolder.filter(function (a) { return a.ack === deltatick; });
|
|
345
|
+
if (deltaSnaps.length == 0 && deltatick >= 0) {
|
|
346
|
+
// console.log("RESET recvtick")
|
|
347
|
+
return { items: [], recvTick: -1 };
|
|
348
|
+
}
|
|
349
|
+
var newSnaps = [];
|
|
350
|
+
deltaSnaps.forEach(function (a) {
|
|
351
|
+
newSnaps.push({ Snapshot: a.Snapshot, ack: recvTick });
|
|
352
|
+
});
|
|
319
353
|
for (var i = 0; i < num_item_deltas; i++) {
|
|
320
354
|
var type_id = unpacker.unpackInt();
|
|
321
355
|
var id = unpacker.unpackInt();
|
|
@@ -330,12 +364,52 @@ var Snapshot = /** @class */ (function () {
|
|
|
330
364
|
for (var j = 0; j < _size; j++) {
|
|
331
365
|
if (unpacker.remaining.length > 0)
|
|
332
366
|
data.push(unpacker.unpackInt());
|
|
367
|
+
// else
|
|
368
|
+
// console.log("wrong size")
|
|
369
|
+
}
|
|
370
|
+
// console.log(index, deltatick)
|
|
371
|
+
if (deltatick >= 0) {
|
|
372
|
+
var index = deltaSnaps.map(function (delta) { return delta.Snapshot.Key; }).indexOf(key);
|
|
373
|
+
if (index > -1) {
|
|
374
|
+
var out = UndiffItem(deltaSnaps[index].Snapshot.Data, data);
|
|
375
|
+
data = out;
|
|
376
|
+
} // else no previous, use new data
|
|
333
377
|
}
|
|
334
378
|
var parsed = this.parseItem(data, type_id);
|
|
379
|
+
this.eSnapHolder.push({ Snapshot: { Data: data, Key: key }, ack: recvTick });
|
|
335
380
|
items.items.push({ data: data, parsed: parsed, type_id: type_id, id: id, key: key });
|
|
336
381
|
}
|
|
337
|
-
|
|
382
|
+
var _loop_2 = function (newSnap) {
|
|
383
|
+
if (this_2.eSnapHolder.filter(function (a) { return a.ack == newSnap.ack && a.Snapshot.Key == newSnap.Snapshot.Key; }).length === 0) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
|
|
384
|
+
this_2.eSnapHolder.push({ Snapshot: { Data: newSnap.Snapshot.Data, Key: newSnap.Snapshot.Key }, ack: recvTick });
|
|
385
|
+
var ____index = this_2.deltas.map(function (delta) { return delta.key; }).indexOf(newSnap.Snapshot.Key);
|
|
386
|
+
if (____index > -1 && deltatick > -1) {
|
|
387
|
+
this_2.deltas[____index] = { data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_2.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff)) };
|
|
388
|
+
}
|
|
389
|
+
else
|
|
390
|
+
this_2.deltas.push({ data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this_2.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff)) });
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
var this_2 = this;
|
|
394
|
+
for (var _i = 0, newSnaps_1 = newSnaps; _i < newSnaps_1.length; _i++) {
|
|
395
|
+
var newSnap = newSnaps_1[_i];
|
|
396
|
+
_loop_2(newSnap);
|
|
397
|
+
}
|
|
398
|
+
return { items: items, recvTick: recvTick };
|
|
338
399
|
};
|
|
339
400
|
return Snapshot;
|
|
340
401
|
}());
|
|
341
402
|
exports.Snapshot = Snapshot;
|
|
403
|
+
function UndiffItem(oldItem, newItem) {
|
|
404
|
+
var out = newItem;
|
|
405
|
+
if (JSON.stringify(newItem) === JSON.stringify(oldItem))
|
|
406
|
+
return newItem;
|
|
407
|
+
oldItem.forEach(function (a, i) {
|
|
408
|
+
if (a !== undefined && out[i] !== undefined) {
|
|
409
|
+
out[i] += a;
|
|
410
|
+
}
|
|
411
|
+
else
|
|
412
|
+
out[i] = 0;
|
|
413
|
+
});
|
|
414
|
+
return out;
|
|
415
|
+
}
|
package/lib/snapshot.ts
CHANGED
|
@@ -50,8 +50,14 @@ export enum items {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export type Item = PlayerInput | PlayerInfo | Projectile | Laser | Pickup | Flag | GameInfo | GameData | CharacterCore | Character | PlayerInfo | ClientInfo | SpectatorInfo | Common | Explosion | Spawn |HammerHit | Death | SoundGlobal | SoundWorld | DamageInd | DdnetCharacter;
|
|
53
|
-
|
|
53
|
+
interface eSnap {
|
|
54
|
+
Snapshot: {Key: number, Data: number[]},
|
|
55
|
+
ack: number,
|
|
56
|
+
}
|
|
54
57
|
export class Snapshot {
|
|
58
|
+
deltas: {'data': number[], 'parsed': Item, 'type_id': number, 'id': number, 'key': number}[] = [];
|
|
59
|
+
eSnapHolder: eSnap[] = [];
|
|
60
|
+
|
|
55
61
|
private IntsToStr(pInts: number[]): string {
|
|
56
62
|
var pIntz: number[] = [];
|
|
57
63
|
var pStr = ''
|
|
@@ -290,10 +296,22 @@ export class Snapshot {
|
|
|
290
296
|
|
|
291
297
|
return _item;
|
|
292
298
|
}
|
|
293
|
-
unpackSnapshot(snap: number[],
|
|
299
|
+
unpackSnapshot(snap: number[], deltatick: number, recvTick: number) {
|
|
294
300
|
let unpacker = new MsgUnpacker(snap);
|
|
301
|
+
if (deltatick == -1) {
|
|
302
|
+
this.eSnapHolder = [];
|
|
303
|
+
this.deltas = [];
|
|
304
|
+
} else {
|
|
305
|
+
this.eSnapHolder = this.eSnapHolder.filter(a => a.ack >= deltatick)
|
|
306
|
+
}
|
|
307
|
+
if (snap.length == 0) {
|
|
308
|
+
// empty snap, copy old one into new ack
|
|
309
|
+
this.eSnapHolder.filter(a => a.ack == deltatick).forEach(snap => {
|
|
310
|
+
this.eSnapHolder.push({Snapshot: snap.Snapshot, ack: recvTick});
|
|
295
311
|
|
|
296
|
-
|
|
312
|
+
})
|
|
313
|
+
return {items: [], recvTick: recvTick};
|
|
314
|
+
}
|
|
297
315
|
/* key = (((type_id) << 16) | (id))
|
|
298
316
|
* key_to_id = ((key) & 0xffff)
|
|
299
317
|
* key_to_type_id = ((key >> 16) & 0xffff)
|
|
@@ -313,15 +331,33 @@ export class Snapshot {
|
|
|
313
331
|
*/
|
|
314
332
|
|
|
315
333
|
for (let i = 0; i < num_removed_items; i++) {
|
|
316
|
-
unpacker.unpackInt(); // removed_item_keys
|
|
334
|
+
let deleted_key = unpacker.unpackInt(); // removed_item_keys
|
|
335
|
+
let index = this.deltas.map(delta => delta.key).indexOf(deleted_key);
|
|
336
|
+
// console.log("deleting ", deleted_key, index)
|
|
337
|
+
if (index > -1)
|
|
338
|
+
this.deltas.splice(index, 1);
|
|
339
|
+
this.eSnapHolder = this.eSnapHolder.filter(a => a.Snapshot.Key !== deleted_key);
|
|
317
340
|
}
|
|
318
341
|
/*item_delta:
|
|
319
342
|
[ 4] type_id
|
|
320
343
|
[ 4] id
|
|
321
344
|
[ 4] _size
|
|
322
345
|
[*4] data_delta*/
|
|
346
|
+
|
|
323
347
|
let items: {'items': {'data': number[], 'parsed': Item, 'type_id': number, 'id': number, 'key': number}[]/*, 'client_infos': client_info[], 'player_infos': player_info[]*/, lost: number} = {items: [],/* client_infos: client_infos, player_infos: player_infos,*/ lost: 0};
|
|
348
|
+
let deltaSnaps = this.eSnapHolder.filter(a => a.ack === deltatick);
|
|
324
349
|
|
|
350
|
+
if (deltaSnaps.length == 0 && deltatick >= 0) {
|
|
351
|
+
// console.log("RESET recvtick")
|
|
352
|
+
return {items: [], recvTick: -1};
|
|
353
|
+
}
|
|
354
|
+
let newSnaps: eSnap[] = [];
|
|
355
|
+
deltaSnaps.forEach((a) => {
|
|
356
|
+
|
|
357
|
+
newSnaps.push({Snapshot: a.Snapshot, ack: recvTick});
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
|
|
325
361
|
for (let i = 0; i < num_item_deltas; i++) {
|
|
326
362
|
let type_id = unpacker.unpackInt();
|
|
327
363
|
let id = unpacker.unpackInt();
|
|
@@ -337,13 +373,52 @@ export class Snapshot {
|
|
|
337
373
|
for (let j = 0; j < _size; j++) {
|
|
338
374
|
if (unpacker.remaining.length > 0)
|
|
339
375
|
data.push(unpacker.unpackInt());
|
|
376
|
+
// else
|
|
377
|
+
// console.log("wrong size")
|
|
340
378
|
}
|
|
379
|
+
// console.log(index, deltatick)
|
|
380
|
+
if (deltatick >= 0) {
|
|
381
|
+
let index = deltaSnaps.map(delta => delta.Snapshot.Key).indexOf(key)
|
|
382
|
+
if (index > -1) {
|
|
383
|
+
|
|
384
|
+
let out = UndiffItem(deltaSnaps[index].Snapshot.Data, data)
|
|
385
|
+
data = out;
|
|
386
|
+
} // else no previous, use new data
|
|
387
|
+
}
|
|
341
388
|
|
|
342
389
|
let parsed = this.parseItem(data, type_id)
|
|
343
|
-
|
|
390
|
+
this.eSnapHolder.push({Snapshot: {Data: data, Key: key}, ack: recvTick});
|
|
391
|
+
|
|
344
392
|
items.items.push({data, parsed, type_id, id, key})
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
345
396
|
}
|
|
397
|
+
for (let newSnap of newSnaps) {
|
|
398
|
+
if (this.eSnapHolder.filter(a => a.ack == newSnap.ack && a.Snapshot.Key == newSnap.Snapshot.Key).length === 0) { // ugly copy new snap to eSnapHolder (if it isnt pushed already)
|
|
399
|
+
this.eSnapHolder.push({Snapshot: {Data: newSnap.Snapshot.Data, Key: newSnap.Snapshot.Key}, ack: recvTick});
|
|
400
|
+
let ____index = this.deltas.map(delta => delta.key).indexOf(newSnap.Snapshot.Key);
|
|
346
401
|
|
|
402
|
+
if (____index > -1 && deltatick > -1) {
|
|
403
|
+
this.deltas[____index] = {data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff))};
|
|
404
|
+
} else
|
|
405
|
+
this.deltas.push({data: newSnap.Snapshot.Data, key: newSnap.Snapshot.Key, id: newSnap.Snapshot.Key & 0xffff, type_id: ((newSnap.Snapshot.Key >> 16) & 0xffff), parsed: this.parseItem(newSnap.Snapshot.Data, ((newSnap.Snapshot.Key >> 16) & 0xffff))});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
347
409
|
|
|
348
|
-
return items;
|
|
349
|
-
}
|
|
410
|
+
return {items, recvTick};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function UndiffItem(oldItem: number[], newItem: number[]): number[] {
|
|
414
|
+
let out: number[] = newItem;
|
|
415
|
+
if (JSON.stringify(newItem) === JSON.stringify(oldItem))
|
|
416
|
+
return newItem;
|
|
417
|
+
oldItem.forEach((a, i) => {
|
|
418
|
+
if (a !== undefined && out[i] !== undefined) {
|
|
419
|
+
out[i] += a;
|
|
420
|
+
} else
|
|
421
|
+
out[i] = 0;
|
|
422
|
+
})
|
|
423
|
+
return out;
|
|
424
|
+
}
|