sonic-ws 1.3.2 → 2.0.0-fix
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/LICENSE +56 -196
- package/README.md +88 -17
- package/bundled/bundle.js +6 -0
- package/bundled/bundle.wasm +0 -0
- package/dist/{index.d.ts → ts/index.d.ts} +9 -2
- package/dist/{index.js → ts/index.js} +10 -11
- package/dist/ts/native/wrapper.d.ts +64 -0
- package/dist/ts/native/wrapper.js +7 -0
- package/dist/ts/version.d.ts +16 -0
- package/dist/ts/version.js +7 -0
- package/dist/{ws → ts/ws}/Connection.d.ts +8 -2
- package/dist/{ws → ts/ws}/Connection.js +3 -2
- package/dist/{ws → ts/ws}/PacketProcessor.d.ts +8 -2
- package/dist/{ws → ts/ws}/PacketProcessor.js +4 -3
- package/dist/{ws → ts/ws}/client/core/ClientCore.d.ts +8 -2
- package/dist/ts/ws/client/core/ClientCore.js +7 -0
- package/dist/{ws → ts/ws}/client/node/ClientNode.d.ts +8 -2
- package/dist/ts/ws/client/node/ClientNode.js +7 -0
- package/dist/ts/ws/debug/DebugServer.d.ts +17 -0
- package/dist/ts/ws/debug/DebugServer.js +7 -0
- package/dist/{ws → ts/ws}/packets/PacketType.d.ts +9 -3
- package/dist/{ws → ts/ws}/packets/PacketType.js +3 -2
- package/dist/{ws → ts/ws}/packets/Packets.d.ts +8 -5
- package/dist/ts/ws/packets/Packets.js +7 -0
- package/dist/{ws → ts/ws}/server/SonicWSConnection.d.ts +8 -2
- package/dist/ts/ws/server/SonicWSConnection.js +7 -0
- package/dist/{ws → ts/ws}/server/SonicWSServer.d.ts +12 -3
- package/dist/ts/ws/server/SonicWSServer.js +7 -0
- package/dist/{ws → ts/ws}/util/BufferUtil.d.ts +8 -2
- package/dist/{ws → ts/ws}/util/BufferUtil.js +3 -2
- package/dist/ts/ws/util/StringUtil.d.ts +13 -0
- package/dist/ts/ws/util/StringUtil.js +7 -0
- package/dist/{ws → ts/ws}/util/enums/EnumHandler.d.ts +8 -2
- package/dist/{ws → ts/ws}/util/enums/EnumHandler.js +3 -2
- package/dist/ts/ws/util/enums/EnumType.d.ts +12 -0
- package/dist/{ws → ts/ws}/util/enums/EnumType.js +4 -3
- package/dist/ts/ws/util/packets/BatchHelper.d.ts +11 -0
- package/dist/ts/ws/util/packets/BatchHelper.js +7 -0
- package/dist/ts/ws/util/packets/CompressionUtil.d.ts +18 -0
- package/dist/ts/ws/util/packets/CompressionUtil.js +7 -0
- package/dist/ts/ws/util/packets/HashUtil.d.ts +12 -0
- package/dist/{ws → ts/ws}/util/packets/HashUtil.js +3 -2
- package/dist/ts/ws/util/packets/JSONUtil.d.ts +12 -0
- package/dist/ts/ws/util/packets/JSONUtil.js +7 -0
- package/dist/ts/ws/util/packets/PacketHolder.d.ts +11 -0
- package/dist/{ws → ts/ws}/util/packets/PacketHolder.js +4 -3
- package/dist/{ws → ts/ws}/util/packets/PacketUtils.d.ts +8 -2
- package/dist/ts/ws/util/packets/PacketUtils.js +7 -0
- package/dist/ts/ws/util/packets/RateHandler.d.ts +11 -0
- package/dist/{ws → ts/ws}/util/packets/RateHandler.js +3 -2
- package/package.json +14 -10
- package/dist/version.d.ts +0 -10
- package/dist/version.js +0 -6
- package/dist/ws/client/core/ClientCore.js +0 -6
- package/dist/ws/client/node/ClientNode.js +0 -6
- package/dist/ws/debug/DebugServer.d.ts +0 -11
- package/dist/ws/debug/DebugServer.js +0 -6
- package/dist/ws/packets/PacketProcessors.d.ts +0 -17
- package/dist/ws/packets/PacketProcessors.js +0 -6
- package/dist/ws/packets/Packets.js +0 -6
- package/dist/ws/server/SonicWSConnection.js +0 -6
- package/dist/ws/server/SonicWSServer.js +0 -6
- package/dist/ws/util/StringUtil.d.ts +0 -9
- package/dist/ws/util/StringUtil.js +0 -6
- package/dist/ws/util/enums/EnumType.d.ts +0 -6
- package/dist/ws/util/packets/BatchHelper.d.ts +0 -5
- package/dist/ws/util/packets/BatchHelper.js +0 -6
- package/dist/ws/util/packets/CompressionUtil.d.ts +0 -39
- package/dist/ws/util/packets/CompressionUtil.js +0 -6
- package/dist/ws/util/packets/HashUtil.d.ts +0 -6
- package/dist/ws/util/packets/JSONUtil.d.ts +0 -6
- package/dist/ws/util/packets/JSONUtil.js +0 -6
- package/dist/ws/util/packets/PacketHolder.d.ts +0 -5
- package/dist/ws/util/packets/PacketUtils.js +0 -6
- package/dist/ws/util/packets/RateHandler.d.ts +0 -5
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
import { PacketType } from "../ws/packets/PacketType";
|
|
12
|
+
import { EnumPackage } from "../ws/util/enums/EnumType";
|
|
13
|
+
export interface SonicNativeCore {
|
|
14
|
+
encodeSigned(kind: number, values: number[]): Uint8Array;
|
|
15
|
+
decodeSigned(kind: number, data: Uint8Array): number[];
|
|
16
|
+
encodeUnsigned(kind: number, values: number[]): Uint8Array;
|
|
17
|
+
decodeUnsigned(kind: number, data: Uint8Array): number[];
|
|
18
|
+
encodeFloats(kind: number, values: number[]): Uint8Array;
|
|
19
|
+
decodeFloats(kind: number, data: Uint8Array): number[];
|
|
20
|
+
encodeStrings(kind: number, values: string[]): Uint8Array;
|
|
21
|
+
decodeStrings(kind: number, data: Uint8Array): string[];
|
|
22
|
+
encodeBooleans(values: boolean[]): Uint8Array;
|
|
23
|
+
decodeBooleans(data: Uint8Array, count: number): boolean[];
|
|
24
|
+
encodeRaw(data: Uint8Array): Uint8Array;
|
|
25
|
+
decodeRaw(data: Uint8Array): Uint8Array;
|
|
26
|
+
encodeHex(value: string): Uint8Array;
|
|
27
|
+
decodeHex(data: Uint8Array): string;
|
|
28
|
+
frameObject(sectors: Uint8Array[]): Uint8Array;
|
|
29
|
+
unframeObject(data: Uint8Array, fieldCount: number): Uint8Array[];
|
|
30
|
+
encodeBatch(payloads: Uint8Array[], compress: boolean): Uint8Array;
|
|
31
|
+
decodeBatch(data: Uint8Array, compressed: boolean, maxBatchSize: number, maxOutputSize?: number): Uint8Array[];
|
|
32
|
+
deflateRaw(data: Uint8Array): Uint8Array;
|
|
33
|
+
inflateRaw(data: Uint8Array, maxOutputSize?: number): Uint8Array;
|
|
34
|
+
validateEncoded(kind: number, data: Uint8Array, min: number, max: number, compressed: boolean, batched: boolean, maxBatchSize?: number): void;
|
|
35
|
+
validateEnum(data: Uint8Array, enumSize: number, min: number, max: number): void;
|
|
36
|
+
validateObject(data: Uint8Array, kinds: number[], minimums: number[], maximums: number[], enumSizes: number[]): void;
|
|
37
|
+
}
|
|
38
|
+
export interface NativeObjectSchema {
|
|
39
|
+
types: readonly PacketType[];
|
|
40
|
+
dataMins: readonly number[];
|
|
41
|
+
dataMaxes: readonly number[];
|
|
42
|
+
enumData?: readonly EnumPackage[];
|
|
43
|
+
}
|
|
44
|
+
/** Injects a native implementation, primarily for tests or custom addon loaders. */
|
|
45
|
+
export declare function setNativeCore(core: SonicNativeCore): void;
|
|
46
|
+
/** Loads and selects the webpack-compatible browser WASM implementation. */
|
|
47
|
+
export declare function initializeWasmCore(): Promise<SonicNativeCore>;
|
|
48
|
+
/** Loads the platform-specific Node addon lazily. */
|
|
49
|
+
export declare function loadNativeCore(addonPath?: string): SonicNativeCore;
|
|
50
|
+
export declare function encodeNative(type: PacketType, input: unknown, enumData?: EnumPackage, core?: SonicNativeCore): Uint8Array;
|
|
51
|
+
export declare function decodeNative(type: PacketType, data: Uint8Array, dataMax?: number, enumData?: EnumPackage, core?: SonicNativeCore): unknown;
|
|
52
|
+
export declare function validateNative(type: PacketType, data: Uint8Array, dataMin: number, dataMax: number, options?: {
|
|
53
|
+
compressed?: boolean;
|
|
54
|
+
batched?: boolean;
|
|
55
|
+
maxBatchSize?: number;
|
|
56
|
+
enumData?: EnumPackage;
|
|
57
|
+
}, core?: SonicNativeCore): void;
|
|
58
|
+
export declare function encodeNativeObject(schema: NativeObjectSchema, fields: readonly unknown[], core?: SonicNativeCore): Uint8Array;
|
|
59
|
+
export declare function decodeNativeObject(schema: NativeObjectSchema, data: Uint8Array, core?: SonicNativeCore): unknown[];
|
|
60
|
+
export declare function validateNativeObject(schema: NativeObjectSchema, data: Uint8Array, core?: SonicNativeCore): void;
|
|
61
|
+
export declare function encodeNativeBatch(payloads: readonly Uint8Array[], compressed: boolean, core?: SonicNativeCore): Uint8Array;
|
|
62
|
+
export declare function decodeNativeBatch(data: Uint8Array, compressed: boolean, maxBatchSize?: number, maxOutputSize?: number, core?: SonicNativeCore): Uint8Array[];
|
|
63
|
+
export declare function deflateNative(data: Uint8Array, core?: SonicNativeCore): Uint8Array;
|
|
64
|
+
export declare function inflateNative(data: Uint8Array, maxOutputSize?: number, core?: SonicNativeCore): Uint8Array;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.setNativeCore=setNativeCore,exports.initializeWasmCore=initializeWasmCore,exports.loadNativeCore=loadNativeCore,exports.encodeNative=encodeNative,exports.decodeNative=decodeNative,exports.validateNative=validateNative,exports.encodeNativeObject=encodeNativeObject,exports.decodeNativeObject=decodeNativeObject,exports.validateNativeObject=validateNativeObject,exports.encodeNativeBatch=encodeNativeBatch,exports.decodeNativeBatch=decodeNativeBatch,exports.deflateNative=deflateNative,exports.inflateNative=inflateNative;const PacketType_1=require("../ws/packets/PacketType");let loadedCore;function buffer(e){return"undefined"!=typeof Buffer?Buffer.from(e.buffer,e.byteOffset,e.byteLength):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}function values(e){return Array.isArray(e)?[...e]:[e]}function assertNativeCore(e){const t=["encodeSigned","decodeSigned","encodeUnsigned","decodeUnsigned","encodeFloats","decodeFloats","encodeStrings","decodeStrings","encodeBooleans","decodeBooleans","encodeRaw","decodeRaw","encodeHex","decodeHex","frameObject","unframeObject","encodeBatch","decodeBatch","deflateRaw","inflateRaw","validateEncoded","validateEnum","validateObject"].filter(t=>"function"!=typeof e[t]);if(t.length>0)throw new Error(`Invalid SonicWS native addon; missing: ${t.join(", ")}`)}function setNativeCore(e){assertNativeCore(e),loadedCore=e}async function initializeWasmCore(){const wasm="undefined"==typeof window?eval("require")("./wasm/node/sonic_ws_core.js"):await import("./wasm/pkg/sonic_ws_core.js");return setNativeCore(wasm),loadedCore}function loadNativeCore(addonPath){if(loadedCore&&!addonPath)return loadedCore;const candidates=addonPath?[addonPath]:["undefined"!=typeof process?process.env.SONIC_WS_CORE_PATH:void 0,"undefined"!=typeof __dirname?`${__dirname}/sonic_ws_core.node`:void 0,"undefined"!=typeof __dirname?`${__dirname}/../../../native/sonic_ws_core.node`:void 0].filter(e=>Boolean(e)),failures=[],nodeRequire="undefined"==typeof window?eval("require"):void 0;for(const e of candidates)try{const t=nodeRequire(e);return assertNativeCore(t),addonPath||(loadedCore=t),t}catch(t){failures.push(`${e}: ${t instanceof Error?t.message:String(t)}`)}if("undefined"==typeof window)try{const e=nodeRequire("./wasm/node/sonic_ws_core.js");return assertNativeCore(e),loadedCore=e,e}catch(e){failures.push(`Node WASM fallback: ${e instanceof Error?e.message:String(e)}`)}throw new Error(`SonicWS codec is not initialized. In Node, set SONIC_WS_CORE_PATH; in browsers, await initializeWasmCore().\n${failures.join("\n")}`)}function enumIndex(e,t){const a=e.values.findIndex(e=>e===t||"number"==typeof e&&"number"==typeof t&&Number.isNaN(e)&&Number.isNaN(t));if(a<0)throw new Error(`Value ${String(t)} does not exist in enum ${e.tag}`);return a}function encodeNative(e,t,a,r=loadNativeCore()){switch(e){case PacketType_1.PacketType.NONE:if(null!=t&&(!Array.isArray(t)||0!==t.length))throw new TypeError("NONE only accepts null, undefined, or an empty array");return new Uint8Array(0);case PacketType_1.PacketType.RAW:case PacketType_1.PacketType.JSON:if(!(t instanceof Uint8Array||Array.isArray(t)))throw new TypeError((e===PacketType_1.PacketType.JSON?"JSON wire data":"RAW")+" requires a Uint8Array or number array");return r.encodeRaw(t instanceof Uint8Array?buffer(t):Uint8Array.from(t));case PacketType_1.PacketType.BYTES:case PacketType_1.PacketType.SHORTS:case PacketType_1.PacketType.VARINT:case PacketType_1.PacketType.DELTAS:return r.encodeSigned(e,values(t));case PacketType_1.PacketType.UBYTES:case PacketType_1.PacketType.USHORTS:case PacketType_1.PacketType.UVARINT:return r.encodeUnsigned(e,values(t));case PacketType_1.PacketType.FLOATS:case PacketType_1.PacketType.DOUBLES:return r.encodeFloats(e,values(t));case PacketType_1.PacketType.STRINGS_ASCII:case PacketType_1.PacketType.STRINGS_UTF16:return r.encodeStrings(e,values(t));case PacketType_1.PacketType.BOOLEANS:return r.encodeBooleans(values(t));case PacketType_1.PacketType.HEX:{const e=Array.isArray(t)?t[0]:t;if("string"!=typeof e)throw new TypeError("HEX requires one string");return r.encodeHex(e)}case PacketType_1.PacketType.ENUMS:if(!a)throw new Error("ENUMS requires an EnumPackage");return Uint8Array.from(values(t).map(e=>enumIndex(a,e)));case PacketType_1.PacketType.KEY_EFFECTIVE:throw new Error("KEY_EFFECTIVE is not implemented");default:throw new Error(`Unknown packet type: ${e}`)}}function decodeNative(e,t,a=4294967295,r,n=loadNativeCore()){const o=buffer(t);switch(e){case PacketType_1.PacketType.NONE:if(0!==t.byteLength)throw new Error("NONE packet contains data");return;case PacketType_1.PacketType.RAW:case PacketType_1.PacketType.JSON:return n.decodeRaw(o);case PacketType_1.PacketType.BYTES:case PacketType_1.PacketType.SHORTS:case PacketType_1.PacketType.VARINT:case PacketType_1.PacketType.DELTAS:return n.decodeSigned(e,o);case PacketType_1.PacketType.UBYTES:case PacketType_1.PacketType.USHORTS:case PacketType_1.PacketType.UVARINT:return n.decodeUnsigned(e,o);case PacketType_1.PacketType.FLOATS:case PacketType_1.PacketType.DOUBLES:return n.decodeFloats(e,o);case PacketType_1.PacketType.STRINGS_ASCII:case PacketType_1.PacketType.STRINGS_UTF16:return n.decodeStrings(e,o);case PacketType_1.PacketType.BOOLEANS:return n.decodeBooleans(o,a);case PacketType_1.PacketType.HEX:return n.decodeHex(o);case PacketType_1.PacketType.ENUMS:if(!r)throw new Error("ENUMS requires an EnumPackage");return[...t].map(e=>{if(e>=r.values.length)throw new Error(`Enum index ${e} is out of range`);return r.values[e]});case PacketType_1.PacketType.KEY_EFFECTIVE:throw new Error("KEY_EFFECTIVE is not implemented");default:throw new Error(`Unknown packet type: ${e}`)}}function validateNative(e,t,a,r,n={},o=loadNativeCore()){if(e!==PacketType_1.PacketType.ENUMS)o.validateEncoded(e,buffer(t),a,r,n.compressed??!1,n.batched??!1,n.maxBatchSize);else{if(!n.enumData)throw new Error("ENUMS requires an EnumPackage");o.validateEnum(buffer(t),n.enumData.values.length,a,r)}}function encodeNativeObject(e,t,a=loadNativeCore()){if(t.length!==e.types.length)throw new Error("Object field count does not match schema");let r=0;const n=e.types.map((n,o)=>encodeNative(n,t[o],n===PacketType_1.PacketType.ENUMS?e.enumData?.[r++]:void 0,a));return a.frameObject(n)}function decodeNativeObject(e,t,a=loadNativeCore()){const r=a.unframeObject(buffer(t),e.types.length);let n=0;return r.map((t,r)=>decodeNative(e.types[r],t,e.dataMaxes[r],e.types[r]===PacketType_1.PacketType.ENUMS?e.enumData?.[n++]:void 0,a))}function validateNativeObject(e,t,a=loadNativeCore()){a.validateObject(buffer(t),[...e.types],[...e.dataMins],[...e.dataMaxes],(e.enumData??[]).map(e=>e.values.length))}function encodeNativeBatch(e,t,a=loadNativeCore()){return a.encodeBatch(e.map(buffer),t)}function decodeNativeBatch(e,t,a=0,r,n=loadNativeCore()){const o=Number.isFinite(a)&&a>0?a:0;return n.decodeBatch(buffer(e),t,o,r)}function deflateNative(e,t=loadNativeCore()){return t.deflateRaw(buffer(e))}function inflateNative(e,t,a=loadNativeCore()){return a.inflateRaw(buffer(e),t)}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
/** Current protocol version */
|
|
12
|
+
export declare const VERSION = 22;
|
|
13
|
+
/** Server data suffix */
|
|
14
|
+
export declare const SERVER_SUFFIX = "SWS";
|
|
15
|
+
/** Server data suffix in array */
|
|
16
|
+
export declare const SERVER_SUFFIX_NUMS: number[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SERVER_SUFFIX_NUMS=exports.SERVER_SUFFIX=exports.VERSION=void 0;const e=require("./ws/util/StringUtil");exports.VERSION=22,exports.SERVER_SUFFIX="SWS",exports.SERVER_SUFFIX_NUMS=(0,e.processCharCodes)(exports.SERVER_SUFFIX);
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import { ConnectionMiddleware, IMiddlewareHolder, MiddlewareHolder } from "./PacketProcessor";
|
|
6
12
|
import { BatchHelper } from "./util/packets/BatchHelper";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.CloseCodes=exports.Connection=void 0,exports.getClosureCause=function(e){if(e>=4e3)return r[e]??"UNKNOWN";switch(e){case 1e3:return"NORMAL_CLOSURE";case 1001:return"GOING_AWAY";case 1002:return"PROTOCOL_ERROR";case 1003:return"UNSUPPORTED_DATA";case 1006:return"ABNORMAL_CLOSURE";default:return"UNKNOWN"}};const e=require("./PacketProcessor"),t=require("./util/packets/BatchHelper");class s extends e.MiddlewareHolder{listeners;name;closed=!1;socket;_timers={};batcher;_on;_off;id;constructor(e,s,r,o,i){super(),this.id=s,this.listeners={},this.name=r,this.socket=e,this.batcher=new t.BatchHelper,this._on=o,this._off=i,this._on("close",()=>{this.callMiddleware("onStatusChange",WebSocket.CLOSED),this.closed=!0;for(const[e,t,s]of Object.values(this._timers))this.clearTimeout(e),s&&t(!0)}),this._on("open",()=>this.callMiddleware("onStatusChange",WebSocket.OPEN))}setTimeout(e,t,s=!1){const r=setTimeout(()=>{e(),this.clearTimeout(r)},t);return this._timers[r]=[r,e,s],r}setInterval(e,t,s=!1){const r=setInterval(e,t);return this._timers[r]=[r,e,s],r}clearTimeout(e){clearTimeout(e),delete this._timers[e]}clearInterval(e){this.clearTimeout(e)}raw_send(e){this.socket.send(e)}raw_onmessage(e){this._on("message",e)}close(e=1e3,t){this.closed=!0,this.socket.close(e,t?.toString())}isClosed(){return this.closed||this.socket.readyState==WebSocket.CLOSED}async setName(e){await this.callMiddleware("onNameChange",e)||(this.name=e)}getName(){return this.name}}var r;exports.Connection=s,function(e){e[e.RATELIMIT=4e3]="RATELIMIT",e[e.SMALL=4001]="SMALL",e[e.INVALID_KEY=4002]="INVALID_KEY",e[e.INVALID_PACKET=4003]="INVALID_PACKET",e[e.INVALID_DATA=4004]="INVALID_DATA",e[e.REPEATED_HANDSHAKE=4005]="REPEATED_HANDSHAKE",e[e.DISABLED_PACKET=4006]="DISABLED_PACKET",e[e.MIDDLEWARE=4007]="MIDDLEWARE",e[e.MANUAL_SHUTDOWN=4008]="MANUAL_SHUTDOWN"}(r||(exports.CloseCodes=r={}));
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import { SonicWSConnection } from "./server/SonicWSConnection";
|
|
6
12
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.MiddlewareHolder=void 0
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.MiddlewareHolder=void 0,exports.MiddlewareHolder=class{middlewares=[];addMiddleware(e){this.middlewares.push(e);const r=e;try{"function"==typeof r.init&&r.init(this)}catch(e){console.warn("Middleware init threw an error:",e)}}async callMiddleware(e,...r){let t=!1;for(const d of this.middlewares){const i=d[e];if(i)try{await i(...r)&&(t=!0)}catch(r){console.warn(`Middleware ${String(e)} threw an error:`,r)}}return t}};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import { PacketHolder } from "../../util/packets/PacketHolder";
|
|
6
12
|
import { Connection } from "../../Connection";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SonicWSCore=void 0;const e=require("../../util/packets/PacketHolder"),t=require("../../util/packets/CompressionUtil"),s=require("../../../native/wrapper"),i=require("../../util/packets/PacketUtils"),a=require("../../../version"),r=require("../../packets/Packets"),n=require("../../util/packets/BatchHelper"),c=require("../../util/BufferUtil"),o=require("../../Connection"),h=require("../../util/StringUtil");class l extends o.Connection{preListen;clientPackets=new e.PacketHolder;serverPackets=new e.PacketHolder;pastKeys=!1;readyListeners=[];bufferHandler;_timers={};asyncData={};asyncMap={};constructor(e,t,s,i){super(e,-1,"LocalSocket",s,i),this.socket=e,this.preListen={},this.invalidPacket=this.invalidPacket.bind(this),this.serverKeyHandler=this.serverKeyHandler.bind(this),this.messageHandler=this.messageHandler.bind(this),this._on("message",this.serverKeyHandler),this._on("close",()=>{this.callMiddleware("onStatusChange",WebSocket.CLOSED);for(const[e,t,s]of Object.values(this._timers))this.clearTimeout(e),s&&t(!0);for(const e of this.clientPackets.getPackets())delete e.lastSent[0];for(const e of this.serverPackets.getPackets())delete e.lastReceived[0]}),this.bufferHandler=t}reading=!1;readQueue=[];async serverKeyHandler(e){if(this.reading)return this.readQueue.push(e);this.reading=!0;const i=await this.bufferHandler(e);if(i.length<3||(0,h.as8String)(i.slice(0,3))!=a.SERVER_SUFFIX)throw this.close(1e3),new Error("The server requested is not a Sonic WS server.");const n=i[3];if(n!=a.VERSION)throw this.close(1e3),new Error(`Version mismatch: ${n>a.VERSION?"client":"server"} is outdated (server: ${n}, client: ${a.VERSION})`);const c=(0,s.inflateNative)(i.subarray(4,i.length)),[o,l]=(0,t.readVarInt)(c,0);this.id=l;const[d,u]=(0,t.readVarInt)(c,o),k=c.subarray(d,d+u);this.clientPackets.holdPackets(r.Packet.deserializeAll(k,!0));const f=c.subarray(d+u,c.length);this.serverPackets.holdPackets(r.Packet.deserializeAll(f,!0)),this.batcher.registerSendPackets(this.clientPackets,this);for(const e of this.serverPackets.getPackets()){const t=this.serverPackets.getKey(e.tag);this.asyncMap[t]=e.async,e.async&&(this.asyncData[t]=[!1,[]])}Object.keys(this.preListen).forEach(e=>this.preListen[e].forEach(t=>{if(!this.serverPackets.hasTag(e))return console.error(new Error(`The server does not send the packet with tag "${e}"!`));this.listen(e,t)})),this.preListen=null,this.pastKeys=!0,this.readyListeners.forEach(e=>e()),this.readyListeners=null,this._off("message",this.serverKeyHandler),this._on("message",this.messageHandler),this.readQueue.forEach(e=>this.messageHandler(e)),this.readQueue=[]}invalidPacket(e){throw console.error(e),new Error("An error occured with data from the server!! This is probably my fault.. make an issue at https://github.com/liwybloc/sonic-ws")}listenLock=!1;packetQueue=[];async listenPacket(e,t,s,i,a){const r=this.listeners[t];if(!r)return console.warn("Warn: No listener for packet "+t);await this.enqueuePacket(e,r,s,i,a),await this.callMiddleware("onReceive_post",t,"string"==typeof e?[e]:e[0])}isAsync(e){return this.asyncMap[e]}async enqueuePacket(e,t,s,a,r){await(0,i.listenPacket)(e,t,this.invalidPacket),await this.triggerNextPacket(s,a,r)}async triggerNextPacket(e,t,s){t?s[0]=!1:this.listenLock=!1,e.length>0&&this.dataHandler(e.shift())}async dataHandler(e){const t=e[0],s=e.slice(1),i=this.isAsync(t);let a,r,c;if(i?(c=this.asyncData[t],a=c[0],r=c[1]):(a=this.listenLock,r=this.packetQueue),a)return void r.push(e);i?c[0]=!0:this.listenLock=!0;const o=this.serverPackets.getTag(t),h=this.serverPackets.getPacket(o);if(await this.callMiddleware("onReceive_pre",h.tag,e,e.length))return;if(h.rereference&&0==s.length)return void 0===h.lastReceived[0]?this.invalidPacket("No previous value to rereference"):void this.listenPacket(h.lastReceived[0],o,r,i,c);if(0==h.dataBatching){const e=h.lastReceived[0]=await h.listen(s,null);return void this.listenPacket(e,o,r,i,c)}const l=await n.BatchHelper.unravelBatch(h,s,null);if("string"==typeof l)return this.invalidPacket(l);l.forEach(e=>this.listenPacket(e,o,r,i,c))}async messageHandler(e){const t=await this.bufferHandler(e);t.length<1||await this.dataHandler(t)}listen(e,t){this.serverPackets.hasTag(e)?(this.listeners[e]??=[]).push(t):console.log("Tag is not available on server: "+e)}raw_onsend(e){this.listeners.send.push(e)}sendQueue=[!1,[],void 0];async send(e,...t){if(await this.callMiddleware("onSend_pre",e,t,Date.now(),performance.now()))return;const[s,a,r]=await(0,i.processPacket)(this.clientPackets,e,t,this.sendQueue,0);0==r.dataBatching?this.raw_send((0,c.toPacketBuffer)(s,a)):this.batcher.batchPacket(s,a),await this.callMiddleware("onSend_post",e,a,a.length)}on_ready(e){this.pastKeys?e():this.readyListeners.push(e)}on_close(e){this._on("close",e)}on(e,t){if(this.socket.readyState!==WebSocket.OPEN)return this.preListen[e]||(this.preListen[e]=[]),void this.preListen[e].push(t);this.listen(e,t)}}exports.SonicWSCore=l;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import WS from 'ws';
|
|
6
12
|
import { SonicWSCore } from "../core/ClientCore";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
var e=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.SonicWS=void 0;const o=e(require("ws")),r=require("../core/ClientCore");class t extends r.SonicWSCore{constructor(e,r){const t=new o.default.WebSocket(e,r);super(t,e=>Promise.resolve(new Uint8Array(e)),t.on.bind(t),t.off.bind(t))}}exports.SonicWS=t;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
import { SonicWSServer } from "../server/SonicWSServer";
|
|
12
|
+
export declare class DebugServer {
|
|
13
|
+
constructor(host: SonicWSServer, data: {
|
|
14
|
+
port?: number;
|
|
15
|
+
password?: string;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
var e=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0}),exports.DebugServer=void 0;const t=require("../Connection"),o=require("../packets/PacketType"),s=require("../server/SonicWSServer"),n=require("../util/packets/PacketUtils"),a=e(require("http")),c=e(require("open")),d=require("../util/enums/EnumHandler");exports.DebugServer=class{constructor(e,i){if(i.port??=0,i.password??="",i.port<0||i.port>=65536)throw new Error("Port out of range!");const l=a.default.createServer((e,t)=>{t.writeHead(200,{"Content-Type":"text/html"}),t.end('<!doctypehtml><meta charset=UTF-8><script src=/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((()=>{window.location.reload()})),ws.on("authenticated",(e=>{e?(localStorage.setItem("req",usedPass.length>0),localStorage.setItem("password",usedPass)):(localStorage.setItem("req",!0),localStorage.removeItem("password"),window.location.reload())}));<\/script>')}),r=(0,d.DefineEnum)("type",["sent","recv"]),p=new s.SonicWSServer({clientPackets:[(0,n.CreatePacket)({tag:"password",type:o.PacketType.STRINGS_UTF16}),(0,n.CreatePacket)({tag:"close",type:o.PacketType.UVARINT})],serverPackets:[(0,n.CreatePacket)({tag:"authenticated",type:o.PacketType.BOOLEANS}),(0,n.CreatePacket)({tag:"connection",type:o.PacketType.UVARINT}),(0,n.CreateObjPacket)({tag:"disconnection",types:[o.PacketType.UVARINT,o.PacketType.STRINGS],noDataRange:!0}),(0,n.CreateObjPacket)({tag:"nameChange",types:[o.PacketType.UVARINT,o.PacketType.STRINGS_UTF16],noDataRange:!0}),(0,n.CreateObjPacket)({tag:"packet",types:[o.PacketType.VARINT,r,o.PacketType.STRINGS_UTF16,o.PacketType.STRINGS_UTF16,o.PacketType.FLOATS],noDataRange:!0,dataBatching:50,maxBatchSize:0}),(0,n.CreatePacket)({tag:"stats",type:o.PacketType.UVARINT,noDataRange:!0,dontSpread:!0}),(0,n.CreatePacket)({tag:"stat",type:o.PacketType.UVARINT,dataMax:2})],websocketOptions:{server:l}}),v=new Proxy({totalSockets:0,totalSent:0,totalRecv:0,totalSentBytes:0,totalRecvBytes:0,totalSaved:0,startTime:Date.now()},{set(e,t,o){const s=String(t);return e[s]!==o&&(e[s]=o,p.broadcast("stat",Object.keys(v).indexOf(s),o)),!0}}),k={};p.on_connect(o=>{let s=!1;const n=o.send.bind(o);let a=[];o.send=async(e,...t)=>{console.log(s,e,t),s?n(e,...t):a.push([e,t])},o.send("stats",...Object.values(v)),e.connections.forEach(e=>{o.send("connection",e.id),o.send("nameChange",e.id,e.getName()),k[e.id]?.forEach(e=>{o.send("packet",...e)})}),o.on("password",e=>{i.password!=e?(n("authenticated",!1),setTimeout(()=>o.close(1008),1e3)):(s=!0,o.send("authenticated",!0),a.forEach(([e,t])=>n(e,...t)))}),o.on("close",o=>{s&&e.connectionMap[o]?.close(t.CloseCodes.MANUAL_SHUTDOWN)})});const u=[],g={},y=new TextEncoder,m=e=>y.encode(JSON.stringify(e)??"[]").length;e.addMiddleware(new class{onClientConnect(e){v.totalSockets++,k[e.id]=[],u.push(e),p.broadcast("connection",e.id);const t={},o={};e.addMiddleware(new class{onNameChange(t){p.broadcast("nameChange",e.id,t)}onSend_pre(e,o,s,n){t[e]??=[],t[e].push([o,n,s])}onSend_post(o,s,n){v.totalSentBytes+=n,v.totalSent++;const[a,c,d]=t[o].shift(),i=m(a)-n;v.totalSaved+=i;const l=[[e.id,n+1,i],r.wrap("sent"),o,JSON.stringify(a),[d,performance.now()-c]];k[e.id].push(l),p.broadcast("packet",...l)}onReceive_pre(e,t,s){v.totalRecvBytes+=s,v.totalRecv++,o[e]??=[],o[e].push([t,performance.now(),Date.now()])}onReceive_post(t,s){const[n,a,c]=o[t].shift(),d=m(s)-n.length;v.totalSaved+=d;const i=[[e.id,n.length+1,d],r.wrap("recv"),t,JSON.stringify(s),[c,performance.now()-a]];k[e.id].push(i),p.broadcast("packet",...i)}})}onClientDisconnect(e,o,s){v.totalSockets--,p.broadcast("disconnection",[e.id,o],[s?.toString()??"UNKNOWN",(0,t.getClosureCause)(o)]),delete k[e.id],u.splice(u.indexOf(e),1)}onPacketBroadcast_pre(e,t,...o){g[e]??=[],g[e].push([o,performance.now(),Date.now()])}onPacketBroadcast_post(e,t,o,s){const[n,a,c]=g[e].shift();t.recipients.forEach(t=>{u.includes(t)&&(t.callMiddleware("onSend_pre",e,n,c,a),t.callMiddleware("onSend_post",e,o,s))})}}),l.listen(i.port,()=>{const e=l.address();console.log(`SWS Debug server running at http://localhost:${e.port}`),(0,c.default)(`http://localhost:${e.port}`)})}};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
/** All different packet types. Use ENUMS for any constant primitive data */
|
|
6
12
|
export declare enum PacketType {
|
|
@@ -38,7 +44,7 @@ export declare enum PacketType {
|
|
|
38
44
|
BOOLEANS = 14,
|
|
39
45
|
/** Consumes multiple keys to describe the value. E.g. if you want to send a boolean, this could take up 2 keys instead of sending 2 bytes. Currently W.I.P. */
|
|
40
46
|
KEY_EFFECTIVE = 15,
|
|
41
|
-
/**
|
|
47
|
+
/** TypeScript-side JSONUtil codec carried as opaque RAW bytes through reserved wire type 16. */
|
|
42
48
|
JSON = 16,
|
|
43
49
|
/** Hex bytes, e.g. 0xFFFFFF - the result will always be returned in lowercase. This can only hold 1 hex string, since it's the same as UBYTES but auto-parses. */
|
|
44
50
|
HEX = 17
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
var S;Object.defineProperty(exports,"__esModule",{value:!0}),exports.PacketType=void 0,function(S){S[S.NONE=0]="NONE",S[S.RAW=1]="RAW",S[S.STRINGS_ASCII=2]="STRINGS_ASCII",S[S.STRINGS_UTF16=3]="STRINGS_UTF16",S[S.STRINGS=2]="STRINGS",S[S.ENUMS=4]="ENUMS",S[S.BYTES=5]="BYTES",S[S.UBYTES=6]="UBYTES",S[S.SHORTS=7]="SHORTS",S[S.USHORTS=8]="USHORTS",S[S.VARINT=9]="VARINT",S[S.UVARINT=10]="UVARINT",S[S.DELTAS=11]="DELTAS",S[S.FLOATS=12]="FLOATS",S[S.DOUBLES=13]="DOUBLES",S[S.BOOLEANS=14]="BOOLEANS",S[S.KEY_EFFECTIVE=15]="KEY_EFFECTIVE",S[S.JSON=16]="JSON",S[S.HEX=17]="HEX"}(S||(exports.PacketType=S={}));
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import { EnumPackage } from "../util/enums/EnumType";
|
|
6
12
|
import { SonicWSConnection } from "../server/SonicWSConnection";
|
|
@@ -28,9 +34,6 @@ export declare class Packet<T extends (PacketType | readonly PacketType[])> {
|
|
|
28
34
|
readonly gzipCompression: boolean;
|
|
29
35
|
readonly object: boolean;
|
|
30
36
|
readonly client: boolean;
|
|
31
|
-
private receiveProcessor;
|
|
32
|
-
private sendProcessor;
|
|
33
|
-
private validator;
|
|
34
37
|
processReceive: (data: Uint8Array, validationResult: any) => any;
|
|
35
38
|
processSend: (data: any[]) => Promise<Uint8Array>;
|
|
36
39
|
validate: (data: Uint8Array) => Promise<[Uint8Array, boolean]>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.PacketSchema=exports.Packet=void 0;const t=require("../util/enums/EnumHandler"),e=require("../util/enums/EnumType"),a=require("../util/packets/CompressionUtil"),i=require("../util/packets/PacketUtils"),s=require("./PacketType"),r=require("../util/StringUtil"),n=require("../../native/wrapper"),c=require("../util/packets/JSONUtil");class h{defaultEnabled;tag;maxSize;minSize;type;enumData;dataMax;dataMin;dataBatching;maxBatchSize;dontSpread;autoFlatten;rateLimit;async;rereference;gzipCompression;object;client;processReceive;processSend;validate;customValidator;lastReceived={};lastSent={};constructor(t,e,a,i,r){if(this.tag=t,this.defaultEnabled=i,this.client=r,this.async=e.async,this.enumData=e.enumData,this.rateLimit=e.rateLimit,this.dontSpread=e.dontSpread,this.autoFlatten=e.autoFlatten,this.rereference=e.rereference,this.dataBatching=e.dataBatching,this.maxBatchSize=r?1/0:e.maxBatchSize,this.gzipCompression=e.gzipCompression,this.object=e.object,this.type=e.type,this.dataMax=e.dataMax,this.dataMin=e.dataMin,e.testObject(this)){this.maxSize=this.minSize=this.type.length;for(let t=0;t<this.type.length;t++)this.type[t]==s.PacketType.NONE&&(this.dataMax[t]=this.dataMin[t]=0);const t={types:this.type,dataMins:this.dataMin,dataMaxes:this.dataMax,enumData:this.enumData};this.processReceive=e=>(0,n.decodeNativeObject)(t,e).map((t,e)=>this.type[e]===s.PacketType.JSON?(0,c.decompressJSON)(t):t),this.processSend=async e=>{let a=0;const i=e.map((t,e)=>{if(this.type[e]===s.PacketType.JSON)return(0,c.compressJSON)(t);if(this.type[e]!==s.PacketType.ENUMS)return t;const i=this.enumData[a++];return(Array.isArray(t)?t:[t]).map(t=>{if(!Number.isInteger(t)||t<0||t>=i.values.length)throw new Error(`Invalid wrapped enum index: ${t}`);return i.values[t]})});return(0,n.encodeNativeObject)(t,i)},this.validate=async e=>((0,n.validateNativeObject)(t,e),(0,n.decodeNativeObject)(t,e).forEach((t,e)=>{if(this.type[e]!==s.PacketType.JSON)return;const a=(0,c.decompressJSON)(t),i=Array.isArray(a)?a.length:1;if(i<this.dataMin[e]||i>this.dataMax[e])throw new Error("JSON value count is outside schema limits")}),[e,!0])}else{this.maxSize=this.dataMax,this.minSize=this.dataMin,this.type==s.PacketType.NONE&&(this.dataMax=this.dataMin=0);const t=this.enumData[0];this.processReceive=e=>this.type===s.PacketType.JSON?(0,c.decompressJSON)(e):(0,n.decodeNative)(this.type,e,this.dataMax,t),this.processSend=async e=>{let a;if(this.type===s.PacketType.JSON)a=(0,c.compressJSON)(e);else if(this.type===s.PacketType.ENUMS)a=Uint8Array.from(e);else{const i=this.type===s.PacketType.RAW&&1===e.length&&e[0]instanceof Uint8Array?e[0]:e;a=(0,n.encodeNative)(this.type,i,t)}return this.gzipCompression&&0===this.dataBatching?(0,n.deflateNative)(a):a},this.validate=async e=>{const a=this.gzipCompression&&0===this.dataBatching?(0,n.inflateNative)(e):e;if(this.type===s.PacketType.JSON){const t=(0,c.decompressJSON)(a),e=Array.isArray(t)?t.length:1;if(e<this.dataMin||e>this.dataMax)throw new Error("JSON value count is outside schema limits")}else(0,n.validateNative)(this.type,a,this.dataMin,this.dataMax,{enumData:t});return[a,!0]}}this.customValidator=a}async listen(t,e){try{const[a,s]=await this.validate(t);if(!this.client&&!1===s)return"Invalid packet";const r=this.processReceive(a,s),n=this.autoFlatten?(0,i.UnFlattenData)(r):r;if(null!=this.customValidator)if(this.dontSpread){if(!this.customValidator(e,n))return"Didn't pass custom validator"}else if(!this.customValidator(e,...n))return"Didn't pass custom validator";return[n,!this.dontSpread]}catch(t){return console.error("There was an error processing the packet! This is probably my fault... report at https://github.com/liwybloc/sonic-ws",t),"Error: "+t}}serialize(){const t=[this.tag.length,...(0,r.processCharCodes)(this.tag),(0,a.compressBools)([this.dontSpread,this.async,this.object,this.autoFlatten,this.gzipCompression,this.rereference]),this.dataBatching,this.enumData.length,...this.enumData.map(t=>t.serialize()).flat()];return this.object?[...t,this.maxSize,...this.dataMax.map(a.convertVarInt).flat(),...this.dataMin.map(a.convertVarInt).flat(),...this.type]:[...t,...(0,a.convertVarInt)(this.dataMax),...(0,a.convertVarInt)(this.dataMin),this.type]}static readVarInts(t,e,i){const s=[];for(let r=0;r<i;r++){const[i,r]=(0,a.readVarInt)(t,e);e=i,s.push(r)}return[s,e]}static deserialize(i,n,c){const o=n,d=i[n++],l=(0,r.as8String)(i.slice(n,n+=d)),[u,m,y,S,f,M]=(0,a.decompressBools)(i[n++]),g=i[n++],N=i[n++],v=[];for(let a=0;a<N;a++){const a=i[n++],s=(0,r.as8String)(i.slice(n,n+=a)),c=i[n++],h=[];for(let t=0;t<c;t++){const t=i[n++],a=i[n++],s=(0,r.as8String)(i.slice(n,n+=t));h.push(e.TYPE_CONVERSION_MAP[a](s))}v.push((0,t.DefineEnum)(s,h))}if(y){const t=i[n++],[e,a]=this.readVarInts(i,n,t);n=a;const[r,d]=this.readVarInts(i,n,t);n=d;const y=Array.from(i.slice(n,n+=t));let M=0;const N=y.map(t=>t==s.PacketType.ENUMS?v[M++]:t),x=new p(!0,N,m,r,e,-1,u,S,!1,g,-1,f);return[new h(l,x,null,!1,c),n-o]}const[x,k]=(0,a.readVarInt)(i,n);n=x;const[O,z]=(0,a.readVarInt)(i,n);n=O;const P=i[n++],b=P==s.PacketType.ENUMS?v[0]:P,E=new p(!1,b,m,z,k,-1,u,!1,M,g,-1,f);return[new h(l,E,null,!1,c),n-o]}static deserializeAll(t,e){const a=[];let i=0;for(;i<t.length;){const[s,r]=this.deserialize(t,i,e);a.push(s),i+=r}return a}}exports.Packet=h;const o=(t,a)=>t instanceof e.EnumPackage?(a.push(t),s.PacketType.ENUMS):t;class p{type;enumData=[];dataMax;dataMin;dataBatching;maxBatchSize;rateLimit;dontSpread=!1;autoFlatten=!1;async=!1;rereference=!1;gzipCompression=!1;object;constructor(t,e,a,i,s,r,n,c,h,p,d,l){this.object=t,this.async=a,this.dataMin=i,this.dataMax=s,this.rateLimit=r,this.dontSpread=n,this.autoFlatten=c,this.rereference=h,this.dataBatching=p,this.maxBatchSize=d,this.gzipCompression=l,this.type=t?e.map(t=>o(t,this.enumData)):o(e,this.enumData)}testObject(t){return this.object}}exports.PacketSchema=p;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import * as WS from 'ws';
|
|
6
12
|
import { SonicWSServer } from "./SonicWSServer";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SonicWSConnection=void 0;const e=require("../util/packets/PacketUtils"),t=require("../util/packets/BatchHelper"),s=require("../util/packets/RateHandler"),a=require("../util/BufferUtil"),i=require("../Connection");class n extends i.Connection{host;print=!1;handshakePacket;handshakeLambda;messageLambda=e=>this.messageHandler(this.parseData(e));handshakedMessageLambda=e=>{const t=this.parseData(e);return null==t?this.socket.close(i.CloseCodes.INVALID_DATA):t[0]==this.handshakePacket?this.socket.close(i.CloseCodes.REPEATED_HANDSHAKE):void this.messageHandler(t)};rater;enabledPackets={};handshakeComplete=!1;asyncMap={};asyncData={};constructor(e,t,a,i,n,r){super(e,a,"Socket "+a,e.addEventListener.bind(e),e.removeEventListener.bind(e)),this.host=t,this.handshakePacket=i;for(const e of t.clientPackets.getPackets()){const t=e.tag;this.listeners[t]=[],e.lastReceived[this.id]=void 0,this.enabledPackets[t]=e.defaultEnabled,this.asyncMap[t]=e.async,e.async&&(this.asyncData[t]=[!1,[]])}this.setInterval=this.setInterval.bind(this),this.batcher.registerSendPackets(this.host.serverPackets,this),this.invalidPacket=this.invalidPacket.bind(this),this.rater=new s.RateHandler(this),this.rater.registerRate("C",n),this.rater.registerRate("S",r),this.rater.registerAll(t.clientPackets,"client"),this.rater.registerAll(t.serverPackets,"server"),this.rater.start(),null==this.handshakePacket?this.socket.addEventListener("message",this.messageLambda):(this.handshakeLambda=e=>this.handshakeHandler(e),this.socket.addEventListener("message",this.handshakeLambda)),this.socket.on("close",()=>{for(const e of t.clientPackets.getPackets())delete e.lastReceived[this.id];for(const e of t.serverPackets.getPackets())delete e.lastSent[this.id]})}parseData(e){if(this.rater.trigger("C"))return null;if(!(e.data instanceof Buffer))return null;const t=new Uint8Array(e.data);if(this.print&&console.log(`[31m⬇ [38;5;245m(${this.id},${t.byteLength})[0m`,t.length>0&&this.host.clientPackets.getTag(t[0])||"<INVALID>",(0,a.stringifyBuffer)(t)),t.byteLength<1)return this.socket.close(i.CloseCodes.SMALL),null;const s=t[0],n=t.slice(1);if(!this.host.clientPackets.hasKey(s))return this.socket.close(i.CloseCodes.INVALID_KEY),null;const r=this.host.clientPackets.getTag(s);return this.enabledPackets[r]?this.rater.trigger("client"+s)?null:[r,n]:(this.socket.close(i.CloseCodes.DISABLED_PACKET),null)}handshakeHandler(e){const t=this.parseData(e);if(null==t)return this.socket.close(i.CloseCodes.INVALID_DATA);t[0]==this.handshakePacket?(this.messageHandler(t),this.socket.removeEventListener("message",this.handshakeLambda),this.socket.addEventListener("message",this.handshakedMessageLambda),this.handshakeComplete=!0):this.socket.close(i.CloseCodes.INVALID_DATA)}invalidPacket(e){console.log("Closure cause",e),this.socket.close(i.CloseCodes.INVALID_PACKET,e)}isAsync(e){return this.asyncMap[e]}listenLock=!1;packetQueue=[];async listenPacket(t,s,a,i,n){this.closed||(await(0,e.listenPacket)(t,this.listeners[s],this.invalidPacket),await this.callMiddleware("onReceive_post",s,"string"==typeof t?[t]:t[0]),i?n[0]=!1:this.listenLock=!1,0==a.length||this.messageHandler(a.shift()))}async messageHandler(e,s=!1){if(null==e)return;const[a,i]=e,n=this.isAsync(a);let r,h,c;if(n?(c=this.asyncData[a],r=c[0],h=c[1]):(r=this.listenLock,h=this.packetQueue),r)return void h.push(e);n?c[0]=!0:this.listenLock=!0;const o=this.host.clientPackets.getPacket(a);if(!s&&await this.callMiddleware("onReceive_pre",o.tag,i,i.length))return;if(o.rereference&&0==i.length){const e=o.lastReceived[this.id];return void 0===e?this.invalidPacket("No previous value to rereference"):void await this.listenPacket(e,a,h,n,c)}if(0==o.dataBatching){const e=await o.listen(i,this);return o.lastReceived[this.id]=e,void await this.listenPacket(e,a,h,n,c)}const l=await t.BatchHelper.unravelBatch(o,i,this);if("string"==typeof l)return this.invalidPacket(l);for(const e of l)n?c[0]=!0:this.listenLock=!0,await this.listenPacket(e,a,h,n,c)}enablePacket(e){this.enabledPackets[e]=!0}disablePacket(e){this.enabledPackets[e]=!1}on_close(e){this.socket.on("close",(t,s)=>e(t,String(s)))}on(e,t){if(!this.host.clientPackets.hasTag(e))throw new Error(`Tag "${String(e)}" has not been created!`);this.listeners[e]??=[],this.listeners[e].push(t)}send_processed(e,t,s){this.rater.trigger("server"+e)||(0==s.dataBatching?this.raw_send((0,a.toPacketBuffer)(e,t)):this.batcher.batchPacket(e,t))}sendQueue=[!1,[],void 0];async send(t,...s){if(await this.callMiddleware("onSend_pre",t,s,Date.now(),performance.now()))return;const[a,i,n]=await(0,e.processPacket)(this.host.serverPackets,t,s,this.sendQueue,this.id);await this.callMiddleware("onSend_post",t,i,i.length)||this.send_processed(a,i,n)}broadcastFiltered(e,t,...s){this.host.broadcastFiltered(e,e=>e!=this&&t(e),...s)}broadcast(e,...t){this.broadcastFiltered(e,()=>!0,...t)}togglePrint(){this.print=!this.print}raw_send(e){this.isClosed()?console.warn("WARN! Connection already closed when trying to send message!",this.id,(0,a.stringifyBuffer)(e)):this.rater.trigger("S")||(this.print&&console.log(`[32m⬆ [38;5;245m(${this.id},${e.byteLength})[0m`,e.length>0&&this.host.serverPackets.getTag(e[0])||"<INVALID>",(0,a.stringifyBuffer)(e)),super.raw_send(e))}tag(e,t=!0){this.host.tag(this,e,t)}}exports.SonicWSConnection=n;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import * as WS from 'ws';
|
|
6
12
|
import { SonicWSConnection } from "./SonicWSConnection";
|
|
@@ -13,6 +19,8 @@ export type SonicServerSettings = {
|
|
|
13
19
|
readonly checkForUpdates?: boolean;
|
|
14
20
|
/** 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. */
|
|
15
21
|
readonly bit64Hash?: boolean;
|
|
22
|
+
/** Automatically serves the browser client at /SonicWS/bundle.js and /SonicWS/bundle.wasm. Defaults to true. */
|
|
23
|
+
readonly serveBrowserClient?: boolean;
|
|
16
24
|
};
|
|
17
25
|
/**
|
|
18
26
|
* Sonic WS Server Options
|
|
@@ -48,6 +56,7 @@ export declare class SonicWSServer extends MiddlewareHolder<ServerMiddleware> {
|
|
|
48
56
|
* @param settings Sonic Server Options such as schema data for client and server packets, alongside websocket options
|
|
49
57
|
*/
|
|
50
58
|
constructor(settings: SonicServerOptions);
|
|
59
|
+
private installBrowserAssetRoutes;
|
|
51
60
|
private generateSocketID;
|
|
52
61
|
/**
|
|
53
62
|
* Requires each client to send this packet upon initialization
|
|
@@ -136,7 +145,7 @@ export declare class SonicWSServer extends MiddlewareHolder<ServerMiddleware> {
|
|
|
136
145
|
* @param port Port of the server/http, defaults to 0 which finds an open port
|
|
137
146
|
* @param password Toggles the requirement of a password to access the server. Defaults to empty, which doesn't ask for a password.
|
|
138
147
|
*/
|
|
139
|
-
OpenDebug(data
|
|
148
|
+
OpenDebug(data?: {
|
|
140
149
|
port?: number;
|
|
141
150
|
password?: string;
|
|
142
151
|
}): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
var e,t=this&&this.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s);var i=Object.getOwnPropertyDescriptor(t,s);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[s]}}),Object.defineProperty(e,n,i)}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||(e=function(t){return e=Object.getOwnPropertyNames||function(e){var t=[];for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&(t[t.length]=s);return t},e(t)},function(n){if(n&&n.__esModule)return n;var i={};if(null!=n)for(var a=e(n),r=0;r<a.length;r++)"default"!==a[r]&&t(i,n,a[r]);return s(i,n),i});Object.defineProperty(exports,"__esModule",{value:!0}),exports.SonicWSServer=void 0;const i=n(require("ws")),a=require("node:fs/promises"),r=require("node:path"),o=require("./SonicWSConnection"),c=require("../util/packets/PacketHolder"),l=require("../util/packets/CompressionUtil"),d=require("../../native/wrapper"),h=require("../../version"),u=require("../util/packets/PacketUtils"),g=require("../PacketProcessor"),p=require("../util/packets/HashUtil"),w=require("../Connection"),v=require("../debug/DebugServer");class f extends g.MiddlewareHolder{wss;availableIds=[];lastId=0;connectListeners=[];clientPackets;serverPackets;connections=[];connectionMap={};clientRateLimit=500;serverRateLimit=500;handshakePacket=null;tags=new Map;tagsInv=new Map;serverwideSendQueue=[!1,[],void 0];constructor(e){super();const{clientPackets:t=[],serverPackets:s=[],websocketOptions:n={}}=e;if(this.wss=new i.WebSocketServer(n),e.sonicServerSettings?.serveBrowserClient??1){const e=n.server??this.wss._server;e&&this.installBrowserAssetRoutes(e)}this.clientPackets=new c.PacketHolder(t),this.serverPackets=new c.PacketHolder(s);const a=this.clientPackets.serialize(),r=this.serverPackets.serialize(),u=[...h.SERVER_SUFFIX_NUMS,h.VERSION],g=[...(0,l.convertVarInt)(a.length),...a,...r];(0,p.setHashFunc)(e.sonicServerSettings?.bit64Hash??!0),this.wss.on("connection",async e=>{const t=new o.SonicWSConnection(e,this,this.generateSocketID(),this.handshakePacket,this.clientRateLimit,this.serverRateLimit);if(await this.callMiddleware("onClientConnect",t))return t.close(w.CloseCodes.MIDDLEWARE,"Connection blocked by middleware."),this.callMiddleware("onClientDisconnect",t,w.CloseCodes.MIDDLEWARE,Buffer.from("Connection blocked by middleware.")),void this.availableIds.push(t.id);const s=new Uint8Array([...(0,l.convertVarInt)(t.id),...g]);e.send([...u,...(0,d.deflateNative)(s)]),this.connections.push(t),this.connectionMap[t.id]=t,this.connectListeners.forEach(e=>e(t)),e.on("close",(e,s)=>{if(this.connections.splice(this.connections.indexOf(t),1),delete this.connectionMap[t.id],this.availableIds.push(t.id),this.tags.has(t)){for(const e of this.tags.get(t))this.tagsInv.get(e)?.delete(t);this.tags.delete(t)}this.callMiddleware("onClientDisconnect",t,e,s)})}),(e.sonicServerSettings?.checkForUpdates??1)&&fetch("https://raw.githubusercontent.com/liwybloc/sonic-ws/refs/heads/main/release/version").then(e=>e.text()).then(e=>{parseInt(e)>h.VERSION&&console.warn(`SonicWS is currently running outdated! (current: ${h.VERSION}, latest: ${e}) Update with "npm update sonic-ws"`)}).catch(e=>{console.error(e),console.warn("Could not check SonicWS version.")})}installBrowserAssetRoutes(e){const t=e.listeners("request");e.removeAllListeners("request"),e.on("request",(s,n)=>{const i=new URL(s.url??"/","http://localhost").pathname,o="/SonicWS/bundle.js"===i?["bundle.js","text/javascript; charset=utf-8"]:"/SonicWS/bundle.wasm"===i?["bundle.wasm","application/wasm"]:void 0;if(!o){for(const i of t)Reflect.apply(i,e,[s,n]);return}const c=(0,r.resolve)(__dirname,"../../../../bundled",o[0]);(0,a.readFile)(c).then(e=>{n.writeHead(200,{"content-type":o[1],"content-length":e.byteLength,"cache-control":"public, max-age=3600"}),n.end(e)}).catch(e=>{n.writeHead(500,{"content-type":"text/plain; charset=utf-8"}),n.end(`Unable to load SonicWS browser asset: ${e instanceof Error?e.message:String(e)}`)})})}generateSocketID(){return 0==this.availableIds.length&&this.availableIds.push(this.lastId+1),this.lastId=this.availableIds.shift(),this.lastId}requireHandshake(e){if(!this.clientPackets.hasTag(e))throw new Error(`The client does not send "${e}" and so it cannot use it as a handshake!`);if(0!=this.clientPackets.getPacket(e).dataBatching)throw new Error(`The packet "${e}" is a batched packet, and cannot be used as a handshake!`);this.handshakePacket=e}setClientRateLimit(e){e>l.MAX_BYTE&&(e=0,console.warn(`A rate limit above ${l.MAX_BYTE} is considered infinite.`)),this.clientRateLimit=e}setServerRateLimit(e){e>l.MAX_BYTE&&(e=0,console.warn(`A rate limit above ${l.MAX_BYTE} is considered infinite.`)),this.serverRateLimit=e}enablePacket(e){this.clientPackets.getPacket(e).defaultEnabled=!0,this.connections.forEach(t=>t.enablePacket(e))}disablePacket(e){this.clientPackets.getPacket(e).defaultEnabled=!1,this.connections.forEach(t=>t.disablePacket(e))}on_connect(e){this.connectListeners.push(e)}on_ready(e){this.wss.on("listening",e)}shutdown(e){this.wss.close(e)}async broadcastInternal(e,t,s){let n;if("all"===t.type)n=this.connections;else if("tagged"===t.type){if(!this.tagsInv.has(t.tag))return;n=Array.from(this.tagsInv.get(t.tag))}else n=this.connections.filter(t.filter);if(await this.callMiddleware("onPacketBroadcast_pre",e,{recipients:n,...t},s))return;if(0===n.length)return;const[i,a,r]=await(0,u.processPacket)(this.serverPackets,e,s,this.serverwideSendQueue,-1);await this.callMiddleware("onPacketBroadcast_post",e,{recipients:n,...t},a,a.length)||n.forEach(e=>e.send_processed(i,a,r))}async broadcastTagged(e,t,...s){await this.broadcastInternal(t,{type:"tagged",tag:e},s)}async broadcastFiltered(e,t,...s){await this.broadcastInternal(e,{type:"filter",filter:t},s)}async broadcast(e,...t){await this.broadcastInternal(e,{type:"all"},t)}getConnected(){return this.connections}getSocket(e){return this.connectionMap[e]}closeSocket(e,t=1e3,s){this.getSocket(e).close(t,s)}tag(e,t,s=!0){this.tags.get(e)||this.tags.set(e,new Set),this.tagsInv.get(t)||this.tagsInv.set(t,new Set),s&&this.tags.get(e).forEach(t=>this.tagsInv.get(t)?.delete(e)),this.tags.get(e).add(t),this.tagsInv.get(t).add(e)}debugServer=null;OpenDebug(e={}){if(null!=this.debugServer)throw new Error("Attempted to open a debug server when one has already been opened.");this.debugServer=new v.DebugServer(this,e)}}exports.SonicWSServer=f;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
type BuildTuple<T, L extends number, R extends unknown[] = []> = R['length'] extends L ? R : BuildTuple<T, L, [T, ...R]>;
|
|
6
12
|
export declare function splitArray<T, N extends number>(arr: T[], size: N): Array<BuildTuple<T, N>>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
function r(r,t){const e=[];for(let n=0;n<r.length;n+=t){const o=r.slice(n,n+t);e.push(o)}return e}Object.defineProperty(exports,"__esModule",{value:!0}),exports.splitArray=r,exports.toPacketBuffer=function(r,t){const e=new Uint8Array(1+t.length);return e[0]=r,e.set(t,1),e},exports.splitBuffer=function(t,e){return r(Array.from(t),e)},exports.stringifyBuffer=function(r){return`<Buffer ${Array.from(r).map(r=>r.toString(16).padStart(2,"0")).join(" ")}>`};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
export declare function processCharCodes(text: string): number[];
|
|
12
|
+
export declare function convertCharCodes(codes: number[]): string;
|
|
13
|
+
export declare function as8String(data: Uint8Array): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function r(r){return String.fromCodePoint(...r)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.processCharCodes=function(r){return Array.from(r,r=>r.codePointAt(0))},exports.convertCharCodes=r,exports.as8String=function(e){return r(Array.from(e))};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
10
|
*/
|
|
5
11
|
import { EnumPackage } from "./EnumType";
|
|
6
12
|
export declare const MAX_ENUM_SIZE = 255;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.SET_PACKAGES=exports.ENUM_KEY_TO_TAG=exports.ENUM_TAG_TO_KEY=exports.MAX_ENUM_SIZE=void 0,exports.DefineEnum=function(e,t){const o=exports.SET_PACKAGES[e];if(o){if(o.values.find((e,r)=>t[r]!=e))throw new Error(`Pre-existing enum package of tag '${e}' is set and different!`);return o}if(t.length>exports.MAX_ENUM_SIZE)throw new Error(`An enum can only hold ${exports.MAX_ENUM_SIZE} possible values.`);return exports.ENUM_TAG_TO_KEY[e]=Object.fromEntries(t.map((e,r)=>[e,r])),exports.ENUM_KEY_TO_TAG[e]=Object.fromEntries(t.map((e,r)=>[r,e])),exports.SET_PACKAGES[e]=new r.EnumPackage(e,t)},exports.WrapEnum=function(e,r){if(!(r in exports.ENUM_TAG_TO_KEY[e]))throw new Error(`Value "${r}" does not exist in enum "${e}"`);return exports.ENUM_TAG_TO_KEY[e][r]},exports.DeWrapEnum=function(e,r){return exports.ENUM_KEY_TO_TAG[e][r]};const e=require("../packets/CompressionUtil"),r=require("./EnumType");exports.MAX_ENUM_SIZE=e.MAX_BYTE,exports.ENUM_TAG_TO_KEY={},exports.ENUM_KEY_TO_TAG={},exports.SET_PACKAGES={};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TYPE_CONVERSION_MAP: Record<number, (data: string) => string | number | boolean | undefined | null>;
|
|
12
|
+
export type EnumValue = string | number | boolean | undefined | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2026 Lily (liwybloc)
|
|
3
|
-
*
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.EnumPackage=exports.TYPE_CONVERSION_MAP=void 0;const e=require("../StringUtil"),t=require("./EnumHandler"),r={string:0,number:1,boolean:2,undefined:3,object:4};function n(e){const t=typeof e;if(!(t in r)&&null!=e)throw new Error(`Cannot serialize type "${t}" in an enum!`);return r[t]}exports.TYPE_CONVERSION_MAP={0:e=>e,1:e=>parseFloat(e),2:e=>"true"==e,3:()=>{},4:()=>null}
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.EnumPackage=exports.TYPE_CONVERSION_MAP=void 0;const e=require("../StringUtil"),t=require("./EnumHandler"),r={string:0,number:1,boolean:2,undefined:3,object:4};function n(e){const t=typeof e;if(!(t in r)&&null!=e)throw new Error(`Cannot serialize type "${t}" in an enum!`);return r[t]}exports.TYPE_CONVERSION_MAP={0:e=>e,1:e=>parseFloat(e),2:e=>"true"==e,3:()=>{},4:()=>null},exports.EnumPackage=class{tag;values;constructor(e,t){this.tag=e,this.values=t,this.wrap=this.wrap.bind(this)}serialize(){const t=(0,e.processCharCodes)(this.tag);return[this.tag.length,...t,this.values.length,...this.values.map(t=>[String(t).length,n(t),...(0,e.processCharCodes)(String(t))]).flat()]}wrap(e){return(0,t.WrapEnum)(this.tag,e)}};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
8
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
9
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Lily (liwybloc)
|
|
3
|
+
* License-Identifier: LicenseRef-Lily-Personal-NonCommercial-2026
|
|
4
|
+
* See LICENSE for personal, non-commercial license terms.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.BatchHelper=void 0;const t=require("../BufferUtil"),e=require("../../../native/wrapper");exports.BatchHelper=class{batchInfo={};batchTimeouts={};batchedData={};conn;registerSendPackets(t,e){this.conn=e,t.getTags().forEach(e=>{const a=t.getPacket(e);if(0==a.dataBatching)return;const c=t.getKey(e);this.initiateBatch(c,a.dataBatching,a.gzipCompression)})}initiateBatch(t,e,a){this.batchedData[t]=[],this.batchInfo[t]=[e,a]}startBatch(a){const[c,s]=this.batchInfo[a];this.batchTimeouts[a]=this.conn.setInterval(()=>{if(0==this.batchedData[a].length)return;const c=(0,e.encodeNativeBatch)(this.batchedData[a],s);this.conn.raw_send((0,t.toPacketBuffer)(a,c)),this.batchedData[a]=[],delete this.batchTimeouts[a]},c)}batchPacket(t,e){this.batchedData[t].push(e),this.batchTimeouts[t]||this.startBatch(t)}static async unravelBatch(t,a,c){let s;try{s=(0,e.decodeNativeBatch)(a,t.gzipCompression,t.maxBatchSize)}catch(t){return"Invalid batch: "+t}const i=[];for(const e of s){const a=await t.listen(e,c);if("string"==typeof a)return"Batched packet: "+a;i.push([a[0],!t.dontSpread])}return i}};
|