teeworlds 2.1.8 → 2.3.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/lib/client.js +174 -89
- package/lib/client.ts +176 -93
- 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,17 @@ 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;
|
|
114
|
+
_this.pingStart = 0;
|
|
137
115
|
if (options)
|
|
138
116
|
_this.options = options;
|
|
139
117
|
_this.timer = 0;
|
|
140
118
|
_this.movement = new movement_1.default();
|
|
141
119
|
_this.snaps = [];
|
|
142
|
-
_this.client_infos = [];
|
|
143
|
-
_this.player_infos = [];
|
|
144
120
|
_this.sentChunkQueue = [];
|
|
121
|
+
_this.queueChunkEx = [];
|
|
145
122
|
_this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
146
123
|
_this.ack = 0; // ack of messages the client has received
|
|
147
124
|
_this.clientAck = 0; // ack of messages the client has sent
|
|
@@ -152,14 +129,28 @@ var Client = /** @class */ (function (_super) {
|
|
|
152
129
|
_this.TKEN = Buffer.from([255, 255, 255, 255]);
|
|
153
130
|
_this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
|
|
154
131
|
_this.lastSendTime = new Date().getTime();
|
|
132
|
+
_this.lastRecvTime = new Date().getTime();
|
|
133
|
+
_this.lastSentMessages = [];
|
|
155
134
|
return _this;
|
|
156
135
|
}
|
|
136
|
+
Client.prototype.ResendAfter = function (lastAck) {
|
|
137
|
+
this.clientAck = lastAck;
|
|
138
|
+
var toResend = [];
|
|
139
|
+
this.lastSentMessages.forEach(function (msg) {
|
|
140
|
+
if (msg.ack > lastAck)
|
|
141
|
+
toResend.push(msg.msg);
|
|
142
|
+
});
|
|
143
|
+
this.SendMsgEx(toResend, 1 | 2);
|
|
144
|
+
};
|
|
157
145
|
Client.prototype.Unpack = function (packet) {
|
|
158
|
-
var unpacked = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
|
|
146
|
+
var unpacked = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0] & 0xf) << 8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] };
|
|
159
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)
|
|
160
148
|
return unpacked;
|
|
149
|
+
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
150
|
+
this.ResendAfter(unpacked.twprotocol.ack);
|
|
151
|
+
}
|
|
161
152
|
packet = packet.slice(3);
|
|
162
|
-
if (unpacked.twprotocol.flags &
|
|
153
|
+
if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
|
|
163
154
|
packet = huff.decompress(packet);
|
|
164
155
|
if (packet.length == 1 && packet[0] == -1)
|
|
165
156
|
return unpacked;
|
|
@@ -220,29 +211,55 @@ var Client = /** @class */ (function (_super) {
|
|
|
220
211
|
_Msgs = Msgs;
|
|
221
212
|
else
|
|
222
213
|
_Msgs = [Msgs];
|
|
214
|
+
if (this.queueChunkEx.length > 0) {
|
|
215
|
+
_Msgs.push.apply(_Msgs, this.queueChunkEx);
|
|
216
|
+
this.queueChunkEx = [];
|
|
217
|
+
}
|
|
223
218
|
this.lastSendTime = new Date().getTime();
|
|
224
219
|
var header = [];
|
|
220
|
+
if (this.clientAck == 0)
|
|
221
|
+
this.lastSentMessages = [];
|
|
225
222
|
_Msgs.forEach(function (Msg, index) {
|
|
226
223
|
header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
|
|
227
224
|
header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
228
225
|
header[index][1] = (Msg.size & 0xf);
|
|
229
226
|
if (Flags & 1) {
|
|
230
227
|
_this.clientAck = (_this.clientAck + 1) % (1 << 10);
|
|
228
|
+
if (_this.clientAck == 0)
|
|
229
|
+
_this.lastSentMessages = [];
|
|
231
230
|
header[index][1] |= (_this.clientAck >> 2) & 0xf0;
|
|
232
231
|
header[index][2] = _this.clientAck & 0xff;
|
|
233
232
|
header[index][0] = (((Flags | 2) & 3) << 6) | ((Msg.size >> 4) & 0x3f); // 2 is resend flag (ugly hack for queue)
|
|
234
|
-
|
|
233
|
+
if ((Flags & 2) == 0)
|
|
234
|
+
_this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
235
235
|
header[index][0] = (((Flags) & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
236
|
+
if ((Flags & 2) == 0)
|
|
237
|
+
_this.lastSentMessages.push({ msg: Msg, ack: _this.clientAck });
|
|
236
238
|
}
|
|
237
239
|
});
|
|
238
|
-
var
|
|
240
|
+
var flags = 0;
|
|
241
|
+
if (this.requestResend)
|
|
242
|
+
flags |= 4;
|
|
243
|
+
var packetHeader = Buffer.from([((flags << 4) & 0xf0) | ((this.ack >> 8) & 0xf), this.ack & 0xff, _Msgs.length]);
|
|
239
244
|
var chunks = Buffer.from([]);
|
|
245
|
+
var skip = false;
|
|
240
246
|
_Msgs.forEach(function (Msg, index) {
|
|
241
|
-
|
|
247
|
+
if (skip)
|
|
248
|
+
return;
|
|
249
|
+
if (chunks.byteLength < 1300)
|
|
250
|
+
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
251
|
+
else {
|
|
252
|
+
skip = true;
|
|
253
|
+
_this.SendMsgEx(_Msgs.slice(index), Flags);
|
|
254
|
+
}
|
|
242
255
|
});
|
|
243
256
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
257
|
+
// packet[0] |= 4;
|
|
244
258
|
this.socket.send(packet, 0, packet.length, this.port, this.host);
|
|
245
259
|
};
|
|
260
|
+
Client.prototype.QueueChunkEx = function (Msg) {
|
|
261
|
+
this.queueChunkEx.push(Msg);
|
|
262
|
+
};
|
|
246
263
|
Client.prototype.SendMsgRaw = function (chunks) {
|
|
247
264
|
if (this.State == States.STATE_OFFLINE)
|
|
248
265
|
return;
|
|
@@ -311,10 +328,20 @@ var Client = /** @class */ (function (_super) {
|
|
|
311
328
|
else
|
|
312
329
|
clearInterval(resendTimeout);
|
|
313
330
|
}, 1000);
|
|
331
|
+
var Timeout = setInterval(function () {
|
|
332
|
+
var _a;
|
|
333
|
+
var timeoutTime = ((_a = _this.options) === null || _a === void 0 ? void 0 : _a.timeout) ? _this.options.timeout : 15000;
|
|
334
|
+
if ((new Date().getTime() - _this.lastRecvTime) > timeoutTime) {
|
|
335
|
+
_this.State = States.STATE_OFFLINE;
|
|
336
|
+
_this.emit("timeout");
|
|
337
|
+
_this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - _this.lastRecvTime) + "ms)");
|
|
338
|
+
clearInterval(Timeout);
|
|
339
|
+
}
|
|
340
|
+
}, 5000);
|
|
314
341
|
this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
|
|
315
342
|
if (this.socket)
|
|
316
343
|
this.socket.on("message", function (a, rinfo) {
|
|
317
|
-
var _a, _b, _c, _d, _e, _f;
|
|
344
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
318
345
|
if (_this.State == 0 || rinfo.address != _this.host || rinfo.port != _this.port)
|
|
319
346
|
return;
|
|
320
347
|
clearInterval(connectInterval);
|
|
@@ -326,16 +353,16 @@ var Client = /** @class */ (function (_super) {
|
|
|
326
353
|
_this.State = States.STATE_LOADING; // loading state
|
|
327
354
|
_this.receivedSnaps = 0;
|
|
328
355
|
var info = new MsgPacker_1.MsgPacker(1, true);
|
|
329
|
-
info.AddString("0.6 626fce9a778df4d4");
|
|
330
|
-
info.AddString(((
|
|
356
|
+
info.AddString(((_a = _this.options) === null || _a === void 0 ? void 0 : _a.NET_VERSION) ? _this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
357
|
+
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
358
|
var client_version = new MsgPacker_1.MsgPacker(0, true);
|
|
332
359
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
333
360
|
var randomUuid = Buffer.alloc(16);
|
|
334
361
|
crypto_1.randomBytes(16).copy(randomUuid);
|
|
335
362
|
client_version.AddBuffer(randomUuid);
|
|
336
|
-
if (((
|
|
337
|
-
client_version.AddInt((
|
|
338
|
-
client_version.AddString("DDNet " + ((
|
|
363
|
+
if (((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
|
|
364
|
+
client_version.AddInt((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
|
|
365
|
+
client_version.AddString("DDNet " + ((_f = _this.options) === null || _f === void 0 ? void 0 : _f.ddnet_version.release_version));
|
|
339
366
|
}
|
|
340
367
|
else {
|
|
341
368
|
client_version.AddInt(16003);
|
|
@@ -349,20 +376,40 @@ var Client = /** @class */ (function (_super) {
|
|
|
349
376
|
var reason = (MsgUnpacker_1.unpackString(a.toJSON().data.slice(4)).result);
|
|
350
377
|
_this.emit("disconnect", reason);
|
|
351
378
|
}
|
|
379
|
+
if (a.toJSON().data[3] !== 0x0) { // keepalive
|
|
380
|
+
_this.lastRecvTime = new Date().getTime();
|
|
381
|
+
}
|
|
352
382
|
}
|
|
383
|
+
else
|
|
384
|
+
_this.lastRecvTime = new Date().getTime();
|
|
353
385
|
var unpacked = _this.Unpack(a);
|
|
354
386
|
unpacked.chunks.forEach(function (a) {
|
|
355
387
|
if (a.flags & 1) { // vital
|
|
356
|
-
if (a.seq
|
|
388
|
+
if (a.seq === (_this.ack + 1) % (1 << 10)) {
|
|
357
389
|
_this.ack = a.seq;
|
|
390
|
+
_this.requestResend = false;
|
|
391
|
+
}
|
|
392
|
+
else { //IsSeqInBackroom (old packet that we already got)
|
|
393
|
+
var Bottom = (_this.ack - (1 << 10) / 2);
|
|
394
|
+
if (Bottom < 0) {
|
|
395
|
+
if ((a.seq <= _this.ack) || (a.seq >= (Bottom + (1 << 10))))
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
if (a.seq <= _this.ack && a.seq >= Bottom)
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
_this.requestResend = true;
|
|
403
|
+
// c_flags |= 4; /* resend flag */
|
|
404
|
+
// continue; // take the next chunk in the packet
|
|
405
|
+
}
|
|
358
406
|
}
|
|
359
407
|
});
|
|
360
408
|
_this.sentChunkQueue.forEach(function (buff, i) {
|
|
361
409
|
var chunk = _this.MsgToChunk(buff);
|
|
362
410
|
if (chunk.flags & 1) {
|
|
363
|
-
if (chunk.seq && chunk.seq
|
|
411
|
+
if (chunk.seq && chunk.seq >= _this.ack)
|
|
364
412
|
_this.sentChunkQueue.splice(i, 1);
|
|
365
|
-
}
|
|
366
413
|
}
|
|
367
414
|
});
|
|
368
415
|
var snapChunks = unpacked.chunks.filter(function (a) { return a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY"; });
|
|
@@ -372,46 +419,42 @@ var Client = /** @class */ (function (_super) {
|
|
|
372
419
|
snapChunks.forEach(function (chunk) {
|
|
373
420
|
var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw.toJSON().data);
|
|
374
421
|
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
422
|
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_1 = Buffer.concat(_this.snaps);
|
|
403
|
-
// mergedSnaps = Buffer.from(unpackInt(mergedSnaps.toJSON().data).remaining);
|
|
404
|
-
var snapUnpacked = SnapUnpacker.unpackSnapshot(mergedSnaps_1.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_1), chunk, AckGameTick, DeltaTick, crc, part_size);
|
|
411
|
-
}
|
|
412
|
-
console.log(_this.client_infos[a.id]);
|
|
423
|
+
if (AckGameTick >= _this.AckGameTick) {
|
|
424
|
+
if (_this.AckGameTick == -1) { // reset ack
|
|
425
|
+
if (DeltaTick == -1) { // acked reset
|
|
426
|
+
_this.AckGameTick = AckGameTick;
|
|
413
427
|
}
|
|
414
|
-
}
|
|
428
|
+
}
|
|
429
|
+
else
|
|
430
|
+
_this.AckGameTick = AckGameTick;
|
|
431
|
+
if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
|
|
432
|
+
_this.PredGameTick = AckGameTick + 1;
|
|
433
|
+
var num_parts_1 = 1;
|
|
434
|
+
var part_1 = 0;
|
|
435
|
+
if (chunk.msg === "SNAP") {
|
|
436
|
+
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // delta tick
|
|
437
|
+
num_parts_1 = unpacker.unpackInt();
|
|
438
|
+
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining); // num parts
|
|
439
|
+
part_1 = unpacker.unpackInt();
|
|
440
|
+
}
|
|
441
|
+
var crc = 0;
|
|
442
|
+
var part_size = 0;
|
|
443
|
+
if (chunk.msg != "SNAP_EMPTY") {
|
|
444
|
+
crc = unpacker.unpackInt(); // crc
|
|
445
|
+
part_size = unpacker.unpackInt();
|
|
446
|
+
}
|
|
447
|
+
if (part_1 === 0 || _this.snaps.length > 30) {
|
|
448
|
+
_this.snaps = [];
|
|
449
|
+
}
|
|
450
|
+
chunk.raw = Buffer.from(unpacker.remaining);
|
|
451
|
+
_this.snaps.push(chunk.raw);
|
|
452
|
+
if ((num_parts_1 - 1) === part_1 && _this.snaps.length === num_parts_1) {
|
|
453
|
+
var mergedSnaps = Buffer.concat(_this.snaps);
|
|
454
|
+
var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, AckGameTick);
|
|
455
|
+
_this.AckGameTick = snapUnpacked.recvTick;
|
|
456
|
+
_this.sendInput();
|
|
457
|
+
}
|
|
415
458
|
}
|
|
416
459
|
});
|
|
417
460
|
}
|
|
@@ -426,11 +469,12 @@ var Client = /** @class */ (function (_super) {
|
|
|
426
469
|
client_id: unpacker.unpackInt(),
|
|
427
470
|
message: unpacker.unpackString()
|
|
428
471
|
};
|
|
429
|
-
if (unpacked.client_id != -1)
|
|
472
|
+
if (unpacked.client_id != -1) {
|
|
430
473
|
unpacked.author = {
|
|
431
|
-
ClientInfo: _this.
|
|
432
|
-
PlayerInfo: _this.
|
|
474
|
+
ClientInfo: _this.client_info(unpacked.client_id),
|
|
475
|
+
PlayerInfo: _this.player_info(unpacked.client_id)
|
|
433
476
|
};
|
|
477
|
+
}
|
|
434
478
|
_this.emit("message", unpacked);
|
|
435
479
|
}
|
|
436
480
|
});
|
|
@@ -445,10 +489,11 @@ var Client = /** @class */ (function (_super) {
|
|
|
445
489
|
unpacked.victim_id = unpacker.unpackInt();
|
|
446
490
|
unpacked.weapon = unpacker.unpackInt();
|
|
447
491
|
unpacked.special_mode = unpacker.unpackInt();
|
|
448
|
-
if (unpacked.victim_id != -1)
|
|
449
|
-
unpacked.victim = { ClientInfo: _this.
|
|
450
|
-
|
|
451
|
-
|
|
492
|
+
if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
|
|
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) };
|
|
452
497
|
_this.emit("kill", unpacked);
|
|
453
498
|
}
|
|
454
499
|
});
|
|
@@ -464,7 +509,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
464
509
|
}
|
|
465
510
|
else if ((unpacked.chunks[0] && chunkMessages.includes("CON_READY") || unpacked.chunks[0] && chunkMessages.includes("SV_MOTD"))) {
|
|
466
511
|
var info = new MsgPacker_1.MsgPacker(20, false);
|
|
467
|
-
if ((
|
|
512
|
+
if ((_g = _this.options) === null || _g === void 0 ? void 0 : _g.identity) {
|
|
468
513
|
info.AddString(_this.options.identity.name);
|
|
469
514
|
info.AddString(_this.options.identity.clan);
|
|
470
515
|
info.AddInt(_this.options.identity.country);
|
|
@@ -482,7 +527,9 @@ var Client = /** @class */ (function (_super) {
|
|
|
482
527
|
info.AddInt(10346103); /* color body */
|
|
483
528
|
info.AddInt(65535); /* color feet */
|
|
484
529
|
}
|
|
485
|
-
|
|
530
|
+
var crashmeplx = new MsgPacker_1.MsgPacker(17, true); // rcon
|
|
531
|
+
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
532
|
+
_this.SendMsgEx([info, crashmeplx], 1);
|
|
486
533
|
}
|
|
487
534
|
else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
488
535
|
var info = new MsgPacker_1.MsgPacker(23, true);
|
|
@@ -583,6 +630,44 @@ var Client = /** @class */ (function (_super) {
|
|
|
583
630
|
packer.AddInt(emote);
|
|
584
631
|
this.SendMsgEx(packer, 1);
|
|
585
632
|
};
|
|
633
|
+
Client.prototype.client_info = function (id) {
|
|
634
|
+
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
635
|
+
return a.type_id == 11
|
|
636
|
+
&& a.id == id;
|
|
637
|
+
});
|
|
638
|
+
if (delta.length == 0)
|
|
639
|
+
return undefined;
|
|
640
|
+
return delta[0].parsed;
|
|
641
|
+
// .sort((a, b) => a.id - b.id)
|
|
642
|
+
// .map(a => a.parsed as ClientInfo);
|
|
643
|
+
};
|
|
644
|
+
Object.defineProperty(Client.prototype, "client_infos", {
|
|
645
|
+
get: function () {
|
|
646
|
+
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 11; })
|
|
647
|
+
.sort(function (a, b) { return a.id - b.id; })
|
|
648
|
+
.map(function (a) { return a.parsed; });
|
|
649
|
+
},
|
|
650
|
+
enumerable: false,
|
|
651
|
+
configurable: true
|
|
652
|
+
});
|
|
653
|
+
Client.prototype.player_info = function (id) {
|
|
654
|
+
var delta = this.SnapUnpacker.deltas.filter(function (a) {
|
|
655
|
+
return a.type_id == 11
|
|
656
|
+
&& a.id == id;
|
|
657
|
+
});
|
|
658
|
+
if (delta.length == 0)
|
|
659
|
+
return undefined;
|
|
660
|
+
return delta[0].parsed;
|
|
661
|
+
};
|
|
662
|
+
Object.defineProperty(Client.prototype, "player_infos", {
|
|
663
|
+
get: function () {
|
|
664
|
+
return this.SnapUnpacker.deltas.filter(function (a) { return a.type_id == 11; })
|
|
665
|
+
.sort(function (a, b) { return a.id - b.id; })
|
|
666
|
+
.map(function (a) { return a.parsed; });
|
|
667
|
+
},
|
|
668
|
+
enumerable: false,
|
|
669
|
+
configurable: true
|
|
670
|
+
});
|
|
586
671
|
return Client;
|
|
587
672
|
}(stream_1.EventEmitter));
|
|
588
673
|
exports.Client = Client;
|
package/lib/client.ts
CHANGED
|
@@ -8,11 +8,10 @@ import { unpackString, MsgUnpacker } from "./MsgUnpacker";
|
|
|
8
8
|
import Movement from './movement';
|
|
9
9
|
|
|
10
10
|
import { MsgPacker } from './MsgPacker';
|
|
11
|
-
import { Snapshot } from './snapshot';
|
|
11
|
+
import { Item, 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,17 @@ 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
|
+
|
|
178
|
+
pingStart: number;
|
|
207
179
|
|
|
208
180
|
options?: iOptions;
|
|
209
181
|
|
|
@@ -212,7 +184,7 @@ export declare interface Client {
|
|
|
212
184
|
|
|
213
185
|
on(event: 'message', listener: (message: iMessage) => 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,10 @@ 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;
|
|
206
|
+
this.pingStart = 0;
|
|
231
207
|
|
|
232
208
|
if (options)
|
|
233
209
|
this.options = options;
|
|
@@ -237,10 +213,9 @@ export class Client extends EventEmitter {
|
|
|
237
213
|
this.movement = new Movement();
|
|
238
214
|
|
|
239
215
|
this.snaps = [];
|
|
240
|
-
this.client_infos = [];
|
|
241
|
-
this.player_infos = [];
|
|
242
216
|
|
|
243
217
|
this.sentChunkQueue = [];
|
|
218
|
+
this.queueChunkEx = [];
|
|
244
219
|
|
|
245
220
|
this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
246
221
|
this.ack = 0; // ack of messages the client has received
|
|
@@ -252,22 +227,40 @@ export class Client extends EventEmitter {
|
|
|
252
227
|
|
|
253
228
|
this.TKEN = Buffer.from([255, 255, 255, 255])
|
|
254
229
|
this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
|
|
255
|
-
|
|
256
230
|
this.lastSendTime = new Date().getTime();
|
|
231
|
+
this.lastRecvTime = new Date().getTime();
|
|
257
232
|
|
|
233
|
+
this.lastSentMessages = [];
|
|
234
|
+
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ResendAfter(lastAck: number) {
|
|
238
|
+
this.clientAck = lastAck;
|
|
239
|
+
let toResend: MsgPacker[] = [];
|
|
240
|
+
this.lastSentMessages.forEach(msg => {
|
|
241
|
+
if (msg.ack > lastAck)
|
|
242
|
+
toResend.push(msg.msg);
|
|
243
|
+
});
|
|
244
|
+
this.SendMsgEx(toResend, 1|2);
|
|
258
245
|
}
|
|
246
|
+
|
|
259
247
|
Unpack(packet: Buffer): _packet {
|
|
260
|
-
var unpacked: _packet = { twprotocol: { flags: packet[0], ack: packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
248
|
+
var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
261
249
|
|
|
262
250
|
|
|
263
251
|
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
252
|
return unpacked;
|
|
253
|
+
if (unpacked.twprotocol.flags & 4) { // resend flag
|
|
254
|
+
this.ResendAfter(unpacked.twprotocol.ack);
|
|
255
|
+
}
|
|
265
256
|
packet = packet.slice(3)
|
|
266
|
-
|
|
257
|
+
|
|
258
|
+
if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
|
|
267
259
|
packet = huff.decompress(packet)
|
|
268
260
|
if (packet.length == 1 && packet[0] == -1)
|
|
269
261
|
return unpacked
|
|
270
|
-
}
|
|
262
|
+
}
|
|
263
|
+
|
|
271
264
|
|
|
272
265
|
for (let i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
|
|
273
266
|
var chunk: chunk = {} as chunk;
|
|
@@ -325,31 +318,58 @@ export class Client extends EventEmitter {
|
|
|
325
318
|
_Msgs = Msgs;
|
|
326
319
|
else
|
|
327
320
|
_Msgs = [Msgs];
|
|
321
|
+
if (this.queueChunkEx.length > 0) {
|
|
322
|
+
_Msgs.push(...this.queueChunkEx);
|
|
323
|
+
this.queueChunkEx = [];
|
|
324
|
+
}
|
|
328
325
|
this.lastSendTime = new Date().getTime();
|
|
329
326
|
var header: Buffer[] = [];
|
|
327
|
+
if (this.clientAck == 0)
|
|
328
|
+
this.lastSentMessages = [];
|
|
330
329
|
_Msgs.forEach((Msg: MsgPacker, index) => {
|
|
331
330
|
header[index] = Buffer.alloc((Flags & 1 ? 3 : 2));
|
|
332
331
|
header[index][0] = ((Flags & 3) << 6) | ((Msg.size >> 4) & 0x3f);
|
|
333
332
|
header[index][1] = (Msg.size & 0xf);
|
|
334
333
|
if (Flags & 1) {
|
|
335
334
|
this.clientAck = (this.clientAck + 1) % (1 << 10);
|
|
335
|
+
if (this.clientAck == 0)
|
|
336
|
+
this.lastSentMessages = [];
|
|
336
337
|
header[index][1] |= (this.clientAck >> 2) & 0xf0;
|
|
337
338
|
header[index][2] = this.clientAck & 0xff;
|
|
338
339
|
header[index][0] = (((Flags | 2)&3)<<6)|((Msg.size>>4)&0x3f); // 2 is resend flag (ugly hack for queue)
|
|
339
|
-
|
|
340
|
-
|
|
340
|
+
if ((Flags & 2) == 0)
|
|
341
|
+
this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
|
|
341
342
|
header[index][0] = (((Flags)&3)<<6)|((Msg.size>>4)&0x3f);
|
|
343
|
+
if ((Flags & 2) == 0)
|
|
344
|
+
this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
|
|
342
345
|
}
|
|
343
346
|
})
|
|
344
|
-
|
|
347
|
+
let flags = 0;
|
|
348
|
+
if (this.requestResend)
|
|
349
|
+
flags |= 4;
|
|
350
|
+
|
|
351
|
+
var packetHeader = Buffer.from([((flags<<4)&0xf0)|((this.ack>>8)&0xf), this.ack & 0xff, _Msgs.length]);
|
|
345
352
|
var chunks = Buffer.from([]);
|
|
353
|
+
let skip = false;
|
|
346
354
|
_Msgs.forEach((Msg: MsgPacker, index) => {
|
|
347
|
-
|
|
355
|
+
if (skip)
|
|
356
|
+
return;
|
|
357
|
+
if (chunks.byteLength < 1300)
|
|
358
|
+
chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
|
|
359
|
+
else {
|
|
360
|
+
skip = true;
|
|
361
|
+
this.SendMsgEx(_Msgs.slice(index), Flags);
|
|
362
|
+
}
|
|
348
363
|
})
|
|
349
364
|
var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
|
|
350
|
-
|
|
365
|
+
// packet[0] |= 4;
|
|
351
366
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
352
367
|
}
|
|
368
|
+
|
|
369
|
+
QueueChunkEx(Msg: MsgPacker) {
|
|
370
|
+
this.queueChunkEx.push(Msg);
|
|
371
|
+
}
|
|
372
|
+
|
|
353
373
|
SendMsgRaw(chunks: Buffer[]) {
|
|
354
374
|
if (this.State == States.STATE_OFFLINE)
|
|
355
375
|
return;
|
|
@@ -390,7 +410,6 @@ export class Client extends EventEmitter {
|
|
|
390
410
|
}
|
|
391
411
|
|
|
392
412
|
connect() {
|
|
393
|
-
|
|
394
413
|
this.State = States.STATE_CONNECTING;
|
|
395
414
|
|
|
396
415
|
let predTimer = setInterval(() => {
|
|
@@ -427,7 +446,16 @@ export class Client extends EventEmitter {
|
|
|
427
446
|
} else
|
|
428
447
|
clearInterval(resendTimeout)
|
|
429
448
|
}, 1000)
|
|
430
|
-
|
|
449
|
+
|
|
450
|
+
let Timeout = setInterval(() => {
|
|
451
|
+
let timeoutTime = this.options?.timeout ? this.options.timeout : 15000;
|
|
452
|
+
if ((new Date().getTime() - this.lastRecvTime) > timeoutTime) {
|
|
453
|
+
this.State = States.STATE_OFFLINE;
|
|
454
|
+
this.emit("timeout");
|
|
455
|
+
this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - this.lastRecvTime) + "ms)");
|
|
456
|
+
clearInterval(Timeout);
|
|
457
|
+
}
|
|
458
|
+
}, 5000)
|
|
431
459
|
|
|
432
460
|
this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
|
|
433
461
|
|
|
@@ -445,7 +473,7 @@ export class Client extends EventEmitter {
|
|
|
445
473
|
this.receivedSnaps = 0;
|
|
446
474
|
|
|
447
475
|
var info = new MsgPacker(1, true);
|
|
448
|
-
info.AddString("0.6 626fce9a778df4d4");
|
|
476
|
+
info.AddString(this.options?.NET_VERSION ? this.options.NET_VERSION : "0.6 626fce9a778df4d4");
|
|
449
477
|
info.AddString(this.options?.password === undefined ? "" : this.options?.password); // password
|
|
450
478
|
|
|
451
479
|
var client_version = new MsgPacker(0, true);
|
|
@@ -469,23 +497,43 @@ export class Client extends EventEmitter {
|
|
|
469
497
|
this.State = States.STATE_OFFLINE;
|
|
470
498
|
let reason: string = (unpackString(a.toJSON().data.slice(4)).result);
|
|
471
499
|
this.emit("disconnect", reason);
|
|
500
|
+
}
|
|
501
|
+
if (a.toJSON().data[3] !== 0x0) { // keepalive
|
|
502
|
+
this.lastRecvTime = new Date().getTime();
|
|
472
503
|
}
|
|
504
|
+
} else
|
|
505
|
+
this.lastRecvTime = new Date().getTime();
|
|
473
506
|
|
|
474
|
-
}
|
|
475
507
|
|
|
476
508
|
var unpacked: _packet = this.Unpack(a)
|
|
477
509
|
unpacked.chunks.forEach(a => {
|
|
478
510
|
if (a.flags & 1) { // vital
|
|
479
|
-
if (a.seq
|
|
480
|
-
this.ack = a.seq
|
|
511
|
+
if (a.seq === (this.ack+1)%(1<<10)) {
|
|
512
|
+
this.ack = a.seq;
|
|
513
|
+
this.requestResend = false;
|
|
514
|
+
}
|
|
515
|
+
else { //IsSeqInBackroom (old packet that we already got)
|
|
516
|
+
let Bottom = (this.ack - (1<<10)/2);
|
|
517
|
+
|
|
518
|
+
if(Bottom < 0) {
|
|
519
|
+
if((a.seq! <= this.ack) || (a.seq! >= (Bottom + (1<<10))))
|
|
520
|
+
return;
|
|
521
|
+
} else {
|
|
522
|
+
if(a.seq! <= this.ack && a.seq! >= Bottom)
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
this.requestResend = true;
|
|
526
|
+
// c_flags |= 4; /* resend flag */
|
|
527
|
+
// continue; // take the next chunk in the packet
|
|
528
|
+
|
|
529
|
+
}
|
|
481
530
|
}
|
|
482
531
|
})
|
|
483
532
|
this.sentChunkQueue.forEach((buff, i) => {
|
|
484
533
|
let chunk = this.MsgToChunk(buff);
|
|
485
534
|
if (chunk.flags & 1) {
|
|
486
|
-
if (chunk.seq && chunk.seq
|
|
535
|
+
if (chunk.seq && chunk.seq >= this.ack)
|
|
487
536
|
this.sentChunkQueue.splice(i, 1);
|
|
488
|
-
}
|
|
489
537
|
}
|
|
490
538
|
})
|
|
491
539
|
var snapChunks = unpacked.chunks.filter(a => a.msg === "SNAP" || a.msg === "SNAP_SINGLE" || a.msg === "SNAP_EMPTY");
|
|
@@ -496,14 +544,18 @@ export class Client extends EventEmitter {
|
|
|
496
544
|
let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
|
|
497
545
|
|
|
498
546
|
let AckGameTick = unpacker.unpackInt();
|
|
499
|
-
|
|
500
|
-
|
|
547
|
+
|
|
548
|
+
let DeltaTick = AckGameTick - unpacker.unpackInt();
|
|
549
|
+
if (AckGameTick >= this.AckGameTick) {
|
|
550
|
+
if (this.AckGameTick == -1) {// reset ack
|
|
551
|
+
if (DeltaTick == -1) {// acked reset
|
|
552
|
+
this.AckGameTick = AckGameTick;
|
|
553
|
+
}
|
|
554
|
+
} else
|
|
555
|
+
this.AckGameTick = AckGameTick;
|
|
501
556
|
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
502
557
|
this.PredGameTick = AckGameTick + 1;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
// chunk.raw = Buffer.from(unpackInt(chunk?.raw?.toJSON().data).remaining);
|
|
506
|
-
let DeltaTick = AckGameTick - unpacker.unpackInt();
|
|
558
|
+
|
|
507
559
|
let num_parts = 1;
|
|
508
560
|
let part = 0;
|
|
509
561
|
|
|
@@ -525,24 +577,18 @@ export class Client extends EventEmitter {
|
|
|
525
577
|
}
|
|
526
578
|
chunk.raw = Buffer.from(unpacker.remaining);
|
|
527
579
|
this.snaps.push(chunk.raw)
|
|
528
|
-
|
|
580
|
+
|
|
529
581
|
if ((num_parts - 1) === part && this.snaps.length === num_parts) {
|
|
582
|
+
|
|
530
583
|
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
584
|
|
|
585
|
+
let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, AckGameTick);
|
|
586
|
+
this.AckGameTick = snapUnpacked.recvTick;
|
|
587
|
+
|
|
588
|
+
this.sendInput();
|
|
589
|
+
}
|
|
545
590
|
|
|
591
|
+
}
|
|
546
592
|
})
|
|
547
593
|
}
|
|
548
594
|
var chunkMessages = unpacked.chunks.map(a => a.msg)
|
|
@@ -557,11 +603,12 @@ export class Client extends EventEmitter {
|
|
|
557
603
|
message: unpacker.unpackString()
|
|
558
604
|
} as iMessage;
|
|
559
605
|
|
|
560
|
-
if (unpacked.client_id != -1)
|
|
606
|
+
if (unpacked.client_id != -1) {
|
|
561
607
|
unpacked.author = {
|
|
562
|
-
ClientInfo: this.
|
|
563
|
-
PlayerInfo: this.
|
|
608
|
+
ClientInfo: this.client_info(unpacked.client_id),
|
|
609
|
+
PlayerInfo: this.player_info(unpacked.client_id)
|
|
564
610
|
}
|
|
611
|
+
}
|
|
565
612
|
this.emit("message", unpacked)
|
|
566
613
|
}
|
|
567
614
|
})
|
|
@@ -576,10 +623,12 @@ export class Client extends EventEmitter {
|
|
|
576
623
|
unpacked.victim_id = unpacker.unpackInt();
|
|
577
624
|
unpacked.weapon = unpacker.unpackInt();
|
|
578
625
|
unpacked.special_mode = unpacker.unpackInt();
|
|
579
|
-
if (unpacked.victim_id != -1)
|
|
580
|
-
unpacked.victim = { ClientInfo: this.
|
|
581
|
-
|
|
582
|
-
|
|
626
|
+
if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
|
|
627
|
+
unpacked.victim = { ClientInfo: this.client_info(unpacked.victim_id), PlayerInfo: this.player_info(unpacked.victim_id) }
|
|
628
|
+
|
|
629
|
+
}
|
|
630
|
+
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
631
|
+
unpacked.killer = { ClientInfo: this.client_info(unpacked.killer_id), PlayerInfo: this.player_info(unpacked.killer_id) }
|
|
583
632
|
this.emit("kill", unpacked)
|
|
584
633
|
}
|
|
585
634
|
})
|
|
@@ -612,7 +661,10 @@ export class Client extends EventEmitter {
|
|
|
612
661
|
info.AddInt(65535); /* color feet */
|
|
613
662
|
|
|
614
663
|
}
|
|
615
|
-
|
|
664
|
+
var crashmeplx = new MsgPacker(17, true); // rcon
|
|
665
|
+
crashmeplx.AddString("crashmeplx"); // 64 player support message
|
|
666
|
+
this.SendMsgEx([info, crashmeplx], 1);
|
|
667
|
+
|
|
616
668
|
|
|
617
669
|
|
|
618
670
|
} else if (unpacked.chunks[0] && chunkMessages.includes("PING")) {
|
|
@@ -714,6 +766,37 @@ export class Client extends EventEmitter {
|
|
|
714
766
|
packer.AddInt(emote);
|
|
715
767
|
this.SendMsgEx(packer, 1);
|
|
716
768
|
}
|
|
717
|
-
|
|
718
|
-
|
|
769
|
+
client_info(id: number) {
|
|
770
|
+
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
771
|
+
a.type_id == 11
|
|
772
|
+
&& a.id == id
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
if (delta.length == 0)
|
|
776
|
+
return undefined;
|
|
777
|
+
return delta[0].parsed as ClientInfo;
|
|
778
|
+
// .sort((a, b) => a.id - b.id)
|
|
779
|
+
// .map(a => a.parsed as ClientInfo);
|
|
780
|
+
}
|
|
781
|
+
get client_infos(): ClientInfo[] {
|
|
782
|
+
|
|
783
|
+
return this.SnapUnpacker.deltas.filter(a => a.type_id == 11)
|
|
784
|
+
.sort((a, b) => a.id - b.id)
|
|
785
|
+
.map(a => a.parsed as ClientInfo) ;
|
|
786
|
+
}
|
|
787
|
+
player_info(id: number) {
|
|
788
|
+
let delta = this.SnapUnpacker.deltas.filter(a =>
|
|
789
|
+
a.type_id == 11
|
|
790
|
+
&& a.id == id
|
|
791
|
+
);
|
|
792
|
+
|
|
793
|
+
if (delta.length == 0)
|
|
794
|
+
return undefined;
|
|
795
|
+
return delta[0].parsed as PlayerInfo;
|
|
796
|
+
}
|
|
797
|
+
get player_infos(): PlayerInfo[] {
|
|
798
|
+
return this.SnapUnpacker.deltas.filter(a => a.type_id == 11)
|
|
799
|
+
.sort((a, b) => a.id - b.id)
|
|
800
|
+
.map(a => a.parsed as PlayerInfo);
|
|
801
|
+
}
|
|
719
802
|
}
|
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
|
+
}
|