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