teeworlds 2.4.4 → 2.4.7
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 +7 -7
- package/docs/events.md +367 -0
- package/docs/examples/chat_bot.js +25 -0
- package/docs/examples/kill_on_freeze.js +20 -0
- package/index.js +0 -1
- package/index.ts +1 -2
- 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 +149 -99
- package/lib/client.ts +197 -126
- package/lib/components/game.js +11 -11
- package/lib/components/game.ts +33 -21
- package/lib/{movement.js → components/movement.js} +28 -0
- package/lib/{movement.ts → components/movement.ts} +25 -0
- package/lib/components/snapshot.js +258 -0
- package/lib/components/snapshot.ts +219 -0
- package/lib/snapshot.js +143 -90
- package/lib/snapshot.ts +166 -106
- package/lib/snapshots.d.ts +81 -29
- package/package.json +1 -1
- package/test.js +57 -0
package/lib/client.ts
CHANGED
|
@@ -5,12 +5,16 @@ import { EventEmitter } from 'stream';
|
|
|
5
5
|
|
|
6
6
|
import { unpackString, MsgUnpacker } from "./MsgUnpacker";
|
|
7
7
|
|
|
8
|
-
import Movement from './movement';
|
|
8
|
+
import Movement from './components/movement';
|
|
9
|
+
import { PlayerInput, PlayerInfo, Projectile, Laser, Pickup, Flag, GameInfo, GameData, CharacterCore, Character, ClientInfo, SpectatorInfo, Common, Explosion, Spawn, HammerHit, Death, SoundGlobal, SoundWorld, DamageInd } from "./snapshots";
|
|
9
10
|
|
|
10
11
|
import { MsgPacker } from './MsgPacker';
|
|
11
|
-
import { Snapshot } from './snapshot';
|
|
12
|
+
import { Item, Snapshot } from './snapshot';
|
|
12
13
|
import Huffman from "./huffman";
|
|
13
14
|
import { Game } from "./components/game";
|
|
15
|
+
import { SnapshotWrapper } from "./components/snapshot";
|
|
16
|
+
|
|
17
|
+
import { UUIDManager } from "./UUIDManager";
|
|
14
18
|
|
|
15
19
|
const huff = new Huffman();
|
|
16
20
|
|
|
@@ -104,6 +108,21 @@ enum NETMSG_Sys {
|
|
|
104
108
|
NETMSG_RCON_CMD_REM,
|
|
105
109
|
|
|
106
110
|
NUM_NETMSGS,
|
|
111
|
+
|
|
112
|
+
NETMSG_WHATIS = 65536,
|
|
113
|
+
NETMSG_ITIS,
|
|
114
|
+
NETMSG_IDONTKNOW,
|
|
115
|
+
|
|
116
|
+
NETMSG_RCONTYPE,
|
|
117
|
+
NETMSG_MAP_DETAILS,
|
|
118
|
+
NETMSG_CAPABILITIES,
|
|
119
|
+
NETMSG_CLIENTVER,
|
|
120
|
+
NETMSG_PINGEX,
|
|
121
|
+
NETMSG_PONGEX,
|
|
122
|
+
NETMSG_CHECKSUM_REQUEST,
|
|
123
|
+
NETMSG_CHECKSUM_RESPONSE,
|
|
124
|
+
NETMSG_CHECKSUM_ERROR
|
|
125
|
+
|
|
107
126
|
}
|
|
108
127
|
|
|
109
128
|
interface chunk {
|
|
@@ -118,9 +137,7 @@ interface chunk {
|
|
|
118
137
|
raw: Buffer,
|
|
119
138
|
extended_msgid?: Buffer;
|
|
120
139
|
}
|
|
121
|
-
|
|
122
|
-
return buff.toJSON().data.map(a => ('0' + (a & 0xff).toString(16)).slice(-2)).join("");
|
|
123
|
-
}
|
|
140
|
+
|
|
124
141
|
interface _packet {
|
|
125
142
|
twprotocol: { flags: number, ack: number, chunkAmount: number, size: number },
|
|
126
143
|
chunks: chunk[]
|
|
@@ -131,42 +148,7 @@ var messageTypes = [
|
|
|
131
148
|
["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
149
|
]
|
|
133
150
|
|
|
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
|
-
|
|
144
|
-
function arrStartsWith(arr: number[], arrStart: number[], start = 0) {
|
|
145
|
-
arr.splice(0, start)
|
|
146
|
-
for (let i = 0; i < arrStart.length; i++) {
|
|
147
|
-
if (arr[i] == arrStart[i])
|
|
148
|
-
continue;
|
|
149
|
-
else return false;
|
|
150
|
-
}
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
declare interface PlayerInfo {
|
|
154
|
-
local: number,
|
|
155
|
-
client_id: number,
|
|
156
|
-
team: number,
|
|
157
|
-
score: number,
|
|
158
|
-
latency: number,
|
|
159
|
-
}
|
|
160
151
|
|
|
161
|
-
declare interface ClientInfo {
|
|
162
|
-
name: string,
|
|
163
|
-
clan: string,
|
|
164
|
-
country: number,
|
|
165
|
-
skin: string,
|
|
166
|
-
use_custom_color: number,
|
|
167
|
-
color_body: number,
|
|
168
|
-
color_feet: number,
|
|
169
|
-
}
|
|
170
152
|
declare interface iMessage {
|
|
171
153
|
team: number,
|
|
172
154
|
client_id: number,
|
|
@@ -210,6 +192,10 @@ export declare interface Client {
|
|
|
210
192
|
on(event: 'kill', listener: (kill: iKillMsg) => void): this;
|
|
211
193
|
on(event: 'motd', listener: (message: string) => void): this;
|
|
212
194
|
|
|
195
|
+
on(event: 'map_details', listener: (message: {map_name: string, map_sha256: Buffer, map_crc: number, map_size: number, map_url: string}) => void): this;
|
|
196
|
+
on(event: 'capabilities', listener: (message: {ChatTimeoutCode: boolean, AnyPlayerFlag: boolean, PingEx: boolean, AllowDummy: boolean, SyncWeaponInput: boolean}) => void): this;
|
|
197
|
+
|
|
198
|
+
on(event: 'snapshot', listener: (items: Item[]) => void): this;
|
|
213
199
|
}
|
|
214
200
|
|
|
215
201
|
export class Client extends EventEmitter {
|
|
@@ -226,6 +212,8 @@ export class Client extends EventEmitter {
|
|
|
226
212
|
private time: number;
|
|
227
213
|
private SnapUnpacker: Snapshot;
|
|
228
214
|
|
|
215
|
+
public SnapshotUnpacker: SnapshotWrapper;
|
|
216
|
+
|
|
229
217
|
private PredGameTick: number;
|
|
230
218
|
private AckGameTick: number;
|
|
231
219
|
|
|
@@ -253,6 +241,7 @@ export class Client extends EventEmitter {
|
|
|
253
241
|
public readonly options?: iOptions;
|
|
254
242
|
private requestResend: boolean;
|
|
255
243
|
|
|
244
|
+
private UUIDManager: UUIDManager;
|
|
256
245
|
|
|
257
246
|
constructor(ip: string, port: number, nickname: string, options?: iOptions) {
|
|
258
247
|
super();
|
|
@@ -265,7 +254,7 @@ export class Client extends EventEmitter {
|
|
|
265
254
|
|
|
266
255
|
this.SnapshotParts = 0;
|
|
267
256
|
|
|
268
|
-
this.SnapUnpacker = new Snapshot();
|
|
257
|
+
this.SnapUnpacker = new Snapshot(this);
|
|
269
258
|
// this.eSnapHolder = [];
|
|
270
259
|
this.requestResend = false;
|
|
271
260
|
|
|
@@ -293,10 +282,28 @@ export class Client extends EventEmitter {
|
|
|
293
282
|
this.lastRecvTime = new Date().getTime();
|
|
294
283
|
|
|
295
284
|
this.lastSentMessages = [];
|
|
285
|
+
|
|
296
286
|
this.movement = new Movement();
|
|
297
287
|
|
|
298
288
|
this.game = new Game(this);
|
|
289
|
+
this.SnapshotUnpacker = new SnapshotWrapper(this);
|
|
299
290
|
|
|
291
|
+
this.UUIDManager = new UUIDManager();
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
this.UUIDManager.RegisterName("what-is@ddnet.tw", NETMSG_Sys.NETMSG_WHATIS);
|
|
295
|
+
this.UUIDManager.RegisterName("it-is@ddnet.tw", NETMSG_Sys.NETMSG_ITIS);
|
|
296
|
+
this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", NETMSG_Sys.NETMSG_IDONTKNOW);
|
|
297
|
+
|
|
298
|
+
this.UUIDManager.RegisterName("rcon-type@ddnet.tw", NETMSG_Sys.NETMSG_RCONTYPE);
|
|
299
|
+
this.UUIDManager.RegisterName("map-details@ddnet.tw", NETMSG_Sys.NETMSG_MAP_DETAILS);
|
|
300
|
+
this.UUIDManager.RegisterName("capabilities@ddnet.tw", NETMSG_Sys.NETMSG_CAPABILITIES);
|
|
301
|
+
this.UUIDManager.RegisterName("clientver@ddnet.tw", NETMSG_Sys.NETMSG_CLIENTVER);
|
|
302
|
+
this.UUIDManager.RegisterName("ping@ddnet.tw", NETMSG_Sys.NETMSG_PING);
|
|
303
|
+
this.UUIDManager.RegisterName("pong@ddnet.tw", NETMSG_Sys.NETMSG_PONGEX);
|
|
304
|
+
this.UUIDManager.RegisterName("checksum-request@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_REQUEST);
|
|
305
|
+
this.UUIDManager.RegisterName("checksum-response@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_RESPONSE);
|
|
306
|
+
this.UUIDManager.RegisterName("checksum-error@ddnet.tw", NETMSG_Sys.NETMSG_CHECKSUM_ERROR);
|
|
300
307
|
|
|
301
308
|
}
|
|
302
309
|
|
|
@@ -347,12 +354,15 @@ export class Client extends EventEmitter {
|
|
|
347
354
|
chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
|
|
348
355
|
chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
|
|
349
356
|
chunk.raw = packet.slice(1, chunk.bytes)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
chunk.
|
|
357
|
+
if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
|
|
358
|
+
let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
|
|
359
|
+
if (uuid !== undefined) {
|
|
360
|
+
chunk.extended_msgid = uuid.hash;
|
|
361
|
+
chunk.msg = uuid.name;
|
|
362
|
+
chunk.raw = chunk.raw.slice(16);
|
|
363
|
+
chunk.msgid = uuid.type_id;
|
|
354
364
|
}
|
|
355
|
-
}
|
|
365
|
+
}
|
|
356
366
|
|
|
357
367
|
packet = packet.slice(chunk.bytes)
|
|
358
368
|
unpacked.chunks.push(chunk)
|
|
@@ -360,8 +370,9 @@ export class Client extends EventEmitter {
|
|
|
360
370
|
return unpacked
|
|
361
371
|
}
|
|
362
372
|
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
|
|
374
|
+
/** Send a Control Msg to the server. (used for disconnect)*/
|
|
375
|
+
SendControlMsg(msg: number, ExtraMsg: string = "") {
|
|
365
376
|
this.lastSendTime = new Date().getTime();
|
|
366
377
|
return new Promise((resolve, reject) => {
|
|
367
378
|
if (this.socket) {
|
|
@@ -380,8 +391,9 @@ export class Client extends EventEmitter {
|
|
|
380
391
|
})
|
|
381
392
|
}
|
|
382
393
|
|
|
383
|
-
|
|
384
|
-
|
|
394
|
+
|
|
395
|
+
/** Send a Msg (or Msg[]) to the server.*/
|
|
396
|
+
SendMsgEx(Msgs: MsgPacker[] | MsgPacker, flags = 0) {
|
|
385
397
|
if (this.State == States.STATE_OFFLINE)
|
|
386
398
|
return;
|
|
387
399
|
if (!this.socket)
|
|
@@ -417,7 +429,7 @@ export class Client extends EventEmitter {
|
|
|
417
429
|
this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
|
|
418
430
|
}
|
|
419
431
|
})
|
|
420
|
-
let flags = 0;
|
|
432
|
+
// let flags = 0;
|
|
421
433
|
if (this.requestResend)
|
|
422
434
|
flags |= 4;
|
|
423
435
|
|
|
@@ -439,12 +451,14 @@ export class Client extends EventEmitter {
|
|
|
439
451
|
return;
|
|
440
452
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
441
453
|
}
|
|
442
|
-
|
|
454
|
+
|
|
455
|
+
/** Queue a chunk (It will get sent in the next packet). */
|
|
443
456
|
QueueChunkEx(Msg: MsgPacker) {
|
|
444
457
|
this.queueChunkEx.push(Msg);
|
|
445
458
|
}
|
|
446
|
-
|
|
447
|
-
|
|
459
|
+
|
|
460
|
+
/** Send a Raw Buffer (as chunk) to the server. */
|
|
461
|
+
SendMsgRaw(chunks: Buffer[]) {
|
|
448
462
|
if (this.State == States.STATE_OFFLINE)
|
|
449
463
|
return;
|
|
450
464
|
if (!this.socket)
|
|
@@ -476,17 +490,22 @@ export class Client extends EventEmitter {
|
|
|
476
490
|
chunk.msgid = (packet[0]-(packet[0]&1))/2;
|
|
477
491
|
chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
|
|
478
492
|
chunk.raw = packet.slice(1, chunk.bytes)
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
chunk.
|
|
483
|
-
|
|
484
|
-
|
|
493
|
+
if (chunk.msgid == 0) {
|
|
494
|
+
let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0,16));
|
|
495
|
+
if (uuid !== undefined) {
|
|
496
|
+
chunk.extended_msgid = uuid.hash;
|
|
497
|
+
chunk.msgid = uuid.type_id;
|
|
498
|
+
chunk.msg = uuid.name;
|
|
499
|
+
chunk.raw = chunk.raw.slice(16);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
}
|
|
485
503
|
return chunk;
|
|
486
504
|
}
|
|
487
505
|
|
|
488
|
-
|
|
489
|
-
|
|
506
|
+
|
|
507
|
+
/** Connect the client to the server. */
|
|
508
|
+
connect() {
|
|
490
509
|
this.State = States.STATE_CONNECTING;
|
|
491
510
|
|
|
492
511
|
let predTimer = setInterval(() => {
|
|
@@ -496,7 +515,7 @@ export class Client extends EventEmitter {
|
|
|
496
515
|
} else if (this.State == States.STATE_OFFLINE)
|
|
497
516
|
clearInterval(predTimer);
|
|
498
517
|
|
|
499
|
-
},
|
|
518
|
+
}, 1000/50); // 50 ticks per second
|
|
500
519
|
|
|
501
520
|
this.SendControlMsg(1, "TKEN")
|
|
502
521
|
let connectInterval = setInterval(() => {
|
|
@@ -513,7 +532,7 @@ export class Client extends EventEmitter {
|
|
|
513
532
|
return;
|
|
514
533
|
this.time = new Date().getTime();
|
|
515
534
|
this.sendInput();
|
|
516
|
-
},
|
|
535
|
+
}, 50)
|
|
517
536
|
}
|
|
518
537
|
|
|
519
538
|
let resendTimeout = setInterval(() => {
|
|
@@ -557,17 +576,15 @@ export class Client extends EventEmitter {
|
|
|
557
576
|
|
|
558
577
|
var client_version = new MsgPacker(0, true, 1);
|
|
559
578
|
client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
|
|
560
|
-
let randomUuid =
|
|
561
|
-
|
|
562
|
-
randomBytes(16).copy(randomUuid);
|
|
579
|
+
let randomUuid = randomBytes(16);
|
|
563
580
|
|
|
564
581
|
client_version.AddBuffer(randomUuid);
|
|
565
582
|
if (this.options?.ddnet_version !== undefined) {
|
|
566
583
|
client_version.AddInt(this.options?.ddnet_version.version);
|
|
567
584
|
client_version.AddString("DDNet " + this.options?.ddnet_version.release_version);
|
|
568
585
|
} else {
|
|
569
|
-
client_version.AddInt(
|
|
570
|
-
client_version.AddString("DDNet 16.0
|
|
586
|
+
client_version.AddInt(16050);
|
|
587
|
+
client_version.AddString("DDNet 16.5.0");
|
|
571
588
|
}
|
|
572
589
|
|
|
573
590
|
this.SendMsgEx([client_version, info])
|
|
@@ -714,12 +731,12 @@ export class Client extends EventEmitter {
|
|
|
714
731
|
|
|
715
732
|
let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps, DeltaTick, GameTick, Crc);
|
|
716
733
|
|
|
717
|
-
this.emit("snapshot");
|
|
734
|
+
this.emit("snapshot", snapUnpacked.items);
|
|
718
735
|
this.AckGameTick = snapUnpacked.recvTick;
|
|
719
|
-
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
736
|
+
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10) {
|
|
720
737
|
this.PredGameTick = this.AckGameTick + 1;
|
|
721
|
-
|
|
722
|
-
|
|
738
|
+
this.sendInput();
|
|
739
|
+
}
|
|
723
740
|
}
|
|
724
741
|
|
|
725
742
|
|
|
@@ -729,7 +746,93 @@ export class Client extends EventEmitter {
|
|
|
729
746
|
// })
|
|
730
747
|
|
|
731
748
|
|
|
732
|
-
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (chunk.msgid >= NETMSG_Sys.NETMSG_WHATIS && chunk.msgid <= NETMSG_Sys.NETMSG_CHECKSUM_ERROR) {
|
|
752
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_WHATIS) {
|
|
753
|
+
let Uuid = chunk.raw.slice(0, 16);
|
|
754
|
+
|
|
755
|
+
let uuid = this.UUIDManager.LookupUUID(Uuid);
|
|
756
|
+
let packer = new MsgPacker(0, true, 1);
|
|
757
|
+
if (uuid !== undefined) {
|
|
758
|
+
// IT_IS msg
|
|
759
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_ITIS)!.hash);
|
|
760
|
+
|
|
761
|
+
packer.AddBuffer(Uuid);
|
|
762
|
+
packer.AddString(uuid.name);
|
|
763
|
+
} else {
|
|
764
|
+
// dont_know msg
|
|
765
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_IDONTKNOW)!.hash);
|
|
766
|
+
|
|
767
|
+
packer.AddBuffer(Uuid);
|
|
768
|
+
}
|
|
769
|
+
this.QueueChunkEx(packer)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (chunk.msgid == NETMSG_Sys.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
|
|
773
|
+
let unpacker = new MsgUnpacker(chunk.raw);
|
|
774
|
+
|
|
775
|
+
let map_name = unpacker.unpackString();
|
|
776
|
+
let map_sha256: Buffer = Buffer.alloc(32);
|
|
777
|
+
if (unpacker.remaining.length >= 32)
|
|
778
|
+
map_sha256 = unpacker.unpackRaw(32);
|
|
779
|
+
let map_crc = unpacker.unpackInt();
|
|
780
|
+
let map_size = unpacker.unpackInt();
|
|
781
|
+
|
|
782
|
+
let map_url = "";
|
|
783
|
+
if (unpacker.remaining.length)
|
|
784
|
+
map_url = unpacker.unpackString();
|
|
785
|
+
|
|
786
|
+
this.emit("map_details", {map_name, map_sha256, map_crc, map_size, map_url})
|
|
787
|
+
// unpacker.unpack
|
|
788
|
+
|
|
789
|
+
} else if (chunk.msgid == NETMSG_Sys.NETMSG_CAPABILITIES) {
|
|
790
|
+
let unpacker = new MsgUnpacker(chunk.raw);
|
|
791
|
+
let Version = unpacker.unpackInt();
|
|
792
|
+
let Flags = unpacker.unpackInt();
|
|
793
|
+
if (Version <= 0)
|
|
794
|
+
return;
|
|
795
|
+
let DDNet = false;
|
|
796
|
+
if (Version >= 1) {
|
|
797
|
+
DDNet = Boolean(Flags & 1);
|
|
798
|
+
|
|
799
|
+
}
|
|
800
|
+
let ChatTimeoutCode = DDNet;
|
|
801
|
+
let AnyPlayerFlag = DDNet;
|
|
802
|
+
let PingEx = false;
|
|
803
|
+
let AllowDummy = true;
|
|
804
|
+
let SyncWeaponInput = false;
|
|
805
|
+
if(Version >= 1)
|
|
806
|
+
{
|
|
807
|
+
ChatTimeoutCode = Boolean(Flags & 2);
|
|
808
|
+
}
|
|
809
|
+
if(Version >= 2)
|
|
810
|
+
{
|
|
811
|
+
AnyPlayerFlag = Boolean(Flags & 4);
|
|
812
|
+
}
|
|
813
|
+
if(Version >= 3)
|
|
814
|
+
{
|
|
815
|
+
PingEx = Boolean(Flags & 8);
|
|
816
|
+
}
|
|
817
|
+
if(Version >= 4)
|
|
818
|
+
{
|
|
819
|
+
AllowDummy = Boolean(Flags & 16);
|
|
820
|
+
}
|
|
821
|
+
if(Version >= 5)
|
|
822
|
+
{
|
|
823
|
+
SyncWeaponInput = Boolean(Flags & 32);
|
|
824
|
+
}
|
|
825
|
+
this.emit("capabilities", {ChatTimeoutCode, AnyPlayerFlag, PingEx, AllowDummy, SyncWeaponInput});
|
|
826
|
+
// https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
|
|
827
|
+
} else if (chunk.msgid == NETMSG_Sys.NETMSG_PINGEX) {
|
|
828
|
+
let packer = new MsgPacker(0, true, 2);
|
|
829
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG_Sys.NETMSG_PONGEX)!.hash);
|
|
830
|
+
|
|
831
|
+
this.SendMsgEx(packer, 2);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
}
|
|
835
|
+
|
|
733
836
|
} else {
|
|
734
837
|
// game messages
|
|
735
838
|
|
|
@@ -743,7 +846,7 @@ export class Client extends EventEmitter {
|
|
|
743
846
|
for (let i = 0; i < 15; i++) {
|
|
744
847
|
list.push(unpacker.unpackString());
|
|
745
848
|
}
|
|
746
|
-
list = list.slice(NumOptions);
|
|
849
|
+
list = list.slice(0, NumOptions);
|
|
747
850
|
|
|
748
851
|
this.VoteList.push(...list);
|
|
749
852
|
} else if (chunk.msgid == NETMSG_Game.SV_VOTEOPTIONADD) {
|
|
@@ -770,8 +873,8 @@ export class Client extends EventEmitter {
|
|
|
770
873
|
|
|
771
874
|
if (unpacked.client_id != -1) {
|
|
772
875
|
unpacked.author = {
|
|
773
|
-
ClientInfo: this.
|
|
774
|
-
PlayerInfo: this.
|
|
876
|
+
ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
|
|
877
|
+
PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
|
|
775
878
|
}
|
|
776
879
|
}
|
|
777
880
|
this.emit("emote", unpacked)
|
|
@@ -792,8 +895,8 @@ export class Client extends EventEmitter {
|
|
|
792
895
|
|
|
793
896
|
if (unpacked.client_id != -1) {
|
|
794
897
|
unpacked.author = {
|
|
795
|
-
ClientInfo: this.
|
|
796
|
-
PlayerInfo: this.
|
|
898
|
+
ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
|
|
899
|
+
PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
|
|
797
900
|
}
|
|
798
901
|
}
|
|
799
902
|
this.emit("message", unpacked)
|
|
@@ -805,11 +908,11 @@ export class Client extends EventEmitter {
|
|
|
805
908
|
unpacked.weapon = unpacker.unpackInt();
|
|
806
909
|
unpacked.special_mode = unpacker.unpackInt();
|
|
807
910
|
if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
|
|
808
|
-
unpacked.victim = { ClientInfo: this.
|
|
911
|
+
unpacked.victim = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.victim_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.victim_id) }
|
|
809
912
|
|
|
810
913
|
}
|
|
811
914
|
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
812
|
-
unpacked.killer = { ClientInfo: this.
|
|
915
|
+
unpacked.killer = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.killer_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.killer_id) }
|
|
813
916
|
this.emit("kill", unpacked)
|
|
814
917
|
} else if (chunk.msgid == NETMSG_Game.SV_MOTD) {
|
|
815
918
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
@@ -834,8 +937,9 @@ export class Client extends EventEmitter {
|
|
|
834
937
|
})
|
|
835
938
|
}
|
|
836
939
|
|
|
837
|
-
|
|
838
|
-
|
|
940
|
+
|
|
941
|
+
/** Sending the input. (automatically done unless options.lightweight is on) */
|
|
942
|
+
sendInput(input = this.movement.input) {
|
|
839
943
|
if (this.State != States.STATE_ONLINE)
|
|
840
944
|
return;
|
|
841
945
|
|
|
@@ -857,12 +961,14 @@ export class Client extends EventEmitter {
|
|
|
857
961
|
|
|
858
962
|
this.SendMsgEx(inputMsg);
|
|
859
963
|
}
|
|
860
|
-
|
|
964
|
+
/** returns the movement object of the client */
|
|
965
|
+
get input() {
|
|
861
966
|
return this.movement.input;
|
|
862
967
|
}
|
|
863
968
|
|
|
864
|
-
|
|
865
|
-
Disconnect
|
|
969
|
+
|
|
970
|
+
/** Disconnect the client. */
|
|
971
|
+
Disconnect() {
|
|
866
972
|
return new Promise((resolve) => {
|
|
867
973
|
this.SendControlMsg(4).then(() => {
|
|
868
974
|
resolve(true);
|
|
@@ -874,46 +980,11 @@ export class Client extends EventEmitter {
|
|
|
874
980
|
})
|
|
875
981
|
}
|
|
876
982
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
let delta = this.SnapUnpacker.deltas.filter(_delta =>
|
|
880
|
-
_delta.type_id == 11
|
|
881
|
-
&& _delta.id == id
|
|
882
|
-
);
|
|
883
|
-
|
|
884
|
-
if (delta.length == 0)
|
|
885
|
-
return undefined;
|
|
886
|
-
return delta[0].parsed as ClientInfo;
|
|
887
|
-
// .sort((a, b) => a.id - b.id)
|
|
888
|
-
// .map(a => a.parsed as ClientInfo);
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/* Get all client infos. */
|
|
892
|
-
get client_infos(): ClientInfo[] {
|
|
893
|
-
|
|
894
|
-
return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 11)
|
|
895
|
-
.sort((a, b) => a.id - b.id)
|
|
896
|
-
.map(a => a.parsed as ClientInfo);
|
|
897
|
-
}
|
|
898
|
-
/* Get the player info from a specific player id. */
|
|
899
|
-
player_info(id: number) {
|
|
900
|
-
let delta = this.SnapUnpacker.deltas.filter(_delta =>
|
|
901
|
-
_delta.type_id == 10
|
|
902
|
-
&& _delta.id == id
|
|
903
|
-
);
|
|
904
|
-
|
|
905
|
-
if (delta.length == 0)
|
|
906
|
-
return undefined;
|
|
907
|
-
return delta[0].parsed as PlayerInfo;
|
|
908
|
-
}
|
|
909
|
-
/* Get all player infos. */
|
|
910
|
-
get player_infos(): PlayerInfo[] {
|
|
911
|
-
return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 10)
|
|
912
|
-
.sort((a, b) => a.id - b.id)
|
|
913
|
-
.map(player => player.parsed as PlayerInfo);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
get VoteOptionList(): string[] {
|
|
983
|
+
/** Get all available vote options (for example for map voting) */
|
|
984
|
+
get VoteOptionList(): string[] {
|
|
917
985
|
return this.VoteList;
|
|
918
986
|
}
|
|
987
|
+
get rawSnapUnpacker(): Snapshot {
|
|
988
|
+
return this.SnapUnpacker;
|
|
989
|
+
}
|
|
919
990
|
}
|
package/lib/components/game.js
CHANGED
|
@@ -59,19 +59,19 @@ var Game = /** @class */ (function () {
|
|
|
59
59
|
packer.AddString(message);
|
|
60
60
|
this.send(packer);
|
|
61
61
|
};
|
|
62
|
-
|
|
62
|
+
/** Set the team of an bot. (-1 spectator team, 0 team red/normal team, 1 team blue) */
|
|
63
63
|
Game.prototype.SetTeam = function (team) {
|
|
64
64
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
|
|
65
65
|
packer.AddInt(team);
|
|
66
66
|
this.send(packer);
|
|
67
67
|
};
|
|
68
|
-
|
|
68
|
+
/** Spectate an player, taking their id as parameter. pretty useless */
|
|
69
69
|
Game.prototype.SpectatorMode = function (SpectatorID) {
|
|
70
70
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETSPECTATORMODE, false, 1);
|
|
71
71
|
packer.AddInt(SpectatorID);
|
|
72
72
|
this.send(packer);
|
|
73
73
|
};
|
|
74
|
-
|
|
74
|
+
/** Change the player info */
|
|
75
75
|
Game.prototype.ChangePlayerInfo = function (playerInfo) {
|
|
76
76
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
|
|
77
77
|
packer.AddString(playerInfo.name);
|
|
@@ -83,18 +83,18 @@ var Game = /** @class */ (function () {
|
|
|
83
83
|
packer.AddInt(playerInfo.color_feet);
|
|
84
84
|
this.send(packer);
|
|
85
85
|
};
|
|
86
|
-
|
|
86
|
+
/** Kill */
|
|
87
87
|
Game.prototype.Kill = function () {
|
|
88
88
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
|
|
89
89
|
this.send(packer);
|
|
90
90
|
};
|
|
91
|
-
|
|
91
|
+
/** Send emote */
|
|
92
92
|
Game.prototype.Emote = function (emote) {
|
|
93
93
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
|
|
94
94
|
packer.AddInt(emote);
|
|
95
95
|
this.send(packer);
|
|
96
96
|
};
|
|
97
|
-
|
|
97
|
+
/** Vote for an already running vote (true = f3 / false = f4) */
|
|
98
98
|
Game.prototype.Vote = function (vote) {
|
|
99
99
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
|
|
100
100
|
packer.AddInt(vote ? 1 : -1);
|
|
@@ -107,24 +107,24 @@ var Game = /** @class */ (function () {
|
|
|
107
107
|
packer.AddString(Reason);
|
|
108
108
|
this.send(packer);
|
|
109
109
|
};
|
|
110
|
-
|
|
110
|
+
/** Call a vote for an server option (for example ddnet maps) */
|
|
111
111
|
Game.prototype.CallVoteOption = function (Value, Reason) {
|
|
112
112
|
this.CallVote("option", Value, Reason);
|
|
113
113
|
};
|
|
114
|
-
|
|
114
|
+
/** Call a vote to kick a player. Requires the player id */
|
|
115
115
|
Game.prototype.CallVoteKick = function (PlayerID, Reason) {
|
|
116
116
|
this.CallVote("kick", PlayerID, Reason);
|
|
117
117
|
};
|
|
118
|
-
|
|
118
|
+
/** Call a vote to set a player in spectator mode. Requires the player id */
|
|
119
119
|
Game.prototype.CallVoteSpectate = function (PlayerID, Reason) {
|
|
120
120
|
this.CallVote("spectate", PlayerID, Reason);
|
|
121
121
|
};
|
|
122
|
-
/** probably some verification of using ddnet client
|
|
122
|
+
/** probably some verification of using ddnet client. */
|
|
123
123
|
Game.prototype.IsDDNetLegacy = function () {
|
|
124
124
|
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_ISDDNETLEGACY, false, 1);
|
|
125
125
|
this.send(packer);
|
|
126
126
|
};
|
|
127
|
-
|
|
127
|
+
/** returns the ping in ms (as a promise) */
|
|
128
128
|
Game.prototype.Ping = function () {
|
|
129
129
|
var _this = this;
|
|
130
130
|
return new Promise(function (resolve, reject) {
|