h1z1-server 0.22.2-0 → 0.22.2-1
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/package.json +8 -8
- package/src/clients/soeclient.js +316 -316
- package/src/packets/ClientProtocol/ClientProtocol_1080/base.ts +3 -3
- package/src/packets/ClientProtocol/ClientProtocol_1080/clientUpdate.ts +7 -1
- package/src/packets/ClientProtocol/ClientProtocol_1080/command.ts +699 -699
- package/src/packets/ClientProtocol/ClientProtocol_1080/mount.ts +86 -86
- package/src/packets/ClientProtocol/ClientProtocol_1080/shared.ts +2543 -2543
- package/src/packets/ClientProtocol/ClientProtocol_1080/vehicle.ts +330 -330
- package/src/packets/ClientProtocol/ClientProtocol_1080/weapon.ts +629 -629
- package/src/packets/ClientProtocol/ClientProtocol_860/base.ts +2460 -2460
- package/src/packets/LoginUdp/LoginUdp_11/loginpackets.ts +519 -519
- package/src/packets/LoginUdp/LoginUdp_9/loginpackets.ts +441 -441
- package/src/servers/GatewayServer/gatewayserver.ts +124 -124
- package/src/servers/H1emuServer/h1emuLoginServer.ts +57 -57
- package/src/servers/H1emuServer/h1emuZoneServer.ts +115 -115
- package/src/servers/H1emuServer/shared/h1emuclient.ts +29 -29
- package/src/servers/H1emuServer/shared/h1emuserver.ts +116 -116
- package/src/servers/LoginServer/loginclient.ts +19 -19
- package/src/servers/LoginServer/loginserver.ts +1079 -1078
- package/src/servers/SoeServer/soeinputstream.ts +258 -260
- package/src/servers/SoeServer/soeoutputstream.ts +119 -119
- package/src/servers/SoeServer/soeserver.ts +596 -596
- package/src/servers/ZoneServer2015/zoneserver.ts +2675 -2675
- package/src/servers/ZoneServer2016/classes/constructionDoor.ts +81 -81
- package/src/servers/ZoneServer2016/classes/constructionParentEntity.ts +621 -621
- package/src/servers/ZoneServer2016/classes/craftmanager.ts +277 -277
- package/src/servers/ZoneServer2016/classes/simpleConstruction.ts +88 -88
- package/src/servers/ZoneServer2016/classes/vehicle.ts +268 -268
- package/src/servers/ZoneServer2016/classes/worldobjectmanager.ts +360 -1242
- package/src/servers/ZoneServer2016/classes/zoneclient.ts +14 -0
- package/src/servers/ZoneServer2016/commands/commandhandler.ts +105 -105
- package/src/servers/ZoneServer2016/commands/commands.ts +1394 -1394
- package/src/servers/ZoneServer2016/commands/dev.ts +579 -579
- package/src/servers/ZoneServer2016/data/Recipes.ts +2054 -2054
- package/src/servers/ZoneServer2016/data/lootspawns.ts +1751 -0
- package/src/servers/ZoneServer2016/enums.ts +411 -411
- package/src/servers/ZoneServer2016/zonepackethandlers.ts +2379 -2377
- package/src/servers/ZoneServer2016/zoneserver.ts +7124 -7099
- package/src/servers/shared/workers/udpServerWorker.ts +88 -88
- package/src/types/LoginUdp_11packets.ts +89 -89
- package/src/types/LoginUdp_9packets.ts +82 -82
- package/src/types/gatewaypackets.ts +19 -19
- package/src/types/packets.ts +5 -5
- package/src/types/zone2015packets.ts +2368 -2368
- package/src/types/zone2016packets.ts +1777 -1774
- package/src/types/zoneserver.ts +19 -1
- package/src/utils/constants.ts +0 -1
- package/src/utils/enums.ts +18 -18
- package/src/utils/processErrorHandling.ts +49 -49
- package/src/utils/utils.ts +643 -631
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "h1z1-server",
|
|
3
|
-
"version": "0.22.2-
|
|
3
|
+
"version": "0.22.2-1",
|
|
4
4
|
"description": "Library for emulating h1z1 servers",
|
|
5
5
|
"author": "Quentin Gruber <quentingruber@gmail.com> (http://github.com/quentingruber)",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -16,20 +16,20 @@
|
|
|
16
16
|
"debug": "4.3.4",
|
|
17
17
|
"h1emu-core": "1.0.0",
|
|
18
18
|
"h1z1-dataschema": "1.7.0",
|
|
19
|
-
"mongodb": "4.
|
|
20
|
-
"typescript": "4.
|
|
19
|
+
"mongodb": "4.12.0",
|
|
20
|
+
"typescript": "4.9.3"
|
|
21
21
|
},
|
|
22
22
|
"directories": {
|
|
23
23
|
"src": "./src"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
27
|
-
"@typescript-eslint/parser": "5.
|
|
28
|
-
"eslint": "8.
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "5.44.0",
|
|
27
|
+
"@typescript-eslint/parser": "5.44.0",
|
|
28
|
+
"eslint": "8.28.0",
|
|
29
29
|
"glob": "8.0.3",
|
|
30
30
|
"prettier": "2.7.1",
|
|
31
|
-
"ts-node": "10.
|
|
32
|
-
"typedoc": "0.23.
|
|
31
|
+
"ts-node": "10.9.1",
|
|
32
|
+
"typedoc": "0.23.21"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"gen-packets-types": "npx ts-node ./scripts/genPacketsNames.ts",
|
package/src/clients/soeclient.js
CHANGED
|
@@ -1,316 +1,316 @@
|
|
|
1
|
-
// ======================================================================
|
|
2
|
-
//
|
|
3
|
-
// GNU GENERAL PUBLIC LICENSE
|
|
4
|
-
// Version 3, 29 June 2007
|
|
5
|
-
// copyright (C) 2020 - 2021 Quentin Gruber
|
|
6
|
-
// copyright (C) 2021 - 2022 H1emu community
|
|
7
|
-
//
|
|
8
|
-
// https://github.com/QuentinGruber/h1z1-server
|
|
9
|
-
// https://www.npmjs.com/package/h1z1-server
|
|
10
|
-
//
|
|
11
|
-
// Based on https://github.com/psemu/soe-network
|
|
12
|
-
// ======================================================================
|
|
13
|
-
|
|
14
|
-
const EventEmitter = require("events").EventEmitter,
|
|
15
|
-
SOEInputStream =
|
|
16
|
-
require("../servers/SoeServer/soeinputstream").SOEInputStream,
|
|
17
|
-
SOEOutputStream =
|
|
18
|
-
require("../servers/SoeServer/soeoutputstream").SOEOutputStream,
|
|
19
|
-
{ Soeprotocol } = require("h1emu-core"),
|
|
20
|
-
util = require("util"),
|
|
21
|
-
fs = require("fs"),
|
|
22
|
-
dgram = require("dgram"),
|
|
23
|
-
debug = require("debug")("SOEClient");
|
|
24
|
-
|
|
25
|
-
function createSessionId() {
|
|
26
|
-
return (Math.random() * 0xffffffff) >>> 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class SOEClient {
|
|
30
|
-
constructor(protocolName, serverAddress, serverPort, cryptoKey, localPort) {
|
|
31
|
-
EventEmitter.call(this);
|
|
32
|
-
const me = this;
|
|
33
|
-
|
|
34
|
-
this._guid = ((Math.random() * 0xffffffff) >>> 0).toString(16);
|
|
35
|
-
debug(this._guid, "Creating new SOEClient instance");
|
|
36
|
-
this._protocolName = protocolName;
|
|
37
|
-
this._serverAddress = serverAddress;
|
|
38
|
-
this._serverPort = serverPort;
|
|
39
|
-
this._localPort = localPort;
|
|
40
|
-
this._cryptoKey = cryptoKey;
|
|
41
|
-
this._useEncryption = true;
|
|
42
|
-
this._dumpData = false;
|
|
43
|
-
|
|
44
|
-
this._outQueue = [];
|
|
45
|
-
|
|
46
|
-
const connection = (this._connection = dgram.createSocket("udp4"));
|
|
47
|
-
const protocol = (this._protocol = new Soeprotocol(false, 0));
|
|
48
|
-
const inputStream = (this._inputStream = new SOEInputStream(cryptoKey));
|
|
49
|
-
const outputStream = (this._outputStream = new SOEOutputStream(cryptoKey));
|
|
50
|
-
|
|
51
|
-
let n1 = 0,
|
|
52
|
-
n2 = 0;
|
|
53
|
-
|
|
54
|
-
inputStream.on("appdata", function (data) {
|
|
55
|
-
if (me._dumpData) {
|
|
56
|
-
fs.writeFileSync("soeclient_apppacket_" + n2++ + ".dat", data);
|
|
57
|
-
}
|
|
58
|
-
me.emit("appdata", null, data);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
inputStream.on("ack", function (sequence) {
|
|
62
|
-
nextAck = sequence;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
inputStream.on("outoforder", function (sequence) {
|
|
66
|
-
outOfOrderPackets.push(sequence);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
outputStream.on("data", function (data, sequence, fragment) {
|
|
70
|
-
if (fragment) {
|
|
71
|
-
me._sendPacket("DataFragment", {
|
|
72
|
-
sequence: sequence,
|
|
73
|
-
data: data,
|
|
74
|
-
});
|
|
75
|
-
} else {
|
|
76
|
-
me._sendPacket("Data", {
|
|
77
|
-
sequence: sequence,
|
|
78
|
-
data: data,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
var lastAck = -1,
|
|
84
|
-
nextAck = -1,
|
|
85
|
-
outOfOrderPackets = [];
|
|
86
|
-
|
|
87
|
-
function checkAck() {
|
|
88
|
-
if (lastAck !== nextAck) {
|
|
89
|
-
lastAck = nextAck;
|
|
90
|
-
me._sendPacket("Ack", {
|
|
91
|
-
channel: 0,
|
|
92
|
-
sequence: nextAck,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
me._ackTimer = setTimeout(checkAck, 50);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
checkAck();
|
|
99
|
-
|
|
100
|
-
function checkOutOfOrderQueue() {
|
|
101
|
-
if (outOfOrderPackets.length) {
|
|
102
|
-
return;
|
|
103
|
-
const packets = [];
|
|
104
|
-
for (let i = 0; i < 20; i++) {
|
|
105
|
-
const sequence = outOfOrderPackets.shift();
|
|
106
|
-
packets.push({
|
|
107
|
-
name: "OutOfOrder",
|
|
108
|
-
soePacket: {
|
|
109
|
-
channel: 0,
|
|
110
|
-
sequence: sequence,
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
if (!outOfOrderPackets.length) {
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
debug("Sending " + packets.length + " OutOfOrder packets");
|
|
118
|
-
me._sendPacket(
|
|
119
|
-
"MultiPacket",
|
|
120
|
-
{
|
|
121
|
-
subPackets: packets,
|
|
122
|
-
},
|
|
123
|
-
true
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
me._outOfOrderTimer = setTimeout(checkOutOfOrderQueue, 10);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
checkOutOfOrderQueue();
|
|
130
|
-
|
|
131
|
-
function checkOutQueue() {
|
|
132
|
-
if (me._outQueue.length) {
|
|
133
|
-
const data = me._outQueue.shift();
|
|
134
|
-
if (me._dumpData) {
|
|
135
|
-
fs.writeFileSync("debug/soeclient_" + n1++ + "_out.dat", data);
|
|
136
|
-
}
|
|
137
|
-
me._connection.send(
|
|
138
|
-
data,
|
|
139
|
-
0,
|
|
140
|
-
data.length,
|
|
141
|
-
me._serverPort,
|
|
142
|
-
me._serverAddress,
|
|
143
|
-
function (err, bytes) {}
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
me._outQueueTimer = setTimeout(checkOutQueue, 0);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
checkOutQueue();
|
|
150
|
-
|
|
151
|
-
function handlePacket(packet) {
|
|
152
|
-
switch (packet.name) {
|
|
153
|
-
case "SessionReply":
|
|
154
|
-
debug("Received session reply from server");
|
|
155
|
-
me._compression = 0;
|
|
156
|
-
me._crcSeed = packet.crc_seed;
|
|
157
|
-
me._crcLength = packet.crc_length;
|
|
158
|
-
me._udpLength = packet.udp_length;
|
|
159
|
-
inputStream.toggleEncryption(me._useEncryption);
|
|
160
|
-
outputStream.toggleEncryption(me._useEncryption);
|
|
161
|
-
outputStream.setFragmentSize(packet.udp_length - 7);
|
|
162
|
-
me.emit("connect", null, packet);
|
|
163
|
-
break;
|
|
164
|
-
case "Disconnect":
|
|
165
|
-
debug("Received disconnect from server");
|
|
166
|
-
me.disconnect();
|
|
167
|
-
me.emit("disconnect");
|
|
168
|
-
break;
|
|
169
|
-
case "MultiPacket":
|
|
170
|
-
let lastOutOfOrder = 0;
|
|
171
|
-
const channel = 0;
|
|
172
|
-
for (let i = 0; i < packet.sub_packets.length; i++) {
|
|
173
|
-
const subPacket = packet.sub_packets[i];
|
|
174
|
-
switch (subPacket.name) {
|
|
175
|
-
case "OutOfOrder":
|
|
176
|
-
if (subPacket.sequence > lastOutOfOrder) {
|
|
177
|
-
lastOutOfOrder = subPacket.sequence;
|
|
178
|
-
}
|
|
179
|
-
break;
|
|
180
|
-
default:
|
|
181
|
-
handlePacket({
|
|
182
|
-
soePacket: subPacket,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (lastOutOfOrder > 0) {
|
|
187
|
-
debug(
|
|
188
|
-
"Received multiple out-order-packet packet on channel " +
|
|
189
|
-
channel +
|
|
190
|
-
", sequence " +
|
|
191
|
-
lastOutOfOrder
|
|
192
|
-
);
|
|
193
|
-
outputStream.resendData(lastOutOfOrder);
|
|
194
|
-
}
|
|
195
|
-
break;
|
|
196
|
-
case "Ping":
|
|
197
|
-
debug("Received ping from server");
|
|
198
|
-
break;
|
|
199
|
-
case "NetStatusReply":
|
|
200
|
-
debug("Received net status reply from server");
|
|
201
|
-
break;
|
|
202
|
-
case "Data":
|
|
203
|
-
debug("Received data packet from server");
|
|
204
|
-
inputStream.write(Buffer.from(packet.data), packet.sequence, false);
|
|
205
|
-
break;
|
|
206
|
-
case "DataFragment":
|
|
207
|
-
debug("Received data fragment from server");
|
|
208
|
-
inputStream.write(Buffer.from(packet.data), packet.sequence, true);
|
|
209
|
-
break;
|
|
210
|
-
case "OutOfOrder":
|
|
211
|
-
debug(
|
|
212
|
-
"Received out-order-packet packet on channel " +
|
|
213
|
-
packet.channel +
|
|
214
|
-
", sequence " +
|
|
215
|
-
packet.sequence
|
|
216
|
-
);
|
|
217
|
-
//outputStream.resendData(result.sequence);
|
|
218
|
-
break;
|
|
219
|
-
case "Ack":
|
|
220
|
-
outputStream.ack(packet.sequence, new Map());
|
|
221
|
-
break;
|
|
222
|
-
case "FatalError":
|
|
223
|
-
debug("Received fatal error from server");
|
|
224
|
-
break;
|
|
225
|
-
case "FatalErrorReply":
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
connection.on("message", function (data, remote) {
|
|
231
|
-
if (me._dumpData) {
|
|
232
|
-
fs.writeFileSync("debug/soeclient_" + n1++ + "_in.dat", data);
|
|
233
|
-
}
|
|
234
|
-
const result = JSON.parse(protocol.parse(data));
|
|
235
|
-
handlePacket(result);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
connection.on("listening", function () {
|
|
239
|
-
const address = this.address();
|
|
240
|
-
debug("Listening on " + address.address + ":" + address.port);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
connect() {
|
|
245
|
-
debug(
|
|
246
|
-
"Setting up connection for " +
|
|
247
|
-
this._serverAddress +
|
|
248
|
-
":" +
|
|
249
|
-
this._serverPort
|
|
250
|
-
);
|
|
251
|
-
this._sessionId = createSessionId();
|
|
252
|
-
const me = this;
|
|
253
|
-
this._connection.bind(this._localPort, function () {
|
|
254
|
-
me._sendPacket("SessionRequest", {
|
|
255
|
-
protocol: me._protocolName,
|
|
256
|
-
crc_length: 3,
|
|
257
|
-
session_id: me._sessionId,
|
|
258
|
-
udp_length: 512,
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
disconnect() {
|
|
264
|
-
clearTimeout(this._outQueueTimer);
|
|
265
|
-
clearTimeout(this._ackTimer);
|
|
266
|
-
clearTimeout(this._outOfOrderTimer);
|
|
267
|
-
try {
|
|
268
|
-
this._sendPacket("Disconnect", {});
|
|
269
|
-
this._connection.close();
|
|
270
|
-
} catch (e) {}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
toggleEncryption(value) {
|
|
274
|
-
value = !!value;
|
|
275
|
-
this._useEncryption = value;
|
|
276
|
-
debug(this._guid, "Toggling encryption: value = " + value);
|
|
277
|
-
this._outputStream.toggleEncryption(value);
|
|
278
|
-
this._inputStream.toggleEncryption(value);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
toggleDataDump(value) {
|
|
282
|
-
this._dumpData = value;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
_sendPacket(packetName, packet, prioritize) {
|
|
286
|
-
if (packet.data) {
|
|
287
|
-
packet.data = [...packet.data];
|
|
288
|
-
}
|
|
289
|
-
const data = Buffer.from(
|
|
290
|
-
this._protocol.pack(packetName, JSON.stringify(packet))
|
|
291
|
-
);
|
|
292
|
-
debug(this._guid, "Sending " + packetName + " packet to server");
|
|
293
|
-
if (this._dumpData) {
|
|
294
|
-
fs.writeFileSync(
|
|
295
|
-
"debug/soeclient_" + this._guid + "_outpacket_" + q++ + ".dat",
|
|
296
|
-
data
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
if (prioritize) {
|
|
300
|
-
this._outQueue.unshift(data);
|
|
301
|
-
} else {
|
|
302
|
-
this._outQueue.push(data);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
sendAppData(data, overrideEncryption) {
|
|
307
|
-
debug(this._guid, "Sending app data: " + data.length + " bytes");
|
|
308
|
-
this._outputStream.write(data, overrideEncryption);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
util.inherits(SOEClient, EventEmitter);
|
|
313
|
-
|
|
314
|
-
var q = 0;
|
|
315
|
-
|
|
316
|
-
exports.SOEClient = SOEClient;
|
|
1
|
+
// ======================================================================
|
|
2
|
+
//
|
|
3
|
+
// GNU GENERAL PUBLIC LICENSE
|
|
4
|
+
// Version 3, 29 June 2007
|
|
5
|
+
// copyright (C) 2020 - 2021 Quentin Gruber
|
|
6
|
+
// copyright (C) 2021 - 2022 H1emu community
|
|
7
|
+
//
|
|
8
|
+
// https://github.com/QuentinGruber/h1z1-server
|
|
9
|
+
// https://www.npmjs.com/package/h1z1-server
|
|
10
|
+
//
|
|
11
|
+
// Based on https://github.com/psemu/soe-network
|
|
12
|
+
// ======================================================================
|
|
13
|
+
|
|
14
|
+
const EventEmitter = require("events").EventEmitter,
|
|
15
|
+
SOEInputStream =
|
|
16
|
+
require("../servers/SoeServer/soeinputstream").SOEInputStream,
|
|
17
|
+
SOEOutputStream =
|
|
18
|
+
require("../servers/SoeServer/soeoutputstream").SOEOutputStream,
|
|
19
|
+
{ Soeprotocol } = require("h1emu-core"),
|
|
20
|
+
util = require("util"),
|
|
21
|
+
fs = require("fs"),
|
|
22
|
+
dgram = require("dgram"),
|
|
23
|
+
debug = require("debug")("SOEClient");
|
|
24
|
+
|
|
25
|
+
function createSessionId() {
|
|
26
|
+
return (Math.random() * 0xffffffff) >>> 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class SOEClient {
|
|
30
|
+
constructor(protocolName, serverAddress, serverPort, cryptoKey, localPort) {
|
|
31
|
+
EventEmitter.call(this);
|
|
32
|
+
const me = this;
|
|
33
|
+
|
|
34
|
+
this._guid = ((Math.random() * 0xffffffff) >>> 0).toString(16);
|
|
35
|
+
debug(this._guid, "Creating new SOEClient instance");
|
|
36
|
+
this._protocolName = protocolName;
|
|
37
|
+
this._serverAddress = serverAddress;
|
|
38
|
+
this._serverPort = serverPort;
|
|
39
|
+
this._localPort = localPort;
|
|
40
|
+
this._cryptoKey = cryptoKey;
|
|
41
|
+
this._useEncryption = true;
|
|
42
|
+
this._dumpData = false;
|
|
43
|
+
|
|
44
|
+
this._outQueue = [];
|
|
45
|
+
|
|
46
|
+
const connection = (this._connection = dgram.createSocket("udp4"));
|
|
47
|
+
const protocol = (this._protocol = new Soeprotocol(false, 0));
|
|
48
|
+
const inputStream = (this._inputStream = new SOEInputStream(cryptoKey));
|
|
49
|
+
const outputStream = (this._outputStream = new SOEOutputStream(cryptoKey));
|
|
50
|
+
|
|
51
|
+
let n1 = 0,
|
|
52
|
+
n2 = 0;
|
|
53
|
+
|
|
54
|
+
inputStream.on("appdata", function (data) {
|
|
55
|
+
if (me._dumpData) {
|
|
56
|
+
fs.writeFileSync("soeclient_apppacket_" + n2++ + ".dat", data);
|
|
57
|
+
}
|
|
58
|
+
me.emit("appdata", null, data);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
inputStream.on("ack", function (sequence) {
|
|
62
|
+
nextAck = sequence;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
inputStream.on("outoforder", function (sequence) {
|
|
66
|
+
outOfOrderPackets.push(sequence);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
outputStream.on("data", function (data, sequence, fragment) {
|
|
70
|
+
if (fragment) {
|
|
71
|
+
me._sendPacket("DataFragment", {
|
|
72
|
+
sequence: sequence,
|
|
73
|
+
data: data,
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
me._sendPacket("Data", {
|
|
77
|
+
sequence: sequence,
|
|
78
|
+
data: data,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
var lastAck = -1,
|
|
84
|
+
nextAck = -1,
|
|
85
|
+
outOfOrderPackets = [];
|
|
86
|
+
|
|
87
|
+
function checkAck() {
|
|
88
|
+
if (lastAck !== nextAck) {
|
|
89
|
+
lastAck = nextAck;
|
|
90
|
+
me._sendPacket("Ack", {
|
|
91
|
+
channel: 0,
|
|
92
|
+
sequence: nextAck,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
me._ackTimer = setTimeout(checkAck, 50);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
checkAck();
|
|
99
|
+
|
|
100
|
+
function checkOutOfOrderQueue() {
|
|
101
|
+
if (outOfOrderPackets.length) {
|
|
102
|
+
return;
|
|
103
|
+
const packets = [];
|
|
104
|
+
for (let i = 0; i < 20; i++) {
|
|
105
|
+
const sequence = outOfOrderPackets.shift();
|
|
106
|
+
packets.push({
|
|
107
|
+
name: "OutOfOrder",
|
|
108
|
+
soePacket: {
|
|
109
|
+
channel: 0,
|
|
110
|
+
sequence: sequence,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
if (!outOfOrderPackets.length) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
debug("Sending " + packets.length + " OutOfOrder packets");
|
|
118
|
+
me._sendPacket(
|
|
119
|
+
"MultiPacket",
|
|
120
|
+
{
|
|
121
|
+
subPackets: packets,
|
|
122
|
+
},
|
|
123
|
+
true
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
me._outOfOrderTimer = setTimeout(checkOutOfOrderQueue, 10);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
checkOutOfOrderQueue();
|
|
130
|
+
|
|
131
|
+
function checkOutQueue() {
|
|
132
|
+
if (me._outQueue.length) {
|
|
133
|
+
const data = me._outQueue.shift();
|
|
134
|
+
if (me._dumpData) {
|
|
135
|
+
fs.writeFileSync("debug/soeclient_" + n1++ + "_out.dat", data);
|
|
136
|
+
}
|
|
137
|
+
me._connection.send(
|
|
138
|
+
data,
|
|
139
|
+
0,
|
|
140
|
+
data.length,
|
|
141
|
+
me._serverPort,
|
|
142
|
+
me._serverAddress,
|
|
143
|
+
function (err, bytes) {}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
me._outQueueTimer = setTimeout(checkOutQueue, 0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
checkOutQueue();
|
|
150
|
+
|
|
151
|
+
function handlePacket(packet) {
|
|
152
|
+
switch (packet.name) {
|
|
153
|
+
case "SessionReply":
|
|
154
|
+
debug("Received session reply from server");
|
|
155
|
+
me._compression = 0;
|
|
156
|
+
me._crcSeed = packet.crc_seed;
|
|
157
|
+
me._crcLength = packet.crc_length;
|
|
158
|
+
me._udpLength = packet.udp_length;
|
|
159
|
+
inputStream.toggleEncryption(me._useEncryption);
|
|
160
|
+
outputStream.toggleEncryption(me._useEncryption);
|
|
161
|
+
outputStream.setFragmentSize(packet.udp_length - 7);
|
|
162
|
+
me.emit("connect", null, packet);
|
|
163
|
+
break;
|
|
164
|
+
case "Disconnect":
|
|
165
|
+
debug("Received disconnect from server");
|
|
166
|
+
me.disconnect();
|
|
167
|
+
me.emit("disconnect");
|
|
168
|
+
break;
|
|
169
|
+
case "MultiPacket":
|
|
170
|
+
let lastOutOfOrder = 0;
|
|
171
|
+
const channel = 0;
|
|
172
|
+
for (let i = 0; i < packet.sub_packets.length; i++) {
|
|
173
|
+
const subPacket = packet.sub_packets[i];
|
|
174
|
+
switch (subPacket.name) {
|
|
175
|
+
case "OutOfOrder":
|
|
176
|
+
if (subPacket.sequence > lastOutOfOrder) {
|
|
177
|
+
lastOutOfOrder = subPacket.sequence;
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
default:
|
|
181
|
+
handlePacket({
|
|
182
|
+
soePacket: subPacket,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (lastOutOfOrder > 0) {
|
|
187
|
+
debug(
|
|
188
|
+
"Received multiple out-order-packet packet on channel " +
|
|
189
|
+
channel +
|
|
190
|
+
", sequence " +
|
|
191
|
+
lastOutOfOrder
|
|
192
|
+
);
|
|
193
|
+
outputStream.resendData(lastOutOfOrder);
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
case "Ping":
|
|
197
|
+
debug("Received ping from server");
|
|
198
|
+
break;
|
|
199
|
+
case "NetStatusReply":
|
|
200
|
+
debug("Received net status reply from server");
|
|
201
|
+
break;
|
|
202
|
+
case "Data":
|
|
203
|
+
debug("Received data packet from server");
|
|
204
|
+
inputStream.write(Buffer.from(packet.data), packet.sequence, false);
|
|
205
|
+
break;
|
|
206
|
+
case "DataFragment":
|
|
207
|
+
debug("Received data fragment from server");
|
|
208
|
+
inputStream.write(Buffer.from(packet.data), packet.sequence, true);
|
|
209
|
+
break;
|
|
210
|
+
case "OutOfOrder":
|
|
211
|
+
debug(
|
|
212
|
+
"Received out-order-packet packet on channel " +
|
|
213
|
+
packet.channel +
|
|
214
|
+
", sequence " +
|
|
215
|
+
packet.sequence
|
|
216
|
+
);
|
|
217
|
+
//outputStream.resendData(result.sequence);
|
|
218
|
+
break;
|
|
219
|
+
case "Ack":
|
|
220
|
+
outputStream.ack(packet.sequence, new Map());
|
|
221
|
+
break;
|
|
222
|
+
case "FatalError":
|
|
223
|
+
debug("Received fatal error from server");
|
|
224
|
+
break;
|
|
225
|
+
case "FatalErrorReply":
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
connection.on("message", function (data, remote) {
|
|
231
|
+
if (me._dumpData) {
|
|
232
|
+
fs.writeFileSync("debug/soeclient_" + n1++ + "_in.dat", data);
|
|
233
|
+
}
|
|
234
|
+
const result = JSON.parse(protocol.parse(data));
|
|
235
|
+
handlePacket(result);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
connection.on("listening", function () {
|
|
239
|
+
const address = this.address();
|
|
240
|
+
debug("Listening on " + address.address + ":" + address.port);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
connect() {
|
|
245
|
+
debug(
|
|
246
|
+
"Setting up connection for " +
|
|
247
|
+
this._serverAddress +
|
|
248
|
+
":" +
|
|
249
|
+
this._serverPort
|
|
250
|
+
);
|
|
251
|
+
this._sessionId = createSessionId();
|
|
252
|
+
const me = this;
|
|
253
|
+
this._connection.bind(this._localPort, function () {
|
|
254
|
+
me._sendPacket("SessionRequest", {
|
|
255
|
+
protocol: me._protocolName,
|
|
256
|
+
crc_length: 3,
|
|
257
|
+
session_id: me._sessionId,
|
|
258
|
+
udp_length: 512,
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
disconnect() {
|
|
264
|
+
clearTimeout(this._outQueueTimer);
|
|
265
|
+
clearTimeout(this._ackTimer);
|
|
266
|
+
clearTimeout(this._outOfOrderTimer);
|
|
267
|
+
try {
|
|
268
|
+
this._sendPacket("Disconnect", {});
|
|
269
|
+
this._connection.close();
|
|
270
|
+
} catch (e) {}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
toggleEncryption(value) {
|
|
274
|
+
value = !!value;
|
|
275
|
+
this._useEncryption = value;
|
|
276
|
+
debug(this._guid, "Toggling encryption: value = " + value);
|
|
277
|
+
this._outputStream.toggleEncryption(value);
|
|
278
|
+
this._inputStream.toggleEncryption(value);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
toggleDataDump(value) {
|
|
282
|
+
this._dumpData = value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
_sendPacket(packetName, packet, prioritize) {
|
|
286
|
+
if (packet.data) {
|
|
287
|
+
packet.data = [...packet.data];
|
|
288
|
+
}
|
|
289
|
+
const data = Buffer.from(
|
|
290
|
+
this._protocol.pack(packetName, JSON.stringify(packet))
|
|
291
|
+
);
|
|
292
|
+
debug(this._guid, "Sending " + packetName + " packet to server");
|
|
293
|
+
if (this._dumpData) {
|
|
294
|
+
fs.writeFileSync(
|
|
295
|
+
"debug/soeclient_" + this._guid + "_outpacket_" + q++ + ".dat",
|
|
296
|
+
data
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
if (prioritize) {
|
|
300
|
+
this._outQueue.unshift(data);
|
|
301
|
+
} else {
|
|
302
|
+
this._outQueue.push(data);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
sendAppData(data, overrideEncryption) {
|
|
307
|
+
debug(this._guid, "Sending app data: " + data.length + " bytes");
|
|
308
|
+
this._outputStream.write(data, overrideEncryption);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
util.inherits(SOEClient, EventEmitter);
|
|
313
|
+
|
|
314
|
+
var q = 0;
|
|
315
|
+
|
|
316
|
+
exports.SOEClient = SOEClient;
|