teeworlds 2.3.8 → 2.4.0
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 +52 -61
- package/lib/MsgUnpacker.js +3 -9
- package/lib/MsgUnpacker.ts +5 -11
- package/lib/client.js +78 -122
- package/lib/client.ts +146 -170
- package/lib/components/game.js +143 -0
- package/lib/components/game.ts +122 -0
- package/lib/movement.js +1 -1
- package/lib/movement.ts +1 -1
- package/lib/snapshot.js +46 -33
- package/lib/snapshot.ts +32 -20
- package/package.json +1 -1
package/lib/client.ts
CHANGED
|
@@ -10,6 +10,7 @@ import Movement from './movement';
|
|
|
10
10
|
import { MsgPacker } from './MsgPacker';
|
|
11
11
|
import { Snapshot } from './snapshot';
|
|
12
12
|
import Huffman from "./huffman";
|
|
13
|
+
import { Game } from "./components/game";
|
|
13
14
|
|
|
14
15
|
const huff = new Huffman();
|
|
15
16
|
|
|
@@ -146,38 +147,7 @@ declare interface iOptions {
|
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
export declare interface Client {
|
|
149
|
-
|
|
150
|
-
port: number;
|
|
151
|
-
name: string;
|
|
152
|
-
State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
153
|
-
ack: number;
|
|
154
|
-
clientAck: number;
|
|
155
|
-
receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
|
|
156
|
-
lastMsg: string;
|
|
157
|
-
socket: net.Socket | undefined;
|
|
158
|
-
TKEN: Buffer;
|
|
159
|
-
time: number;
|
|
160
|
-
SnapUnpacker: Snapshot;
|
|
161
|
-
|
|
162
|
-
timer: number;
|
|
163
|
-
PredGameTick: number;
|
|
164
|
-
AckGameTick: number;
|
|
165
|
-
|
|
166
|
-
movement: Movement;
|
|
167
|
-
|
|
168
|
-
snaps: Buffer[];
|
|
169
|
-
|
|
170
|
-
sentChunkQueue: Buffer[];
|
|
171
|
-
queueChunkEx: MsgPacker[];
|
|
172
|
-
lastSendTime: number;
|
|
173
|
-
lastRecvTime: number;
|
|
174
|
-
|
|
175
|
-
lastSentMessages: {msg: MsgPacker, ack: number}[];
|
|
176
|
-
|
|
177
|
-
// eSnapHolder: eSnap[];
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
options?: iOptions;
|
|
150
|
+
|
|
181
151
|
|
|
182
152
|
on(event: 'connected', listener: () => void): this;
|
|
183
153
|
on(event: 'disconnect', listener: (reason: string) => void): this;
|
|
@@ -187,15 +157,49 @@ export declare interface Client {
|
|
|
187
157
|
on(event: 'kill', listener: (kill: iKillMsg) => void): this;
|
|
188
158
|
on(event: 'motd', listener: (message: string) => void): this;
|
|
189
159
|
|
|
190
|
-
requestResend: boolean;
|
|
191
160
|
}
|
|
192
161
|
|
|
162
|
+
export class Client extends EventEmitter {
|
|
193
163
|
|
|
164
|
+
private host: string;
|
|
165
|
+
private port: number;
|
|
166
|
+
private name: string;
|
|
167
|
+
private State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
168
|
+
private ack: number;
|
|
169
|
+
private clientAck: number;
|
|
170
|
+
private receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
|
|
171
|
+
private socket: net.Socket | undefined;
|
|
172
|
+
private TKEN: Buffer;
|
|
173
|
+
private time: number;
|
|
174
|
+
private SnapUnpacker: Snapshot;
|
|
175
|
+
|
|
176
|
+
private PredGameTick: number;
|
|
177
|
+
private AckGameTick: number;
|
|
178
|
+
|
|
179
|
+
private SnapshotParts: number;
|
|
180
|
+
private currentSnapshotGameTick: number;
|
|
194
181
|
|
|
195
|
-
|
|
182
|
+
|
|
183
|
+
private snaps: Buffer[];
|
|
184
|
+
|
|
185
|
+
private sentChunkQueue: Buffer[];
|
|
186
|
+
private queueChunkEx: MsgPacker[];
|
|
187
|
+
private lastSendTime: number;
|
|
188
|
+
private lastRecvTime: number;
|
|
189
|
+
|
|
190
|
+
private lastSentMessages: {msg: MsgPacker, ack: number}[];
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
public movement: Movement;
|
|
194
|
+
public game: Game;
|
|
195
|
+
|
|
196
|
+
// eSnapHolder: eSnap[];
|
|
196
197
|
|
|
197
198
|
|
|
199
|
+
public readonly options?: iOptions;
|
|
200
|
+
private requestResend: boolean;
|
|
198
201
|
|
|
202
|
+
|
|
199
203
|
constructor(ip: string, port: number, nickname: string, options?: iOptions) {
|
|
200
204
|
super();
|
|
201
205
|
this.host = ip;
|
|
@@ -203,6 +207,10 @@ export class Client extends EventEmitter {
|
|
|
203
207
|
this.name = nickname;
|
|
204
208
|
this.AckGameTick = 0;
|
|
205
209
|
this.PredGameTick = 0;
|
|
210
|
+
this.currentSnapshotGameTick = 0;
|
|
211
|
+
|
|
212
|
+
this.SnapshotParts = 0;
|
|
213
|
+
|
|
206
214
|
this.SnapUnpacker = new Snapshot();
|
|
207
215
|
// this.eSnapHolder = [];
|
|
208
216
|
this.requestResend = false;
|
|
@@ -210,10 +218,7 @@ export class Client extends EventEmitter {
|
|
|
210
218
|
if (options)
|
|
211
219
|
this.options = options;
|
|
212
220
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
this.movement = new Movement();
|
|
216
|
-
|
|
221
|
+
|
|
217
222
|
this.snaps = [];
|
|
218
223
|
|
|
219
224
|
this.sentChunkQueue = [];
|
|
@@ -223,8 +228,7 @@ export class Client extends EventEmitter {
|
|
|
223
228
|
this.ack = 0; // ack of messages the client has received
|
|
224
229
|
this.clientAck = 0; // ack of messages the client has sent
|
|
225
230
|
this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
|
|
226
|
-
this.
|
|
227
|
-
this.socket = net.createSocket("udp4")
|
|
231
|
+
this.socket = net.createSocket("udp4");
|
|
228
232
|
this.socket.bind();
|
|
229
233
|
|
|
230
234
|
this.TKEN = Buffer.from([255, 255, 255, 255])
|
|
@@ -233,10 +237,14 @@ export class Client extends EventEmitter {
|
|
|
233
237
|
this.lastRecvTime = new Date().getTime();
|
|
234
238
|
|
|
235
239
|
this.lastSentMessages = [];
|
|
240
|
+
this.movement = new Movement();
|
|
241
|
+
|
|
242
|
+
this.game = new Game(this);
|
|
243
|
+
|
|
236
244
|
|
|
237
245
|
}
|
|
238
246
|
|
|
239
|
-
ResendAfter(lastAck: number) {
|
|
247
|
+
private ResendAfter(lastAck: number) {
|
|
240
248
|
this.clientAck = lastAck;
|
|
241
249
|
|
|
242
250
|
|
|
@@ -249,7 +257,7 @@ export class Client extends EventEmitter {
|
|
|
249
257
|
this.SendMsgEx(toResend);
|
|
250
258
|
}
|
|
251
259
|
|
|
252
|
-
Unpack(packet: Buffer): _packet {
|
|
260
|
+
private Unpack(packet: Buffer): _packet {
|
|
253
261
|
var unpacked: _packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
|
|
254
262
|
|
|
255
263
|
|
|
@@ -294,6 +302,8 @@ export class Client extends EventEmitter {
|
|
|
294
302
|
}
|
|
295
303
|
return unpacked
|
|
296
304
|
}
|
|
305
|
+
|
|
306
|
+
/* Send a Control Msg to the server. (used for disconnect)*/
|
|
297
307
|
SendControlMsg(msg: number, ExtraMsg: string = "") {
|
|
298
308
|
this.lastSendTime = new Date().getTime();
|
|
299
309
|
return new Promise((resolve, reject) => {
|
|
@@ -313,6 +323,7 @@ export class Client extends EventEmitter {
|
|
|
313
323
|
})
|
|
314
324
|
}
|
|
315
325
|
|
|
326
|
+
/* Send a Msg (or Msg[]) to the server.*/
|
|
316
327
|
SendMsgEx(Msgs: MsgPacker[] | MsgPacker) {
|
|
317
328
|
if (this.State == States.STATE_OFFLINE)
|
|
318
329
|
return;
|
|
@@ -371,11 +382,11 @@ export class Client extends EventEmitter {
|
|
|
371
382
|
return;
|
|
372
383
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
373
384
|
}
|
|
374
|
-
|
|
385
|
+
/* Queue a chunk (It will get sent in the next packet). */
|
|
375
386
|
QueueChunkEx(Msg: MsgPacker) {
|
|
376
387
|
this.queueChunkEx.push(Msg);
|
|
377
388
|
}
|
|
378
|
-
|
|
389
|
+
/* Send a Raw Buffer (as chunk) to the server. */
|
|
379
390
|
SendMsgRaw(chunks: Buffer[]) {
|
|
380
391
|
if (this.State == States.STATE_OFFLINE)
|
|
381
392
|
return;
|
|
@@ -392,7 +403,7 @@ export class Client extends EventEmitter {
|
|
|
392
403
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
393
404
|
}
|
|
394
405
|
|
|
395
|
-
MsgToChunk(packet: Buffer) {
|
|
406
|
+
private MsgToChunk(packet: Buffer) {
|
|
396
407
|
var chunk: chunk = {} as chunk;
|
|
397
408
|
chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
|
|
398
409
|
chunk.flags = (packet[0] >> 6) & 3;
|
|
@@ -416,6 +427,7 @@ export class Client extends EventEmitter {
|
|
|
416
427
|
return chunk;
|
|
417
428
|
}
|
|
418
429
|
|
|
430
|
+
/* Connect the client to the server. */
|
|
419
431
|
connect() {
|
|
420
432
|
this.State = States.STATE_CONNECTING;
|
|
421
433
|
|
|
@@ -468,14 +480,15 @@ export class Client extends EventEmitter {
|
|
|
468
480
|
this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
|
|
469
481
|
|
|
470
482
|
if (this.socket)
|
|
471
|
-
this.socket.on("message", (
|
|
483
|
+
this.socket.on("message", (packet, rinfo) => {
|
|
472
484
|
if (this.State == 0 || rinfo.address != this.host || rinfo.port != this.port)
|
|
473
485
|
return;
|
|
474
486
|
clearInterval(connectInterval)
|
|
475
|
-
|
|
476
|
-
|
|
487
|
+
|
|
488
|
+
if (packet.toJSON().data[0] == 0x10) {
|
|
489
|
+
if (packet.toString().includes("TKEN") || packet.toJSON().data[3] == 0x2) {
|
|
477
490
|
clearInterval(connectInterval);
|
|
478
|
-
this.TKEN = Buffer.from(
|
|
491
|
+
this.TKEN = Buffer.from(packet.toJSON().data.slice(packet.toJSON().data.length - 4, packet.toJSON().data.length))
|
|
479
492
|
this.SendControlMsg(3);
|
|
480
493
|
this.State = States.STATE_LOADING; // loading state
|
|
481
494
|
this.receivedSnaps = 0;
|
|
@@ -500,26 +513,26 @@ export class Client extends EventEmitter {
|
|
|
500
513
|
}
|
|
501
514
|
|
|
502
515
|
this.SendMsgEx([client_version, info])
|
|
503
|
-
} else if (
|
|
516
|
+
} else if (packet.toJSON().data[3] == 0x4) {
|
|
504
517
|
// disconnected
|
|
505
518
|
this.State = States.STATE_OFFLINE;
|
|
506
|
-
let reason: string = (unpackString(
|
|
519
|
+
let reason: string = (unpackString(packet.toJSON().data.slice(4)).result);
|
|
507
520
|
this.emit("disconnect", reason);
|
|
508
521
|
}
|
|
509
|
-
if (
|
|
522
|
+
if (packet.toJSON().data[3] !== 0x0) { // keepalive
|
|
510
523
|
this.lastRecvTime = new Date().getTime();
|
|
511
524
|
}
|
|
512
525
|
} else {
|
|
513
526
|
this.lastRecvTime = new Date().getTime();
|
|
514
|
-
|
|
515
527
|
}
|
|
516
|
-
|
|
517
528
|
|
|
518
|
-
var unpacked: _packet = this.Unpack(
|
|
519
|
-
unpacked.chunks.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
529
|
+
var unpacked: _packet = this.Unpack(packet);
|
|
530
|
+
unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
|
|
531
|
+
|
|
532
|
+
unpacked.chunks.forEach(chunk => {
|
|
533
|
+
if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
|
|
534
|
+
if (chunk.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
|
|
535
|
+
this.ack = chunk.seq!;
|
|
523
536
|
|
|
524
537
|
this.requestResend = false;
|
|
525
538
|
}
|
|
@@ -527,10 +540,10 @@ export class Client extends EventEmitter {
|
|
|
527
540
|
let Bottom = (this.ack - (1<<10)/2);
|
|
528
541
|
|
|
529
542
|
if(Bottom < 0) {
|
|
530
|
-
if((
|
|
543
|
+
if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10))))
|
|
531
544
|
return;
|
|
532
545
|
} else {
|
|
533
|
-
if(
|
|
546
|
+
if(chunk.seq! <= this.ack && chunk.seq! >= Bottom)
|
|
534
547
|
return;
|
|
535
548
|
}
|
|
536
549
|
this.requestResend = true;
|
|
@@ -539,7 +552,7 @@ export class Client extends EventEmitter {
|
|
|
539
552
|
}
|
|
540
553
|
|
|
541
554
|
})
|
|
542
|
-
unpacked.chunks.filter(
|
|
555
|
+
unpacked.chunks.filter(chunk => chunk.msgid == NETMSGTYPE.SV_BROADCAST && chunk.type == 'game').forEach(a => {
|
|
543
556
|
let unpacker = new MsgUnpacker(a.raw.toJSON().data);
|
|
544
557
|
|
|
545
558
|
this.emit("broadcast", unpacker.unpackString());
|
|
@@ -553,62 +566,72 @@ export class Client extends EventEmitter {
|
|
|
553
566
|
})
|
|
554
567
|
let snapChunks: chunk[] = [];
|
|
555
568
|
if (this.options?.lightweight !== true)
|
|
556
|
-
snapChunks = unpacked.chunks.filter(
|
|
569
|
+
snapChunks = unpacked.chunks.filter(chunk => chunk.msg === "SNAP" || chunk.msg === "SNAP_SINGLE" || chunk.msg === "SNAP_EMPTY");
|
|
557
570
|
if (snapChunks.length > 0) {
|
|
558
|
-
|
|
559
|
-
|
|
571
|
+
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
572
|
+
this.PredGameTick = this.AckGameTick + 1;
|
|
573
|
+
|
|
560
574
|
snapChunks.forEach(chunk => {
|
|
561
575
|
let unpacker = new MsgUnpacker(chunk.raw.toJSON().data);
|
|
562
576
|
|
|
563
|
-
let
|
|
564
|
-
|
|
565
|
-
let
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
let num_parts = 1;
|
|
577
|
-
let part = 0;
|
|
577
|
+
let NumParts = 1;
|
|
578
|
+
let Part = 0;
|
|
579
|
+
let GameTick = unpacker.unpackInt();
|
|
580
|
+
let DeltaTick = GameTick - unpacker.unpackInt();
|
|
581
|
+
let PartSize = 0;
|
|
582
|
+
let Crc = 0;
|
|
583
|
+
let CompleteSize = 0;
|
|
584
|
+
|
|
585
|
+
if (chunk.msg == "SNAP") {
|
|
586
|
+
NumParts = unpacker.unpackInt();
|
|
587
|
+
Part = unpacker.unpackInt();
|
|
588
|
+
}
|
|
578
589
|
|
|
579
|
-
if (chunk.msg === "SNAP") {
|
|
580
|
-
num_parts = unpacker.unpackInt();
|
|
581
|
-
part = unpacker.unpackInt();
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
let crc = 0;
|
|
585
|
-
let part_size = 0;
|
|
586
590
|
if (chunk.msg != "SNAP_EMPTY") {
|
|
587
|
-
|
|
588
|
-
|
|
591
|
+
Crc = unpacker.unpackInt();
|
|
592
|
+
PartSize = unpacker.unpackInt();
|
|
589
593
|
}
|
|
590
|
-
if (part === 0 || this.snaps.length > 30) {
|
|
591
|
-
this.snaps = [];
|
|
592
|
-
}
|
|
593
|
-
chunk.raw = Buffer.from(unpacker.remaining);
|
|
594
|
-
this.snaps.push(chunk.raw)
|
|
595
|
-
|
|
596
|
-
if ((num_parts - 1) === part && this.snaps.length === num_parts) {
|
|
597
594
|
|
|
598
|
-
|
|
595
|
+
if (PartSize < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize <= 0 || PartSize > 900)
|
|
596
|
+
return;
|
|
599
597
|
|
|
600
|
-
|
|
601
|
-
this.
|
|
602
|
-
|
|
603
|
-
|
|
598
|
+
if (GameTick >= this.currentSnapshotGameTick) {
|
|
599
|
+
if (GameTick != this.currentSnapshotGameTick) {
|
|
600
|
+
this.snaps = [];
|
|
601
|
+
this.SnapshotParts = 0;
|
|
602
|
+
this.currentSnapshotGameTick = GameTick;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// chunk.raw = Buffer.from(unpacker.remaining);
|
|
606
|
+
this.snaps[Part] = Buffer.from(unpacker.remaining);
|
|
607
|
+
|
|
608
|
+
this.SnapshotParts |= 1 << Part;
|
|
604
609
|
|
|
605
|
-
this.
|
|
606
|
-
|
|
610
|
+
if (this.SnapshotParts == ((1 << NumParts) - 1)) {
|
|
611
|
+
let mergedSnaps = Buffer.concat(this.snaps);
|
|
612
|
+
this.SnapshotParts = 0;
|
|
607
613
|
|
|
608
|
-
|
|
614
|
+
let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps.toJSON().data, DeltaTick, GameTick);
|
|
615
|
+
|
|
616
|
+
this.emit("snapshot");
|
|
617
|
+
this.AckGameTick = snapUnpacked.recvTick;
|
|
618
|
+
if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
|
|
619
|
+
this.PredGameTick = this.AckGameTick + 1;
|
|
620
|
+
|
|
621
|
+
this.sendInput();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
|
|
609
628
|
})
|
|
610
629
|
}
|
|
630
|
+
|
|
611
631
|
var chunkMessages = unpacked.chunks.map(a => a.msg)
|
|
632
|
+
if (unpacked.chunks.findIndex(chunk => chunk.msgid == 23 && chunk.type == "sys") !== -1) {
|
|
633
|
+
this.game._ping_resolve(new Date().getTime())
|
|
634
|
+
}
|
|
612
635
|
if (chunkMessages.includes("SV_CHAT")) {
|
|
613
636
|
var chat = unpacked.chunks.filter(a => a.msg == "SV_CHAT");
|
|
614
637
|
chat.forEach(a => {
|
|
@@ -630,7 +653,7 @@ export class Client extends EventEmitter {
|
|
|
630
653
|
}
|
|
631
654
|
})
|
|
632
655
|
}
|
|
633
|
-
var chat = unpacked.chunks.filter(
|
|
656
|
+
var chat = unpacked.chunks.filter(chunk => chunk.msg == "SV_KILL_MSG" || chunk.msg == "SV_MOTD");
|
|
634
657
|
chat.forEach(a => {
|
|
635
658
|
if (a.msg == "SV_KILL_MSG") {
|
|
636
659
|
var unpacked: iKillMsg = {} as iKillMsg;
|
|
@@ -706,6 +729,7 @@ export class Client extends EventEmitter {
|
|
|
706
729
|
})
|
|
707
730
|
}
|
|
708
731
|
|
|
732
|
+
/* Sending the input. (automatically done unless options.lightweight is on) */
|
|
709
733
|
sendInput(input = this.movement.input) {
|
|
710
734
|
if (this.State != States.STATE_ONLINE)
|
|
711
735
|
return;
|
|
@@ -737,6 +761,7 @@ export class Client extends EventEmitter {
|
|
|
737
761
|
return this.movement.input;
|
|
738
762
|
}
|
|
739
763
|
|
|
764
|
+
/* Disconnect the client. */
|
|
740
765
|
Disconnect() {
|
|
741
766
|
return new Promise((resolve) => {
|
|
742
767
|
this.SendControlMsg(4).then(() => {
|
|
@@ -749,64 +774,11 @@ export class Client extends EventEmitter {
|
|
|
749
774
|
})
|
|
750
775
|
}
|
|
751
776
|
|
|
752
|
-
|
|
753
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
|
|
754
|
-
packer.AddInt(team ? 1 : 0); // team
|
|
755
|
-
packer.AddString(message);
|
|
756
|
-
if (!this.options?.lightweight)
|
|
757
|
-
this.QueueChunkEx(packer);
|
|
758
|
-
else
|
|
759
|
-
this.SendMsgEx(packer);
|
|
760
|
-
}
|
|
761
|
-
Vote(vote: boolean) {
|
|
762
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
|
|
763
|
-
packer.AddInt(vote ? 1 : -1);
|
|
764
|
-
if (!this.options?.lightweight)
|
|
765
|
-
this.QueueChunkEx(packer);
|
|
766
|
-
else
|
|
767
|
-
this.SendMsgEx(packer);
|
|
768
|
-
}
|
|
769
|
-
ChangePlayerInfo(playerInfo: ClientInfo) {
|
|
770
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
|
|
771
|
-
packer.AddString(playerInfo.name); //m_pName);
|
|
772
|
-
packer.AddString(playerInfo.clan); //m_pClan);
|
|
773
|
-
packer.AddInt(playerInfo.country); //m_Country);
|
|
774
|
-
packer.AddString(playerInfo.skin); //m_pSkin);
|
|
775
|
-
packer.AddInt(playerInfo.use_custom_color ? 1 : 0); //m_UseCustomColor);
|
|
776
|
-
packer.AddInt(playerInfo.color_body); //m_ColorBody);
|
|
777
|
-
packer.AddInt(playerInfo.color_feet); //m_ColorFeet);
|
|
778
|
-
if (!this.options?.lightweight)
|
|
779
|
-
this.QueueChunkEx(packer);
|
|
780
|
-
else
|
|
781
|
-
this.SendMsgEx(packer);
|
|
782
|
-
}
|
|
783
|
-
Kill() {
|
|
784
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
|
|
785
|
-
if (!this.options?.lightweight)
|
|
786
|
-
this.QueueChunkEx(packer);
|
|
787
|
-
else
|
|
788
|
-
this.SendMsgEx(packer);
|
|
789
|
-
}
|
|
790
|
-
ChangeTeam(team: number) {
|
|
791
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
|
|
792
|
-
packer.AddInt(team);
|
|
793
|
-
if (!this.options?.lightweight)
|
|
794
|
-
this.QueueChunkEx(packer);
|
|
795
|
-
else
|
|
796
|
-
this.SendMsgEx(packer);
|
|
797
|
-
}
|
|
798
|
-
Emote(emote: number) {
|
|
799
|
-
var packer = new MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
|
|
800
|
-
packer.AddInt(emote);
|
|
801
|
-
if (!this.options?.lightweight)
|
|
802
|
-
this.QueueChunkEx(packer);
|
|
803
|
-
else
|
|
804
|
-
this.SendMsgEx(packer);
|
|
805
|
-
}
|
|
777
|
+
/* Get the client_info from a specific player id. */
|
|
806
778
|
client_info(id: number) {
|
|
807
|
-
let delta = this.SnapUnpacker.deltas.filter(
|
|
808
|
-
|
|
809
|
-
&&
|
|
779
|
+
let delta = this.SnapUnpacker.deltas.filter(_delta =>
|
|
780
|
+
_delta.type_id == 11
|
|
781
|
+
&& _delta.id == id
|
|
810
782
|
);
|
|
811
783
|
|
|
812
784
|
if (delta.length == 0)
|
|
@@ -815,25 +787,29 @@ export class Client extends EventEmitter {
|
|
|
815
787
|
// .sort((a, b) => a.id - b.id)
|
|
816
788
|
// .map(a => a.parsed as ClientInfo);
|
|
817
789
|
}
|
|
790
|
+
|
|
791
|
+
/* Get all client infos. */
|
|
818
792
|
get client_infos(): ClientInfo[] {
|
|
819
793
|
|
|
820
|
-
return this.SnapUnpacker.deltas.filter(
|
|
821
|
-
|
|
822
|
-
|
|
794
|
+
return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 11)
|
|
795
|
+
.sort((a, b) => a.id - b.id)
|
|
796
|
+
.map(a => a.parsed as ClientInfo);
|
|
823
797
|
}
|
|
798
|
+
/* Get the player info from a specific player id. */
|
|
824
799
|
player_info(id: number) {
|
|
825
|
-
let delta = this.SnapUnpacker.deltas.filter(
|
|
826
|
-
|
|
827
|
-
&&
|
|
800
|
+
let delta = this.SnapUnpacker.deltas.filter(_delta =>
|
|
801
|
+
_delta.type_id == 10
|
|
802
|
+
&& _delta.id == id
|
|
828
803
|
);
|
|
829
804
|
|
|
830
805
|
if (delta.length == 0)
|
|
831
806
|
return undefined;
|
|
832
807
|
return delta[0].parsed as PlayerInfo;
|
|
833
808
|
}
|
|
809
|
+
/* Get all player infos. */
|
|
834
810
|
get player_infos(): PlayerInfo[] {
|
|
835
|
-
return this.SnapUnpacker.deltas.filter(
|
|
811
|
+
return this.SnapUnpacker.deltas.filter(_delta => _delta.type_id == 10)
|
|
836
812
|
.sort((a, b) => a.id - b.id)
|
|
837
|
-
.map(
|
|
813
|
+
.map(player => player.parsed as PlayerInfo);
|
|
838
814
|
}
|
|
839
815
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Game = void 0;
|
|
4
|
+
var MsgPacker_1 = require("../MsgPacker");
|
|
5
|
+
var NETMSGTYPE;
|
|
6
|
+
(function (NETMSGTYPE) {
|
|
7
|
+
NETMSGTYPE[NETMSGTYPE["EX"] = 0] = "EX";
|
|
8
|
+
NETMSGTYPE[NETMSGTYPE["SV_MOTD"] = 1] = "SV_MOTD";
|
|
9
|
+
NETMSGTYPE[NETMSGTYPE["SV_BROADCAST"] = 2] = "SV_BROADCAST";
|
|
10
|
+
NETMSGTYPE[NETMSGTYPE["SV_CHAT"] = 3] = "SV_CHAT";
|
|
11
|
+
NETMSGTYPE[NETMSGTYPE["SV_KILLMSG"] = 4] = "SV_KILLMSG";
|
|
12
|
+
NETMSGTYPE[NETMSGTYPE["SV_SOUNDGLOBAL"] = 5] = "SV_SOUNDGLOBAL";
|
|
13
|
+
NETMSGTYPE[NETMSGTYPE["SV_TUNEPARAMS"] = 6] = "SV_TUNEPARAMS";
|
|
14
|
+
NETMSGTYPE[NETMSGTYPE["SV_EXTRAPROJECTILE"] = 7] = "SV_EXTRAPROJECTILE";
|
|
15
|
+
NETMSGTYPE[NETMSGTYPE["SV_READYTOENTER"] = 8] = "SV_READYTOENTER";
|
|
16
|
+
NETMSGTYPE[NETMSGTYPE["SV_WEAPONPICKUP"] = 9] = "SV_WEAPONPICKUP";
|
|
17
|
+
NETMSGTYPE[NETMSGTYPE["SV_EMOTICON"] = 10] = "SV_EMOTICON";
|
|
18
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTECLEAROPTIONS"] = 11] = "SV_VOTECLEAROPTIONS";
|
|
19
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONLISTADD"] = 12] = "SV_VOTEOPTIONLISTADD";
|
|
20
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONADD"] = 13] = "SV_VOTEOPTIONADD";
|
|
21
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTEOPTIONREMOVE"] = 14] = "SV_VOTEOPTIONREMOVE";
|
|
22
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTESET"] = 15] = "SV_VOTESET";
|
|
23
|
+
NETMSGTYPE[NETMSGTYPE["SV_VOTESTATUS"] = 16] = "SV_VOTESTATUS";
|
|
24
|
+
NETMSGTYPE[NETMSGTYPE["CL_SAY"] = 17] = "CL_SAY";
|
|
25
|
+
NETMSGTYPE[NETMSGTYPE["CL_SETTEAM"] = 18] = "CL_SETTEAM";
|
|
26
|
+
NETMSGTYPE[NETMSGTYPE["CL_SETSPECTATORMODE"] = 19] = "CL_SETSPECTATORMODE";
|
|
27
|
+
NETMSGTYPE[NETMSGTYPE["CL_STARTINFO"] = 20] = "CL_STARTINFO";
|
|
28
|
+
NETMSGTYPE[NETMSGTYPE["CL_CHANGEINFO"] = 21] = "CL_CHANGEINFO";
|
|
29
|
+
NETMSGTYPE[NETMSGTYPE["CL_KILL"] = 22] = "CL_KILL";
|
|
30
|
+
NETMSGTYPE[NETMSGTYPE["CL_EMOTICON"] = 23] = "CL_EMOTICON";
|
|
31
|
+
NETMSGTYPE[NETMSGTYPE["CL_VOTE"] = 24] = "CL_VOTE";
|
|
32
|
+
NETMSGTYPE[NETMSGTYPE["CL_CALLVOTE"] = 25] = "CL_CALLVOTE";
|
|
33
|
+
NETMSGTYPE[NETMSGTYPE["CL_ISDDNETLEGACY"] = 26] = "CL_ISDDNETLEGACY";
|
|
34
|
+
NETMSGTYPE[NETMSGTYPE["SV_DDRACETIMELEGACY"] = 27] = "SV_DDRACETIMELEGACY";
|
|
35
|
+
NETMSGTYPE[NETMSGTYPE["SV_RECORDLEGACY"] = 28] = "SV_RECORDLEGACY";
|
|
36
|
+
NETMSGTYPE[NETMSGTYPE["UNUSED"] = 29] = "UNUSED";
|
|
37
|
+
NETMSGTYPE[NETMSGTYPE["SV_TEAMSSTATELEGACY"] = 30] = "SV_TEAMSSTATELEGACY";
|
|
38
|
+
NETMSGTYPE[NETMSGTYPE["CL_SHOWOTHERSLEGACY"] = 31] = "CL_SHOWOTHERSLEGACY";
|
|
39
|
+
NETMSGTYPE[NETMSGTYPE["NUM"] = 32] = "NUM";
|
|
40
|
+
})(NETMSGTYPE || (NETMSGTYPE = {}));
|
|
41
|
+
;
|
|
42
|
+
var Game = /** @class */ (function () {
|
|
43
|
+
function Game(_client) {
|
|
44
|
+
// this.SendMsgEx = callback;
|
|
45
|
+
this._client = _client;
|
|
46
|
+
this._ping_resolve = function () { };
|
|
47
|
+
}
|
|
48
|
+
Game.prototype.send = function (packer) {
|
|
49
|
+
var _a;
|
|
50
|
+
if (!((_a = this._client.options) === null || _a === void 0 ? void 0 : _a.lightweight))
|
|
51
|
+
this._client.QueueChunkEx(packer);
|
|
52
|
+
else
|
|
53
|
+
this._client.SendMsgEx(packer);
|
|
54
|
+
};
|
|
55
|
+
Game.prototype.Say = function (message, team) {
|
|
56
|
+
if (team === void 0) { team = false; }
|
|
57
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SAY, false, 1);
|
|
58
|
+
packer.AddInt(team ? 1 : 0); // team
|
|
59
|
+
packer.AddString(message);
|
|
60
|
+
this.send(packer);
|
|
61
|
+
};
|
|
62
|
+
/* Set the team of an bot. (-1 spectator team, 0 team red/normal team, 1 team blue) */
|
|
63
|
+
Game.prototype.SetTeam = function (team) {
|
|
64
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETTEAM, false, 1);
|
|
65
|
+
packer.AddInt(team);
|
|
66
|
+
this.send(packer);
|
|
67
|
+
};
|
|
68
|
+
/* Spectate an player, taking their id as parameter. pretty useless */
|
|
69
|
+
Game.prototype.SpectatorMode = function (SpectatorID) {
|
|
70
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_SETSPECTATORMODE, false, 1);
|
|
71
|
+
packer.AddInt(SpectatorID);
|
|
72
|
+
this.send(packer);
|
|
73
|
+
};
|
|
74
|
+
/* Change the player info */
|
|
75
|
+
Game.prototype.ChangePlayerInfo = function (playerInfo) {
|
|
76
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CHANGEINFO, false, 1);
|
|
77
|
+
packer.AddString(playerInfo.name);
|
|
78
|
+
packer.AddString(playerInfo.clan);
|
|
79
|
+
packer.AddInt(playerInfo.country);
|
|
80
|
+
packer.AddString(playerInfo.skin);
|
|
81
|
+
packer.AddInt(playerInfo.use_custom_color ? 1 : 0);
|
|
82
|
+
packer.AddInt(playerInfo.color_body);
|
|
83
|
+
packer.AddInt(playerInfo.color_feet);
|
|
84
|
+
this.send(packer);
|
|
85
|
+
};
|
|
86
|
+
/* Kill */
|
|
87
|
+
Game.prototype.Kill = function () {
|
|
88
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_KILL, false, 1);
|
|
89
|
+
this.send(packer);
|
|
90
|
+
};
|
|
91
|
+
/* Send emote */
|
|
92
|
+
Game.prototype.Emote = function (emote) {
|
|
93
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_EMOTICON, false, 1);
|
|
94
|
+
packer.AddInt(emote);
|
|
95
|
+
this.send(packer);
|
|
96
|
+
};
|
|
97
|
+
/* Vote for an already running vote (f3 / f4) */
|
|
98
|
+
Game.prototype.Vote = function (vote) {
|
|
99
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_VOTE, false, 1);
|
|
100
|
+
packer.AddInt(vote ? 1 : -1);
|
|
101
|
+
this.send(packer);
|
|
102
|
+
};
|
|
103
|
+
Game.prototype.CallVote = function (Type, Value, Reason) {
|
|
104
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_CALLVOTE, false, 1);
|
|
105
|
+
packer.AddString(Type);
|
|
106
|
+
packer.AddString(String(Value));
|
|
107
|
+
packer.AddString(Reason);
|
|
108
|
+
this.send(packer);
|
|
109
|
+
};
|
|
110
|
+
/* Call a vote for an server option (for example ddnet maps) */
|
|
111
|
+
Game.prototype.CallVoteOption = function (Value, Reason) {
|
|
112
|
+
this.CallVote("option", Value, Reason);
|
|
113
|
+
};
|
|
114
|
+
/* Call a vote to kick a player. Requires the player id */
|
|
115
|
+
Game.prototype.CallVoteKick = function (PlayerID, Reason) {
|
|
116
|
+
this.CallVote("kick", PlayerID, Reason);
|
|
117
|
+
};
|
|
118
|
+
/* Call a vote to set a player in spectator mode. Requires the player id */
|
|
119
|
+
Game.prototype.CallVoteSpectate = function (PlayerID, Reason) {
|
|
120
|
+
this.CallVote("spectate", PlayerID, Reason);
|
|
121
|
+
};
|
|
122
|
+
/** probably some verification of using ddnet client.*/
|
|
123
|
+
Game.prototype.IsDDNetLegacy = function () {
|
|
124
|
+
var packer = new MsgPacker_1.MsgPacker(NETMSGTYPE.CL_ISDDNETLEGACY, false, 1);
|
|
125
|
+
this.send(packer);
|
|
126
|
+
};
|
|
127
|
+
/* returns the ping in ms */
|
|
128
|
+
Game.prototype.Ping = function () {
|
|
129
|
+
var _this = this;
|
|
130
|
+
return new Promise(function (resolve, reject) {
|
|
131
|
+
var packer = new MsgPacker_1.MsgPacker(22, true, 0);
|
|
132
|
+
var startTime = new Date().getTime();
|
|
133
|
+
_this.send(packer);
|
|
134
|
+
var callback = function (_time) {
|
|
135
|
+
resolve(_time - startTime);
|
|
136
|
+
_this._ping_resolve = function () { };
|
|
137
|
+
};
|
|
138
|
+
_this._ping_resolve = callback;
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
return Game;
|
|
142
|
+
}());
|
|
143
|
+
exports.Game = Game;
|