teeworlds 2.5.4 → 2.5.6

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.
Files changed (54) hide show
  1. package/README.md +4 -3
  2. package/{index.ts → index.d.ts} +6 -6
  3. package/index.js +25 -24
  4. package/index.js.map +1 -0
  5. package/lib/MsgPacker.d.ts +11 -0
  6. package/lib/MsgPacker.js +47 -55
  7. package/lib/MsgPacker.js.map +1 -0
  8. package/lib/MsgUnpacker.d.ts +16 -0
  9. package/lib/MsgUnpacker.js +53 -53
  10. package/lib/MsgUnpacker.js.map +1 -0
  11. package/lib/UUIDManager.d.ts +27 -0
  12. package/lib/UUIDManager.js +45 -50
  13. package/lib/UUIDManager.js.map +1 -0
  14. package/lib/client.d.ts +135 -0
  15. package/lib/client.js +762 -830
  16. package/lib/client.js.map +1 -0
  17. package/lib/components/game.d.ts +32 -0
  18. package/lib/components/game.js +104 -106
  19. package/lib/components/game.js.map +1 -0
  20. package/lib/components/movement.d.ts +34 -0
  21. package/lib/components/movement.js +75 -82
  22. package/lib/components/movement.js.map +1 -0
  23. package/lib/components/rcon.d.ts +33 -0
  24. package/lib/components/rcon.js +92 -122
  25. package/lib/components/rcon.js.map +1 -0
  26. package/lib/components/snapshot.d.ts +59 -0
  27. package/lib/components/snapshot.js +144 -234
  28. package/lib/components/snapshot.js.map +1 -0
  29. package/lib/enums_types/protocol.d.ts +120 -0
  30. package/lib/enums_types/protocol.js +14 -13
  31. package/lib/enums_types/protocol.js.map +1 -0
  32. package/lib/huffman.d.ts +24 -0
  33. package/lib/huffman.js +187 -192
  34. package/lib/huffman.js.map +1 -0
  35. package/lib/snapshot.d.ts +49 -0
  36. package/lib/snapshot.js +599 -610
  37. package/lib/snapshot.js.map +1 -0
  38. package/package.json +21 -24
  39. package/docs/documentation.md +0 -368
  40. package/docs/examples/chat_bot.js +0 -25
  41. package/docs/examples/kill_on_freeze.js +0 -20
  42. package/lib/MsgPacker.ts +0 -45
  43. package/lib/MsgUnpacker.ts +0 -58
  44. package/lib/UUIDManager.ts +0 -45
  45. package/lib/client.ts +0 -926
  46. package/lib/components/game.ts +0 -133
  47. package/lib/components/movement.ts +0 -88
  48. package/lib/components/rcon.ts +0 -114
  49. package/lib/components/snapshot.ts +0 -187
  50. package/lib/enums_types/protocol.ts +0 -137
  51. package/lib/enums_types/types.d.ts +0 -236
  52. package/lib/huffman.ts +0 -226
  53. package/lib/snapshot.ts +0 -632
  54. package/tsconfig.json +0 -69
