sonic-ws 1.3.2 → 1.3.3

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