sonic-ws 1.3.2 → 1.3.3
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/dist/index.d.ts +0 -4
- package/dist/version.d.ts +1 -5
- package/dist/version.js +23 -4
- package/dist/ws/Connection.d.ts +0 -4
- package/dist/ws/Connection.js +124 -4
- package/dist/ws/PacketProcessor.d.ts +0 -4
- package/dist/ws/PacketProcessor.js +50 -4
- package/dist/ws/client/core/ClientCore.d.ts +0 -4
- package/dist/ws/client/core/ClientCore.js +249 -4
- package/dist/ws/client/node/ClientNode.d.ts +0 -4
- package/dist/ws/client/node/ClientNode.js +34 -4
- package/dist/ws/debug/DebugServer.d.ts +0 -4
- package/dist/ws/debug/DebugServer.js +605 -4
- package/dist/ws/packets/PacketProcessors.d.ts +0 -4
- package/dist/ws/packets/PacketProcessors.js +315 -4
- package/dist/ws/packets/PacketType.d.ts +0 -4
- package/dist/ws/packets/PacketType.js +59 -4
- package/dist/ws/packets/Packets.d.ts +0 -4
- package/dist/ws/packets/Packets.js +275 -4
- package/dist/ws/server/SonicWSConnection.d.ts +0 -4
- package/dist/ws/server/SonicWSConnection.js +296 -4
- package/dist/ws/server/SonicWSServer.d.ts +1 -5
- package/dist/ws/server/SonicWSServer.js +305 -4
- package/dist/ws/util/BufferUtil.d.ts +0 -4
- package/dist/ws/util/BufferUtil.js +40 -4
- package/dist/ws/util/StringUtil.d.ts +0 -4
- package/dist/ws/util/StringUtil.js +49 -4
- package/dist/ws/util/enums/EnumHandler.d.ts +0 -4
- package/dist/ws/util/enums/EnumHandler.js +58 -4
- package/dist/ws/util/enums/EnumType.d.ts +0 -4
- package/dist/ws/util/enums/EnumType.js +70 -4
- package/dist/ws/util/packets/BatchHelper.d.ts +0 -4
- package/dist/ws/util/packets/BatchHelper.js +83 -4
- package/dist/ws/util/packets/CompressionUtil.d.ts +5 -9
- package/dist/ws/util/packets/CompressionUtil.js +275 -4
- package/dist/ws/util/packets/HashUtil.d.ts +0 -4
- package/dist/ws/util/packets/HashUtil.js +120 -4
- package/dist/ws/util/packets/JSONUtil.d.ts +0 -4
- package/dist/ws/util/packets/JSONUtil.js +177 -4
- package/dist/ws/util/packets/PacketHolder.d.ts +0 -4
- package/dist/ws/util/packets/PacketHolder.js +124 -4
- package/dist/ws/util/packets/PacketUtils.d.ts +0 -4
- package/dist/ws/util/packets/PacketUtils.js +281 -4
- package/dist/ws/util/packets/RateHandler.d.ts +0 -4
- package/dist/ws/util/packets/RateHandler.js +64 -4
- package/package.json +3 -3
|
@@ -1,6 +1,277 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
2
3
|
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
4
16
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.PacketSchema = exports.Packet = void 0;
|
|
19
|
+
const EnumHandler_1 = require("../util/enums/EnumHandler");
|
|
20
|
+
const EnumType_1 = require("../util/enums/EnumType");
|
|
21
|
+
const CompressionUtil_1 = require("../util/packets/CompressionUtil");
|
|
22
|
+
const PacketUtils_1 = require("../util/packets/PacketUtils");
|
|
23
|
+
const PacketProcessors_1 = require("./PacketProcessors");
|
|
24
|
+
const PacketType_1 = require("./PacketType");
|
|
25
|
+
const StringUtil_1 = require("../util/StringUtil");
|
|
26
|
+
class Packet {
|
|
27
|
+
defaultEnabled;
|
|
28
|
+
tag;
|
|
29
|
+
maxSize;
|
|
30
|
+
minSize;
|
|
31
|
+
type;
|
|
32
|
+
enumData;
|
|
33
|
+
dataMax;
|
|
34
|
+
dataMin;
|
|
35
|
+
dataBatching;
|
|
36
|
+
maxBatchSize;
|
|
37
|
+
dontSpread;
|
|
38
|
+
autoFlatten;
|
|
39
|
+
rateLimit;
|
|
40
|
+
async;
|
|
41
|
+
rereference;
|
|
42
|
+
gzipCompression;
|
|
43
|
+
object;
|
|
44
|
+
client;
|
|
45
|
+
receiveProcessor;
|
|
46
|
+
sendProcessor;
|
|
47
|
+
validator;
|
|
48
|
+
processReceive;
|
|
49
|
+
processSend;
|
|
50
|
+
validate;
|
|
51
|
+
customValidator;
|
|
52
|
+
lastReceived = {};
|
|
53
|
+
lastSent = {};
|
|
54
|
+
constructor(tag, schema, customValidator, enabled, client) {
|
|
55
|
+
this.tag = tag;
|
|
56
|
+
this.defaultEnabled = enabled;
|
|
57
|
+
this.client = client;
|
|
58
|
+
this.async = schema.async;
|
|
59
|
+
this.enumData = schema.enumData;
|
|
60
|
+
this.rateLimit = schema.rateLimit;
|
|
61
|
+
this.dontSpread = schema.dontSpread;
|
|
62
|
+
this.autoFlatten = schema.autoFlatten;
|
|
63
|
+
this.rereference = schema.rereference;
|
|
64
|
+
this.dataBatching = schema.dataBatching;
|
|
65
|
+
this.maxBatchSize = client ? Infinity : schema.maxBatchSize;
|
|
66
|
+
this.gzipCompression = schema.gzipCompression;
|
|
67
|
+
this.object = schema.object;
|
|
68
|
+
this.type = schema.type;
|
|
69
|
+
this.dataMax = schema.dataMax;
|
|
70
|
+
this.dataMin = schema.dataMin;
|
|
71
|
+
if (schema.testObject(this)) {
|
|
72
|
+
this.maxSize = this.minSize = this.type.length;
|
|
73
|
+
for (let i = 0; i < this.type.length; i++)
|
|
74
|
+
if (this.type[i] == PacketType_1.PacketType.NONE)
|
|
75
|
+
this.dataMax[i] = this.dataMin[i] = 0;
|
|
76
|
+
this.receiveProcessor = (0, PacketProcessors_1.createObjReceiveProcessor)(this);
|
|
77
|
+
this.sendProcessor = (0, PacketProcessors_1.createObjSendProcessor)(this);
|
|
78
|
+
this.validator = (0, PacketProcessors_1.createObjValidator)(this);
|
|
79
|
+
}
|
|
80
|
+
else if (((_) => true)(this)) {
|
|
81
|
+
this.maxSize = this.dataMax;
|
|
82
|
+
this.minSize = this.dataMin;
|
|
83
|
+
if (this.type == PacketType_1.PacketType.NONE)
|
|
84
|
+
this.dataMax = this.dataMin = 0;
|
|
85
|
+
this.receiveProcessor = (0, PacketProcessors_1.createReceiveProcessor)(this.type, this.enumData, this.dataMax);
|
|
86
|
+
this.sendProcessor = (0, PacketProcessors_1.createSendProcessor)(this.type, this.gzipCompression, this.dataBatching != 0);
|
|
87
|
+
this.validator = (0, PacketProcessors_1.createValidator)(this.type, this.dataMax, this.dataMin, this, this.gzipCompression);
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
throw '';
|
|
91
|
+
this.processReceive = (data, validationResult) => this.receiveProcessor(data, validationResult, 0);
|
|
92
|
+
this.processSend = async (data) => new Uint8Array(await this.sendProcessor(tag, data));
|
|
93
|
+
this.validate = (data) => this.validator(data, 0);
|
|
94
|
+
this.customValidator = customValidator;
|
|
95
|
+
}
|
|
96
|
+
async listen(value, socket) {
|
|
97
|
+
try {
|
|
98
|
+
const [dcData, validationResult] = await this.validate(value);
|
|
99
|
+
// holy shit i used === to fix another bug
|
|
100
|
+
if (!this.client && validationResult === false)
|
|
101
|
+
return "Invalid packet";
|
|
102
|
+
const processed = this.processReceive(dcData, validationResult);
|
|
103
|
+
const useableData = this.autoFlatten ? (0, PacketUtils_1.UnFlattenData)(processed) : processed;
|
|
104
|
+
if (this.customValidator != null) {
|
|
105
|
+
if (!this.dontSpread) {
|
|
106
|
+
if (!this.customValidator(socket, ...useableData))
|
|
107
|
+
return "Didn't pass custom validator";
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
if (!this.customValidator(socket, useableData))
|
|
111
|
+
return "Didn't pass custom validator";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return [useableData, !this.dontSpread];
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
console.error("There was an error processing the packet! This is probably my fault... report at https://github.com/liwybloc/sonic-ws", err);
|
|
118
|
+
return "Error: " + err;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
serialize() {
|
|
122
|
+
// shared values for both
|
|
123
|
+
const sharedData = [
|
|
124
|
+
this.tag.length, ...(0, StringUtil_1.processCharCodes)(this.tag),
|
|
125
|
+
(0, CompressionUtil_1.compressBools)([this.dontSpread, this.async, this.object, this.autoFlatten, this.gzipCompression, this.rereference]),
|
|
126
|
+
this.dataBatching,
|
|
127
|
+
this.enumData.length, ...this.enumData.map(x => x.serialize()).flat(),
|
|
128
|
+
];
|
|
129
|
+
// single-value packet (not an object schema)
|
|
130
|
+
if (!this.object) {
|
|
131
|
+
return [
|
|
132
|
+
...sharedData, // shared
|
|
133
|
+
...(0, CompressionUtil_1.convertVarInt)(this.dataMax), // the data max
|
|
134
|
+
...(0, CompressionUtil_1.convertVarInt)(this.dataMin), // the data min
|
|
135
|
+
this.type,
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
// object packet
|
|
139
|
+
return [
|
|
140
|
+
...sharedData,
|
|
141
|
+
this.maxSize, // size
|
|
142
|
+
...this.dataMax.map(CompressionUtil_1.convertVarInt).flat(), // all data maxes, serialized
|
|
143
|
+
...this.dataMin.map(CompressionUtil_1.convertVarInt).flat(), // all data mins, serialized
|
|
144
|
+
...this.type, // all types
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
static readVarInts(data, offset, size) {
|
|
148
|
+
const res = [];
|
|
149
|
+
for (let i = 0; i < size; i++) {
|
|
150
|
+
const [off, varint] = (0, CompressionUtil_1.readVarInt)(data, offset);
|
|
151
|
+
offset = off;
|
|
152
|
+
res.push(varint);
|
|
153
|
+
}
|
|
154
|
+
return [res, offset];
|
|
155
|
+
}
|
|
156
|
+
static deserialize(data, offset, client) {
|
|
157
|
+
const beginningOffset = offset;
|
|
158
|
+
// read length, go up 1
|
|
159
|
+
const tagLength = data[offset++];
|
|
160
|
+
// read tag as it's up 1, and add offset
|
|
161
|
+
const tag = (0, StringUtil_1.as8String)(data.slice(offset, offset += tagLength));
|
|
162
|
+
// then read dontSpread and async
|
|
163
|
+
const [dontSpread, async, isObject, autoFlatten, gzipCompression, rereference] = (0, CompressionUtil_1.decompressBools)(data[offset++]);
|
|
164
|
+
// read batching, up 1
|
|
165
|
+
const dataBatching = data[offset++];
|
|
166
|
+
// read enum length, up 1
|
|
167
|
+
const enumLength = data[offset++];
|
|
168
|
+
const enums = [];
|
|
169
|
+
for (let i = 0; i < enumLength; i++) {
|
|
170
|
+
// read tag length, go up 1
|
|
171
|
+
const enumTagLength = data[offset++];
|
|
172
|
+
// up 1 so read offset -> offset += tag length, to add tag length and skip over it
|
|
173
|
+
const enumTag = (0, StringUtil_1.as8String)(data.slice(offset, offset += enumTagLength));
|
|
174
|
+
// read amount of values
|
|
175
|
+
const valueCount = data[offset++];
|
|
176
|
+
const values = [];
|
|
177
|
+
for (let j = 0; j < valueCount; j++) {
|
|
178
|
+
// read the length of the value, go up 1
|
|
179
|
+
const valueLength = data[offset++];
|
|
180
|
+
// then read the type of value, up 1
|
|
181
|
+
const valueType = data[offset++];
|
|
182
|
+
// now can just read the values, increase offset for later use
|
|
183
|
+
const value = (0, StringUtil_1.as8String)(data.slice(offset, offset += valueLength));
|
|
184
|
+
// process it
|
|
185
|
+
values.push(EnumType_1.TYPE_CONVERSION_MAP[valueType](value));
|
|
186
|
+
}
|
|
187
|
+
// define the enum with the values
|
|
188
|
+
enums.push((0, EnumHandler_1.DefineEnum)(enumTag, values));
|
|
189
|
+
}
|
|
190
|
+
// objects
|
|
191
|
+
if (isObject) {
|
|
192
|
+
// read size
|
|
193
|
+
const size = data[offset++];
|
|
194
|
+
// read var ints for the datamaxes
|
|
195
|
+
const [dataMaxes, o1] = this.readVarInts(data, offset, size);
|
|
196
|
+
offset = o1;
|
|
197
|
+
// read var ints for the datamins
|
|
198
|
+
const [dataMins, o2] = this.readVarInts(data, offset, size);
|
|
199
|
+
offset = o2;
|
|
200
|
+
// get types, skip past size since there'll be size of these
|
|
201
|
+
const types = Array.from(data.slice(offset, offset += size));
|
|
202
|
+
// convert any enums into their indexed form for best bandwidth
|
|
203
|
+
let index = 0;
|
|
204
|
+
const finalTypes = types.map(x => x == PacketType_1.PacketType.ENUMS ? enums[index++] : x); // convert enums to their enum packages
|
|
205
|
+
// make schema
|
|
206
|
+
const schema = new PacketSchema(true, finalTypes, async, dataMins, dataMaxes, -1, dontSpread, autoFlatten, false, dataBatching, -1, gzipCompression);
|
|
207
|
+
return [
|
|
208
|
+
new Packet(tag, schema, null, false, client),
|
|
209
|
+
// +1 to go next
|
|
210
|
+
(offset - beginningOffset),
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
// single packet
|
|
214
|
+
// read varint for datamax
|
|
215
|
+
const [o1, dataMax] = (0, CompressionUtil_1.readVarInt)(data, offset);
|
|
216
|
+
offset = o1;
|
|
217
|
+
// read varint for datamin
|
|
218
|
+
const [o2, dataMin] = (0, CompressionUtil_1.readVarInt)(data, offset);
|
|
219
|
+
offset = o2;
|
|
220
|
+
// read type
|
|
221
|
+
const type = data[offset++];
|
|
222
|
+
// do enum stuff
|
|
223
|
+
const finalType = type == PacketType_1.PacketType.ENUMS ? enums[0] : type; // convert enum to enum package
|
|
224
|
+
// make schema
|
|
225
|
+
const schema = new PacketSchema(false, finalType, async, dataMin, dataMax, -1, dontSpread, false, rereference, dataBatching, -1, gzipCompression);
|
|
226
|
+
return [
|
|
227
|
+
new Packet(tag, schema, null, false, client),
|
|
228
|
+
(offset - beginningOffset),
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
static deserializeAll(data, client) {
|
|
232
|
+
const arr = [];
|
|
233
|
+
let offset = 0;
|
|
234
|
+
while (offset < data.length) {
|
|
235
|
+
const [packet, len] = this.deserialize(data, offset, client);
|
|
236
|
+
arr.push(packet);
|
|
237
|
+
offset += len;
|
|
238
|
+
}
|
|
239
|
+
return arr;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.Packet = Packet;
|
|
243
|
+
const convertType = (type, ed) => (type instanceof EnumType_1.EnumPackage ? (ed.push(type), PacketType_1.PacketType.ENUMS) : type);
|
|
244
|
+
class PacketSchema {
|
|
245
|
+
type;
|
|
246
|
+
enumData = [];
|
|
247
|
+
dataMax;
|
|
248
|
+
dataMin;
|
|
249
|
+
dataBatching;
|
|
250
|
+
maxBatchSize;
|
|
251
|
+
rateLimit;
|
|
252
|
+
dontSpread = false;
|
|
253
|
+
autoFlatten = false;
|
|
254
|
+
async = false;
|
|
255
|
+
rereference = false;
|
|
256
|
+
gzipCompression = false;
|
|
257
|
+
object;
|
|
258
|
+
constructor(object, type, async, dataMin, dataMax, rateLimit, dontSpread, autoFlatten, rereference, dataBatching, maxBatchSize, gzipCompression) {
|
|
259
|
+
// todo add rereference to objects
|
|
260
|
+
this.object = object;
|
|
261
|
+
this.async = async;
|
|
262
|
+
this.dataMin = dataMin;
|
|
263
|
+
this.dataMax = dataMax;
|
|
264
|
+
this.rateLimit = rateLimit;
|
|
265
|
+
this.dontSpread = dontSpread;
|
|
266
|
+
this.autoFlatten = autoFlatten;
|
|
267
|
+
this.rereference = rereference;
|
|
268
|
+
this.dataBatching = dataBatching;
|
|
269
|
+
this.maxBatchSize = maxBatchSize;
|
|
270
|
+
this.gzipCompression = gzipCompression;
|
|
271
|
+
this.type = (object ? type.map(t => convertType(t, this.enumData)) : convertType(type, this.enumData));
|
|
272
|
+
}
|
|
273
|
+
testObject(packet) {
|
|
274
|
+
return this.object;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
exports.PacketSchema = PacketSchema;
|
|
@@ -1,6 +1,298 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
2
3
|
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
4
16
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.SonicWSConnection = void 0;
|
|
19
|
+
const PacketUtils_1 = require("../util/packets/PacketUtils");
|
|
20
|
+
const BatchHelper_1 = require("../util/packets/BatchHelper");
|
|
21
|
+
const RateHandler_1 = require("../util/packets/RateHandler");
|
|
22
|
+
const BufferUtil_1 = require("../util/BufferUtil");
|
|
23
|
+
const Connection_1 = require("../Connection");
|
|
24
|
+
const CLIENT_RATELIMIT_TAG = "C", SERVER_RATELIMIT_TAG = "S";
|
|
25
|
+
class SonicWSConnection extends Connection_1.Connection {
|
|
26
|
+
host;
|
|
27
|
+
print = false;
|
|
28
|
+
handshakePacket;
|
|
29
|
+
handshakeLambda;
|
|
30
|
+
messageLambda = (data) => this.messageHandler(this.parseData(data));
|
|
31
|
+
handshakedMessageLambda = (data) => {
|
|
32
|
+
const parsed = this.parseData(data);
|
|
33
|
+
if (parsed == null)
|
|
34
|
+
return this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
35
|
+
if (parsed[0] == this.handshakePacket)
|
|
36
|
+
return this.socket.close(Connection_1.CloseCodes.REPEATED_HANDSHAKE);
|
|
37
|
+
this.messageHandler(parsed);
|
|
38
|
+
};
|
|
39
|
+
rater;
|
|
40
|
+
enabledPackets = {};
|
|
41
|
+
/** If the packet handshake has been completed; `wss.requireHandshake(packet)` */
|
|
42
|
+
handshakeComplete = false;
|
|
43
|
+
asyncMap = {};
|
|
44
|
+
asyncData = {};
|
|
45
|
+
constructor(socket, host, id, handshakePacket, clientRateLimit, serverRateLimit) {
|
|
46
|
+
super(socket, id, "Socket " + id, socket.addEventListener.bind(socket), socket.removeEventListener.bind(socket));
|
|
47
|
+
this.host = host;
|
|
48
|
+
this.handshakePacket = handshakePacket;
|
|
49
|
+
for (const pack of host.clientPackets.getPackets()) {
|
|
50
|
+
const tag = pack.tag;
|
|
51
|
+
this.listeners[tag] = [];
|
|
52
|
+
pack.lastReceived[this.id] = undefined;
|
|
53
|
+
this.enabledPackets[tag] = pack.defaultEnabled;
|
|
54
|
+
this.asyncMap[tag] = pack.async;
|
|
55
|
+
if (pack.async)
|
|
56
|
+
this.asyncData[tag] = [false, []];
|
|
57
|
+
}
|
|
58
|
+
this.setInterval = this.setInterval.bind(this);
|
|
59
|
+
this.batcher.registerSendPackets(this.host.serverPackets, this);
|
|
60
|
+
this.invalidPacket = this.invalidPacket.bind(this);
|
|
61
|
+
this.rater = new RateHandler_1.RateHandler(this);
|
|
62
|
+
this.rater.registerRate(CLIENT_RATELIMIT_TAG, clientRateLimit);
|
|
63
|
+
this.rater.registerRate(SERVER_RATELIMIT_TAG, serverRateLimit);
|
|
64
|
+
this.rater.registerAll(host.clientPackets, "client");
|
|
65
|
+
this.rater.registerAll(host.serverPackets, "server");
|
|
66
|
+
this.rater.start();
|
|
67
|
+
if (this.handshakePacket == null) {
|
|
68
|
+
this.socket.addEventListener('message', this.messageLambda);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.handshakeLambda = (data) => this.handshakeHandler(data);
|
|
72
|
+
this.socket.addEventListener('message', this.handshakeLambda);
|
|
73
|
+
}
|
|
74
|
+
this.socket.on('close', () => {
|
|
75
|
+
for (const packet of host.clientPackets.getPackets()) {
|
|
76
|
+
delete packet.lastReceived[this.id];
|
|
77
|
+
}
|
|
78
|
+
for (const packet of host.serverPackets.getPackets()) {
|
|
79
|
+
delete packet.lastSent[this.id];
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
parseData(event) {
|
|
84
|
+
if (this.rater.trigger(CLIENT_RATELIMIT_TAG))
|
|
85
|
+
return null;
|
|
86
|
+
if (!(event.data instanceof Buffer))
|
|
87
|
+
return null;
|
|
88
|
+
const message = new Uint8Array(event.data);
|
|
89
|
+
if (this.print)
|
|
90
|
+
console.log(`\x1b[31m⬇ \x1b[38;5;245m(${this.id},${message.byteLength})\x1b[0m`, (message.length > 0 && this.host.clientPackets.getTag(message[0])) || "<INVALID>", (0, BufferUtil_1.stringifyBuffer)(message));
|
|
91
|
+
if (message.byteLength < 1) {
|
|
92
|
+
this.socket.close(Connection_1.CloseCodes.SMALL);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const key = message[0];
|
|
96
|
+
const value = message.slice(1);
|
|
97
|
+
// not a key, bye bye
|
|
98
|
+
if (!this.host.clientPackets.hasKey(key)) {
|
|
99
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_KEY);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const tag = this.host.clientPackets.getTag(key);
|
|
103
|
+
// disabled, bye bye
|
|
104
|
+
if (!this.enabledPackets[tag]) {
|
|
105
|
+
this.socket.close(Connection_1.CloseCodes.DISABLED_PACKET);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (this.rater.trigger("client" + key))
|
|
109
|
+
return null;
|
|
110
|
+
return [tag, value];
|
|
111
|
+
}
|
|
112
|
+
handshakeHandler(data) {
|
|
113
|
+
const parsed = this.parseData(data);
|
|
114
|
+
if (parsed == null)
|
|
115
|
+
return this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
116
|
+
if (parsed[0] != this.handshakePacket) {
|
|
117
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.messageHandler(parsed);
|
|
121
|
+
this.socket.removeEventListener('message', this.handshakeLambda);
|
|
122
|
+
this.socket.addEventListener('message', this.handshakedMessageLambda);
|
|
123
|
+
this.handshakeComplete = true;
|
|
124
|
+
}
|
|
125
|
+
invalidPacket(listened) {
|
|
126
|
+
console.log("Closure cause", listened);
|
|
127
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_PACKET, listened);
|
|
128
|
+
}
|
|
129
|
+
isAsync(tag) {
|
|
130
|
+
return this.asyncMap[tag];
|
|
131
|
+
}
|
|
132
|
+
listenLock = false;
|
|
133
|
+
packetQueue = [];
|
|
134
|
+
async listenPacket(data, tag, packetQueue, isAsync, asyncData) {
|
|
135
|
+
if (this.closed)
|
|
136
|
+
return;
|
|
137
|
+
await (0, PacketUtils_1.listenPacket)(data, this.listeners[tag], this.invalidPacket);
|
|
138
|
+
await this.callMiddleware('onReceive_post', tag, typeof data == 'string' ? [data] : data[0]);
|
|
139
|
+
if (isAsync)
|
|
140
|
+
asyncData[0] = false;
|
|
141
|
+
else
|
|
142
|
+
this.listenLock = false;
|
|
143
|
+
if (packetQueue.length != 0) {
|
|
144
|
+
this.messageHandler(packetQueue.shift());
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async messageHandler(data, recall = false) {
|
|
149
|
+
if (data == null)
|
|
150
|
+
return;
|
|
151
|
+
const [tag, value] = data;
|
|
152
|
+
const isAsync = this.isAsync(tag);
|
|
153
|
+
let locked, packetQueue, asyncData;
|
|
154
|
+
if (isAsync) {
|
|
155
|
+
asyncData = this.asyncData[tag];
|
|
156
|
+
locked = asyncData[0];
|
|
157
|
+
packetQueue = asyncData[1];
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
locked = this.listenLock;
|
|
161
|
+
packetQueue = this.packetQueue;
|
|
162
|
+
}
|
|
163
|
+
if (locked) {
|
|
164
|
+
packetQueue.push(data);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (isAsync)
|
|
168
|
+
asyncData[0] = true;
|
|
169
|
+
else
|
|
170
|
+
this.listenLock = true;
|
|
171
|
+
const packet = this.host.clientPackets.getPacket(tag);
|
|
172
|
+
if (!recall && await this.callMiddleware('onReceive_pre', packet.tag, value, value.length))
|
|
173
|
+
return;
|
|
174
|
+
if (packet.rereference && value.length == 0) {
|
|
175
|
+
const lastRecv = packet.lastReceived[this.id];
|
|
176
|
+
if (lastRecv === undefined)
|
|
177
|
+
return this.invalidPacket("No previous value to rereference");
|
|
178
|
+
await this.listenPacket(lastRecv, tag, packetQueue, isAsync, asyncData);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (packet.dataBatching == 0) {
|
|
182
|
+
const res = await packet.listen(value, this);
|
|
183
|
+
packet.lastReceived[this.id] = res;
|
|
184
|
+
await this.listenPacket(res, tag, packetQueue, isAsync, asyncData);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const batchData = await BatchHelper_1.BatchHelper.unravelBatch(packet, value, this);
|
|
188
|
+
if (typeof batchData == 'string')
|
|
189
|
+
return this.invalidPacket(batchData);
|
|
190
|
+
for (const data of batchData) {
|
|
191
|
+
if (isAsync)
|
|
192
|
+
asyncData[0] = true;
|
|
193
|
+
else
|
|
194
|
+
this.listenLock = true;
|
|
195
|
+
await this.listenPacket(data, tag, packetQueue, isAsync, asyncData);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Enables a packet for the client.
|
|
200
|
+
* @param tag The tag of the packet
|
|
201
|
+
*/
|
|
202
|
+
enablePacket(tag) {
|
|
203
|
+
this.enabledPackets[tag] = true;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Disables a packet for the client.
|
|
207
|
+
* @param tag The tag of the packet
|
|
208
|
+
*/
|
|
209
|
+
disablePacket(tag) {
|
|
210
|
+
this.enabledPackets[tag] = false;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Listens for when the connection closes
|
|
214
|
+
* @param listener Called when it closes
|
|
215
|
+
*/
|
|
216
|
+
on_close(listener) {
|
|
217
|
+
this.socket.on('close', (code, reason) => listener(code, String(reason)));
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Listens for a packet
|
|
221
|
+
* @param tag The tag of the key to listen for
|
|
222
|
+
* @param listener A function to listen for it
|
|
223
|
+
*/
|
|
224
|
+
on(tag, listener) {
|
|
225
|
+
if (!this.host.clientPackets.hasTag(tag))
|
|
226
|
+
throw new Error(`Tag "${String(tag)}" has not been created!`);
|
|
227
|
+
this.listeners[tag] ??= [];
|
|
228
|
+
this.listeners[tag].push(listener);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* For internal use.
|
|
232
|
+
*/
|
|
233
|
+
send_processed(code, data, packet) {
|
|
234
|
+
if (this.rater.trigger("server" + code))
|
|
235
|
+
return;
|
|
236
|
+
if (packet.dataBatching == 0)
|
|
237
|
+
this.raw_send((0, BufferUtil_1.toPacketBuffer)(code, data));
|
|
238
|
+
else
|
|
239
|
+
this.batcher.batchPacket(code, data);
|
|
240
|
+
}
|
|
241
|
+
sendQueue = [false, [], undefined];
|
|
242
|
+
/**
|
|
243
|
+
* Sends a packet with the tag and values
|
|
244
|
+
* @param tag The tag to send
|
|
245
|
+
* @param values The values to send
|
|
246
|
+
*/
|
|
247
|
+
async send(tag, ...values) {
|
|
248
|
+
if (await this.callMiddleware('onSend_pre', tag, values, Date.now(), performance.now()))
|
|
249
|
+
return;
|
|
250
|
+
const [code, data, packet] = await (0, PacketUtils_1.processPacket)(this.host.serverPackets, tag, values, this.sendQueue, this.id);
|
|
251
|
+
if (await this.callMiddleware('onSend_post', tag, data, data.length))
|
|
252
|
+
return;
|
|
253
|
+
this.send_processed(code, data, packet);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Broadcasts a packet to all other users connected
|
|
257
|
+
* @param tag The tag to send
|
|
258
|
+
* @param values The values to send
|
|
259
|
+
*/
|
|
260
|
+
broadcastFiltered(tag, filter, ...values) {
|
|
261
|
+
this.host.broadcastFiltered(tag, (socket) => socket != this && filter(socket), ...values);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Broadcasts a packet to all other users connected
|
|
265
|
+
* @param tag The tag to send
|
|
266
|
+
* @param values The values to send
|
|
267
|
+
*/
|
|
268
|
+
broadcast(tag, ...values) {
|
|
269
|
+
this.broadcastFiltered(tag, () => true, ...values);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Toggles printing all sent and received messages
|
|
273
|
+
*/
|
|
274
|
+
togglePrint() {
|
|
275
|
+
this.print = !this.print;
|
|
276
|
+
}
|
|
277
|
+
/* JSDocs in Connection.ts class */
|
|
278
|
+
raw_send(data) {
|
|
279
|
+
if (this.isClosed()) {
|
|
280
|
+
console.warn("WARN! Connection already closed when trying to send message!", this.id, (0, BufferUtil_1.stringifyBuffer)(data));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (this.rater.trigger(SERVER_RATELIMIT_TAG))
|
|
284
|
+
return;
|
|
285
|
+
if (this.print)
|
|
286
|
+
console.log(`\x1b[32m⬆ \x1b[38;5;245m(${this.id},${data.byteLength})\x1b[0m`, (data.length > 0 && this.host.serverPackets.getTag(data[0])) || "<INVALID>", (0, BufferUtil_1.stringifyBuffer)(data));
|
|
287
|
+
super.raw_send(data);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Tags the socket with a key
|
|
291
|
+
* @param tag The tag to add
|
|
292
|
+
* @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
|
|
293
|
+
*/
|
|
294
|
+
tag(tag, replace = true) {
|
|
295
|
+
this.host.tag(this, tag, replace);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.SonicWSConnection = SonicWSConnection;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
* Licensed under the Apache License, Version 2.0.
|
|
4
|
-
*/
|
|
5
1
|
import * as WS from 'ws';
|
|
6
2
|
import { SonicWSConnection } from "./SonicWSConnection";
|
|
7
3
|
import { PacketHolder } from "../util/packets/PacketHolder";
|
|
@@ -136,7 +132,7 @@ export declare class SonicWSServer extends MiddlewareHolder<ServerMiddleware> {
|
|
|
136
132
|
* @param port Port of the server/http, defaults to 0 which finds an open port
|
|
137
133
|
* @param password Toggles the requirement of a password to access the server. Defaults to empty, which doesn't ask for a password.
|
|
138
134
|
*/
|
|
139
|
-
OpenDebug(data
|
|
135
|
+
OpenDebug(data?: {
|
|
140
136
|
port?: number;
|
|
141
137
|
password?: string;
|
|
142
138
|
}): void;
|