package/lib/client.ts DELETED
@@ -1,926 +0,0 @@
1
-
2
-
3
- import { randomBytes } from "crypto";
4
- import { lookup as dnsLookup } from 'dns';
5
- import net from 'dgram';
6
- import { EventEmitter } from 'stream';
7
-
8
- import { unpackString, MsgUnpacker } from "./MsgUnpacker";
9
- let { version } = require('../package.json');
10
-
11
- import Movement from './components/movement';
12
- import { _Packet, Chunk, DeltaItem, SnapshotItemTypes } from './enums_types/types';
13
-
14
- import { MsgPacker } from './MsgPacker';
15
- import { Snapshot } from './snapshot';
16
- import { Huffman } from "./huffman";
17
- import { Game } from "./components/game";
18
- import { SnapshotWrapper } from "./components/snapshot";
19
-
20
- import { UUIDManager } from "./UUIDManager";
21
- import { NETMSG, States } from "./enums_types/protocol";
22
- import { Rcon } from "./components/rcon";
23
-
24
- const huff = new Huffman();
25
-
26
- var messageTypes = [
27
- ["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"],
28
- ["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"]
29
- ]
30
-
31
-
32
- declare interface iMessage {
33
- team: number,
34
- client_id: number,
35
- author?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
36
- message: string
37
- }
38
-
39
- declare interface iEmoticon {
40
- client_id: number,
41
- emoticon: number,
42
- author?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo }
43
- }
44
-
45
- declare interface iKillMsg {
46
- killer_id: number,
47
- killer?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
48
- victim_id: number,
49
- victim?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
50
- weapon: number,
51
- special_mode: number
52
- }
53
-
54
- declare interface iOptions {
55
- identity?: SnapshotItemTypes.ClientInfo,
56
- password?: string,
57
- ddnet_version?: {version: number, release_version: string},
58
- timeout?: number, // in ms
59
- NET_VERSION?: string,
60
- lightweight?: boolean // experimental, only sends keepalive's (sendinput has to be called manually)
61
- }
62
-
63
- interface ClientEvents {
64
- connected: () => void;
65
- disconnect: (reason: string) => void;
66
- emote: (message: iEmoticon) => void;
67
- message: (message: iMessage) => void;
68
- broadcast: (message: string) => void;
69
- kill: (kill: iKillMsg) => void;
70
- motd: (message: string) => void;
71
- map_details: (message: { map_name: string, map_sha256: Buffer, map_crc: number, map_size: number, map_url: string }) => void;
72
- capabilities: (message: { ChatTimeoutCode: boolean, AnyPlayerFlag: boolean, PingEx: boolean, AllowDummy: boolean, SyncWeaponInput: boolean }) => void;
73
- snapshot: (items: DeltaItem[]) => void;
74
- rcon_line: (line: string) => void;
75
- }
76
-
77
- export class Client extends EventEmitter {
78
- on<K extends keyof ClientEvents>(event: K, listener: ClientEvents[K]): this {
79
- return super.on(event, listener);
80
- }
81
-
82
- emit<K extends keyof ClientEvents>(event: K, ...args: Parameters<ClientEvents[K]>): boolean {
83
- return super.emit(event, ...args);
84
- }
85
-
86
- public rcon: Rcon;
87
- private host: string;
88
- private port: number;
89
- private name: string;
90
- private State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
91
- private ack: number;
92
- private clientAck: number;
93
- private lastCheckedChunkAck: number;
94
- private receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
95
- private socket: net.Socket | undefined;
96
- private TKEN: Buffer;
97
- private time: number;
98
- private SnapUnpacker: Snapshot;
99
-
100
- public SnapshotUnpacker: SnapshotWrapper;
101
-
102
- private PredGameTick: number;
103
- private AckGameTick: number;
104
-
105
- private SnapshotParts: number;
106
- private currentSnapshotGameTick: number;
107
-
108
-
109
- private snaps: Buffer[];
110
-
111
- private sentChunkQueue: Buffer[];
112
- private queueChunkEx: MsgPacker[];
113
- private lastSendTime: number;
114
- private lastRecvTime: number;
115
-
116
- private lastSentMessages: {msg: MsgPacker, ack: number}[];
117
-
118
-
119
- public movement: Movement;
120
- public game: Game;
121
-
122
- private VoteList: string[];
123
- // eSnapHolder: eSnap[];
124
-
125
-
126
- public readonly options?: iOptions;
127
- private requestResend: boolean;
128
-
129
- private UUIDManager: UUIDManager;
130
-
131
- constructor(ip: string, port: number, nickname: string, options?: iOptions) {
132
- super();
133
- this.host = ip;
134
- this.port = port;
135
- this.name = nickname;
136
- this.AckGameTick = 0;
137
- this.PredGameTick = 0;
138
- this.currentSnapshotGameTick = 0;
139
-
140
- this.SnapshotParts = 0;
141
- this.rcon = new Rcon(this);
142
- this.SnapUnpacker = new Snapshot(this);
143
- // this.eSnapHolder = [];
144
- this.requestResend = false;
145
-
146
- this.VoteList = [];
147
-
148
- if (options)
149
- this.options = options;
150
-
151
-
152
- this.snaps = [];
153
-
154
- this.sentChunkQueue = [];
155
- this.queueChunkEx = [];
156
-
157
- this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
158
- this.ack = 0; // ack of messages the client has received
159
- this.clientAck = 0; // ack of messages the client has sent
160
- this.lastCheckedChunkAck = 0; // this.ack gets reset to this when flushing - used for resetting tick on e.g. map change
161
- this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
162
- this.socket = net.createSocket("udp4");
163
- this.socket.bind();
164
-
165
- this.TKEN = Buffer.from([255, 255, 255, 255])
166
- this.time = new Date().getTime() + 2000; // time (used for keepalives, start to send keepalives after 2 seconds)
167
- this.lastSendTime = new Date().getTime();
168
- this.lastRecvTime = new Date().getTime();
169
-
170
- this.lastSentMessages = [];
171
-
172
- this.movement = new Movement();
173
-
174
- this.game = new Game(this);
175
- this.SnapshotUnpacker = new SnapshotWrapper(this);
176
-
177
- this.UUIDManager = new UUIDManager();
178
-
179
- this.UUIDManager.RegisterName("what-is@ddnet.tw", NETMSG.System.NETMSG_WHATIS);
180
- this.UUIDManager.RegisterName("it-is@ddnet.tw", NETMSG.System.NETMSG_ITIS);
181
- this.UUIDManager.RegisterName("i-dont-know@ddnet.tw", NETMSG.System.NETMSG_IDONTKNOW);
182
-
183
- this.UUIDManager.RegisterName("rcon-type@ddnet.tw", NETMSG.System.NETMSG_RCONTYPE);
184
- this.UUIDManager.RegisterName("map-details@ddnet.tw", NETMSG.System.NETMSG_MAP_DETAILS);
185
- this.UUIDManager.RegisterName("capabilities@ddnet.tw", NETMSG.System.NETMSG_CAPABILITIES);
186
- this.UUIDManager.RegisterName("clientver@ddnet.tw", NETMSG.System.NETMSG_CLIENTVER);
187
- this.UUIDManager.RegisterName("ping@ddnet.tw", NETMSG.System.NETMSG_PING);
188
- this.UUIDManager.RegisterName("pong@ddnet.tw", NETMSG.System.NETMSG_PONGEX);
189
- this.UUIDManager.RegisterName("checksum-request@ddnet.tw", NETMSG.System.NETMSG_CHECKSUM_REQUEST);
190
- this.UUIDManager.RegisterName("checksum-response@ddnet.tw", NETMSG.System.NETMSG_CHECKSUM_RESPONSE);
191
- this.UUIDManager.RegisterName("checksum-error@ddnet.tw", NETMSG.System.NETMSG_CHECKSUM_ERROR);
192
- this.UUIDManager.RegisterName("redirect@ddnet.org", NETMSG.System.NETMSG_REDIRECT);
193
-
194
- this.UUIDManager.RegisterName("i-am-npm-package@swarfey.gitlab.io", NETMSG.System.NETMSG_I_AM_NPM_PACKAGE);
195
-
196
- }
197
-
198
- private OnEnterGame() {
199
- this.snaps = [];
200
- this.SnapUnpacker = new Snapshot(this);
201
- this.SnapshotParts = 0;
202
- this.receivedSnaps = 0;
203
- this.SnapshotUnpacker = new SnapshotWrapper(this);
204
- this.currentSnapshotGameTick = 0;
205
- this.AckGameTick = -1;
206
- this.PredGameTick = 0;
207
- }
208
- private ResendAfter(lastAck: number) {
209
- this.clientAck = lastAck;
210
-
211
-
212
- let toResend: MsgPacker[] = [];
213
- this.lastSentMessages.forEach(msg => {
214
- if (msg.ack > lastAck)
215
- toResend.push(msg.msg);
216
- });
217
- toResend.forEach(a => a.flag = 1|2);
218
- this.SendMsgEx(toResend);
219
- }
220
-
221
- private Unpack(packet: Buffer): _Packet {
222
- var unpacked: _Packet = { twprotocol: { flags: packet[0] >> 4, ack: ((packet[0]&0xf)<<8) | packet[1], chunkAmount: packet[2], size: packet.byteLength - 3 }, chunks: [] }
223
-
224
-
225
- 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)
226
- return unpacked;
227
- if (unpacked.twprotocol.flags & 4) { // resend flag
228
- this.ResendAfter(unpacked.twprotocol.ack);
229
- }
230
- packet = packet.slice(3)
231
-
232
- if (unpacked.twprotocol.flags & 8 && !(unpacked.twprotocol.flags & 1)) { // compression flag
233
- packet = huff.decompress(packet)
234
- if (packet.length == 1 && packet[0] == -1)
235
- return unpacked
236
- }
237
-
238
-
239
- for (let i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
240
- var chunk: Chunk = {} as Chunk;
241
- chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
242
- chunk.flags = (packet[0] >> 6) & 3;
243
-
244
- if (chunk.flags & 1) {
245
- chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
246
- packet = packet.slice(3) // remove flags & size
247
- } else
248
- packet = packet.slice(2)
249
- // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
250
- chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
251
- chunk.msgid = (packet[0] - (packet[0] & 1)) / 2;
252
- chunk.msg = messageTypes[packet[0] & 1][chunk.msgid];
253
- chunk.raw = packet.slice(1, chunk.bytes)
254
- if (chunk.msgid == 0 && chunk.raw.byteLength >= 16) {
255
- let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0, 16));
256
- if (uuid !== undefined) {
257
- chunk.extended_msgid = uuid.hash;
258
- chunk.msg = uuid.name;
259
- chunk.raw = chunk.raw.slice(16);
260
- chunk.msgid = uuid.type_id;
261
- }
262
- }
263
-
264
- packet = packet.slice(chunk.bytes)
265
- unpacked.chunks.push(chunk)
266
- }
267
- return unpacked
268
- }
269
-
270
-
271
- /** Send a Control Msg to the server. (used for disconnect)*/
272
- SendControlMsg(msg: number, ExtraMsg: string = "") {
273
- this.lastSendTime = new Date().getTime();
274
- return new Promise((resolve, reject) => {
275
- if (this.socket) {
276
- var latestBuf = Buffer.from([0x10 + (((16 << 4) & 0xf0) | ((this.ack >> 8) & 0xf)), this.ack & 0xff, 0x00, msg])
277
- latestBuf = Buffer.concat([latestBuf, Buffer.from(ExtraMsg), this.TKEN]) // move header (latestBuf), optional extraMsg & TKEN into 1 buffer
278
- this.socket.send(latestBuf, 0, latestBuf.length, this.port, this.host, (err, bytes) => {
279
- resolve(bytes)
280
- })
281
-
282
- }
283
- setTimeout(() => { resolve("failed, rip") }, 2000)
284
- /* after 2 seconds it was probably not able to send,
285
- so when sending a quit message the user doesnt
286
- stay stuck not being able to ctrl + c
287
- */
288
- })
289
- }
290
-
291
-
292
- /** Send a Msg (or Msg[]) to the server.*/
293
- SendMsgEx(Msgs: MsgPacker[] | MsgPacker, flags = 0) {
294
- if (this.State == States.STATE_OFFLINE)
295
- return;
296
- if (!this.socket)
297
- return;
298
- let _Msgs: MsgPacker[];
299
- if (Msgs instanceof Array)
300
- _Msgs = Msgs;
301
- else
302
- _Msgs = [Msgs];
303
- if (this.queueChunkEx.length > 0) {
304
- _Msgs.push(...this.queueChunkEx);
305
- this.queueChunkEx = [];
306
- }
307
- this.lastSendTime = new Date().getTime();
308
- var header: Buffer[] = [];
309
- if (this.clientAck == 0)
310
- this.lastSentMessages = [];
311
- _Msgs.forEach((Msg: MsgPacker, index) => {
312
- header[index] = Buffer.alloc((Msg.flag & 1 ? 3 : 2));
313
- header[index][0] = ((Msg.flag & 3) << 6) | ((Msg.size >> 4) & 0x3f);
314
- header[index][1] = (Msg.size & 0xf);
315
- if (Msg.flag & 1) {
316
- this.clientAck = (this.clientAck + 1) % (1 << 10);
317
- if (this.clientAck == 0)
318
- this.lastSentMessages = [];
319
- header[index][1] |= (this.clientAck >> 2) & 0xf0;
320
- header[index][2] = this.clientAck & 0xff;
321
- header[index][0] = (((Msg.flag | 2)&3)<<6)|((Msg.size>>4)&0x3f); // 2 is resend flag (ugly hack for queue)
322
- if ((Msg.flag & 2) == 0)
323
- this.sentChunkQueue.push(Buffer.concat([header[index], Msg.buffer]));
324
- header[index][0] = (((Msg.flag)&3)<<6)|((Msg.size>>4)&0x3f);
325
- if ((Msg.flag & 2) == 0)
326
- this.lastSentMessages.push({msg: Msg, ack: this.clientAck})
327
- }
328
- })
329
- // let flags = 0;
330
- if (this.requestResend)
331
- flags |= 4;
332
-
333
- var packetHeader = Buffer.from([((flags<<4)&0xf0)|((this.ack>>8)&0xf), this.ack & 0xff, _Msgs.length]);
334
- var chunks = Buffer.from([]);
335
- let skip = false;
336
- _Msgs.forEach((Msg: MsgPacker, index) => {
337
- if (skip)
338
- return;
339
- if (chunks.byteLength < 1300)
340
- chunks = Buffer.concat([chunks, Buffer.from(header[index]), Msg.buffer]);
341
- else {
342
- skip = true;
343
- this.SendMsgEx(_Msgs.slice(index));
344
- }
345
- })
346
- var packet = Buffer.concat([(packetHeader), chunks, this.TKEN]);
347
- if (chunks.length < 0)
348
- return;
349
- this.socket.send(packet, 0, packet.length, this.port, this.host)
350
- }
351
-
352
- /** Queue a chunk (instantly sent if flush flag is set - otherwise it will be sent in the next packet). */
353
- QueueChunkEx(Msg: MsgPacker | MsgPacker[]) {
354
- if (Msg instanceof Array) {
355
- for (let chunk of Msg)
356
- this.QueueChunkEx(chunk);
357
- return;
358
- }
359
- if (this.queueChunkEx.length > 0) {
360
- let total_size = 0;
361
- for (let chunk of this.queueChunkEx)
362
- total_size += chunk.size;
363
- if (total_size + Msg.size + 3 > 1394 - 4)
364
- this.Flush();
365
- }
366
- this.queueChunkEx.push(Msg);
367
- if (Msg.flag & 4)
368
- this.Flush();
369
- }
370
-
371
- /** Send a Raw Buffer (as chunk) to the server. */
372
- SendMsgRaw(chunks: Buffer[]) {
373
- if (this.State == States.STATE_OFFLINE)
374
- return;
375
- if (!this.socket)
376
- return;
377
-
378
- this.lastSendTime = new Date().getTime();
379
-
380
- var packetHeader = Buffer.from([0x0+(((16<<4)&0xf0)|((this.ack>>8)&0xf)), this.ack&0xff, chunks.length]);
381
-
382
- var packet = Buffer.concat([(packetHeader), Buffer.concat(chunks), this.TKEN]);
383
- if (chunks.length < 0)
384
- return;
385
- this.socket.send(packet, 0, packet.length, this.port, this.host)
386
- }
387
-
388
- private MsgToChunk(packet: Buffer) {
389
- var chunk: Chunk = {} as Chunk;
390
- chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
391
- chunk.flags = (packet[0] >> 6) & 3;
392
-
393
- if (chunk.flags & 1) {
394
- chunk.seq = ((packet[1]&0xf0)<<2) | packet[2];
395
- packet = packet.slice(3) // remove flags & size
396
- } else
397
- packet = packet.slice(2)
398
- // chunk.type = packet[0] & 1 ? "sys" : "game"; // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
399
- chunk.sys = Boolean(packet[0] & 1); // & 1 = binary, ****_***1. e.g 0001_0111 sys, 0001_0110 game
400
- chunk.msgid = (packet[0]-(packet[0]&1))/2;
401
- chunk.msg = messageTypes[packet[0]&1][chunk.msgid];
402
- chunk.raw = packet.slice(1, chunk.bytes)
403
- if (chunk.msgid == 0) {
404
- let uuid = this.UUIDManager.LookupUUID(chunk.raw.slice(0,16));
405
- if (uuid !== undefined) {
406
- chunk.extended_msgid = uuid.hash;
407
- chunk.msgid = uuid.type_id;
408
- chunk.msg = uuid.name;
409
- chunk.raw = chunk.raw.slice(16);
410
- }
411
-
412
- }
413
- return chunk;
414
- }
415
- Flush() {
416
- // if (this.queueChunkEx.length == 0)
417
- console.log("flushing");
418
- this.SendMsgEx(this.queueChunkEx);
419
- this.queueChunkEx = [];
420
- this.ack = this.lastCheckedChunkAck;
421
-
422
- }
423
-
424
- /** Connect the client to the server. */
425
- async connect() {
426
- // test via regex whether or not this.host is a domain or an ip
427
- // if not, resolve it
428
- if (!this.host.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)) {
429
- dnsLookup(this.host, 4, (err, address, family) => {
430
- if (err) throw err;
431
- this.host = address;
432
- console.log(err, address, family)
433
- })
434
- }
435
-
436
- this.State = States.STATE_CONNECTING;
437
-
438
- let predTimer = setInterval(() => {
439
- if (this.State == States.STATE_ONLINE) {
440
- if (this.AckGameTick > 0)
441
- this.PredGameTick++;
442
- } else if (this.State == States.STATE_OFFLINE)
443
- clearInterval(predTimer);
444
-
445
- }, 1000/50); // 50 ticks per second
446
-
447
- this.SendControlMsg(1, "TKEN")
448
- let connectInterval = setInterval(() => {
449
- if (this.State == States.STATE_CONNECTING)
450
- this.SendControlMsg(1, "TKEN")
451
- else
452
- clearInterval(connectInterval)
453
- }, 500);
454
- if (!this.options?.lightweight) {
455
- let inputInterval = setInterval(() => {
456
- if (this.State == States.STATE_OFFLINE) {
457
- clearInterval(inputInterval)
458
- // console.log("???");
459
- }
460
- if (this.State != States.STATE_ONLINE)
461
- return;
462
- this.time = new Date().getTime();
463
- this.sendInput();
464
- }, 50)
465
- }
466
-
467
- let resendTimeout = setInterval(() => {
468
- if (this.State != States.STATE_OFFLINE) {
469
- if (((new Date().getTime()) - this.lastSendTime) > 900 && this.sentChunkQueue.length > 0) {
470
- this.SendMsgRaw([this.sentChunkQueue[0]]);
471
- }
472
- } else
473
- clearInterval(resendTimeout)
474
- }, 1000)
475
-
476
- let Timeout = setInterval(() => {
477
- let timeoutTime = this.options?.timeout ? this.options.timeout : 15000;
478
- if ((new Date().getTime() - this.lastRecvTime) > timeoutTime) {
479
- this.State = States.STATE_OFFLINE;
480
- this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - this.lastRecvTime) + "ms)");
481
- clearInterval(Timeout);
482
- }
483
- }, 5000)
484
-
485
- this.time = new Date().getTime() + 2000; // start sending keepalives after 2s
486
-
487
- if (this.socket)
488
- this.socket.on("message", (packet, rinfo) => {
489
- if (this.State == 0 || rinfo.address != this.host || rinfo.port != this.port)
490
- return;
491
- clearInterval(connectInterval)
492
-
493
- if (packet[0] == 0x10) {
494
- if (packet.toString().includes("TKEN") || packet[3] == 0x2) {
495
- clearInterval(connectInterval);
496
- this.TKEN = packet.slice(-4)
497
- this.SendControlMsg(3);
498
- this.State = States.STATE_LOADING; // loading state
499
- this.receivedSnaps = 0;
500
-
501
- var info = new MsgPacker(1, true, 1);
502
- info.AddString(this.options?.NET_VERSION ? this.options.NET_VERSION : "0.6 626fce9a778df4d4");
503
- info.AddString(this.options?.password === undefined ? "" : this.options?.password); // password
504
-
505
- var client_version = new MsgPacker(0, true, 1);
506
- client_version.AddBuffer(Buffer.from("8c00130484613e478787f672b3835bd4", 'hex'));
507
- let randomUuid = randomBytes(16);
508
-
509
- client_version.AddBuffer(randomUuid);
510
- if (this.options?.ddnet_version !== undefined) {
511
- client_version.AddInt(this.options?.ddnet_version.version);
512
- client_version.AddString(`DDNet ${this.options?.ddnet_version.release_version}; https://www.npmjs.com/package/teeworlds/v/${version}`);
513
- } else {
514
- client_version.AddInt(16050);
515
- client_version.AddString(`DDNet 16.5.0; https://www.npmjs.com/package/teeworlds/v/${version}`);
516
- }
517
-
518
- var i_am_npm_package = new MsgPacker(0, true, 1);
519
- i_am_npm_package.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_I_AM_NPM_PACKAGE)!.hash);
520
-
521
- i_am_npm_package.AddString(`https://www.npmjs.com/package/teeworlds/v/${version}`);
522
-
523
-
524
- this.SendMsgEx([i_am_npm_package, client_version, info])
525
- } else if (packet[3] == 0x4) {
526
- // disconnected
527
- this.State = States.STATE_OFFLINE;
528
- let reason: string = (unpackString(packet.slice(4)).result);
529
- this.emit("disconnect", reason);
530
- }
531
- if (packet[3] !== 0x0) { // keepalive
532
- this.lastRecvTime = new Date().getTime();
533
- }
534
- } else {
535
- this.lastRecvTime = new Date().getTime();
536
- }
537
-
538
- var unpacked: _Packet = this.Unpack(packet);
539
- // unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
540
- this.sentChunkQueue.forEach((buff, i) => {
541
- let chunkFlags = (buff[0] >> 6) & 3;
542
- if (chunkFlags & 1) {
543
- let chunk = this.MsgToChunk(buff);
544
- if (chunk.seq && chunk.seq >= this.ack)
545
- this.sentChunkQueue.splice(i, 1);
546
- }
547
- })
548
- unpacked.chunks.forEach(chunk => {
549
- if (!(((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true))
550
- return; // filter out already received chunks
551
- if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
552
- this.lastCheckedChunkAck = chunk.seq!;
553
- if (chunk.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
554
- this.ack = chunk.seq!;
555
-
556
- this.requestResend = false;
557
- }
558
- else { //IsSeqInBackroom (old packet that we already got)
559
- let Bottom = (this.ack - (1<<10)/2);
560
-
561
- if(Bottom < 0) {
562
- if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10)))) {}
563
- else
564
- this.requestResend = true;
565
- } else {
566
- if(chunk.seq! <= this.ack && chunk.seq! >= Bottom) {}
567
- else
568
- this.requestResend = true;
569
- }
570
- }
571
- }
572
-
573
- if (chunk.sys) {
574
- // system messages
575
- if (chunk.msgid == NETMSG.System.NETMSG_PING) { // ping
576
- let packer = new MsgPacker(NETMSG.System.NETMSG_PING_REPLY, true, 0);
577
-
578
- this.SendMsgEx(packer); // send ping reply
579
- } else if (chunk.msgid == NETMSG.System.NETMSG_PING_REPLY) { // Ping reply
580
- this.game._ping_resolve(new Date().getTime())
581
- } else if (this.rcon._checkChunks(chunk)) {}
582
- // packets neccessary for connection
583
- // https://ddnet.org/docs/libtw2/connection/
584
-
585
- if (chunk.msgid == NETMSG.System.NETMSG_MAP_CHANGE) {
586
- this.Flush();
587
- var Msg = new MsgPacker(NETMSG.System.NETMSG_READY, true, 1); /* ready */
588
- this.SendMsgEx(Msg);
589
- } else if (chunk.msgid == NETMSG.System.NETMSG_CON_READY) {
590
- var info = new MsgPacker(NETMSG.Game.CL_STARTINFO, false, 1);
591
- if (this.options?.identity) {
592
- info.AddString(this.options.identity.name);
593
- info.AddString(this.options.identity.clan);
594
- info.AddInt(this.options.identity.country);
595
- info.AddString(this.options.identity.skin);
596
- info.AddInt(this.options.identity.use_custom_color);
597
- info.AddInt(this.options.identity.color_body);
598
- info.AddInt(this.options.identity.color_feet);
599
- } else {
600
- info.AddString(this.name); /* name */
601
- info.AddString(""); /* clan */
602
- info.AddInt(-1); /* country */
603
- info.AddString("greyfox"); /* skin */
604
- info.AddInt(1); /* use custom color */
605
- info.AddInt(10346103); /* color body */
606
- info.AddInt(65535); /* color feet */
607
-
608
- }
609
- var crashmeplx = new MsgPacker(17, true, 1); // rcon
610
- crashmeplx.AddString("crashmeplx"); // 64 player support message
611
- this.SendMsgEx([info, crashmeplx]);
612
- }
613
-
614
- if (chunk.msgid >= NETMSG.System.NETMSG_SNAP && chunk.msgid <= NETMSG.System.NETMSG_SNAPSINGLE) {
615
- this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
616
- if (this.receivedSnaps == 2) {
617
- if (this.State != States.STATE_ONLINE)
618
- this.emit('connected')
619
- this.State = States.STATE_ONLINE;
620
- }
621
- if (Math.abs(this.PredGameTick - this.AckGameTick) > 10)
622
- this.PredGameTick = this.AckGameTick + 1;
623
-
624
- // snapChunks.forEach(chunk => {
625
- let unpacker = new MsgUnpacker(chunk.raw);
626
-
627
- let NumParts = 1;
628
- let Part = 0;
629
- let GameTick = unpacker.unpackInt();
630
- let DeltaTick = GameTick - unpacker.unpackInt();
631
- let PartSize = 0;
632
- let Crc = 0;
633
- let CompleteSize = 0;
634
-
635
- if (chunk.msgid == NETMSG.System.NETMSG_SNAP) {
636
- NumParts = unpacker.unpackInt();
637
- Part = unpacker.unpackInt();
638
- }
639
-
640
- if (chunk.msgid != NETMSG.System.NETMSG_SNAPEMPTY) {
641
- Crc = unpacker.unpackInt();
642
- PartSize = unpacker.unpackInt();
643
- }
644
-
645
- if (NumParts < 1 || NumParts > 64 || Part < 0 || Part >= NumParts || PartSize < 0 || PartSize > 900)
646
- return;
647
-
648
- if (GameTick >= this.currentSnapshotGameTick) {
649
- if (GameTick != this.currentSnapshotGameTick) {
650
- this.snaps = [];
651
- this.SnapshotParts = 0;
652
- this.currentSnapshotGameTick = GameTick;
653
- }
654
-
655
- // chunk.raw = Buffer.from(unpacker.remaining);
656
- this.snaps[Part] = unpacker.remaining;
657
-
658
- this.SnapshotParts |= 1 << Part;
659
-
660
- if (this.SnapshotParts == ((1 << NumParts) - 1)) {
661
- let mergedSnaps = Buffer.concat(this.snaps);
662
- this.SnapshotParts = 0;
663
-
664
- let snapUnpacked = this.SnapUnpacker.unpackSnapshot(mergedSnaps, DeltaTick, GameTick, Crc);
665
-
666
- this.emit("snapshot", snapUnpacked.items);
667
- this.AckGameTick = snapUnpacked.recvTick;
668
- if (Math.abs(this.PredGameTick - this.AckGameTick) > 10) {
669
- this.PredGameTick = this.AckGameTick + 1;
670
- this.sendInput();
671
- }
672
- }
673
-
674
-
675
- }
676
-
677
-
678
- // })
679
-
680
-
681
- }
682
-
683
- if (chunk.msgid >= NETMSG.System.NETMSG_WHATIS && chunk.msgid <= NETMSG.System.NETMSG_CHECKSUM_ERROR) {
684
- if (chunk.msgid == NETMSG.System.NETMSG_WHATIS) {
685
- let Uuid = chunk.raw.slice(0, 16);
686
-
687
- let uuid = this.UUIDManager.LookupUUID(Uuid);
688
- let packer = new MsgPacker(0, true, 1);
689
- if (uuid !== undefined) {
690
- // IT_IS msg
691
- packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_ITIS)!.hash);
692
-
693
- packer.AddBuffer(Uuid);
694
- packer.AddString(uuid.name);
695
- } else {
696
- // dont_know msg
697
- packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_IDONTKNOW)!.hash);
698
-
699
- packer.AddBuffer(Uuid);
700
- }
701
- this.QueueChunkEx(packer)
702
- }
703
-
704
- if (chunk.msgid == NETMSG.System.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
705
- let unpacker = new MsgUnpacker(chunk.raw);
706
-
707
- let map_name = unpacker.unpackString();
708
- let map_sha256: Buffer = Buffer.alloc(32);
709
- if (unpacker.remaining.length >= 32)
710
- map_sha256 = unpacker.unpackRaw(32);
711
- let map_crc = unpacker.unpackInt();
712
- let map_size = unpacker.unpackInt();
713
-
714
- let map_url = "";
715
- if (unpacker.remaining.length)
716
- map_url = unpacker.unpackString();
717
-
718
- this.emit("map_details", {map_name, map_sha256, map_crc, map_size, map_url})
719
- // unpacker.unpack
720
-
721
- } else if (chunk.msgid == NETMSG.System.NETMSG_CAPABILITIES) {
722
- let unpacker = new MsgUnpacker(chunk.raw);
723
- let Version = unpacker.unpackInt();
724
- let Flags = unpacker.unpackInt();
725
- if (Version <= 0)
726
- return;
727
- let DDNet = false;
728
- if (Version >= 1) {
729
- DDNet = Boolean(Flags & 1);
730
-
731
- }
732
- let ChatTimeoutCode = DDNet;
733
- let AnyPlayerFlag = DDNet;
734
- let PingEx = false;
735
- let AllowDummy = true;
736
- let SyncWeaponInput = false;
737
- if(Version >= 1)
738
- {
739
- ChatTimeoutCode = Boolean(Flags & 2);
740
- }
741
- if(Version >= 2)
742
- {
743
- AnyPlayerFlag = Boolean(Flags & 4);
744
- }
745
- if(Version >= 3)
746
- {
747
- PingEx = Boolean(Flags & 8);
748
- }
749
- if(Version >= 4)
750
- {
751
- AllowDummy = Boolean(Flags & 16);
752
- }
753
- if(Version >= 5)
754
- {
755
- SyncWeaponInput = Boolean(Flags & 32);
756
- }
757
- this.emit("capabilities", {ChatTimeoutCode, AnyPlayerFlag, PingEx, AllowDummy, SyncWeaponInput});
758
- // https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
759
- } else if (chunk.msgid == NETMSG.System.NETMSG_PINGEX) {
760
- let packer = new MsgPacker(0, true, 2);
761
- packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_PONGEX)!.hash);
762
-
763
- this.SendMsgEx(packer, 2);
764
- }
765
-
766
- }
767
-
768
- } else {
769
- // game messages
770
-
771
- // vote list:
772
- if (chunk.msgid == NETMSG.Game.SV_VOTECLEAROPTIONS) {
773
- this.VoteList = [];
774
- } else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONLISTADD) {
775
- let unpacker = new MsgUnpacker(chunk.raw)
776
- let NumOptions = unpacker.unpackInt()
777
- let list: string[] = [];
778
- for (let i = 0; i < 15; i++) {
779
- list.push(unpacker.unpackString());
780
- }
781
- list = list.slice(0, NumOptions);
782
-
783
- this.VoteList.push(...list);
784
- } else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONADD) {
785
- let unpacker = new MsgUnpacker(chunk.raw)
786
-
787
- this.VoteList.push(unpacker.unpackString());
788
- } else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONREMOVE) {
789
- let unpacker = new MsgUnpacker(chunk.raw)
790
-
791
- let index = this.VoteList.indexOf(unpacker.unpackString());
792
-
793
- if (index > -1)
794
- this.VoteList = this.VoteList.splice(index, 1);
795
-
796
- }
797
-
798
- // events
799
- if (chunk.msgid == NETMSG.Game.SV_EMOTICON) {
800
- let unpacker = new MsgUnpacker(chunk.raw);
801
- let unpacked = {
802
- client_id: unpacker.unpackInt(),
803
- emoticon: unpacker.unpackInt()
804
- } as iEmoticon;
805
-
806
- if (unpacked.client_id != -1) {
807
- unpacked.author = {
808
- ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
809
- PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
810
- }
811
- }
812
- this.emit("emote", unpacked)
813
-
814
-
815
-
816
- } else if (chunk.msgid == NETMSG.Game.SV_BROADCAST) {
817
- let unpacker = new MsgUnpacker(chunk.raw);
818
-
819
- this.emit("broadcast", unpacker.unpackString());
820
- } if (chunk.msgid == NETMSG.Game.SV_CHAT) {
821
- let unpacker = new MsgUnpacker(chunk.raw);
822
- let unpacked: iMessage = {
823
- team: unpacker.unpackInt(),
824
- client_id: unpacker.unpackInt(),
825
- message: unpacker.unpackString()
826
- } as iMessage;
827
-
828
- if (unpacked.client_id != -1) {
829
- unpacked.author = {
830
- ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.client_id),
831
- PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.client_id)
832
- }
833
- }
834
- this.emit("message", unpacked)
835
- } else if (chunk.msgid == NETMSG.Game.SV_KILLMSG) {
836
- let unpacked: iKillMsg = {} as iKillMsg;
837
- let unpacker = new MsgUnpacker(chunk.raw);
838
- unpacked.killer_id = unpacker.unpackInt();
839
- unpacked.victim_id = unpacker.unpackInt();
840
- unpacked.weapon = unpacker.unpackInt();
841
- unpacked.special_mode = unpacker.unpackInt();
842
- if (unpacked.victim_id != -1 && unpacked.victim_id < 64) {
843
- unpacked.victim = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.victim_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.victim_id) }
844
-
845
- }
846
- if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
847
- unpacked.killer = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.killer_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.killer_id) }
848
- this.emit("kill", unpacked)
849
- } else if (chunk.msgid == NETMSG.Game.SV_MOTD) {
850
- let unpacker = new MsgUnpacker(chunk.raw);
851
- let message = unpacker.unpackString();
852
- this.emit("motd", message);
853
- }
854
-
855
- // packets neccessary for connection
856
- // https://ddnet.org/docs/libtw2/connection/
857
- if (chunk.msgid == NETMSG.Game.SV_READYTOENTER) {
858
- var Msg = new MsgPacker(NETMSG.System.NETMSG_ENTERGAME, true, 1); /* entergame */
859
- this.SendMsgEx(Msg);
860
- this.OnEnterGame();
861
- }
862
- }
863
- })
864
-
865
- if (this.State == States.STATE_ONLINE) {
866
- if (new Date().getTime() - this.time >= 500) {
867
- this.Flush();
868
- }
869
- if (new Date().getTime() - this.time >= 1000) {
870
- this.time = new Date().getTime();
871
- this.SendControlMsg(0);
872
- }
873
-
874
- }
875
- })
876
- }
877
- /** Sending the input. (automatically done unless options.lightweight is on) */
878
- sendInput(input = this.movement.input) {
879
- if (this.State != States.STATE_ONLINE)
880
- return;
881
-
882
- let inputMsg = new MsgPacker(16, true, 0);
883
- inputMsg.AddInt(this.AckGameTick);
884
- inputMsg.AddInt(this.PredGameTick);
885
- inputMsg.AddInt(40);
886
-
887
- inputMsg.AddInt(input.m_Direction)
888
- inputMsg.AddInt(input.m_TargetX)
889
- inputMsg.AddInt(input.m_TargetY)
890
- inputMsg.AddInt(input.m_Jump)
891
- inputMsg.AddInt(input.m_Fire)
892
- inputMsg.AddInt(input.m_Hook)
893
- inputMsg.AddInt(input.m_PlayerFlags)
894
- inputMsg.AddInt(input.m_WantedWeapon)
895
- inputMsg.AddInt(input.m_NextWeapon)
896
- inputMsg.AddInt(input.m_PrevWeapon)
897
-
898
- this.SendMsgEx(inputMsg);
899
- }
900
- /** returns the movement object of the client */
901
- get input() {
902
- return this.movement.input;
903
- }
904
-
905
-
906
- /** Disconnect the client. */
907
- Disconnect() {
908
- return new Promise((resolve) => {
909
- this.SendControlMsg(4).then(() => {
910
- resolve(true);
911
- if (this.socket)
912
- this.socket.close();
913
- this.socket = undefined
914
- this.State = States.STATE_OFFLINE;
915
- })
916
- })
917
- }
918
-
919
- /** Get all available vote options (for example for map voting) */
920
- get VoteOptionList(): string[] {
921
- return this.VoteList;
922
- }
923
- get rawSnapUnpacker(): Snapshot {
924
- return this.SnapUnpacker;
925
- }
926
- }