sonic-ws 1.2.2 → 1.3.0
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 +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/ws/Connection.d.ts +14 -13
- package/dist/ws/Connection.js +33 -1
- package/dist/ws/PacketProcessor.d.ts +124 -0
- package/dist/ws/PacketProcessor.js +19 -0
- package/dist/ws/client/core/ClientCore.d.ts +9 -6
- package/dist/ws/client/core/ClientCore.js +61 -52
- package/dist/ws/packets/Packets.d.ts +1 -1
- package/dist/ws/server/SonicWSConnection.d.ts +15 -5
- package/dist/ws/server/SonicWSConnection.js +63 -45
- package/dist/ws/server/SonicWSServer.d.ts +25 -21
- package/dist/ws/server/SonicWSServer.js +655 -27
- package/dist/ws/util/packets/CompressionUtil.js +1 -1
- package/dist/ws/util/packets/HashUtil.d.ts +2 -0
- package/dist/ws/util/packets/HashUtil.js +122 -0
- package/dist/ws/util/packets/PacketHolder.d.ts +2 -2
- package/dist/ws/util/packets/PacketHolder.js +2 -0
- package/dist/ws/util/packets/PacketUtils.d.ts +4 -2
- package/dist/ws/util/packets/PacketUtils.js +57 -38
- package/dist/ws/util/packets/RateHandler.js +2 -1
- package/package.json +3 -4
|
@@ -54,6 +54,7 @@ const PacketUtils_1 = require("../util/packets/PacketUtils");
|
|
|
54
54
|
const BatchHelper_1 = require("../util/packets/BatchHelper");
|
|
55
55
|
const RateHandler_1 = require("../util/packets/RateHandler");
|
|
56
56
|
const BufferUtil_1 = require("../util/BufferUtil");
|
|
57
|
+
const Connection_1 = require("../Connection");
|
|
57
58
|
const CLIENT_RATELIMIT_TAG = "C", SERVER_RATELIMIT_TAG = "S";
|
|
58
59
|
class SonicWSConnection {
|
|
59
60
|
/** Raw 'ws' library socket */
|
|
@@ -61,15 +62,16 @@ class SonicWSConnection {
|
|
|
61
62
|
host;
|
|
62
63
|
listeners;
|
|
63
64
|
print = false;
|
|
65
|
+
name;
|
|
64
66
|
handshakePacket;
|
|
65
67
|
handshakeLambda;
|
|
66
68
|
messageLambda = (data) => this.messageHandler(this.parseData(data));
|
|
67
69
|
handshakedMessageLambda = (data) => {
|
|
68
70
|
const parsed = this.parseData(data);
|
|
69
71
|
if (parsed == null)
|
|
70
|
-
return this.socket.close(
|
|
72
|
+
return this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
71
73
|
if (parsed[0] == this.handshakePacket)
|
|
72
|
-
return this.socket.close(
|
|
74
|
+
return this.socket.close(Connection_1.CloseCodes.REPEATED_HANDSHAKE);
|
|
73
75
|
this.messageHandler(parsed);
|
|
74
76
|
};
|
|
75
77
|
batcher;
|
|
@@ -83,11 +85,11 @@ class SonicWSConnection {
|
|
|
83
85
|
asyncMap = {};
|
|
84
86
|
asyncData = {};
|
|
85
87
|
closed = false;
|
|
86
|
-
_state = WebSocket.OPEN;
|
|
87
88
|
constructor(socket, host, id, handshakePacket, clientRateLimit, serverRateLimit) {
|
|
88
89
|
this.socket = socket;
|
|
89
90
|
this.host = host;
|
|
90
91
|
this.id = id;
|
|
92
|
+
this.name = "Socket " + id;
|
|
91
93
|
this.handshakePacket = handshakePacket;
|
|
92
94
|
this.listeners = {};
|
|
93
95
|
for (const tag of host.clientPackets.getTags()) {
|
|
@@ -128,6 +130,9 @@ class SonicWSConnection {
|
|
|
128
130
|
for (const packet of host.clientPackets.getPackets()) {
|
|
129
131
|
delete packet.lastReceived[this.id];
|
|
130
132
|
}
|
|
133
|
+
for (const packet of host.serverPackets.getPackets()) {
|
|
134
|
+
delete packet.lastSent[this.id];
|
|
135
|
+
}
|
|
131
136
|
});
|
|
132
137
|
}
|
|
133
138
|
parseData(event) {
|
|
@@ -137,22 +142,22 @@ class SonicWSConnection {
|
|
|
137
142
|
return null;
|
|
138
143
|
const message = new Uint8Array(event.data);
|
|
139
144
|
if (this.print)
|
|
140
|
-
console.log(`\x1b[31m⬇ \x1b[38;5;245m(${this.id},${message.byteLength})\x1b[0m`, (0, BufferUtil_1.stringifyBuffer)(message));
|
|
145
|
+
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));
|
|
141
146
|
if (message.byteLength < 1) {
|
|
142
|
-
this.socket.close(
|
|
147
|
+
this.socket.close(Connection_1.CloseCodes.SMALL);
|
|
143
148
|
return null;
|
|
144
149
|
}
|
|
145
150
|
const key = message[0];
|
|
146
151
|
const value = message.slice(1);
|
|
147
152
|
// not a key, bye bye
|
|
148
153
|
if (!this.host.clientPackets.hasKey(key)) {
|
|
149
|
-
this.socket.close(
|
|
154
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_KEY);
|
|
150
155
|
return null;
|
|
151
156
|
}
|
|
152
157
|
const tag = this.host.clientPackets.getTag(key);
|
|
153
158
|
// disabled, bye bye
|
|
154
159
|
if (!this.enabledPackets[tag]) {
|
|
155
|
-
this.socket.close(
|
|
160
|
+
this.socket.close(Connection_1.CloseCodes.DISABLED_PACKET);
|
|
156
161
|
return null;
|
|
157
162
|
}
|
|
158
163
|
if (this.rater.trigger("client" + key))
|
|
@@ -162,9 +167,9 @@ class SonicWSConnection {
|
|
|
162
167
|
handshakeHandler(data) {
|
|
163
168
|
const parsed = this.parseData(data);
|
|
164
169
|
if (parsed == null)
|
|
165
|
-
return this.socket.close(
|
|
170
|
+
return this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
166
171
|
if (parsed[0] != this.handshakePacket) {
|
|
167
|
-
this.socket.close(
|
|
172
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_DATA);
|
|
168
173
|
return;
|
|
169
174
|
}
|
|
170
175
|
this.messageHandler(parsed);
|
|
@@ -174,18 +179,31 @@ class SonicWSConnection {
|
|
|
174
179
|
}
|
|
175
180
|
invalidPacket(listened) {
|
|
176
181
|
console.log("Closure cause", listened);
|
|
177
|
-
this.socket.close(
|
|
182
|
+
this.socket.close(Connection_1.CloseCodes.INVALID_PACKET, listened);
|
|
178
183
|
}
|
|
179
184
|
isAsync(tag) {
|
|
180
185
|
return this.asyncMap[tag];
|
|
181
186
|
}
|
|
182
187
|
listenLock = false;
|
|
183
188
|
packetQueue = [];
|
|
184
|
-
async listenPacket(data, tag) {
|
|
189
|
+
async listenPacket(data, tag, packetQueue, isAsync, asyncData) {
|
|
185
190
|
if (this.closed)
|
|
186
191
|
return;
|
|
187
|
-
|
|
192
|
+
await (0, PacketUtils_1.listenPacket)(data, this.listeners[tag], this.invalidPacket);
|
|
193
|
+
await this.callMiddleware('onReceive_post', tag, typeof data == 'string' ? [data] : data[0]);
|
|
194
|
+
if (isAsync)
|
|
195
|
+
asyncData[0] = false;
|
|
196
|
+
else
|
|
197
|
+
this.listenLock = false;
|
|
198
|
+
if (packetQueue.length != 0) {
|
|
199
|
+
this.messageHandler(packetQueue.shift());
|
|
188
200
|
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async messageHandler(data, recall = false) {
|
|
204
|
+
if (data == null)
|
|
205
|
+
return;
|
|
206
|
+
const [tag, value] = data;
|
|
189
207
|
const isAsync = this.isAsync(tag);
|
|
190
208
|
let locked, packetQueue, asyncData;
|
|
191
209
|
if (isAsync) {
|
|
@@ -198,54 +216,39 @@ class SonicWSConnection {
|
|
|
198
216
|
packetQueue = this.packetQueue;
|
|
199
217
|
}
|
|
200
218
|
if (locked) {
|
|
201
|
-
packetQueue.push(
|
|
219
|
+
packetQueue.push(data);
|
|
202
220
|
return;
|
|
203
221
|
}
|
|
204
222
|
if (isAsync)
|
|
205
223
|
asyncData[0] = true;
|
|
206
224
|
else
|
|
207
225
|
this.listenLock = true;
|
|
208
|
-
let currentData = data;
|
|
209
|
-
let currentTag = tag;
|
|
210
|
-
while (true) {
|
|
211
|
-
await (0, PacketUtils_1.listenPacket)(currentData, this.listeners[currentTag], this.invalidPacket);
|
|
212
|
-
if (packetQueue.length === 0)
|
|
213
|
-
break;
|
|
214
|
-
[currentData, currentTag] = packetQueue.shift();
|
|
215
|
-
}
|
|
216
|
-
if (isAsync)
|
|
217
|
-
asyncData[0] = false;
|
|
218
|
-
else
|
|
219
|
-
this.listenLock = false;
|
|
220
|
-
}
|
|
221
|
-
async messageHandler(data) {
|
|
222
|
-
if (data == null)
|
|
223
|
-
return;
|
|
224
|
-
const [tag, value] = data;
|
|
225
226
|
const packet = this.host.clientPackets.getPacket(tag);
|
|
226
|
-
if (await this.callMiddleware('onReceive_pre', packet.tag, value))
|
|
227
|
+
if (!recall && await this.callMiddleware('onReceive_pre', packet.tag, value, value.length))
|
|
227
228
|
return;
|
|
228
229
|
if (packet.rereference && value.length == 0) {
|
|
229
|
-
|
|
230
|
+
const lastRecv = packet.lastReceived[this.id];
|
|
231
|
+
if (lastRecv === undefined)
|
|
230
232
|
return this.invalidPacket("No previous value to rereference");
|
|
231
|
-
this.listenPacket(
|
|
233
|
+
this.listenPacket(lastRecv, tag, packetQueue, isAsync, asyncData);
|
|
232
234
|
return;
|
|
233
235
|
}
|
|
234
236
|
if (packet.dataBatching == 0) {
|
|
235
|
-
const res =
|
|
236
|
-
this.
|
|
237
|
+
const res = await packet.listen(value, this);
|
|
238
|
+
packet.lastReceived[this.id] = res;
|
|
239
|
+
this.listenPacket(res, tag, packetQueue, isAsync, asyncData);
|
|
237
240
|
return;
|
|
238
241
|
}
|
|
239
242
|
const batchData = await BatchHelper_1.BatchHelper.unravelBatch(packet, value, this);
|
|
240
243
|
if (typeof batchData == 'string')
|
|
241
244
|
return this.invalidPacket(batchData);
|
|
242
245
|
for (const data of batchData) {
|
|
243
|
-
this.listenPacket(data, tag);
|
|
246
|
+
this.listenPacket(data, tag, packetQueue, isAsync, asyncData);
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.
|
|
249
|
+
middlewares = [];
|
|
250
|
+
addMiddleware(middleware) {
|
|
251
|
+
this.middlewares.push(middleware);
|
|
249
252
|
const m = middleware;
|
|
250
253
|
try {
|
|
251
254
|
if (typeof m.init === 'function')
|
|
@@ -257,12 +260,12 @@ class SonicWSConnection {
|
|
|
257
260
|
}
|
|
258
261
|
async callMiddleware(method, ...values) {
|
|
259
262
|
let cancelled = false;
|
|
260
|
-
for (const middleware of this.
|
|
263
|
+
for (const middleware of this.middlewares) {
|
|
261
264
|
const fn = middleware[method];
|
|
262
265
|
if (!fn)
|
|
263
266
|
continue;
|
|
264
267
|
try {
|
|
265
|
-
if (
|
|
268
|
+
if (await fn(...values)) {
|
|
266
269
|
cancelled = true;
|
|
267
270
|
}
|
|
268
271
|
}
|
|
@@ -322,16 +325,17 @@ class SonicWSConnection {
|
|
|
322
325
|
else
|
|
323
326
|
this.batcher.batchPacket(code, data);
|
|
324
327
|
}
|
|
328
|
+
sendQueue = [false, [], undefined];
|
|
325
329
|
/**
|
|
326
330
|
* Sends a packet with the tag and values
|
|
327
331
|
* @param tag The tag to send
|
|
328
332
|
* @param values The values to send
|
|
329
333
|
*/
|
|
330
334
|
async send(tag, ...values) {
|
|
331
|
-
if (await this.callMiddleware('onSend_pre', tag, values))
|
|
335
|
+
if (await this.callMiddleware('onSend_pre', tag, values, Date.now(), performance.now()))
|
|
332
336
|
return;
|
|
333
|
-
const [code, data, packet] = await (0, PacketUtils_1.processPacket)(this.host.serverPackets, tag, values, this.id);
|
|
334
|
-
if (await this.callMiddleware('onSend_post', tag, data))
|
|
337
|
+
const [code, data, packet] = await (0, PacketUtils_1.processPacket)(this.host.serverPackets, tag, values, this.sendQueue, this.id);
|
|
338
|
+
if (await this.callMiddleware('onSend_post', tag, data, data.length))
|
|
335
339
|
return;
|
|
336
340
|
this.send_processed(code, data, packet);
|
|
337
341
|
}
|
|
@@ -366,7 +370,7 @@ class SonicWSConnection {
|
|
|
366
370
|
if (this.rater.trigger(SERVER_RATELIMIT_TAG))
|
|
367
371
|
return;
|
|
368
372
|
if (this.print)
|
|
369
|
-
console.log(`\x1b[32m⬆ \x1b[38;5;245m(${this.id},${data.byteLength})\x1b[0m`, (0, BufferUtil_1.stringifyBuffer)(data));
|
|
373
|
+
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));
|
|
370
374
|
this.socket.send(data);
|
|
371
375
|
}
|
|
372
376
|
close(code = 1000, reason) {
|
|
@@ -401,5 +405,19 @@ class SonicWSConnection {
|
|
|
401
405
|
tag(tag, replace = true) {
|
|
402
406
|
this.host.tag(this, tag, replace);
|
|
403
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Sets the name of this connection for the debug menu; good for setting e.g. usernames on games
|
|
410
|
+
*/
|
|
411
|
+
async setName(name) {
|
|
412
|
+
if (await this.callMiddleware("onNameChange", name))
|
|
413
|
+
return;
|
|
414
|
+
this.name = name;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* @returns Name of the socket, defaults to Socket [ID] unless set with setName()
|
|
418
|
+
*/
|
|
419
|
+
getName() {
|
|
420
|
+
return this.name;
|
|
421
|
+
}
|
|
404
422
|
}
|
|
405
423
|
exports.SonicWSConnection = SonicWSConnection;
|
|
@@ -3,6 +3,13 @@ import { SonicWSConnection } from './SonicWSConnection';
|
|
|
3
3
|
import { PacketHolder } from '../util/packets/PacketHolder';
|
|
4
4
|
import { Packet } from '../packets/Packets';
|
|
5
5
|
import { PacketType } from '../packets/PacketType';
|
|
6
|
+
import { FuncKeys, MiddlewareHolder, ServerMiddleware } from '../PacketProcessor';
|
|
7
|
+
export type SonicServerSettings = {
|
|
8
|
+
/** If it should check for updates; defaults to true. */
|
|
9
|
+
readonly checkForUpdates?: boolean;
|
|
10
|
+
/** If the rereference should use a 64 bit hash which is less prone to collision (1% after ~600 million) or a 32 bit hash. Defaults to true. */
|
|
11
|
+
readonly bit64Hash?: boolean;
|
|
12
|
+
};
|
|
6
13
|
/**
|
|
7
14
|
* Sonic WS Server Options
|
|
8
15
|
*/
|
|
@@ -13,11 +20,10 @@ export type SonicServerOptions = {
|
|
|
13
20
|
readonly serverPackets?: PacketTypings;
|
|
14
21
|
/** Default WS Options */
|
|
15
22
|
readonly websocketOptions?: WS.ServerOptions;
|
|
16
|
-
|
|
17
|
-
readonly checkForUpdates?: boolean;
|
|
23
|
+
readonly sonicServerSettings?: SonicServerSettings;
|
|
18
24
|
};
|
|
19
25
|
export type PacketTypings = readonly Packet<PacketType | readonly PacketType[]>[];
|
|
20
|
-
export declare class SonicWSServer {
|
|
26
|
+
export declare class SonicWSServer implements MiddlewareHolder<ServerMiddleware> {
|
|
21
27
|
private wss;
|
|
22
28
|
private availableIds;
|
|
23
29
|
private lastId;
|
|
@@ -31,12 +37,16 @@ export declare class SonicWSServer {
|
|
|
31
37
|
private handshakePacket;
|
|
32
38
|
tags: Map<SonicWSConnection, Set<String>>;
|
|
33
39
|
tagsInv: Map<String, Set<SonicWSConnection>>;
|
|
40
|
+
private serverwideSendQueue;
|
|
34
41
|
/**
|
|
35
42
|
* Initializes and hosts a websocket with sonic protocol
|
|
36
43
|
* Rate limits can be set with wss.setClientRateLimit(x) and wss.setServerRateLimit(x); it is defaulted at 500/second per both
|
|
37
44
|
* @param settings Sonic Server Options such as schema data for client and server packets, alongside websocket options
|
|
38
45
|
*/
|
|
39
46
|
constructor(settings: SonicServerOptions);
|
|
47
|
+
private middlewares;
|
|
48
|
+
addMiddleware(middleware: ServerMiddleware): void;
|
|
49
|
+
callMiddleware<K extends FuncKeys<ServerMiddleware> & keyof ServerMiddleware>(method: K, ...values: Parameters<NonNullable<Extract<ServerMiddleware[K], (...args: any[]) => any>>>): Promise<boolean>;
|
|
40
50
|
private generateSocketID;
|
|
41
51
|
/**
|
|
42
52
|
* Requires each client to send this packet upon initialization
|
|
@@ -94,26 +104,10 @@ export declare class SonicWSServer {
|
|
|
94
104
|
* @param callback Called when server closes
|
|
95
105
|
*/
|
|
96
106
|
shutdown(callback: (err?: Error) => void): void;
|
|
97
|
-
|
|
98
|
-
* Broadcasts a packet to tagged users; this is fast as it is a record rather than looping and filtering
|
|
99
|
-
* @param tag The tag to send packets to
|
|
100
|
-
* @param packetTag Packet tag to send
|
|
101
|
-
* @param values Values to send
|
|
102
|
-
*/
|
|
107
|
+
private broadcastInternal;
|
|
103
108
|
broadcastTagged(tag: string, packetTag: string, ...values: any): Promise<void>;
|
|
104
|
-
/**
|
|
105
|
-
* Broadcasts a packet to all users connected, but with a filter
|
|
106
|
-
* @param tag The tag to send
|
|
107
|
-
* @param filter The filter for who to send to
|
|
108
|
-
* @param values The values to send
|
|
109
|
-
*/
|
|
110
109
|
broadcastFiltered(tag: string, filter: (socket: SonicWSConnection) => boolean, ...values: any): Promise<void>;
|
|
111
|
-
|
|
112
|
-
* Broadcasts a packet to all users connected
|
|
113
|
-
* @param tag The tag to send
|
|
114
|
-
* @param values The values to send
|
|
115
|
-
*/
|
|
116
|
-
broadcast(tag: string, ...values: any): void;
|
|
110
|
+
broadcast(tag: string, ...values: any): Promise<void>;
|
|
117
111
|
/**
|
|
118
112
|
* @returns All users connected to the socket
|
|
119
113
|
*/
|
|
@@ -135,4 +129,14 @@ export declare class SonicWSServer {
|
|
|
135
129
|
* @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
|
|
136
130
|
*/
|
|
137
131
|
tag(socket: SonicWSConnection, tag: string, replace?: boolean): void;
|
|
132
|
+
private debugServer;
|
|
133
|
+
/**
|
|
134
|
+
* Opens a debug menu; this launches the browser and starts a subserver
|
|
135
|
+
* @param port Port of the server/http, defaults to 0 which finds an open port
|
|
136
|
+
* @param password Toggles the requirement of a password to access the server. Defaults to empty, which doesn't ask for a password.
|
|
137
|
+
*/
|
|
138
|
+
OpenDebug(data: {
|
|
139
|
+
port?: number;
|
|
140
|
+
password?: string;
|
|
141
|
+
}): void;
|
|
138
142
|
}
|