teeworlds 2.5.2 → 2.5.4
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/lib/client.js +492 -469
- package/lib/client.ts +164 -241
- package/lib/components/game.js +9 -46
- package/lib/components/game.ts +12 -13
- package/lib/components/rcon.js +122 -0
- package/lib/components/rcon.ts +114 -0
- package/lib/components/snapshot.js +24 -48
- package/lib/components/snapshot.ts +68 -100
- package/lib/enums_types/protocol.js +13 -0
- package/lib/enums_types/protocol.ts +137 -0
- package/lib/enums_types/types.d.ts +236 -0
- package/lib/snapshot.ts +36 -38
- package/package.json +1 -1
- package/lib/snapshots.d.ts +0 -233
package/lib/client.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { randomBytes } from "crypto";
|
|
2
1
|
|
|
2
|
+
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
4
|
+
import { lookup as dnsLookup } from 'dns';
|
|
3
5
|
import net from 'dgram';
|
|
4
6
|
import { EventEmitter } from 'stream';
|
|
5
7
|
|
|
@@ -7,147 +9,20 @@ import { unpackString, MsgUnpacker } from "./MsgUnpacker";
|
|
|
7
9
|
let { version } = require('../package.json');
|
|
8
10
|
|
|
9
11
|
import Movement from './components/movement';
|
|
10
|
-
import {
|
|
12
|
+
import { _Packet, Chunk, DeltaItem, SnapshotItemTypes } from './enums_types/types';
|
|
11
13
|
|
|
12
14
|
import { MsgPacker } from './MsgPacker';
|
|
13
|
-
import {
|
|
15
|
+
import { Snapshot } from './snapshot';
|
|
14
16
|
import { Huffman } from "./huffman";
|
|
15
17
|
import { Game } from "./components/game";
|
|
16
18
|
import { SnapshotWrapper } from "./components/snapshot";
|
|
17
19
|
|
|
18
20
|
import { UUIDManager } from "./UUIDManager";
|
|
21
|
+
import { NETMSG, States } from "./enums_types/protocol";
|
|
22
|
+
import { Rcon } from "./components/rcon";
|
|
19
23
|
|
|
20
24
|
const huff = new Huffman();
|
|
21
25
|
|
|
22
|
-
enum States {
|
|
23
|
-
STATE_OFFLINE = 0,
|
|
24
|
-
STATE_CONNECTING,
|
|
25
|
-
STATE_LOADING,
|
|
26
|
-
STATE_ONLINE,
|
|
27
|
-
STATE_DEMOPLAYBACK,
|
|
28
|
-
STATE_QUITTING,
|
|
29
|
-
STATE_RESTARTING
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
enum NETMSG_Game {
|
|
34
|
-
EX,
|
|
35
|
-
SV_MOTD,
|
|
36
|
-
SV_BROADCAST,
|
|
37
|
-
SV_CHAT,
|
|
38
|
-
SV_KILLMSG,
|
|
39
|
-
SV_SOUNDGLOBAL,
|
|
40
|
-
SV_TUNEPARAMS,
|
|
41
|
-
SV_EXTRAPROJECTILE,
|
|
42
|
-
SV_READYTOENTER,
|
|
43
|
-
SV_WEAPONPICKUP,
|
|
44
|
-
SV_EMOTICON,
|
|
45
|
-
SV_VOTECLEAROPTIONS,
|
|
46
|
-
SV_VOTEOPTIONLISTADD,
|
|
47
|
-
SV_VOTEOPTIONADD,
|
|
48
|
-
SV_VOTEOPTIONREMOVE,
|
|
49
|
-
SV_VOTESET,
|
|
50
|
-
SV_VOTESTATUS,
|
|
51
|
-
CL_SAY,
|
|
52
|
-
CL_SETTEAM,
|
|
53
|
-
CL_SETSPECTATORMODE,
|
|
54
|
-
CL_STARTINFO,
|
|
55
|
-
CL_CHANGEINFO,
|
|
56
|
-
CL_KILL,
|
|
57
|
-
CL_EMOTICON,
|
|
58
|
-
CL_VOTE,
|
|
59
|
-
CL_CALLVOTE,
|
|
60
|
-
CL_ISDDNETLEGACY,
|
|
61
|
-
SV_DDRACETIMELEGACY,
|
|
62
|
-
SV_RECORDLEGACY,
|
|
63
|
-
UNUSED,
|
|
64
|
-
SV_TEAMSSTATELEGACY,
|
|
65
|
-
CL_SHOWOTHERSLEGACY,
|
|
66
|
-
NUM
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
enum NETMSG_Sys {
|
|
70
|
-
NETMSG_EX = 0,
|
|
71
|
-
|
|
72
|
-
// the first thing sent by the client
|
|
73
|
-
// contains the version info for the client
|
|
74
|
-
NETMSG_INFO = 1,
|
|
75
|
-
|
|
76
|
-
// sent by server
|
|
77
|
-
NETMSG_MAP_CHANGE, // sent when client should switch map
|
|
78
|
-
NETMSG_MAP_DATA, // map transfer, contains a chunk of the map file
|
|
79
|
-
NETMSG_CON_READY, // connection is ready, client should send start info
|
|
80
|
-
NETMSG_SNAP, // normal snapshot, multiple parts
|
|
81
|
-
NETMSG_SNAPEMPTY, // empty snapshot
|
|
82
|
-
NETMSG_SNAPSINGLE, // ?
|
|
83
|
-
NETMSG_SNAPSMALL, //
|
|
84
|
-
NETMSG_INPUTTIMING, // reports how off the input was
|
|
85
|
-
NETMSG_RCON_AUTH_STATUS, // result of the authentication
|
|
86
|
-
NETMSG_RCON_LINE, // line that should be printed to the remote console
|
|
87
|
-
|
|
88
|
-
NETMSG_AUTH_CHALLANGE, //
|
|
89
|
-
NETMSG_AUTH_RESULT, //
|
|
90
|
-
|
|
91
|
-
// sent by client
|
|
92
|
-
NETMSG_READY, //
|
|
93
|
-
NETMSG_ENTERGAME,
|
|
94
|
-
NETMSG_INPUT, // contains the inputdata from the client
|
|
95
|
-
NETMSG_RCON_CMD, //
|
|
96
|
-
NETMSG_RCON_AUTH, //
|
|
97
|
-
NETMSG_REQUEST_MAP_DATA, //
|
|
98
|
-
|
|
99
|
-
NETMSG_AUTH_START, //
|
|
100
|
-
NETMSG_AUTH_RESPONSE, //
|
|
101
|
-
|
|
102
|
-
// sent by both
|
|
103
|
-
NETMSG_PING,
|
|
104
|
-
NETMSG_PING_REPLY,
|
|
105
|
-
NETMSG_ERROR,
|
|
106
|
-
|
|
107
|
-
// sent by server (todo: move it up)
|
|
108
|
-
NETMSG_RCON_CMD_ADD,
|
|
109
|
-
NETMSG_RCON_CMD_REM,
|
|
110
|
-
|
|
111
|
-
NUM_NETMSGS,
|
|
112
|
-
|
|
113
|
-
NETMSG_WHATIS = 65536,
|
|
114
|
-
NETMSG_ITIS,
|
|
115
|
-
NETMSG_IDONTKNOW,
|
|
116
|
-
|
|
117
|
-
NETMSG_RCONTYPE,
|
|
118
|
-
NETMSG_MAP_DETAILS,
|
|
119
|
-
NETMSG_CAPABILITIES,
|
|
120
|
-
NETMSG_CLIENTVER,
|
|
121
|
-
NETMSG_PINGEX,
|
|
122
|
-
NETMSG_PONGEX,
|
|
123
|
-
NETMSG_CHECKSUM_REQUEST,
|
|
124
|
-
NETMSG_CHECKSUM_RESPONSE,
|
|
125
|
-
NETMSG_CHECKSUM_ERROR,
|
|
126
|
-
|
|
127
|
-
NETMSG_REDIRECT,
|
|
128
|
-
|
|
129
|
-
NETMSG_I_AM_NPM_PACKAGE
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
interface chunk {
|
|
134
|
-
bytes: number,
|
|
135
|
-
flags: number,
|
|
136
|
-
sequence?: number,
|
|
137
|
-
seq?: number,
|
|
138
|
-
// type: 'sys' | 'game',
|
|
139
|
-
sys: Boolean,
|
|
140
|
-
msgid: number,
|
|
141
|
-
msg: string,
|
|
142
|
-
raw: Buffer,
|
|
143
|
-
extended_msgid?: Buffer;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
interface _packet {
|
|
147
|
-
twprotocol: { flags: number, ack: number, chunkAmount: number, size: number },
|
|
148
|
-
chunks: chunk[]
|
|
149
|
-
}
|
|
150
|
-
|
|
151
26
|
var messageTypes = [
|
|
152
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"],
|
|
153
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"]
|
|
@@ -157,27 +32,27 @@ var messageTypes = [
|
|
|
157
32
|
declare interface iMessage {
|
|
158
33
|
team: number,
|
|
159
34
|
client_id: number,
|
|
160
|
-
author?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
35
|
+
author?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
|
|
161
36
|
message: string
|
|
162
37
|
}
|
|
163
38
|
|
|
164
39
|
declare interface iEmoticon {
|
|
165
40
|
client_id: number,
|
|
166
41
|
emoticon: number,
|
|
167
|
-
author?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo }
|
|
42
|
+
author?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo }
|
|
168
43
|
}
|
|
169
44
|
|
|
170
45
|
declare interface iKillMsg {
|
|
171
46
|
killer_id: number,
|
|
172
|
-
killer?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
47
|
+
killer?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
|
|
173
48
|
victim_id: number,
|
|
174
|
-
victim?: { ClientInfo?: ClientInfo, PlayerInfo?: PlayerInfo },
|
|
49
|
+
victim?: { ClientInfo?: SnapshotItemTypes.ClientInfo, PlayerInfo?: SnapshotItemTypes.PlayerInfo },
|
|
175
50
|
weapon: number,
|
|
176
51
|
special_mode: number
|
|
177
52
|
}
|
|
178
53
|
|
|
179
54
|
declare interface iOptions {
|
|
180
|
-
identity?: ClientInfo,
|
|
55
|
+
identity?: SnapshotItemTypes.ClientInfo,
|
|
181
56
|
password?: string,
|
|
182
57
|
ddnet_version?: {version: number, release_version: string},
|
|
183
58
|
timeout?: number, // in ms
|
|
@@ -185,32 +60,37 @@ declare interface iOptions {
|
|
|
185
60
|
lightweight?: boolean // experimental, only sends keepalive's (sendinput has to be called manually)
|
|
186
61
|
}
|
|
187
62
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
on(event: 'map_details', listener: (message: {map_name: string, map_sha256: Buffer, map_crc: number, map_size: number, map_url: string}) => void): this;
|
|
201
|
-
on(event: 'capabilities', listener: (message: {ChatTimeoutCode: boolean, AnyPlayerFlag: boolean, PingEx: boolean, AllowDummy: boolean, SyncWeaponInput: boolean}) => void): this;
|
|
202
|
-
|
|
203
|
-
on(event: 'snapshot', listener: (items: Item[]) => void): this;
|
|
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;
|
|
204
75
|
}
|
|
205
76
|
|
|
206
77
|
export class Client extends EventEmitter {
|
|
207
|
-
|
|
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;
|
|
208
87
|
private host: string;
|
|
209
88
|
private port: number;
|
|
210
89
|
private name: string;
|
|
211
90
|
private State: number; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
212
91
|
private ack: number;
|
|
213
92
|
private clientAck: number;
|
|
93
|
+
private lastCheckedChunkAck: number;
|
|
214
94
|
private receivedSnaps: number; /* wait for 2 ss before seeing self as connected */
|
|
215
95
|
private socket: net.Socket | undefined;
|
|
216
96
|
private TKEN: Buffer;
|
|
@@ -258,7 +138,7 @@ export class Client extends EventEmitter {
|
|
|
258
138
|
this.currentSnapshotGameTick = 0;
|
|
259
139
|
|
|
260
140
|
this.SnapshotParts = 0;
|
|
261
|
-
|
|
141
|
+
this.rcon = new Rcon(this);
|
|
262
142
|
this.SnapUnpacker = new Snapshot(this);
|
|
263
143
|
// this.eSnapHolder = [];
|
|
264
144
|
this.requestResend = false;
|
|
@@ -277,6 +157,7 @@ export class Client extends EventEmitter {
|
|
|
277
157
|
this.State = States.STATE_OFFLINE; // 0 = offline; 1 = STATE_CONNECTING = 1, STATE_LOADING = 2, STATE_ONLINE = 3
|
|
278
158
|
this.ack = 0; // ack of messages the client has received
|
|
279
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
|
|
280
161
|
this.receivedSnaps = 0; /* wait for 2 snaps before seeing self as connected */
|
|
281
162
|
this.socket = net.createSocket("udp4");
|
|
282
163
|
this.socket.bind();
|
|
@@ -295,25 +176,35 @@ export class Client extends EventEmitter {
|
|
|
295
176
|
|
|
296
177
|
this.UUIDManager = new UUIDManager();
|
|
297
178
|
|
|
298
|
-
this.UUIDManager.RegisterName("what-is@ddnet.tw",
|
|
299
|
-
this.UUIDManager.RegisterName("it-is@ddnet.tw",
|
|
300
|
-
this.UUIDManager.RegisterName("i-dont-know@ddnet.tw",
|
|
301
|
-
|
|
302
|
-
this.UUIDManager.RegisterName("rcon-type@ddnet.tw",
|
|
303
|
-
this.UUIDManager.RegisterName("map-details@ddnet.tw",
|
|
304
|
-
this.UUIDManager.RegisterName("capabilities@ddnet.tw",
|
|
305
|
-
this.UUIDManager.RegisterName("clientver@ddnet.tw",
|
|
306
|
-
this.UUIDManager.RegisterName("ping@ddnet.tw",
|
|
307
|
-
this.UUIDManager.RegisterName("pong@ddnet.tw",
|
|
308
|
-
this.UUIDManager.RegisterName("checksum-request@ddnet.tw",
|
|
309
|
-
this.UUIDManager.RegisterName("checksum-response@ddnet.tw",
|
|
310
|
-
this.UUIDManager.RegisterName("checksum-error@ddnet.tw",
|
|
311
|
-
this.UUIDManager.RegisterName("redirect@ddnet.org",
|
|
312
|
-
|
|
313
|
-
this.UUIDManager.RegisterName("i-am-npm-package@swarfey.gitlab.io",
|
|
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);
|
|
314
195
|
|
|
315
196
|
}
|
|
316
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
|
+
}
|
|
317
208
|
private ResendAfter(lastAck: number) {
|
|
318
209
|
this.clientAck = lastAck;
|
|
319
210
|
|
|
@@ -327,8 +218,8 @@ export class Client extends EventEmitter {
|
|
|
327
218
|
this.SendMsgEx(toResend);
|
|
328
219
|
}
|
|
329
220
|
|
|
330
|
-
private Unpack(packet: Buffer):
|
|
331
|
-
var unpacked:
|
|
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: [] }
|
|
332
223
|
|
|
333
224
|
|
|
334
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)
|
|
@@ -346,10 +237,9 @@ export class Client extends EventEmitter {
|
|
|
346
237
|
|
|
347
238
|
|
|
348
239
|
for (let i = 0; i < unpacked.twprotocol.chunkAmount; i++) {
|
|
349
|
-
var chunk:
|
|
240
|
+
var chunk: Chunk = {} as Chunk;
|
|
350
241
|
chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
|
|
351
242
|
chunk.flags = (packet[0] >> 6) & 3;
|
|
352
|
-
chunk.sequence = -1;
|
|
353
243
|
|
|
354
244
|
if (chunk.flags & 1) {
|
|
355
245
|
chunk.seq = ((packet[1] & 0xf0) << 2) | packet[2];
|
|
@@ -459,9 +349,23 @@ export class Client extends EventEmitter {
|
|
|
459
349
|
this.socket.send(packet, 0, packet.length, this.port, this.host)
|
|
460
350
|
}
|
|
461
351
|
|
|
462
|
-
/** Queue a chunk (
|
|
463
|
-
QueueChunkEx(Msg: MsgPacker) {
|
|
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
|
+
}
|
|
464
366
|
this.queueChunkEx.push(Msg);
|
|
367
|
+
if (Msg.flag & 4)
|
|
368
|
+
this.Flush();
|
|
465
369
|
}
|
|
466
370
|
|
|
467
371
|
/** Send a Raw Buffer (as chunk) to the server. */
|
|
@@ -482,10 +386,9 @@ export class Client extends EventEmitter {
|
|
|
482
386
|
}
|
|
483
387
|
|
|
484
388
|
private MsgToChunk(packet: Buffer) {
|
|
485
|
-
var chunk:
|
|
389
|
+
var chunk: Chunk = {} as Chunk;
|
|
486
390
|
chunk.bytes = ((packet[0] & 0x3f) << 4) | (packet[1] & ((1 << 4) - 1));
|
|
487
391
|
chunk.flags = (packet[0] >> 6) & 3;
|
|
488
|
-
chunk.sequence = -1;
|
|
489
392
|
|
|
490
393
|
if (chunk.flags & 1) {
|
|
491
394
|
chunk.seq = ((packet[1]&0xf0)<<2) | packet[2];
|
|
@@ -509,10 +412,27 @@ export class Client extends EventEmitter {
|
|
|
509
412
|
}
|
|
510
413
|
return chunk;
|
|
511
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;
|
|
512
421
|
|
|
422
|
+
}
|
|
513
423
|
|
|
514
424
|
/** Connect the client to the server. */
|
|
515
|
-
connect() {
|
|
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
|
+
|
|
516
436
|
this.State = States.STATE_CONNECTING;
|
|
517
437
|
|
|
518
438
|
let predTimer = setInterval(() => {
|
|
@@ -533,8 +453,10 @@ export class Client extends EventEmitter {
|
|
|
533
453
|
}, 500);
|
|
534
454
|
if (!this.options?.lightweight) {
|
|
535
455
|
let inputInterval = setInterval(() => {
|
|
536
|
-
if (this.State == States.STATE_OFFLINE)
|
|
456
|
+
if (this.State == States.STATE_OFFLINE) {
|
|
537
457
|
clearInterval(inputInterval)
|
|
458
|
+
// console.log("???");
|
|
459
|
+
}
|
|
538
460
|
if (this.State != States.STATE_ONLINE)
|
|
539
461
|
return;
|
|
540
462
|
this.time = new Date().getTime();
|
|
@@ -555,7 +477,6 @@ export class Client extends EventEmitter {
|
|
|
555
477
|
let timeoutTime = this.options?.timeout ? this.options.timeout : 15000;
|
|
556
478
|
if ((new Date().getTime() - this.lastRecvTime) > timeoutTime) {
|
|
557
479
|
this.State = States.STATE_OFFLINE;
|
|
558
|
-
this.emit("timeout");
|
|
559
480
|
this.emit("disconnect", "Timed Out. (no packets received for " + (new Date().getTime() - this.lastRecvTime) + "ms)");
|
|
560
481
|
clearInterval(Timeout);
|
|
561
482
|
}
|
|
@@ -595,7 +516,7 @@ export class Client extends EventEmitter {
|
|
|
595
516
|
}
|
|
596
517
|
|
|
597
518
|
var i_am_npm_package = new MsgPacker(0, true, 1);
|
|
598
|
-
i_am_npm_package.AddBuffer(this.UUIDManager.LookupType(
|
|
519
|
+
i_am_npm_package.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_I_AM_NPM_PACKAGE)!.hash);
|
|
599
520
|
|
|
600
521
|
i_am_npm_package.AddString(`https://www.npmjs.com/package/teeworlds/v/${version}`);
|
|
601
522
|
|
|
@@ -614,11 +535,21 @@ export class Client extends EventEmitter {
|
|
|
614
535
|
this.lastRecvTime = new Date().getTime();
|
|
615
536
|
}
|
|
616
537
|
|
|
617
|
-
var unpacked:
|
|
618
|
-
unpacked.chunks = unpacked.chunks.filter(chunk => ((chunk.flags & 2) && (chunk.flags & 1)) ? chunk.seq! > this.ack : true); // filter out already received chunks
|
|
619
|
-
|
|
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
|
+
})
|
|
620
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
|
|
621
551
|
if (chunk.flags & 1 && (chunk.flags !== 15)) { // vital and not connless
|
|
552
|
+
this.lastCheckedChunkAck = chunk.seq!;
|
|
622
553
|
if (chunk.seq === (this.ack+1)%(1<<10)) { // https://github.com/nobody-mb/twchatonly/blob/master/chatonly.cpp#L237
|
|
623
554
|
this.ack = chunk.seq!;
|
|
624
555
|
|
|
@@ -628,47 +559,35 @@ export class Client extends EventEmitter {
|
|
|
628
559
|
let Bottom = (this.ack - (1<<10)/2);
|
|
629
560
|
|
|
630
561
|
if(Bottom < 0) {
|
|
631
|
-
if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10))))
|
|
632
|
-
|
|
562
|
+
if((chunk.seq! <= this.ack) || (chunk.seq! >= (Bottom + (1<<10)))) {}
|
|
563
|
+
else
|
|
564
|
+
this.requestResend = true;
|
|
633
565
|
} else {
|
|
634
|
-
if(chunk.seq! <= this.ack && chunk.seq! >= Bottom)
|
|
635
|
-
|
|
566
|
+
if(chunk.seq! <= this.ack && chunk.seq! >= Bottom) {}
|
|
567
|
+
else
|
|
568
|
+
this.requestResend = true;
|
|
636
569
|
}
|
|
637
|
-
this.requestResend = true;
|
|
638
|
-
|
|
639
570
|
}
|
|
640
571
|
}
|
|
641
572
|
|
|
642
|
-
})
|
|
643
|
-
this.sentChunkQueue.forEach((buff, i) => {
|
|
644
|
-
let chunkFlags = (buff[0] >> 6) & 3;
|
|
645
|
-
if (chunkFlags & 1) {
|
|
646
|
-
let chunk = this.MsgToChunk(buff);
|
|
647
|
-
if (chunk.seq && chunk.seq >= this.ack)
|
|
648
|
-
this.sentChunkQueue.splice(i, 1);
|
|
649
|
-
}
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
unpacked.chunks.forEach((chunk, index) => {
|
|
654
573
|
if (chunk.sys) {
|
|
655
574
|
// system messages
|
|
656
|
-
if (chunk.msgid ==
|
|
657
|
-
let packer = new MsgPacker(
|
|
575
|
+
if (chunk.msgid == NETMSG.System.NETMSG_PING) { // ping
|
|
576
|
+
let packer = new MsgPacker(NETMSG.System.NETMSG_PING_REPLY, true, 0);
|
|
658
577
|
|
|
659
578
|
this.SendMsgEx(packer); // send ping reply
|
|
660
|
-
} else if (chunk.msgid ==
|
|
579
|
+
} else if (chunk.msgid == NETMSG.System.NETMSG_PING_REPLY) { // Ping reply
|
|
661
580
|
this.game._ping_resolve(new Date().getTime())
|
|
662
|
-
}
|
|
663
|
-
|
|
581
|
+
} else if (this.rcon._checkChunks(chunk)) {}
|
|
664
582
|
// packets neccessary for connection
|
|
665
583
|
// https://ddnet.org/docs/libtw2/connection/
|
|
666
584
|
|
|
667
|
-
if (chunk.msgid ==
|
|
668
|
-
|
|
585
|
+
if (chunk.msgid == NETMSG.System.NETMSG_MAP_CHANGE) {
|
|
586
|
+
this.Flush();
|
|
587
|
+
var Msg = new MsgPacker(NETMSG.System.NETMSG_READY, true, 1); /* ready */
|
|
669
588
|
this.SendMsgEx(Msg);
|
|
670
|
-
} else if (chunk.msgid ==
|
|
671
|
-
var info = new MsgPacker(
|
|
589
|
+
} else if (chunk.msgid == NETMSG.System.NETMSG_CON_READY) {
|
|
590
|
+
var info = new MsgPacker(NETMSG.Game.CL_STARTINFO, false, 1);
|
|
672
591
|
if (this.options?.identity) {
|
|
673
592
|
info.AddString(this.options.identity.name);
|
|
674
593
|
info.AddString(this.options.identity.clan);
|
|
@@ -692,7 +611,7 @@ export class Client extends EventEmitter {
|
|
|
692
611
|
this.SendMsgEx([info, crashmeplx]);
|
|
693
612
|
}
|
|
694
613
|
|
|
695
|
-
if (chunk.msgid >=
|
|
614
|
+
if (chunk.msgid >= NETMSG.System.NETMSG_SNAP && chunk.msgid <= NETMSG.System.NETMSG_SNAPSINGLE) {
|
|
696
615
|
this.receivedSnaps++; /* wait for 2 ss before seeing self as connected */
|
|
697
616
|
if (this.receivedSnaps == 2) {
|
|
698
617
|
if (this.State != States.STATE_ONLINE)
|
|
@@ -713,12 +632,12 @@ export class Client extends EventEmitter {
|
|
|
713
632
|
let Crc = 0;
|
|
714
633
|
let CompleteSize = 0;
|
|
715
634
|
|
|
716
|
-
if (chunk.msgid ==
|
|
635
|
+
if (chunk.msgid == NETMSG.System.NETMSG_SNAP) {
|
|
717
636
|
NumParts = unpacker.unpackInt();
|
|
718
637
|
Part = unpacker.unpackInt();
|
|
719
638
|
}
|
|
720
639
|
|
|
721
|
-
if (chunk.msgid !=
|
|
640
|
+
if (chunk.msgid != NETMSG.System.NETMSG_SNAPEMPTY) {
|
|
722
641
|
Crc = unpacker.unpackInt();
|
|
723
642
|
PartSize = unpacker.unpackInt();
|
|
724
643
|
}
|
|
@@ -761,28 +680,28 @@ export class Client extends EventEmitter {
|
|
|
761
680
|
|
|
762
681
|
}
|
|
763
682
|
|
|
764
|
-
if (chunk.msgid >=
|
|
765
|
-
if (chunk.msgid ==
|
|
683
|
+
if (chunk.msgid >= NETMSG.System.NETMSG_WHATIS && chunk.msgid <= NETMSG.System.NETMSG_CHECKSUM_ERROR) {
|
|
684
|
+
if (chunk.msgid == NETMSG.System.NETMSG_WHATIS) {
|
|
766
685
|
let Uuid = chunk.raw.slice(0, 16);
|
|
767
686
|
|
|
768
687
|
let uuid = this.UUIDManager.LookupUUID(Uuid);
|
|
769
688
|
let packer = new MsgPacker(0, true, 1);
|
|
770
689
|
if (uuid !== undefined) {
|
|
771
690
|
// IT_IS msg
|
|
772
|
-
packer.AddBuffer(this.UUIDManager.LookupType(
|
|
691
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_ITIS)!.hash);
|
|
773
692
|
|
|
774
693
|
packer.AddBuffer(Uuid);
|
|
775
694
|
packer.AddString(uuid.name);
|
|
776
695
|
} else {
|
|
777
696
|
// dont_know msg
|
|
778
|
-
packer.AddBuffer(this.UUIDManager.LookupType(
|
|
697
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_IDONTKNOW)!.hash);
|
|
779
698
|
|
|
780
699
|
packer.AddBuffer(Uuid);
|
|
781
700
|
}
|
|
782
701
|
this.QueueChunkEx(packer)
|
|
783
702
|
}
|
|
784
703
|
|
|
785
|
-
if (chunk.msgid ==
|
|
704
|
+
if (chunk.msgid == NETMSG.System.NETMSG_MAP_DETAILS) { // TODO: option for downloading maps
|
|
786
705
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
787
706
|
|
|
788
707
|
let map_name = unpacker.unpackString();
|
|
@@ -799,7 +718,7 @@ export class Client extends EventEmitter {
|
|
|
799
718
|
this.emit("map_details", {map_name, map_sha256, map_crc, map_size, map_url})
|
|
800
719
|
// unpacker.unpack
|
|
801
720
|
|
|
802
|
-
} else if (chunk.msgid ==
|
|
721
|
+
} else if (chunk.msgid == NETMSG.System.NETMSG_CAPABILITIES) {
|
|
803
722
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
804
723
|
let Version = unpacker.unpackInt();
|
|
805
724
|
let Flags = unpacker.unpackInt();
|
|
@@ -837,9 +756,9 @@ export class Client extends EventEmitter {
|
|
|
837
756
|
}
|
|
838
757
|
this.emit("capabilities", {ChatTimeoutCode, AnyPlayerFlag, PingEx, AllowDummy, SyncWeaponInput});
|
|
839
758
|
// https://github.com/ddnet/ddnet/blob/06e3eb564150e9ab81b3a5595c48e9fe5952ed32/src/engine/client/client.cpp#L1565
|
|
840
|
-
} else if (chunk.msgid ==
|
|
759
|
+
} else if (chunk.msgid == NETMSG.System.NETMSG_PINGEX) {
|
|
841
760
|
let packer = new MsgPacker(0, true, 2);
|
|
842
|
-
packer.AddBuffer(this.UUIDManager.LookupType(
|
|
761
|
+
packer.AddBuffer(this.UUIDManager.LookupType(NETMSG.System.NETMSG_PONGEX)!.hash);
|
|
843
762
|
|
|
844
763
|
this.SendMsgEx(packer, 2);
|
|
845
764
|
}
|
|
@@ -850,9 +769,9 @@ export class Client extends EventEmitter {
|
|
|
850
769
|
// game messages
|
|
851
770
|
|
|
852
771
|
// vote list:
|
|
853
|
-
if (chunk.msgid ==
|
|
772
|
+
if (chunk.msgid == NETMSG.Game.SV_VOTECLEAROPTIONS) {
|
|
854
773
|
this.VoteList = [];
|
|
855
|
-
} else if (chunk.msgid ==
|
|
774
|
+
} else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONLISTADD) {
|
|
856
775
|
let unpacker = new MsgUnpacker(chunk.raw)
|
|
857
776
|
let NumOptions = unpacker.unpackInt()
|
|
858
777
|
let list: string[] = [];
|
|
@@ -862,11 +781,11 @@ export class Client extends EventEmitter {
|
|
|
862
781
|
list = list.slice(0, NumOptions);
|
|
863
782
|
|
|
864
783
|
this.VoteList.push(...list);
|
|
865
|
-
} else if (chunk.msgid ==
|
|
784
|
+
} else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONADD) {
|
|
866
785
|
let unpacker = new MsgUnpacker(chunk.raw)
|
|
867
786
|
|
|
868
787
|
this.VoteList.push(unpacker.unpackString());
|
|
869
|
-
} else if (chunk.msgid ==
|
|
788
|
+
} else if (chunk.msgid == NETMSG.Game.SV_VOTEOPTIONREMOVE) {
|
|
870
789
|
let unpacker = new MsgUnpacker(chunk.raw)
|
|
871
790
|
|
|
872
791
|
let index = this.VoteList.indexOf(unpacker.unpackString());
|
|
@@ -877,7 +796,7 @@ export class Client extends EventEmitter {
|
|
|
877
796
|
}
|
|
878
797
|
|
|
879
798
|
// events
|
|
880
|
-
if (chunk.msgid ==
|
|
799
|
+
if (chunk.msgid == NETMSG.Game.SV_EMOTICON) {
|
|
881
800
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
882
801
|
let unpacked = {
|
|
883
802
|
client_id: unpacker.unpackInt(),
|
|
@@ -894,11 +813,11 @@ export class Client extends EventEmitter {
|
|
|
894
813
|
|
|
895
814
|
|
|
896
815
|
|
|
897
|
-
} else if (chunk.msgid ==
|
|
816
|
+
} else if (chunk.msgid == NETMSG.Game.SV_BROADCAST) {
|
|
898
817
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
899
818
|
|
|
900
819
|
this.emit("broadcast", unpacker.unpackString());
|
|
901
|
-
} if (chunk.msgid ==
|
|
820
|
+
} if (chunk.msgid == NETMSG.Game.SV_CHAT) {
|
|
902
821
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
903
822
|
let unpacked: iMessage = {
|
|
904
823
|
team: unpacker.unpackInt(),
|
|
@@ -913,7 +832,7 @@ export class Client extends EventEmitter {
|
|
|
913
832
|
}
|
|
914
833
|
}
|
|
915
834
|
this.emit("message", unpacked)
|
|
916
|
-
} else if (chunk.msgid ==
|
|
835
|
+
} else if (chunk.msgid == NETMSG.Game.SV_KILLMSG) {
|
|
917
836
|
let unpacked: iKillMsg = {} as iKillMsg;
|
|
918
837
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
919
838
|
unpacked.killer_id = unpacker.unpackInt();
|
|
@@ -927,7 +846,7 @@ export class Client extends EventEmitter {
|
|
|
927
846
|
if (unpacked.killer_id != -1 && unpacked.killer_id < 64)
|
|
928
847
|
unpacked.killer = { ClientInfo: this.SnapshotUnpacker.getObjClientInfo(unpacked.killer_id), PlayerInfo: this.SnapshotUnpacker.getObjPlayerInfo(unpacked.killer_id) }
|
|
929
848
|
this.emit("kill", unpacked)
|
|
930
|
-
} else if (chunk.msgid ==
|
|
849
|
+
} else if (chunk.msgid == NETMSG.Game.SV_MOTD) {
|
|
931
850
|
let unpacker = new MsgUnpacker(chunk.raw);
|
|
932
851
|
let message = unpacker.unpackString();
|
|
933
852
|
this.emit("motd", message);
|
|
@@ -935,22 +854,26 @@ export class Client extends EventEmitter {
|
|
|
935
854
|
|
|
936
855
|
// packets neccessary for connection
|
|
937
856
|
// https://ddnet.org/docs/libtw2/connection/
|
|
938
|
-
if (chunk.msgid ==
|
|
939
|
-
var Msg = new MsgPacker(
|
|
857
|
+
if (chunk.msgid == NETMSG.Game.SV_READYTOENTER) {
|
|
858
|
+
var Msg = new MsgPacker(NETMSG.System.NETMSG_ENTERGAME, true, 1); /* entergame */
|
|
940
859
|
this.SendMsgEx(Msg);
|
|
860
|
+
this.OnEnterGame();
|
|
941
861
|
}
|
|
942
862
|
}
|
|
943
863
|
})
|
|
944
864
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
+
|
|
949
874
|
}
|
|
950
875
|
})
|
|
951
876
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
877
|
/** Sending the input. (automatically done unless options.lightweight is on) */
|
|
955
878
|
sendInput(input = this.movement.input) {
|
|
956
879
|
if (this.State != States.STATE_ONLINE)
|