teeworlds 2.4.5 → 2.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/docs/documentation.md +368 -0
- package/docs/examples/chat_bot.js +25 -0
- package/docs/examples/kill_on_freeze.js +20 -0
- package/lib/MsgUnpacker.js +6 -0
- package/lib/MsgUnpacker.ts +7 -0
- package/lib/UUIDManager.js +50 -0
- package/lib/UUIDManager.ts +45 -0
- package/lib/client.js +127 -31
- package/lib/client.ts +156 -35
- package/lib/components/movement.js +28 -0
- package/lib/components/movement.ts +25 -0
- package/lib/components/snapshot.js +58 -0
- package/lib/components/snapshot.ts +47 -2
- package/lib/snapshot.js +114 -20
- package/lib/snapshot.ts +130 -32
- package/lib/snapshots.d.ts +57 -9
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -23,12 +23,14 @@ var crypto_1 = require("crypto");
|
|
|
23
23
|
var dgram_1 = __importDefault(require("dgram"));
|
|
24
24
|
var stream_1 = require("stream");
|
|
25
25
|
var MsgUnpacker_1 = require("./MsgUnpacker");
|
|
26
|
+
var version = require('../package.json').version;
|
|
26
27
|
var movement_1 = __importDefault(require("./components/movement"));
|
|
27
28
|
var MsgPacker_1 = require("./MsgPacker");
|
|
28
29
|
var snapshot_1 = require("./snapshot");
|
|
29
30
|
var huffman_1 = __importDefault(require("./huffman"));
|
|
30
31
|
var game_1 = require("./components/game");
|
|
31
32
|
var snapshot_2 = require("./components/snapshot");
|
|
33
|
+
var UUIDManager_1 = require("./UUIDManager");
|
|
32
34
|
var huff = new huffman_1.default();
|
|
33
35
|
var States;
|
|
34
36
|
(function (States) {
|
|
@@ -112,20 +114,23 @@ var NETMSG_Sys;
|
|
|
112
114
|
NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_ADD"] = 25] = "NETMSG_RCON_CMD_ADD";
|
|
113
115
|
NETMSG_Sys[NETMSG_Sys["NETMSG_RCON_CMD_REM"] = 26] = "NETMSG_RCON_CMD_REM";
|
|
114
116
|
NETMSG_Sys[NETMSG_Sys["NUM_NETMSGS"] = 27] = "NUM_NETMSGS";
|
|
117
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_WHATIS"] = 65536] = "NETMSG_WHATIS";
|
|
118
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_ITIS"] = 65537] = "NETMSG_ITIS";
|
|
119
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_IDONTKNOW"] = 65538] = "NETMSG_IDONTKNOW";
|
|
120
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_RCONTYPE"] = 65539] = "NETMSG_RCONTYPE";
|
|
121
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_MAP_DETAILS"] = 65540] = "NETMSG_MAP_DETAILS";
|
|
122
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_CAPABILITIES"] = 65541] = "NETMSG_CAPABILITIES";
|
|
123
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_CLIENTVER"] = 65542] = "NETMSG_CLIENTVER";
|
|
124
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_PINGEX"] = 65543] = "NETMSG_PINGEX";
|
|
125
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_PONGEX"] = 65544] = "NETMSG_PONGEX";
|
|
126
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_REQUEST"] = 65545] = "NETMSG_CHECKSUM_REQUEST";
|
|
127
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_RESPONSE"] = 65546] = "NETMSG_CHECKSUM_RESPONSE";
|
|
128
|
+
NETMSG_Sys[NETMSG_Sys["NETMSG_CHECKSUM_ERROR"] = 65547] = "NETMSG_CHECKSUM_ERROR";
|
|
115
129
|
})(NETMSG_Sys || (NETMSG_Sys = {}));
|
|
116
130
|
var messageTypes = [
|
|
117
131
|
["none, starts at 1", "SV_MOTD", "SV_BROADCAST", "SV_CHAT", "SV_KILL_MSG", "SV_SOUND_GLOBAL", "SV_TUNE_PARAMS", "SV_EXTRA_PROJECTILE", "SV_READY_TO_ENTER", "SV_WEAPON_PICKUP", "SV_EMOTICON", "SV_VOTE_CLEAR_OPTIONS", "SV_VOTE_OPTION_LIST_ADD", "SV_VOTE_OPTION_ADD", "SV_VOTE_OPTION_REMOVE", "SV_VOTE_SET", "SV_VOTE_STATUS", "CL_SAY", "CL_SET_TEAM", "CL_SET_SPECTATOR_MODE", "CL_START_INFO", "CL_CHANGE_INFO", "CL_KILL", "CL_EMOTICON", "CL_VOTE", "CL_CALL_VOTE", "CL_IS_DDNET", "SV_DDRACE_TIME", "SV_RECORD", "UNUSED", "SV_TEAMS_STATE", "CL_SHOW_OTHERS_LEGACY"],
|
|
118
132
|
["none, starts at 1", "INFO", "MAP_CHANGE", "MAP_DATA", "CON_READY", "SNAP", "SNAP_EMPTY", "SNAP_SINGLE", "INPUT_TIMING", "RCON_AUTH_STATUS", "RCON_LINE", "READY", "ENTER_GAME", "INPUT", "RCON_CMD", "RCON_AUTH", "REQUEST_MAP_DATA", "PING", "PING_REPLY", "RCON_CMD_ADD", "RCON_CMD_REMOVE"]
|
|
119
133
|
];
|
|
120
|
-
var messageUUIDs = {
|
|
121
|
-
"WHAT_IS": Buffer.from([0x24, 0x5e, 0x50, 0x97, 0x9f, 0xe0, 0x39, 0xd6, 0xbf, 0x7d, 0x9a, 0x29, 0xe1, 0x69, 0x1e, 0x4c]),
|
|
122
|
-
"IT_IS": Buffer.from([0x69, 0x54, 0x84, 0x7e, 0x2e, 0x87, 0x36, 0x03, 0xb5, 0x62, 0x36, 0xda, 0x29, 0xed, 0x1a, 0xca]),
|
|
123
|
-
"I_DONT_KNOW": Buffer.from([0x41, 0x69, 0x11, 0xb5, 0x79, 0x73, 0x33, 0xbf, 0x8d, 0x52, 0x7b, 0xf0, 0x1e, 0x51, 0x9c, 0xf0]),
|
|
124
|
-
"RCON_TYPE": Buffer.from([0x12, 0x81, 0x0e, 0x1f, 0xa1, 0xdb, 0x33, 0x78, 0xb4, 0xfb, 0x16, 0x4e, 0xd6, 0x50, 0x59, 0x26]),
|
|
125
|
-
"MAP_DETAILS": Buffer.from([0xf9, 0x11, 0x7b, 0x3c, 0x80, 0x39, 0x34, 0x16, 0x9f, 0xc0, 0xae, 0xf2, 0xbc, 0xb7, 0x5c, 0x03]),
|
|
126
|
-
"CLIENT_VERSION": Buffer.from([0x8c, 0x00, 0x13, 0x04, 0x84, 0x61, 0x3e, 0x47, 0x87, 0x87, 0xf6, 0x72, 0xb3, 0x83, 0x5b, 0xd4]),
|
|
127
|
-
"CAPABILITIES": Buffer.from([0xf6, 0x21, 0xa5, 0xa1, 0xf5, 0x85, 0x37, 0x75, 0x8e, 0x73, 0x41, 0xbe, 0xee, 0x79, 0xf2, 0xb2]),
|
|
128
|
-
};
|
|
129
134
|
var Client = /** @class */ (function (_super) {
|
|
130
135
|
__extends(Client, _super);
|
|
131
136
|
function Client(ip, port, nickname, options) {
|
|
@@ -160,6 +165,19 @@ var Client = /** @class */ (function (_super) {
|
|
|
160
165
|
_this.movement = new movement_1.default();
|
|
161
166
|
_this.game = new game_1.Game(_this);
|
|
162
167
|
_this.SnapshotUnpacker = new snapshot_2.SnapshotWrapper(_this);
|
|
168
|
+
_this.UUIDManager = new UUIDManager_1.UUIDManager();
|
|
169
|
+
_this.UUIDManager.RegisterName("what-is@ddnet.tw", NETMSG_Sys.NETMSG_WHATIS);
|
|
170
|
+
_this.UUIDManager.RegisterName("it-is@ddnet.tw", NETMSG_Sys.NETMSG_ITIS);
|
|
171
|
+
_this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", NETMSG_Sys.NETMSG_IDONTKNOW);
|
|
172
|
+
_this.UUIDManager.RegisterName("rcon-type@ddnet.tw", NETMSG_Sys.NETMSG_RCONTYPE);
|
|
173
|
+
_this.UUIDManager.RegisterName("map-details@ddnet.tw", NETMSG_Sys.NETMSG_MAP_DETAILS);
|
|
174
|
+
_this.UUIDManager.RegisterName("capabilities@ddnet.tw", NETMSG_Sys.NETMSG_CAPABILITIES);
|
|
175
|
+
_this.UUIDManager.RegisterName("clientver@ddnet.tw", NETMSG_Sys.NETMSG_CLIENTVER);
|
|
176
|
+
_this.UUIDManager.RegisterName("ping@ddnet.tw", NETMSG_Sys.NETMSG_PING);
|
|
177
|
+
_this.UUIDManager.RegisterName("pong@ddnet.tw", NETMSG_Sys.NETMSG_PONGEX);
|
|
178
|
+
_this.UUIDManager.RegisterName("checksum-request@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_REQUEST);
|
|
179
|
+
_this.UUIDManager.RegisterName("checksum-response@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_RESPONSE);
|
|
180
|
+
_this.UUIDManager.RegisterName("checksum-error@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_ERROR);
|
|
163
181
|
return _this;
|
|
164
182
|
}
|
|
165
183
|
Client.prototype.ResendAfter = function (lastAck) {
|
|
@@ -201,12 +219,15 @@ var Client = /** @class */ (function (_super) {
|
|
|
201
219
|
chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
|
|
202
220
|
chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
|
|
203
221
|
chunk.raw = packet.slice(1, chunk.bytes);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
chunk.
|
|
222
|
+
if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
|
|
223
|
+
var uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
|
|
224
|
+
if (uuid !== undefined) {
|
|
225
|
+
chunk.extended_msgid = uuid.hash;
|
|
226
|
+
chunk.msg = uuid.name;
|
|
227
|
+
chunk.raw = chunk.raw.slice(16);
|
|
228
|
+
chunk.msgid = uuid.type_id;
|
|
208
229
|
}
|
|
209
|
-
}
|
|
230
|
+
}
|
|
210
231
|
packet = packet.slice(chunk.bytes);
|
|
211
232
|
unpacked.chunks.push(chunk);
|
|
212
233
|
}
|
|
@@ -233,8 +254,9 @@ var Client = /** @class */ (function (_super) {
|
|
|
233
254
|
});
|
|
234
255
|
};
|
|
235
256
|
/** Send a Msg (or Msg[]) to the server.*/
|
|
236
|
-
Client.prototype.SendMsgEx = function (Msgs) {
|
|
257
|
+
Client.prototype.SendMsgEx = function (Msgs, flags) {
|
|
237
258
|
var _this = this;
|
|
259
|
+
if (flags === void 0) { flags = 0; }
|
|
238
260
|
if (this.State == States.STATE_OFFLINE)
|
|
239
261
|
return;
|
|
240
262
|
if (!this.socket)
|
|
@@ -270,7 +292,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
270
292
|
_this.lastSentMessages.push({ msg: Msg, ack: _this.clientAck });
|
|
271
293
|
}
|
|
272
294
|
});
|
|
273
|
-
|
|
295
|
+
// let flags = 0;
|
|
274
296
|
if (this.requestResend)
|
|
275
297
|
flags |= 4;
|
|
276
298
|
var packetHeader = Buffer.from([((flags << 4) & 0xf0) | ((this.ack >> 8) & 0xf), this.ack & 0xff, _Msgs.length]);
|
|
@@ -324,12 +346,15 @@ var Client = /** @class */ (function (_super) {
|
|
|
324
346
|
chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
|
|
325
347
|
chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
|
|
326
348
|
chunk.raw = packet.slice(1, chunk.bytes);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
chunk.
|
|
349
|
+
if (chunk.msgid == 0) {
|
|
350
|
+
var uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
|
|
351
|
+
if (uuid !== undefined) {
|
|
352
|
+
chunk.extended_msgid = uuid.hash;
|
|
353
|
+
chunk.msgid = uuid.type_id;
|
|
354
|
+
chunk.msg = uuid.name;
|
|
355
|
+
chunk.raw = chunk.raw.slice(16);
|
|
331
356
|
}
|
|
332
|
-
}
|
|
357
|
+
}
|
|
333
358
|
return chunk;
|
|
334
359
|
};
|
|
335
360
|
/** Connect the client to the server. */
|
|
@@ -344,7 +369,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
344
369
|
}
|
|
345
370
|
else if (_this.State == States.STATE_OFFLINE)
|
|
346
371
|
clearInterval(predTimer);
|
|
347
|
-
},
|
|
372
|
+
}, 1000 / 50); // 50 ticks per second
|
|
348
373
|
this.SendControlMsg(1, "TKEN");
|
|
349
374
|
var connectInterval = setInterval(function () {
|
|
350
375
|
if (_this.State == States.STATE_CONNECTING)
|
|
@@ -360,7 +385,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
360
385
|
return;
|
|
361
386
|
_this.time = new Date().getTime();
|
|
362
387
|
_this.sendInput();
|
|
363
|
-
},
|
|
388
|
+
}, 50);
|
|
364
389
|
}
|
|
365
390
|
var resendTimeout = setInterval(function () {
|
|
366
391
|
if (_this.State != States.STATE_OFFLINE) {
|
|
@@ -400,16 +425,15 @@ var Client = /** @class */ (function (_super) {
|
|
|
400
425
|
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
|
|
401
426
|
var client_version = new MsgPacker_1.MsgPacker(0, true, 1);
|
|
402
427
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
403
|
-
var randomUuid =
|
|
404
|
-
(0, crypto_1.randomBytes)(16).copy(randomUuid);
|
|
428
|
+
var randomUuid = (0, crypto_1.randomBytes)(16);
|
|
405
429
|
client_version.AddBuffer(randomUuid);
|
|
406
430
|
if (((_d = _this.options) === null || _d === void 0 ? void 0 : _d.ddnet_version) !== undefined) {
|
|
407
431
|
client_version.AddInt((_e = _this.options) === null || _e === void 0 ? void 0 : _e.ddnet_version.version);
|
|
408
|
-
client_version.AddString("DDNet "
|
|
432
|
+
client_version.AddString("DDNet ".concat((_f = _this.options) === null || _f === void 0 ? void 0 : _f.ddnet_version.release_version, "; https://www.npmjs.com/package/teeworlds/v/").concat(version));
|
|
409
433
|
}
|
|
410
434
|
else {
|
|
411
|
-
client_version.AddInt(
|
|
412
|
-
client_version.AddString("DDNet 16.0.
|
|
435
|
+
client_version.AddInt(16050);
|
|
436
|
+
client_version.AddString("DDNet 16.5.0; https://www.npmjs.com/package/teeworlds/v/".concat(version));
|
|
413
437
|
}
|
|
414
438
|
_this.SendMsgEx([client_version, info]);
|
|
415
439
|
}
|
|
@@ -541,13 +565,85 @@ var Client = /** @class */ (function (_super) {
|
|
|
541
565
|
var snapUnpacked = _this.SnapUnpacker.unpackSnapshot(mergedSnaps, DeltaTick, GameTick, Crc);
|
|
542
566
|
_this.emit("snapshot", snapUnpacked.items);
|
|
543
567
|
_this.AckGameTick = snapUnpacked.recvTick;
|
|
544
|
-
if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10)
|
|
568
|
+
if (Math.abs(_this.PredGameTick - _this.AckGameTick) > 10) {
|
|
545
569
|
_this.PredGameTick = _this.AckGameTick + 1;
|
|
546
|
-
|
|
570
|
+
_this.sendInput();
|
|
571
|
+
}
|
|
547
572
|
}
|
|
548
573
|
}
|
|
549
574
|
// })
|
|
550
575
|
}
|
|
576
|
+
if (chunk.msgid >= NETMSG_Sys.NETMSG_WHATIS && chunk.msgid <= NETMSG_Sys.NETMSG_CHECKSUM_ERROR) {
|
|
577
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_WHATIS) {
|
|
578
|
+
var Uuid = chunk.raw.slice(0, 16);
|
|
579
|
+
var uuid = _this.UUIDManager.LookupUUID(Uuid);
|
|
580
|
+
var packer = new MsgPacker_1.MsgPacker(0, true, 1);
|
|
581
|
+
if (uuid !== undefined) {
|
|
582
|
+
// IT_IS msg
|
|
583
|
+
packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_ITIS).hash);
|
|
584
|
+
packer.AddBuffer(Uuid);
|
|
585
|
+
packer.AddString(uuid.name);
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
// dont_know msg
|
|
589
|
+
packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_IDONTKNOW).hash);
|
|
590
|
+
packer.AddBuffer(Uuid);
|
|
591
|
+
}
|
|
592
|
+
_this.QueueChunkEx(packer);
|
|
593
|
+
}
|
|
594
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
|
|
595
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
|
|
596
|
+
var map_name = unpacker.unpackString();
|
|
597
|
+
var map_sha256 = Buffer.alloc(32);
|
|
598
|
+
if (unpacker.remaining.length >= 32)
|
|
599
|
+
map_sha256 = unpacker.unpackRaw(32);
|
|
600
|
+
var map_crc = unpacker.unpackInt();
|
|
601
|
+
var map_size = unpacker.unpackInt();
|
|
602
|
+
var map_url = "";
|
|
603
|
+
if (unpacker.remaining.length)
|
|
604
|
+
map_url = unpacker.unpackString();
|
|
605
|
+
_this.emit("map_details", { map_name: map_name, map_sha256: map_sha256, map_crc: map_crc, map_size: map_size, map_url: map_url });
|
|
606
|
+
// unpacker.unpack
|
|
607
|
+
}
|
|
608
|
+
else if (chunk.msgid == NETMSG_Sys.NETMSG_CAPABILITIES) {
|
|
609
|
+
var unpacker = new MsgUnpacker_1.MsgUnpacker(chunk.raw);
|
|
610
|
+
var Version = unpacker.unpackInt();
|
|
611
|
+
var Flags = unpacker.unpackInt();
|
|
612
|
+
if (Version <= 0)
|
|
613
|
+
return;
|
|
614
|
+
var DDNet = false;
|
|
615
|
+
if (Version >= 1) {
|
|
616
|
+
DDNet = Boolean(Flags & 1);
|
|
617
|
+
}
|
|
618
|
+
var ChatTimeoutCode = DDNet;
|
|
619
|
+
var AnyPlayerFlag = DDNet;
|
|
620
|
+
var PingEx = false;
|
|
621
|
+
var AllowDummy = true;
|
|
622
|
+
var SyncWeaponInput = false;
|
|
623
|
+
if (Version >= 1) {
|
|
624
|
+
ChatTimeoutCode = Boolean(Flags & 2);
|
|
625
|
+
}
|
|
626
|
+
if (Version >= 2) {
|
|
627
|
+
AnyPlayerFlag = Boolean(Flags & 4);
|
|
628
|
+
}
|
|
629
|
+
if (Version >= 3) {
|
|
630
|
+
PingEx = Boolean(Flags & 8);
|
|
631
|
+
}
|
|
632
|
+
if (Version >= 4) {
|
|
633
|
+
AllowDummy = Boolean(Flags & 16);
|
|
634
|
+
}
|
|
635
|
+
if (Version >= 5) {
|
|
636
|
+
SyncWeaponInput = Boolean(Flags & 32);
|
|
637
|
+
}
|
|
638
|
+
_this.emit("capabilities", { ChatTimeoutCode: ChatTimeoutCode, AnyPlayerFlag: AnyPlayerFlag, PingEx: PingEx, AllowDummy: AllowDummy, SyncWeaponInput: SyncWeaponInput });
|
|
639
|
+
// https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
|
|
640
|
+
}
|
|
641
|
+
else if (chunk.msgid == NETMSG_Sys.NETMSG_PINGEX) {
|
|
642
|
+
var packer = new MsgPacker_1.MsgPacker(0, true, 2);
|
|
643
|
+
packer.AddBuffer(_this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_PONGEX).hash);
|
|
644
|
+
_this.SendMsgEx(packer, 2);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
551
647
|
}
|
|
552
648
|
else {
|
|
553
649
|
// game messages
|
|
@@ -562,7 +658,7 @@ var Client = /** @class */ (function (_super) {
|
|
|
562
658
|
for (var i = 0; i < 15; i++) {
|
|
563
659
|
list.push(unpacker.unpackString());
|
|
564
660
|
}
|
|
565
|
-
list = list.slice(NumOptions);
|
|
661
|
+
list = list.slice(0, NumOptions);
|
|
566
662
|
(_a = _this.VoteList).push.apply(_a, list);
|
|
567
663
|
}
|
|
568
664
|
else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
|
package/lib/client.ts
CHANGED
|
@@ -4,6 +4,7 @@ import net from 'dgram';
|
|
|
4
4
|
import { EventEmitter } from 'stream';
|
|
5
5
|
|
|
6
6
|
import { unpackString, MsgUnpacker } from "./MsgUnpacker";
|
|
7
|
+
let { version } = require('../package.json');
|
|
7
8
|
|
|
8
9
|
import Movement from './components/movement';
|
|
9
10
|
import { PlayerInput, PlayerInfo, Projectile, Laser, Pickup, Flag, GameInfo, GameData, CharacterCore, Character, ClientInfo, SpectatorInfo, Common, Explosion, Spawn, HammerHit, Death, SoundGlobal, SoundWorld, DamageInd } from "./snapshots";
|
|
@@ -14,6 +15,8 @@ import Huffman from "./huffman";
|
|
|
14
15
|
import { Game } from "./components/game";
|
|
15
16
|
import { SnapshotWrapper } from "./components/snapshot";
|
|
16
17
|
|
|
18
|
+
import { UUIDManager } from "./UUIDManager";
|
|
19
|
+
|
|
17
20
|
const huff = new Huffman();
|
|
18
21
|
|
|
19
22
|
enum States {
|
|
@@ -106,6 +109,21 @@ enum NETMSG_Sys {
|
|
|
106
109
|
NETMSG_RCON_CMD_REM,
|
|
107
110
|
|
|
108
111
|
NUM_NETMSGS,
|
|
112
|
+
|
|
113
|
+
NETMSG_WHATIS = 65536,
|
|
114
|
+
NETMSG_ITIS,
|
|
115
|
+
NETMSG_IDONTKNOW,
|
|
116
|
+
|
|
117
|
+
NETMSG_RCONTYPE,
|
|
118
|
+
NETMSG_MAP_DETAILS,
|
|
119
|
+
NETMSG_CAPABILITIES,
|
|
120
|
+
NETMSG_CLIENTVER,
|
|
121
|
+
NETMSG_PINGEX,
|
|
122
|
+
NETMSG_PONGEX,
|
|
123
|
+
NETMSG_CHECKSUM_REQUEST,
|
|
124
|
+
NETMSG_CHECKSUM_RESPONSE,
|
|
125
|
+
NETMSG_CHECKSUM_ERROR
|
|
126
|
+
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
interface chunk {
|
|
@@ -131,15 +149,6 @@ var messageTypes = [
|
|
|
131
149
|
["none, starts at 1", "INFO", "MAP_CHANGE", "MAP_DATA", "CON_READY", "SNAP", "SNAP_EMPTY", "SNAP_SINGLE", "INPUT_TIMING", "RCON_AUTH_STATUS", "RCON_LINE", "READY", "ENTER_GAME", "INPUT", "RCON_CMD", "RCON_AUTH", "REQUEST_MAP_DATA", "PING", "PING_REPLY", "RCON_CMD_ADD", "RCON_CMD_REMOVE"]
|
|
132
150
|
]
|
|
133
151
|
|
|
134
|
-
var messageUUIDs = {
|
|
135
|
-
"WHAT_IS": Buffer.from([0x24, 0x5e, 0x50, 0x97, 0x9f, 0xe0, 0x39, 0xd6, 0xbf, 0x7d, 0x9a, 0x29, 0xe1, 0x69, 0x1e, 0x4c]),
|
|
136
|
-
"IT_IS": Buffer.from([0x69, 0x54, 0x84, 0x7e, 0x2e, 0x87, 0x36, 0x03, 0xb5, 0x62, 0x36, 0xda, 0x29, 0xed, 0x1a, 0xca]),
|
|
137
|
-
"I_DONT_KNOW": Buffer.from([0x41, 0x69, 0x11, 0xb5, 0x79, 0x73, 0x33, 0xbf, 0x8d, 0x52, 0x7b, 0xf0, 0x1e, 0x51, 0x9c, 0xf0]),
|
|
138
|
-
"RCON_TYPE": Buffer.from([0x12, 0x81, 0x0e, 0x1f, 0xa1, 0xdb, 0x33, 0x78, 0xb4, 0xfb, 0x16, 0x4e, 0xd6, 0x50, 0x59, 0x26]),
|
|
139
|
-
"MAP_DETAILS": Buffer.from([0xf9, 0x11, 0x7b, 0x3c, 0x80, 0x39, 0x34, 0x16, 0x9f, 0xc0, 0xae, 0xf2, 0xbc, 0xb7, 0x5c, 0x03]),
|
|
140
|
-
"CLIENT_VERSION": Buffer.from([0x8c, 0x00, 0x13, 0x04, 0x84, 0x61, 0x3e, 0x47, 0x87, 0x87, 0xf6, 0x72, 0xb3, 0x83, 0x5b, 0xd4]),
|
|
141
|
-
"CAPABILITIES": Buffer.from([0xf6, 0x21, 0xa5, 0xa1, 0xf5, 0x85, 0x37, 0x75, 0x8e, 0x73, 0x41, 0xbe, 0xee, 0x79, 0xf2, 0xb2]),
|
|
142
|
-
}
|
|
143
152
|
|
|
144
153
|
declare interface iMessage {
|
|
145
154
|
team: number,
|
|
@@ -184,6 +193,9 @@ export declare interface Client {
|
|
|
184
193
|
on(event: 'kill', listener: (kill: iKillMsg) => void): this;
|
|
185
194
|
on(event: 'motd', listener: (message: string) => void): this;
|
|
186
195
|
|
|
196
|
+
on(event: 'map_details', listener: (message: {map_name: string, map_sha256: Buffer, map_crc: number, map_size: number, map_url: string}) => void): this;
|
|
197
|
+
on(event: 'capabilities', listener: (message: {ChatTimeoutCode: boolean, AnyPlayerFlag: boolean, PingEx: boolean, AllowDummy: boolean, SyncWeaponInput: boolean}) => void): this;
|
|
198
|
+
|
|
187
199
|
on(event: 'snapshot', listener: (items: Item[]) => void): this;
|
|
188
200
|
}
|
|
189
201
|
|
|
@@ -230,6 +242,7 @@ export class Client extends EventEmitter {
|
|
|
230
242
|
public readonly options?: iOptions;
|
|
231
243
|
private requestResend: boolean;
|
|
232
244
|
|
|
245
|
+
private UUIDManager: UUIDManager;
|
|
233
246
|
|
|
234
247
|
constructor(ip: string, port: number, nickname: string, options?: iOptions) {
|
|
235
248
|
super();
|
|
@@ -270,11 +283,28 @@ export class Client extends EventEmitter {
|
|
|
270
283
|
this.lastRecvTime = new Date().getTime();
|
|
271
284
|
|
|
272
285
|
this.lastSentMessages = [];
|
|
286
|
+
|
|
273
287
|
this.movement = new Movement();
|
|
274
288
|
|
|
275
289
|
this.game = new Game(this);
|
|
276
290
|
this.SnapshotUnpacker = new SnapshotWrapper(this);
|
|
277
291
|
|
|
292
|
+
this.UUIDManager = new UUIDManager();
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
this.UUIDManager.RegisterName("what-is@ddnet.tw", NETMSG_Sys.NETMSG_WHATIS);
|
|
296
|
+
this.UUIDManager.RegisterName("it-is@ddnet.tw", NETMSG_Sys.NETMSG_ITIS);
|
|
297
|
+
this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", NETMSG_Sys.NETMSG_IDONTKNOW);
|
|
298
|
+
|
|
299
|
+
this.UUIDManager.RegisterName("rcon-type@ddnet.tw", NETMSG_Sys.NETMSG_RCONTYPE);
|
|
300
|
+
this.UUIDManager.RegisterName("map-details@ddnet.tw", NETMSG_Sys.NETMSG_MAP_DETAILS);
|
|
301
|
+
this.UUIDManager.RegisterName("capabilities@ddnet.tw", NETMSG_Sys.NETMSG_CAPABILITIES);
|
|
302
|
+
this.UUIDManager.RegisterName("clientver@ddnet.tw", NETMSG_Sys.NETMSG_CLIENTVER);
|
|
303
|
+
this.UUIDManager.RegisterName("ping@ddnet.tw", NETMSG_Sys.NETMSG_PING);
|
|
304
|
+
this.UUIDManager.RegisterName("pong@ddnet.tw", NETMSG_Sys.NETMSG_PONGEX);
|
|
305
|
+
this.UUIDManager.RegisterName("checksum-request@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_REQUEST);
|
|
306
|
+
this.UUIDManager.RegisterName("checksum-response@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_RESPONSE);
|
|
307
|
+
this.UUIDManager.RegisterName("checksum-error@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_ERROR);
|
|
278
308
|
|
|
279
309
|
}
|
|
280
310
|
|
|
@@ -325,12 +355,15 @@ export class Client extends EventEmitter {
|
|
|
325
355
|
chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
|
|
326
356
|
chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
|
|
327
357
|
chunk.raw = packet.slice(1, chunk.bytes)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
chunk.
|
|
358
|
+
if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
|
|
359
|
+
let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
|
|
360
|
+
if (uuid !== undefined) {
|
|
361
|
+
chunk.extended_msgid = uuid.hash;
|
|
362
|
+
chunk.msg = uuid.name;
|
|
363
|
+
chunk.raw = chunk.raw.slice(16);
|
|
364
|
+
chunk.msgid = uuid.type_id;
|
|
332
365
|
}
|
|
333
|
-
}
|
|
366
|
+
}
|
|
334
367
|
|
|
335
368
|
packet = packet.slice(chunk.bytes)
|
|
336
369
|
unpacked.chunks.push(chunk)
|
|
@@ -361,7 +394,7 @@ export class Client extends EventEmitter {
|
|
|
361
394
|
|
|
362
395
|
|
|
363
396
|
/** Send a Msg (or Msg[]) to the server.*/
|
|
364
|
-
SendMsgEx(Msgs: MsgPacker[] | MsgPacker) {
|
|
397
|
+
SendMsgEx(Msgs: MsgPacker[] | MsgPacker, flags = 0) {
|
|
365
398
|
if (this.State == States.STATE_OFFLINE)
|
|
366
399
|
return;
|
|
367
400
|
if (!this.socket)
|
|
@@ -397,7 +430,7 @@ export class Client extends EventEmitter {
|
|
|
397
430
|
this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
|
|
398
431
|
}
|
|
399
432
|
})
|
|
400
|
-
let flags = 0;
|
|
433
|
+
// let flags = 0;
|
|
401
434
|
if (this.requestResend)
|
|
402
435
|
flags |= 4;
|
|
403
436
|
|
|
@@ -458,12 +491,16 @@ export class Client extends EventEmitter {
|
|
|
458
491
|
chunk.msgid = (packet[0]-(packet[0]&1))/2;
|
|
459
492
|
chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
|
|
460
493
|
chunk.raw = packet.slice(1, chunk.bytes)
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
chunk.
|
|
465
|
-
|
|
466
|
-
|
|
494
|
+
if (chunk.msgid == 0) {
|
|
495
|
+
let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0,16));
|
|
496
|
+
if (uuid !== undefined) {
|
|
497
|
+
chunk.extended_msgid = uuid.hash;
|
|
498
|
+
chunk.msgid = uuid.type_id;
|
|
499
|
+
chunk.msg = uuid.name;
|
|
500
|
+
chunk.raw = chunk.raw.slice(16);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
}
|
|
467
504
|
return chunk;
|
|
468
505
|
}
|
|
469
506
|
|
|
@@ -479,7 +516,7 @@ export class Client extends EventEmitter {
|
|
|
479
516
|
} else if (this.State == States.STATE_OFFLINE)
|
|
480
517
|
clearInterval(predTimer);
|
|
481
518
|
|
|
482
|
-
},
|
|
519
|
+
}, 1000/50); // 50 ticks per second
|
|
483
520
|
|
|
484
521
|
this.SendControlMsg(1, "TKEN")
|
|
485
522
|
let connectInterval = setInterval(() => {
|
|
@@ -496,7 +533,7 @@ export class Client extends EventEmitter {
|
|
|
496
533
|
return;
|
|
497
534
|
this.time = new Date().getTime();
|
|
498
535
|
this.sendInput();
|
|
499
|
-
},
|
|
536
|
+
}, 50)
|
|
500
537
|
}
|
|
501
538
|
|
|
502
539
|
let resendTimeout = setInterval(() => {
|
|
@@ -540,17 +577,15 @@ export class Client extends EventEmitter {
|
|
|
540
577
|
|
|
541
578
|
var client_version = new MsgPacker(0, true, 1);
|
|
542
579
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
543
|
-
let randomUuid =
|
|
544
|
-
|
|
545
|
-
randomBytes(16).copy(randomUuid);
|
|
580
|
+
let randomUuid = randomBytes(16);
|
|
546
581
|
|
|
547
582
|
client_version.AddBuffer(randomUuid);
|
|
548
583
|
if (this.options?.ddnet_version !== undefined) {
|
|
549
584
|
client_version.AddInt(this.options?.ddnet_version.version);
|
|
550
|
-
client_version.AddString(
|
|
585
|
+
client_version.AddString(`DDNet ${this.options?.ddnet_version.release_version}; https://www.npmjs.com/package/teeworlds/v/${version}`);
|
|
551
586
|
} else {
|
|
552
|
-
client_version.AddInt(
|
|
553
|
-
client_version.AddString(
|
|
587
|
+
client_version.AddInt(16050);
|
|
588
|
+
client_version.AddString(`DDNet 16.5.0; https://www.npmjs.com/package/teeworlds/v/${version}`);
|
|
554
589
|
}
|
|
555
590
|
|
|
556
591
|
this.SendMsgEx([client_version, info])
|
|
@@ -699,10 +734,10 @@ export class Client extends EventEmitter {
|
|
|
699
734
|
|
|
700
735
|
this.emit("snapshot", snapUnpacked.items);
|
|
701
736
|
this.AckGameTick = snapUnpacked.recvTick;
|
|
702
|
-
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
737
|
+
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10) {
|
|
703
738
|
this.PredGameTick = this.AckGameTick + 1;
|
|
704
|
-
|
|
705
|
-
|
|
739
|
+
this.sendInput();
|
|
740
|
+
}
|
|
706
741
|
}
|
|
707
742
|
|
|
708
743
|
|
|
@@ -712,7 +747,93 @@ export class Client extends EventEmitter {
|
|
|
712
747
|
// })
|
|
713
748
|
|
|
714
749
|
|
|
715
|
-
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
if (chunk.msgid >= NETMSG_Sys.NETMSG_WHATIS && chunk.msgid <= NETMSG_Sys.NETMSG_CHECKSUM_ERROR) {
|
|
753
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_WHATIS) {
|
|
754
|
+
let Uuid = chunk.raw.slice(0, 16);
|
|
755
|
+
|
|
756
|
+
let uuid = this.UUIDManager.LookupUUID(Uuid);
|
|
757
|
+
let packer = new MsgPacker(0, true, 1);
|
|
758
|
+
if (uuid !== undefined) {
|
|
759
|
+
// IT_IS msg
|
|
760
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_ITIS)!.hash);
|
|
761
|
+
|
|
762
|
+
packer.AddBuffer(Uuid);
|
|
763
|
+
packer.AddString(uuid.name);
|
|
764
|
+
} else {
|
|
765
|
+
// dont_know msg
|
|
766
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_IDONTKNOW)!.hash);
|
|
767
|
+
|
|
768
|
+
packer.AddBuffer(Uuid);
|
|
769
|
+
}
|
|
770
|
+
this.QueueChunkEx(packer)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
|
|
774
|
+
let unpacker = new MsgUnpacker(chunk.raw);
|
|
775
|
+
|
|
776
|
+
let map_name = unpacker.unpackString();
|
|
777
|
+
let map_sha256: Buffer = Buffer.alloc(32);
|
|
778
|
+
if (unpacker.remaining.length >= 32)
|
|
779
|
+
map_sha256 = unpacker.unpackRaw(32);
|
|
780
|
+
let map_crc = unpacker.unpackInt();
|
|
781
|
+
let map_size = unpacker.unpackInt();
|
|
782
|
+
|
|
783
|
+
let map_url = "";
|
|
784
|
+
if (unpacker.remaining.length)
|
|
785
|
+
map_url = unpacker.unpackString();
|
|
786
|
+
|
|
787
|
+
this.emit("map_details", {map_name, map_sha256, map_crc, map_size, map_url})
|
|
788
|
+
// unpacker.unpack
|
|
789
|
+
|
|
790
|
+
} else if (chunk.msgid == NETMSG_Sys.NETMSG_CAPABILITIES) {
|
|
791
|
+
let unpacker = new MsgUnpacker(chunk.raw);
|
|
792
|
+
let Version = unpacker.unpackInt();
|
|
793
|
+
let Flags = unpacker.unpackInt();
|
|
794
|
+
if (Version <= 0)
|
|
795
|
+
return;
|
|
796
|
+
let DDNet = false;
|
|
797
|
+
if (Version >= 1) {
|
|
798
|
+
DDNet = Boolean(Flags & 1);
|
|
799
|
+
|
|
800
|
+
}
|
|
801
|
+
let ChatTimeoutCode = DDNet;
|
|
802
|
+
let AnyPlayerFlag = DDNet;
|
|
803
|
+
let PingEx = false;
|
|
804
|
+
let AllowDummy = true;
|
|
805
|
+
let SyncWeaponInput = false;
|
|
806
|
+
if(Version >= 1)
|
|
807
|
+
{
|
|
808
|
+
ChatTimeoutCode = Boolean(Flags & 2);
|
|
809
|
+
}
|
|
810
|
+
if(Version >= 2)
|
|
811
|
+
{
|
|
812
|
+
AnyPlayerFlag = Boolean(Flags & 4);
|
|
813
|
+
}
|
|
814
|
+
if(Version >= 3)
|
|
815
|
+
{
|
|
816
|
+
PingEx = Boolean(Flags & 8);
|
|
817
|
+
}
|
|
818
|
+
if(Version >= 4)
|
|
819
|
+
{
|
|
820
|
+
AllowDummy = Boolean(Flags & 16);
|
|
821
|
+
}
|
|
822
|
+
if(Version >= 5)
|
|
823
|
+
{
|
|
824
|
+
SyncWeaponInput = Boolean(Flags & 32);
|
|
825
|
+
}
|
|
826
|
+
this.emit("capabilities", {ChatTimeoutCode, AnyPlayerFlag, PingEx, AllowDummy, SyncWeaponInput});
|
|
827
|
+
// https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
|
|
828
|
+
} else if (chunk.msgid == NETMSG_Sys.NETMSG_PINGEX) {
|
|
829
|
+
let packer = new MsgPacker(0, true, 2);
|
|
830
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_PONGEX)!.hash);
|
|
831
|
+
|
|
832
|
+
this.SendMsgEx(packer, 2);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
}
|
|
836
|
+
|
|
716
837
|
} else {
|
|
717
838
|
// game messages
|
|
718
839
|
|
|
@@ -726,7 +847,7 @@ export class Client extends EventEmitter {
|
|
|
726
847
|
for (let i = 0; i < 15; i++) {
|
|
727
848
|
list.push(unpacker.unpackString());
|
|
728
849
|
}
|
|
729
|
-
list = list.slice(NumOptions);
|
|
850
|
+
list = list.slice(0, NumOptions);
|
|
730
851
|
|
|
731
852
|
this.VoteList.push(...list);
|
|
732
853
|
} else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
|
|
@@ -40,6 +40,34 @@ var Movement = /** @class */ (function () {
|
|
|
40
40
|
this.input.m_TargetX = x;
|
|
41
41
|
this.input.m_TargetY = y;
|
|
42
42
|
};
|
|
43
|
+
Movement.prototype.Flag = function (toggle, num) {
|
|
44
|
+
if (toggle) {
|
|
45
|
+
this.input.m_PlayerFlags |= num;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.input.m_PlayerFlags &= ~num;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
Movement.prototype.FlagPlaying = function (toggle) {
|
|
52
|
+
if (toggle === void 0) { toggle = true; }
|
|
53
|
+
this.Flag(toggle, 1);
|
|
54
|
+
};
|
|
55
|
+
Movement.prototype.FlagInMenu = function (toggle) {
|
|
56
|
+
if (toggle === void 0) { toggle = true; }
|
|
57
|
+
this.Flag(toggle, 2);
|
|
58
|
+
};
|
|
59
|
+
Movement.prototype.FlagChatting = function (toggle) {
|
|
60
|
+
if (toggle === void 0) { toggle = true; }
|
|
61
|
+
this.Flag(toggle, 4);
|
|
62
|
+
};
|
|
63
|
+
Movement.prototype.FlagScoreboard = function (toggle) {
|
|
64
|
+
if (toggle === void 0) { toggle = true; }
|
|
65
|
+
this.Flag(toggle, 8);
|
|
66
|
+
};
|
|
67
|
+
Movement.prototype.FlagHookline = function (toggle) {
|
|
68
|
+
if (toggle === void 0) { toggle = true; }
|
|
69
|
+
this.Flag(toggle, 16);
|
|
70
|
+
};
|
|
43
71
|
Movement.prototype.Reset = function () {
|
|
44
72
|
this.input.m_Direction = 0;
|
|
45
73
|
this.input.m_Jump = 0;
|
|
@@ -50,6 +50,31 @@ class Movement {
|
|
|
50
50
|
this.input.m_TargetX = x;
|
|
51
51
|
this.input.m_TargetY = y;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
private Flag(toggle: boolean, num: number) {
|
|
55
|
+
if (toggle) {
|
|
56
|
+
this.input.m_PlayerFlags |= num;
|
|
57
|
+
} else {
|
|
58
|
+
this.input.m_PlayerFlags &= ~num;
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
FlagPlaying(toggle = true) {
|
|
63
|
+
this.Flag(toggle, 1);
|
|
64
|
+
}
|
|
65
|
+
FlagInMenu(toggle = true) {
|
|
66
|
+
this.Flag(toggle, 2);
|
|
67
|
+
}
|
|
68
|
+
FlagChatting(toggle = true) {
|
|
69
|
+
this.Flag(toggle, 4);
|
|
70
|
+
}
|
|
71
|
+
FlagScoreboard(toggle = true) {
|
|
72
|
+
this.Flag(toggle, 8);
|
|
73
|
+
}
|
|
74
|
+
FlagHookline(toggle = true) {
|
|
75
|
+
this.Flag(toggle, 16);
|
|
76
|
+
}
|
|
77
|
+
|
|
53
78
|
Reset() {
|
|
54
79
|
this.input.m_Direction = 0;
|
|
55
80
|
this.input.m_Jump = 0;
|