sonic-ws 1.2.2-why → 1.3.0-min
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 +656 -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
|
@@ -47,14 +47,22 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
47
47
|
return result;
|
|
48
48
|
};
|
|
49
49
|
})();
|
|
50
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
+
};
|
|
50
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
54
|
exports.SonicWSServer = void 0;
|
|
52
55
|
const WS = __importStar(require("ws"));
|
|
56
|
+
const http_1 = __importDefault(require("http"));
|
|
57
|
+
const open_1 = __importDefault(require("open"));
|
|
53
58
|
const SonicWSConnection_1 = require("./SonicWSConnection");
|
|
54
59
|
const PacketHolder_1 = require("../util/packets/PacketHolder");
|
|
55
60
|
const CompressionUtil_1 = require("../util/packets/CompressionUtil");
|
|
56
61
|
const version_1 = require("../../version");
|
|
57
62
|
const PacketUtils_1 = require("../util/packets/PacketUtils");
|
|
63
|
+
const PacketType_1 = require("../packets/PacketType");
|
|
64
|
+
const HashUtil_1 = require("../util/packets/HashUtil");
|
|
65
|
+
const Connection_1 = require("../Connection");
|
|
58
66
|
class SonicWSServer {
|
|
59
67
|
wss;
|
|
60
68
|
availableIds = [];
|
|
@@ -69,6 +77,7 @@ class SonicWSServer {
|
|
|
69
77
|
handshakePacket = null;
|
|
70
78
|
tags = new Map();
|
|
71
79
|
tagsInv = new Map();
|
|
80
|
+
serverwideSendQueue = [false, [], undefined];
|
|
72
81
|
/**
|
|
73
82
|
* Initializes and hosts a websocket with sonic protocol
|
|
74
83
|
* Rate limits can be set with wss.setClientRateLimit(x) and wss.setServerRateLimit(x); it is defaulted at 500/second per both
|
|
@@ -83,15 +92,22 @@ class SonicWSServer {
|
|
|
83
92
|
const s_serverPackets = this.serverPackets.serialize();
|
|
84
93
|
const serverData = [...version_1.SERVER_SUFFIX_NUMS, version_1.VERSION];
|
|
85
94
|
const keyData = [...(0, CompressionUtil_1.convertVarInt)(s_clientPackets.length), ...s_clientPackets, ...s_serverPackets];
|
|
95
|
+
(0, HashUtil_1.setHashFunc)(settings.sonicServerSettings?.bit64Hash ?? true);
|
|
86
96
|
this.wss.on('connection', async (socket) => {
|
|
87
97
|
const sonicConnection = new SonicWSConnection_1.SonicWSConnection(socket, this, this.generateSocketID(), this.handshakePacket, this.clientRateLimit, this.serverRateLimit);
|
|
98
|
+
if (await this.callMiddleware("onClientConnect", sonicConnection)) {
|
|
99
|
+
sonicConnection.close(Connection_1.CloseCodes.MIDDLEWARE, "Connection blocked by middleware.");
|
|
100
|
+
this.callMiddleware("onClientDisconnect", sonicConnection, Connection_1.CloseCodes.MIDDLEWARE, Buffer.from("Connection blocked by middleware."));
|
|
101
|
+
this.availableIds.push(sonicConnection.id);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
88
104
|
// send tags to the client so it doesn't have to hard code them in
|
|
89
105
|
const data = new Uint8Array([...(0, CompressionUtil_1.convertVarInt)(sonicConnection.id), ...keyData]);
|
|
90
106
|
socket.send([...serverData, ...await (0, CompressionUtil_1.compressGzip)(data)]);
|
|
91
107
|
this.connections.push(sonicConnection);
|
|
92
108
|
this.connectionMap[sonicConnection.id] = sonicConnection;
|
|
93
109
|
this.connectListeners.forEach(l => l(sonicConnection));
|
|
94
|
-
socket.on('close', () => {
|
|
110
|
+
socket.on('close', (code, reason) => {
|
|
95
111
|
this.connections.splice(this.connections.indexOf(sonicConnection), 1);
|
|
96
112
|
delete this.connectionMap[sonicConnection.id];
|
|
97
113
|
this.availableIds.push(sonicConnection.id);
|
|
@@ -100,9 +116,10 @@ class SonicWSServer {
|
|
|
100
116
|
this.tagsInv.get(tag)?.delete(sonicConnection);
|
|
101
117
|
this.tags.delete(sonicConnection);
|
|
102
118
|
}
|
|
119
|
+
this.callMiddleware("onClientDisconnect", sonicConnection, code, reason);
|
|
103
120
|
});
|
|
104
121
|
});
|
|
105
|
-
if (settings.checkForUpdates ?? true) {
|
|
122
|
+
if (settings.sonicServerSettings?.checkForUpdates ?? true) {
|
|
106
123
|
fetch('https://raw.githubusercontent.com/liwybloc/sonic-ws/refs/heads/main/release/version')
|
|
107
124
|
.then((res) => res.text())
|
|
108
125
|
.then((ver) => {
|
|
@@ -116,6 +133,35 @@ class SonicWSServer {
|
|
|
116
133
|
});
|
|
117
134
|
}
|
|
118
135
|
}
|
|
136
|
+
middlewares = [];
|
|
137
|
+
addMiddleware(middleware) {
|
|
138
|
+
this.middlewares.push(middleware);
|
|
139
|
+
const m = middleware;
|
|
140
|
+
try {
|
|
141
|
+
if (typeof m.init === 'function')
|
|
142
|
+
m.init(this);
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
console.warn('Middleware init threw an error:', e);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async callMiddleware(method, ...values) {
|
|
149
|
+
let cancelled = false;
|
|
150
|
+
for (const middleware of this.middlewares) {
|
|
151
|
+
const fn = middleware[method];
|
|
152
|
+
if (!fn)
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
if (await fn(...values)) {
|
|
156
|
+
cancelled = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
console.warn(`Middleware ${String(method)} threw an error:`, e);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return cancelled;
|
|
164
|
+
}
|
|
119
165
|
generateSocketID() {
|
|
120
166
|
if (this.availableIds.length == 0)
|
|
121
167
|
this.availableIds.push(this.lastId + 1);
|
|
@@ -210,35 +256,36 @@ class SonicWSServer {
|
|
|
210
256
|
shutdown(callback) {
|
|
211
257
|
this.wss.close(callback);
|
|
212
258
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
259
|
+
async broadcastInternal(packetTag, target, values) {
|
|
260
|
+
let recipients;
|
|
261
|
+
if (target.type === "all") {
|
|
262
|
+
recipients = this.connections;
|
|
263
|
+
}
|
|
264
|
+
else if (target.type === "tagged") {
|
|
265
|
+
if (!this.tagsInv.has(target.tag))
|
|
266
|
+
return;
|
|
267
|
+
recipients = Array.from(this.tagsInv.get(target.tag));
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
recipients = this.connections.filter(target.filter);
|
|
271
|
+
}
|
|
272
|
+
if (await this.callMiddleware("onPacketBroadcast_pre", packetTag, { recipients, ...target }, values))
|
|
273
|
+
return;
|
|
274
|
+
if (recipients.length === 0)
|
|
221
275
|
return;
|
|
222
|
-
const data = await (0, PacketUtils_1.processPacket)(this.serverPackets, packetTag, values, -1);
|
|
223
|
-
this.
|
|
276
|
+
const [code, data, packet] = await (0, PacketUtils_1.processPacket)(this.serverPackets, packetTag, values, this.serverwideSendQueue, -1);
|
|
277
|
+
if (await this.callMiddleware("onPacketBroadcast_post", packetTag, { recipients, ...target }, data, data.length))
|
|
278
|
+
return;
|
|
279
|
+
recipients.forEach(conn => conn.send_processed(code, data, packet));
|
|
280
|
+
}
|
|
281
|
+
async broadcastTagged(tag, packetTag, ...values) {
|
|
282
|
+
await this.broadcastInternal(packetTag, { type: "tagged", tag }, values);
|
|
224
283
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Broadcasts a packet to all users connected, but with a filter
|
|
227
|
-
* @param tag The tag to send
|
|
228
|
-
* @param filter The filter for who to send to
|
|
229
|
-
* @param values The values to send
|
|
230
|
-
*/
|
|
231
284
|
async broadcastFiltered(tag, filter, ...values) {
|
|
232
|
-
|
|
233
|
-
this.connections.filter(filter).forEach(conn => conn.send_processed(...data));
|
|
285
|
+
await this.broadcastInternal(tag, { type: "filter", filter }, values);
|
|
234
286
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
* @param tag The tag to send
|
|
238
|
-
* @param values The values to send
|
|
239
|
-
*/
|
|
240
|
-
broadcast(tag, ...values) {
|
|
241
|
-
this.broadcastFiltered(tag, () => true, ...values);
|
|
287
|
+
async broadcast(tag, ...values) {
|
|
288
|
+
await this.broadcastInternal(tag, { type: "all" }, values);
|
|
242
289
|
}
|
|
243
290
|
/**
|
|
244
291
|
* @returns All users connected to the socket
|
|
@@ -277,5 +324,587 @@ class SonicWSServer {
|
|
|
277
324
|
this.tags.get(socket).add(tag);
|
|
278
325
|
this.tagsInv.get(tag).add(socket);
|
|
279
326
|
}
|
|
327
|
+
debugServer = null;
|
|
328
|
+
/**
|
|
329
|
+
* Opens a debug menu; this launches the browser and starts a subserver
|
|
330
|
+
* @param port Port of the server/http, defaults to 0 which finds an open port
|
|
331
|
+
* @param password Toggles the requirement of a password to access the server. Defaults to empty, which doesn't ask for a password.
|
|
332
|
+
*/
|
|
333
|
+
OpenDebug(data) {
|
|
334
|
+
if (this.debugServer != null)
|
|
335
|
+
throw new Error("Attempted to open a debug server when one has already been opened.");
|
|
336
|
+
data.port ??= 0;
|
|
337
|
+
data.password ??= "";
|
|
338
|
+
if (data.port < 0 || data.port >= 65536)
|
|
339
|
+
throw new Error("Port out of range!");
|
|
340
|
+
/**
|
|
341
|
+
* `
|
|
342
|
+
<!DOCTYPE html>
|
|
343
|
+
<html>
|
|
344
|
+
<head>
|
|
345
|
+
<meta charset="UTF-8">
|
|
346
|
+
<script src="https://cdn.jsdelivr.net/gh/liwybloc/sonic-ws/bundled/SonicWS_bundle.js"></script>
|
|
347
|
+
<title>SonicWS Debug Menu</title>
|
|
348
|
+
<style>
|
|
349
|
+
body {
|
|
350
|
+
margin: 0;
|
|
351
|
+
font-family: Inter, Arial, sans-serif;
|
|
352
|
+
background: #0f1115;
|
|
353
|
+
color: #e6e6e6;
|
|
354
|
+
height: 100vh;
|
|
355
|
+
display: flex;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
#sidebar {
|
|
359
|
+
width: 260px;
|
|
360
|
+
background: #141821;
|
|
361
|
+
border-right: 1px solid #1f2533;
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-direction: column;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
#sidebar-header {
|
|
367
|
+
padding: 16px;
|
|
368
|
+
font-weight: 600;
|
|
369
|
+
font-size: 18px;
|
|
370
|
+
border-bottom: 1px solid #1f2533;
|
|
371
|
+
cursor: pointer;
|
|
372
|
+
transition: color 0.2s;
|
|
373
|
+
}
|
|
374
|
+
#sidebar-header:hover {
|
|
375
|
+
color: #dddddd;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
#socket-list {
|
|
379
|
+
flex: 1;
|
|
380
|
+
overflow-y: auto;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.socket-item {
|
|
384
|
+
padding: 10px 14px;
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
border-bottom: 1px solid #1f2533;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.socket-item:hover {
|
|
390
|
+
background: #1b2030;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.socket-item.active {
|
|
394
|
+
background: #22294a;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.socket-id {
|
|
398
|
+
font-size: 12px;
|
|
399
|
+
opacity: 0.7;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#main {
|
|
403
|
+
flex: 1;
|
|
404
|
+
display: flex;
|
|
405
|
+
flex-direction: column;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
#main-header {
|
|
409
|
+
padding: 17px;
|
|
410
|
+
border-bottom: 1px solid #1f2533;
|
|
411
|
+
display: flex;
|
|
412
|
+
justify-content: space-between;
|
|
413
|
+
align-items: center;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#stats {
|
|
417
|
+
display: flex;
|
|
418
|
+
gap: 20px;
|
|
419
|
+
font-size: 13px;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#packets {
|
|
423
|
+
flex: 1;
|
|
424
|
+
overflow-y: auto;
|
|
425
|
+
padding: 12px;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.packet {
|
|
429
|
+
background: #1a1f2e;
|
|
430
|
+
border-radius: 6px;
|
|
431
|
+
padding: 6px 8px;
|
|
432
|
+
margin-bottom: 4px;
|
|
433
|
+
font-size: 12px;
|
|
434
|
+
cursor: pointer;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.packet.sent { border-left: 3px solid #3cff7a; }
|
|
438
|
+
.packet.recv { border-left: 3px solid #ff5a5a; }
|
|
439
|
+
|
|
440
|
+
.packet-details {
|
|
441
|
+
display: none;
|
|
442
|
+
margin-top: 4px;
|
|
443
|
+
opacity: 0.75;
|
|
444
|
+
font-size: 11px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.packet.expanded .packet-details {
|
|
448
|
+
display: block;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
button {
|
|
452
|
+
background: none;
|
|
453
|
+
}
|
|
454
|
+
</style>
|
|
455
|
+
</head>
|
|
456
|
+
<body>
|
|
457
|
+
<div id="sidebar">
|
|
458
|
+
<div id="sidebar-header">Sonic WS Debug Menu</div>
|
|
459
|
+
<div id="socket-list"></div>
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<div id="main">
|
|
463
|
+
<div id="main-header">
|
|
464
|
+
<button id="close-socket" style="display:none;">❌</button>
|
|
465
|
+
<div id="socket-title">Server Home</div>
|
|
466
|
+
<div id="stats"></div>
|
|
467
|
+
</div>
|
|
468
|
+
<div id="home" style="display: block; padding: 16px;">
|
|
469
|
+
<h2>Server Dashboard</h2>
|
|
470
|
+
<div id="global-stats" style="margin-bottom:16px;"></div>
|
|
471
|
+
<h3>Connection Logs</h3>
|
|
472
|
+
<ul id="connection-logs" style="max-height:200px; overflow-y:auto; padding-left:16px;"></ul>
|
|
473
|
+
<div style="margin-top:16px;">
|
|
474
|
+
<table style="width:100%; border-collapse:collapse;">
|
|
475
|
+
<thead>
|
|
476
|
+
<tr>
|
|
477
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Socket ID</th>
|
|
478
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Name</th>
|
|
479
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Status</th>
|
|
480
|
+
</tr>
|
|
481
|
+
</thead>
|
|
482
|
+
<tbody id="connection-table"></tbody>
|
|
483
|
+
</table>
|
|
484
|
+
</div>
|
|
485
|
+
</div>
|
|
486
|
+
<div id="packets"></div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<script>
|
|
490
|
+
const socketList = document.getElementById('socket-list');
|
|
491
|
+
const packetsDiv = document.getElementById('packets');
|
|
492
|
+
const socketTitle = document.getElementById('socket-title');
|
|
493
|
+
const debugTitle = document.getElementById('sidebar-header');
|
|
494
|
+
const statsDiv = document.getElementById('stats');
|
|
495
|
+
const home = document.getElementById('home');
|
|
496
|
+
const closeSocketBtn = document.getElementById('close-socket');
|
|
497
|
+
|
|
498
|
+
debugTitle.onclick = () => {
|
|
499
|
+
if(activeId !== null) {
|
|
500
|
+
activeId = null;
|
|
501
|
+
packetsDiv.style.display = 'none';
|
|
502
|
+
home.style.display = 'block';
|
|
503
|
+
closeSocketBtn.style.display = 'none';
|
|
504
|
+
renderGlobalStats();
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const globalStats = {
|
|
509
|
+
totalSockets: 0,
|
|
510
|
+
totalSent: 0,
|
|
511
|
+
totalRecv: 0,
|
|
512
|
+
totalSentBytes: 0,
|
|
513
|
+
totalRecvBytes: 0,
|
|
514
|
+
totalSaved: 0,
|
|
515
|
+
startTime: 0,
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
function formatMilliseconds(ms) {
|
|
519
|
+
if (ms < 1) return '0.0s';
|
|
520
|
+
|
|
521
|
+
const units = [
|
|
522
|
+
{ label: 'week', value: 7 * 24 * 60 * 60 * 1000 },
|
|
523
|
+
{ label: 'day', value: 24 * 60 * 60 * 1000 },
|
|
524
|
+
{ label: 'hour', value: 60 * 60 * 1000 },
|
|
525
|
+
{ label: 'minute', value: 60 * 1000 },
|
|
526
|
+
];
|
|
527
|
+
|
|
528
|
+
const parts = [];
|
|
529
|
+
|
|
530
|
+
for (const { label, value } of units) {
|
|
531
|
+
const amount = Math.floor(ms / value);
|
|
532
|
+
if (amount > 0) {
|
|
533
|
+
parts.push(amount + ' ' + label + (amount !== 1 ? 's' : ''));
|
|
534
|
+
ms -= amount * value;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (ms > 0 || parts.length === 0) {
|
|
539
|
+
const seconds = (ms / 1000).toFixed(1);
|
|
540
|
+
parts.push(seconds + 's');
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (parts.length === 1) return parts[0];
|
|
544
|
+
const last = parts.pop();
|
|
545
|
+
return parts.join(', ') + ' and ' + last;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
const globalStatsDiv = document.getElementById('global-stats');
|
|
550
|
+
function renderGlobalStats() {
|
|
551
|
+
const stats = globalStats;
|
|
552
|
+
const uptime = Date.now() - stats.startTime;
|
|
553
|
+
const formattedUptime = formatMilliseconds(uptime);
|
|
554
|
+
|
|
555
|
+
globalStatsDiv.innerHTML = '<div><strong>Total Sockets:</strong> ' + stats.totalSockets + '</div><div><strong>Total Sent Packets:</strong> ' + stats.totalSent + '</div><div><strong>Total Received Packets:</strong> ' + stats.totalRecv + '</div><div><strong>Total Sent Bytes:</strong> ' + stats.totalSentBytes + ' B</div><div><strong>Total Received Bytes:</strong> ' + stats.totalRecvBytes + ' B</div><div><strong>Total Bandwidth Saved:</strong> ' + stats.totalSaved + ' B</div><div><strong>Uptime:</strong> ' + formattedUptime + '</div>';
|
|
556
|
+
}
|
|
557
|
+
setInterval(renderGlobalStats, 50);
|
|
558
|
+
|
|
559
|
+
function updateConnectionTable() {
|
|
560
|
+
const tbody = document.getElementById('connection-table');
|
|
561
|
+
tbody.innerHTML = '';
|
|
562
|
+
sockets.forEach(s => {
|
|
563
|
+
const row = document.createElement('tr');
|
|
564
|
+
row.innerHTML = '<td>' + s.id + '</td><td>' + s.name + '</td><td style="color:' + (s.el.style.color || '#0f0') + '">' + (s.el.style.color === '#f00' ? 'Disconnected' : 'Connected') + '</td>';
|
|
565
|
+
tbody.appendChild(row);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
setInterval(updateConnectionTable, 1000);
|
|
569
|
+
|
|
570
|
+
const sockets = new Map();
|
|
571
|
+
let activeId = null;
|
|
572
|
+
|
|
573
|
+
function selectSocket(id) {
|
|
574
|
+
activeId = id;
|
|
575
|
+
[...socketList.children].forEach(e => e.classList.toggle('active', e.dataset.id == id));
|
|
576
|
+
|
|
577
|
+
const s = sockets.get(id);
|
|
578
|
+
socketTitle.textContent = s.name;
|
|
579
|
+
packetsDiv.innerHTML = '';
|
|
580
|
+
s.packets.forEach(p => packetsDiv.appendChild(p.el));
|
|
581
|
+
renderStats(s);
|
|
582
|
+
home.style.display = 'none';
|
|
583
|
+
packetsDiv.style.display = 'block';
|
|
584
|
+
closeSocketBtn.style.display = 'block';
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
closeSocketBtn.onclick = () => {
|
|
588
|
+
if(activeId === null) return;
|
|
589
|
+
ws.send("close", Number(activeId));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function renderStats(s) {
|
|
593
|
+
statsDiv.innerHTML = "<div>Sent: " + s.sent + "</div><div>Recv: " + s.recv + "</div><div>Sent bytes: " + s.sentBytes + "</div><div>Recv bytes: " + s.recvBytes + "</div><div>Saved: " + s.saved + "</div>";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function addSocket(id, name) {
|
|
597
|
+
const el = document.createElement('div');
|
|
598
|
+
el.className = 'socket-item';
|
|
599
|
+
el.dataset.id = id;
|
|
600
|
+
el.innerHTML = "<div>" + name + "</div><div class=\\"socket-id\\">#" + id + "</div>";
|
|
601
|
+
el.onclick = () => selectSocket(id);
|
|
602
|
+
socketList.appendChild(el);
|
|
603
|
+
|
|
604
|
+
sockets.set(id, {
|
|
605
|
+
id,
|
|
606
|
+
name,
|
|
607
|
+
el,
|
|
608
|
+
packets: [],
|
|
609
|
+
sent: 0,
|
|
610
|
+
recv: 0,
|
|
611
|
+
sentBytes: 0,
|
|
612
|
+
recvBytes: 0,
|
|
613
|
+
saved: 0
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function removeSocket(id, code, reason, codeReason) {
|
|
618
|
+
const s = sockets.get(id);
|
|
619
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
620
|
+
|
|
621
|
+
s.el.dataset.id = id + "-closed";
|
|
622
|
+
s.el.onclick = () => selectSocket(id + "-closed");
|
|
623
|
+
sockets.set(id + "-closed", s);
|
|
624
|
+
sockets.delete(id);
|
|
625
|
+
|
|
626
|
+
if(activeId == id) activeId = id + "-closed";
|
|
627
|
+
|
|
628
|
+
const nameNode = s.el.childNodes[0];
|
|
629
|
+
nameNode.style.color = "#f00";
|
|
630
|
+
|
|
631
|
+
let circle = document.createElement('span');
|
|
632
|
+
circle.style.display = 'inline-block';
|
|
633
|
+
circle.style.width = '10px';
|
|
634
|
+
circle.style.height = '10px';
|
|
635
|
+
circle.style.borderRadius = '50%';
|
|
636
|
+
circle.style.background = '#ff5a5a';
|
|
637
|
+
circle.style.marginLeft = '8px';
|
|
638
|
+
nameNode.appendChild(circle);
|
|
639
|
+
|
|
640
|
+
// add disconnection info to home page logs
|
|
641
|
+
const logItem = document.createElement('li');
|
|
642
|
+
logItem.textContent = 'Socket #' + id + ' disconnected — Code: ' + code + ', Reason: ' + reason + ', Closure Cause: ' + codeReason;
|
|
643
|
+
document.getElementById('connection-logs').appendChild(logItem);
|
|
644
|
+
|
|
645
|
+
requestAnimationFrame(() => circle.style.width = '100%');
|
|
646
|
+
setTimeout(() => {
|
|
647
|
+
circle.remove();
|
|
648
|
+
s.el.remove();
|
|
649
|
+
if(activeId == id + "-closed") {
|
|
650
|
+
packetsDiv.style.display = 'none';
|
|
651
|
+
home.style.display = 'block';
|
|
652
|
+
closeSocketBtn.style.display = 'none';
|
|
653
|
+
}
|
|
654
|
+
}, 30000);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function addPacket(id, dir, tag, rawSize, saved, info, date, processTime) {
|
|
658
|
+
const s = sockets.get(id);
|
|
659
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
660
|
+
|
|
661
|
+
const el = document.createElement('div');
|
|
662
|
+
el.className = 'packet ' + (dir === 'sent' ? 'sent' : 'recv');
|
|
663
|
+
el.innerHTML = '<div>' + tag + (info !== "undefined" ? ' — ' + info : '') + '</div><div class="packet-details">Raw Bytes: ' + rawSize + 'b (saved: ~' + saved + 'b)<br>Processed At: ' + new Date(date).toISOString() + '<br>Processing Time: ' + processTime.toFixed(2) + 'ms</div>';
|
|
664
|
+
|
|
665
|
+
el.onclick = () => el.classList.toggle('expanded');
|
|
666
|
+
|
|
667
|
+
s.packets.push({ el });
|
|
668
|
+
if (dir === 'sent') {
|
|
669
|
+
s.sent++;
|
|
670
|
+
s.sentBytes += rawSize;
|
|
671
|
+
} else {
|
|
672
|
+
s.recv++;
|
|
673
|
+
s.recvBytes += rawSize;
|
|
674
|
+
}
|
|
675
|
+
s.saved += saved;
|
|
676
|
+
|
|
677
|
+
if (activeId === id) {
|
|
678
|
+
packetsDiv.appendChild(el);
|
|
679
|
+
renderStats(s);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function setStat(i, v) {
|
|
684
|
+
globalStats[Object.keys(globalStats)[i]] = v;
|
|
685
|
+
if (activeId === null) renderGlobalStats();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const ws = new SonicWS('ws://' + location.host);
|
|
689
|
+
|
|
690
|
+
ws.on("connection", id => addSocket(id, "Socket " + id));
|
|
691
|
+
ws.on("disconnection", ([id, code], [reason, codeReason]) => removeSocket(id, code, reason, codeReason));
|
|
692
|
+
ws.on("nameChange", ([id], [name]) => {
|
|
693
|
+
const s = sockets.get(id);
|
|
694
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
695
|
+
s.name = name;
|
|
696
|
+
s.el.firstChild.textContent = name;
|
|
697
|
+
if (activeId === id) socketTitle.textContent = name;
|
|
698
|
+
});
|
|
699
|
+
ws.on("packet", ([id, size, saved], [dir], [tag], [values], [time, processTime]) => {
|
|
700
|
+
console.log("Received packet", { id, size, saved, dir, tag, values, time, processTime });
|
|
701
|
+
addPacket(id, dir, tag, size, saved, values, time, processTime);
|
|
702
|
+
});
|
|
703
|
+
ws.on("stats", (stats) => {
|
|
704
|
+
console.log("Received stats", stats);
|
|
705
|
+
stats.forEach((v, i) => setStat(i, v));
|
|
706
|
+
});
|
|
707
|
+
ws.on("stat", (i, v) => setStat(i, v));
|
|
708
|
+
|
|
709
|
+
const lastKnownPassword = localStorage.getItem("password");
|
|
710
|
+
const empty = !localStorage.getItem("req");
|
|
711
|
+
let usedPass = "";
|
|
712
|
+
ws.on_ready(() => {
|
|
713
|
+
if(empty) ws.send("password", "");
|
|
714
|
+
else ws.send("password", usedPass = (lastKnownPassword ?? prompt("Please enter password")));
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
ws.on_close(() => {
|
|
718
|
+
localStorage.setItem("req", true);
|
|
719
|
+
localStorage.removeItem("password");
|
|
720
|
+
setTimeout(() => window.location.reload(), 1000);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
ws.on("authenticated", (success) => {
|
|
724
|
+
console.log("Auth status:", success);
|
|
725
|
+
if(!success) {
|
|
726
|
+
} else {
|
|
727
|
+
localStorage.setItem("req", usedPass.length > 0);
|
|
728
|
+
localStorage.setItem("password", usedPass);
|
|
729
|
+
}
|
|
730
|
+
})
|
|
731
|
+
</script>
|
|
732
|
+
</body>
|
|
733
|
+
</html>
|
|
734
|
+
`
|
|
735
|
+
*/
|
|
736
|
+
const server = http_1.default.createServer((req, res) => {
|
|
737
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
738
|
+
res.end(`<!doctypehtml><meta charset=UTF-8><script src=https://cdn.jsdelivr.net/gh/liwybloc/sonic-ws/bundled/SonicWS_bundle.js></script><title>SonicWS Debug Menu</title><style>body{margin:0;font-family:Inter,Arial,sans-serif;background:#0f1115;color:#e6e6e6;height:100vh;display:flex}#sidebar{width:260px;background:#141821;border-right:1px solid #1f2533;display:flex;flex-direction:column}#sidebar-header{padding:16px;font-weight:600;font-size:18px;border-bottom:1px solid #1f2533;cursor:pointer;transition:color .2s}#sidebar-header:hover{color:#ddd}#socket-list{flex:1;overflow-y:auto}.socket-item{padding:10px 14px;cursor:pointer;border-bottom:1px solid #1f2533}.socket-item:hover{background:#1b2030}.socket-item.active{background:#22294a}.socket-id{font-size:12px;opacity:.7}#main{flex:1;display:flex;flex-direction:column}#main-header{padding:17px;border-bottom:1px solid #1f2533;display:flex;justify-content:space-between;align-items:center}#stats{display:flex;gap:20px;font-size:13px}#packets{flex:1;overflow-y:auto;padding:12px}.packet{background:#1a1f2e;border-radius:6px;padding:6px 8px;margin-bottom:4px;font-size:12px;cursor:pointer}.packet.sent{border-left:3px solid #3cff7a}.packet.recv{border-left:3px solid #ff5a5a}.packet-details{display:none;margin-top:4px;opacity:.75;font-size:11px}.packet.expanded .packet-details{display:block}button{background:0 0}</style><div id=sidebar><div id=sidebar-header>Sonic WS Debug Menu</div><div id=socket-list></div></div><div id=main><div id=main-header><button id=close-socket style=display:none>❌</button><div id=socket-title>Server Home</div><div id=stats></div></div><div id=home style=display:block;padding:16px><h2>Server Dashboard</h2><div id=global-stats style=margin-bottom:16px></div><h3>Connection Logs</h3><ul id=connection-logs style=max-height:200px;overflow-y:auto;padding-left:16px></ul><div style=margin-top:16px><table style=width:100%;border-collapse:collapse><thead><tr><th style="border-bottom:1px solid #444;text-align:left">Socket ID<th style="border-bottom:1px solid #444;text-align:left">Name<th style="border-bottom:1px solid #444;text-align:left">Status<tbody id=connection-table></table></div></div><div id=packets></div></div><script>const socketList=document.getElementById("socket-list"),packetsDiv=document.getElementById("packets"),socketTitle=document.getElementById("socket-title"),debugTitle=document.getElementById("sidebar-header"),statsDiv=document.getElementById("stats"),home=document.getElementById("home"),closeSocketBtn=document.getElementById("close-socket");debugTitle.onclick=()=>{null!==activeId&&(activeId=null,packetsDiv.style.display="none",home.style.display="block",closeSocketBtn.style.display="none",renderGlobalStats())};const globalStats={totalSockets:0,totalSent:0,totalRecv:0,totalSentBytes:0,totalRecvBytes:0,totalSaved:0,startTime:0};function formatMilliseconds(e){if(e<1)return"0.0s";const t=[{label:"week",value:6048e5},{label:"day",value:864e5},{label:"hour",value:36e5},{label:"minute",value:6e4}],s=[];for(const{label:o,value:n}of t){const t=Math.floor(e/n);t>0&&(s.push(t+" "+o+(1!==t?"s":"")),e-=t*n)}if(e>0||0===s.length){const t=(e/1e3).toFixed(1);s.push(t+"s")}if(1===s.length)return s[0];const o=s.pop();return s.join(", ")+" and "+o}const globalStatsDiv=document.getElementById("global-stats");function renderGlobalStats(){const e=globalStats,t=formatMilliseconds(Date.now()-e.startTime);globalStatsDiv.innerHTML="<div><strong>Total Sockets:</strong> "+e.totalSockets+"</div><div><strong>Total Sent Packets:</strong> "+e.totalSent+"</div><div><strong>Total Received Packets:</strong> "+e.totalRecv+"</div><div><strong>Total Sent Bytes:</strong> "+e.totalSentBytes+" B</div><div><strong>Total Received Bytes:</strong> "+e.totalRecvBytes+" B</div><div><strong>Total Bandwidth Saved:</strong> "+e.totalSaved+" B</div><div><strong>Uptime:</strong> "+t+"</div>"}function updateConnectionTable(){const e=document.getElementById("connection-table");e.innerHTML="",sockets.forEach((t=>{const s=document.createElement("tr");s.innerHTML="<td>"+t.id+"</td><td>"+t.name+'</td><td style="color:'+(t.el.style.color||"#0f0")+'">'+("#f00"===t.el.style.color?"Disconnected":"Connected")+"</td>",e.appendChild(s)}))}setInterval(renderGlobalStats,50),setInterval(updateConnectionTable,1e3);const sockets=new Map;let activeId=null;function selectSocket(e){activeId=e,[...socketList.children].forEach((t=>t.classList.toggle("active",t.dataset.id==e)));const t=sockets.get(e);socketTitle.textContent=t.name,packetsDiv.innerHTML="",t.packets.forEach((e=>packetsDiv.appendChild(e.el))),renderStats(t),home.style.display="none",packetsDiv.style.display="block",closeSocketBtn.style.display="block"}function renderStats(e){statsDiv.innerHTML="<div>Sent: "+e.sent+"</div><div>Recv: "+e.recv+"</div><div>Sent bytes: "+e.sentBytes+"</div><div>Recv bytes: "+e.recvBytes+"</div><div>Saved: "+e.saved+"</div>"}function addSocket(e,t){const s=document.createElement("div");s.className="socket-item",s.dataset.id=e,s.innerHTML="<div>"+t+'</div><div class="socket-id">#'+e+"</div>",s.onclick=()=>selectSocket(e),socketList.appendChild(s),sockets.set(e,{id:e,name:t,el:s,packets:[],sent:0,recv:0,sentBytes:0,recvBytes:0,saved:0})}function removeSocket(e,t,s,o){const n=sockets.get(e);if(!n)return console.error("Unknown socket!!",e);n.el.dataset.id=e+"-closed",n.el.onclick=()=>selectSocket(e+"-closed"),sockets.set(e+"-closed",n),sockets.delete(e),activeId==e&&(activeId=e+"-closed");const c=n.el.childNodes[0];c.style.color="#f00";let l=document.createElement("span");l.style.display="inline-block",l.style.width="10px",l.style.height="10px",l.style.borderRadius="50%",l.style.background="#ff5a5a",l.style.marginLeft="8px",c.appendChild(l);const a=document.createElement("li");a.textContent="Socket #"+e+" disconnected — Code: "+t+", Reason: "+s+", Closure Cause: "+o,document.getElementById("connection-logs").appendChild(a),requestAnimationFrame((()=>l.style.width="100%")),setTimeout((()=>{l.remove(),n.el.remove(),activeId==e+"-closed"&&(packetsDiv.style.display="none",home.style.display="block",closeSocketBtn.style.display="none")}),3e4)}function addPacket(e,t,s,o,n,c,l,a){const d=sockets.get(e);if(!d)return console.error("Unknown socket!!",e);const i=document.createElement("div");i.className="packet "+("sent"===t?"sent":"recv"),i.innerHTML="<div>"+s+("undefined"!==c?" — "+c:"")+'</div><div class="packet-details">Raw Bytes: '+o+"b (saved: ~"+n+"b)<br>Processed At: "+new Date(l).toISOString()+"<br>Processing Time: "+a.toFixed(2)+"ms</div>",i.onclick=()=>i.classList.toggle("expanded"),d.packets.push({el:i}),"sent"===t?(d.sent++,d.sentBytes+=o):(d.recv++,d.recvBytes+=o),d.saved+=n,activeId===e&&(packetsDiv.appendChild(i),renderStats(d))}function setStat(e,t){globalStats[Object.keys(globalStats)[e]]=t,null===activeId&&renderGlobalStats()}closeSocketBtn.onclick=()=>{null!==activeId&&ws.send("close",Number(activeId))};const ws=new SonicWS("ws://"+location.host);ws.on("connection",(e=>addSocket(e,"Socket "+e))),ws.on("disconnection",(([e,t],[s,o])=>removeSocket(e,t,s,o))),ws.on("nameChange",(([e],[t])=>{const s=sockets.get(e);if(!s)return console.error("Unknown socket!!",e);s.name=t,s.el.firstChild.textContent=t,activeId===e&&(socketTitle.textContent=t)})),ws.on("packet",(([e,t,s],[o],[n],[c],[l,a])=>{console.log("Received packet",{id:e,size:t,saved:s,dir:o,tag:n,values:c,time:l,processTime:a}),addPacket(e,o,n,t,s,c,l,a)})),ws.on("stats",(e=>{console.log("Received stats",e),e.forEach(((e,t)=>setStat(t,e)))})),ws.on("stat",((e,t)=>setStat(e,t)));const lastKnownPassword=localStorage.getItem("password"),empty=!localStorage.getItem("req");let usedPass="";ws.on_ready((()=>{empty?ws.send("password",""):ws.send("password",usedPass=lastKnownPassword??prompt("Please enter password"))})),ws.on_close((()=>{localStorage.setItem("req",!0),localStorage.removeItem("password"),setTimeout((()=>window.location.reload()),1e3)})),ws.on("authenticated",(e=>{console.log("Auth status:",e),e&&(localStorage.setItem("req",usedPass.length>0),localStorage.setItem("password",usedPass))}));</script>`);
|
|
739
|
+
});
|
|
740
|
+
const wss = new SonicWSServer({
|
|
741
|
+
clientPackets: [
|
|
742
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "password", type: PacketType_1.PacketType.STRINGS_UTF16 }),
|
|
743
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "close", type: PacketType_1.PacketType.UVARINT }),
|
|
744
|
+
],
|
|
745
|
+
serverPackets: [
|
|
746
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "authenticated", type: PacketType_1.PacketType.BOOLEANS }),
|
|
747
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "connection", type: PacketType_1.PacketType.UVARINT }),
|
|
748
|
+
(0, PacketUtils_1.CreateObjPacket)({ tag: "disconnection", types: [PacketType_1.PacketType.UVARINT, PacketType_1.PacketType.STRINGS], noDataRange: true }),
|
|
749
|
+
(0, PacketUtils_1.CreateObjPacket)({ tag: "nameChange", types: [PacketType_1.PacketType.UVARINT, PacketType_1.PacketType.STRINGS_UTF16], noDataRange: true }),
|
|
750
|
+
(0, PacketUtils_1.CreateObjPacket)({
|
|
751
|
+
tag: "packet",
|
|
752
|
+
types: [
|
|
753
|
+
PacketType_1.PacketType.VARINT,
|
|
754
|
+
PacketType_1.PacketType.STRINGS,
|
|
755
|
+
PacketType_1.PacketType.STRINGS_UTF16,
|
|
756
|
+
PacketType_1.PacketType.STRINGS_UTF16,
|
|
757
|
+
PacketType_1.PacketType.FLOATS,
|
|
758
|
+
],
|
|
759
|
+
gzipCompression: true,
|
|
760
|
+
noDataRange: true
|
|
761
|
+
}),
|
|
762
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "stats", type: PacketType_1.PacketType.UVARINT, noDataRange: true, dontSpread: true }),
|
|
763
|
+
(0, PacketUtils_1.CreatePacket)({ tag: "stat", type: PacketType_1.PacketType.UVARINT, dataMax: 2 }),
|
|
764
|
+
],
|
|
765
|
+
websocketOptions: { server },
|
|
766
|
+
});
|
|
767
|
+
const globalStats = new Proxy({
|
|
768
|
+
totalSockets: 0,
|
|
769
|
+
totalSent: 0,
|
|
770
|
+
totalRecv: 0,
|
|
771
|
+
totalSentBytes: 0,
|
|
772
|
+
totalRecvBytes: 0,
|
|
773
|
+
totalSaved: 0,
|
|
774
|
+
startTime: Date.now(),
|
|
775
|
+
}, {
|
|
776
|
+
set(target, prop, value) {
|
|
777
|
+
const key = String(prop);
|
|
778
|
+
if (target[key] !== value) {
|
|
779
|
+
target[key] = value;
|
|
780
|
+
wss.broadcast("stat", Object.keys(globalStats).indexOf(key), value);
|
|
781
|
+
}
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
// TODO: i think this is fucked by async
|
|
786
|
+
const storedPacketData = {};
|
|
787
|
+
wss.on_connect(ws => {
|
|
788
|
+
let authenticated = false;
|
|
789
|
+
const ogs = ws.send.bind(ws);
|
|
790
|
+
let queue = [];
|
|
791
|
+
ws.send = async (tag, ...values) => {
|
|
792
|
+
if (!authenticated)
|
|
793
|
+
queue.push([tag, values]);
|
|
794
|
+
else
|
|
795
|
+
ogs(tag, ...values);
|
|
796
|
+
};
|
|
797
|
+
ws.send("stats", ...Object.values(globalStats));
|
|
798
|
+
this.connections.forEach(conn => {
|
|
799
|
+
ws.send("connection", conn.id);
|
|
800
|
+
ws.send("nameChange", conn.id, conn.getName());
|
|
801
|
+
storedPacketData[conn.id]?.forEach((data) => {
|
|
802
|
+
ws.send("packet", ...data);
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
ws.on("password", (pword) => {
|
|
806
|
+
if (data.password != pword) {
|
|
807
|
+
ws.send("authenticated", false);
|
|
808
|
+
setTimeout(() => ws.close(1008), 1000);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
authenticated = true;
|
|
812
|
+
ws.send("authenticated", true);
|
|
813
|
+
queue.forEach(([tag, values]) => ogs(tag, ...values));
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
ws.on("close", (id) => {
|
|
817
|
+
if (!authenticated)
|
|
818
|
+
return;
|
|
819
|
+
this.connectionMap[id]?.close(Connection_1.CloseCodes.MANUAL_SHUTDOWN);
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
const innerConns = [];
|
|
823
|
+
const broadcastSends = {};
|
|
824
|
+
const textEncoder = new TextEncoder();
|
|
825
|
+
const length = (values) => textEncoder.encode(JSON.stringify(values) ?? "[]").length;
|
|
826
|
+
this.addMiddleware(new (class {
|
|
827
|
+
onClientConnect(connection) {
|
|
828
|
+
globalStats.totalSockets++;
|
|
829
|
+
storedPacketData[connection.id] = [];
|
|
830
|
+
innerConns.push(connection);
|
|
831
|
+
wss.broadcast("connection", connection.id);
|
|
832
|
+
const packetsSend = {};
|
|
833
|
+
const packetsRecv = {};
|
|
834
|
+
connection.addMiddleware(new (class {
|
|
835
|
+
onNameChange(name) {
|
|
836
|
+
wss.broadcast("nameChange", connection.id, name);
|
|
837
|
+
}
|
|
838
|
+
onSend_pre(tag, values, date, perfTime) {
|
|
839
|
+
packetsSend[tag] ??= [];
|
|
840
|
+
packetsSend[tag].push([values, perfTime, date]);
|
|
841
|
+
}
|
|
842
|
+
onSend_post(tag, data, sendSize) {
|
|
843
|
+
globalStats.totalSentBytes += sendSize;
|
|
844
|
+
globalStats.totalSent++;
|
|
845
|
+
const [values, perfTime, date] = packetsSend[tag].shift();
|
|
846
|
+
const jsonLength = length(values);
|
|
847
|
+
const saved = jsonLength - sendSize;
|
|
848
|
+
globalStats.totalSaved += saved;
|
|
849
|
+
const record = [
|
|
850
|
+
[connection.id, sendSize + 1, saved],
|
|
851
|
+
"sent",
|
|
852
|
+
tag,
|
|
853
|
+
JSON.stringify(values),
|
|
854
|
+
[date, performance.now() - perfTime],
|
|
855
|
+
];
|
|
856
|
+
storedPacketData[connection.id].push(record);
|
|
857
|
+
wss.broadcast("packet", ...record);
|
|
858
|
+
}
|
|
859
|
+
onReceive_pre(tag, data, recvSize) {
|
|
860
|
+
globalStats.totalRecvBytes += recvSize;
|
|
861
|
+
globalStats.totalRecv++;
|
|
862
|
+
packetsRecv[tag] ??= [];
|
|
863
|
+
packetsRecv[tag].push([data, performance.now(), Date.now()]);
|
|
864
|
+
}
|
|
865
|
+
onReceive_post(tag, values) {
|
|
866
|
+
const [data, time, date] = packetsRecv[tag].shift();
|
|
867
|
+
const jsonLength = length(values);
|
|
868
|
+
const saved = jsonLength - data.length;
|
|
869
|
+
globalStats.totalSaved += saved;
|
|
870
|
+
const record = [
|
|
871
|
+
[connection.id, data.length + 1, saved],
|
|
872
|
+
"recv",
|
|
873
|
+
tag,
|
|
874
|
+
JSON.stringify(values),
|
|
875
|
+
[date, performance.now() - time],
|
|
876
|
+
];
|
|
877
|
+
storedPacketData[connection.id].push(record);
|
|
878
|
+
wss.broadcast("packet", ...record);
|
|
879
|
+
}
|
|
880
|
+
})());
|
|
881
|
+
}
|
|
882
|
+
onClientDisconnect(connection, code, reason) {
|
|
883
|
+
globalStats.totalSockets--;
|
|
884
|
+
wss.broadcast("disconnection", [connection.id, code], [reason?.toString() ?? "UNKNOWN", (0, Connection_1.getClosureCause)(code)]);
|
|
885
|
+
delete storedPacketData[connection.id];
|
|
886
|
+
innerConns.splice(innerConns.indexOf(connection), 1);
|
|
887
|
+
}
|
|
888
|
+
onPacketBroadcast_pre(tag, info, ...values) {
|
|
889
|
+
broadcastSends[tag] ??= [];
|
|
890
|
+
broadcastSends[tag].push([values, performance.now(), Date.now()]);
|
|
891
|
+
}
|
|
892
|
+
onPacketBroadcast_post(tag, info, data, sendSize) {
|
|
893
|
+
const [values, time, date] = broadcastSends[tag].shift();
|
|
894
|
+
info.recipients.forEach(k => {
|
|
895
|
+
if (innerConns.includes(k)) {
|
|
896
|
+
k.callMiddleware("onSend_pre", tag, values, date, time);
|
|
897
|
+
k.callMiddleware("onSend_post", tag, data, sendSize);
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
})());
|
|
902
|
+
this.debugServer = server;
|
|
903
|
+
server.listen(data.port, () => {
|
|
904
|
+
const address = server.address();
|
|
905
|
+
console.log(`SWS Debug server running at http://localhost:${address.port}`);
|
|
906
|
+
(0, open_1.default)(`http://localhost:${address.port}`);
|
|
907
|
+
});
|
|
908
|
+
}
|
|
280
909
|
}
|
|
281
910
|
exports.SonicWSServer = SonicWSServer;
|