@sanctumterra/raknet 1.4.12 → 1.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +14 -4
- package/dist/client/types/client-options.d.ts +1 -0
- package/dist/client/types/client-options.d.ts.map +1 -1
- package/dist/client/types/client-options.js +1 -0
- package/dist/server/connection.d.ts.map +1 -1
- package/dist/server/connection.js +1 -1
- package/dist/shared/network_session.d.ts +7 -1
- package/dist/shared/network_session.d.ts.map +1 -1
- package/dist/shared/network_session.js +71 -13
- package/package.json +1 -1
- package/dist/tests/frame-reordering.d.ts +0 -2
- package/dist/tests/frame-reordering.d.ts.map +0 -1
- package/dist/tests/frame-reordering.js +0 -48
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,YAAY,EAWZ,QAAQ,EAIR,KAAK,KAAK,EAKV,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACN,KAAK,aAAa,EAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAgB,KAAK,UAAU,EAAe,MAAM,YAAY,CAAC;AAKxE,qBAAa,MAAO,SAAQ,YAAY,CAAC,YAAY,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAiC;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAO;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAE3C,OAAO,EAAE,aAAa,CAAC;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,MAAM,CAAmB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM;YAmBlC,UAAU;IAoGxB,OAAO,CAAC,wBAAwB;IAIhC,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,oBAAoB;IA2Bf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,YAAY,EAWZ,QAAQ,EAIR,KAAK,KAAK,EAKV,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EACN,KAAK,aAAa,EAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAgB,KAAK,UAAU,EAAe,MAAM,YAAY,CAAC;AAKxE,qBAAa,MAAO,SAAQ,YAAY,CAAC,YAAY,CAAC;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAiC;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAO;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAE3C,OAAO,EAAE,aAAa,CAAC;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,MAAM,CAAmB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM;YAmBlC,UAAU;IAoGxB,OAAO,CAAC,wBAAwB;IAIhC,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,oBAAoB;IA2Bf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6D9B,MAAM,IAAI,IAAI;IA6BrB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,OAAO;IAgBR,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAoEhD,YAAY,CAAC,IAAI,EAAE,MAAM;IAiDzB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,QAA0B;IAI5D,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,QAA0B;IAI/D,IAAI,IAAI,IAAI;IAOZ,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAWxB,UAAU,IAAI,IAAI;CAWzB"}
|
package/dist/client/client.js
CHANGED
|
@@ -36,7 +36,7 @@ class Client extends shared_1.EventEmitter {
|
|
|
36
36
|
this.socket.bind();
|
|
37
37
|
this.interval = setInterval(this.onTick.bind(this), 1000 / this.options.tickRate);
|
|
38
38
|
this.socket.on("message", this.onMessage.bind(this));
|
|
39
|
-
this.session = new shared_1.NetworkSession(this.options.mtu);
|
|
39
|
+
this.session = new shared_1.NetworkSession(this.options.mtu, this.options.debug);
|
|
40
40
|
this.session.send = this.send.bind(this);
|
|
41
41
|
this.session.handle = (data) => {
|
|
42
42
|
this.handleOnline(data);
|
|
@@ -207,15 +207,25 @@ class Client extends shared_1.EventEmitter {
|
|
|
207
207
|
}
|
|
208
208
|
this.status = shared_1.ConnectionStatus.Connecting;
|
|
209
209
|
this.gotReply1 = false;
|
|
210
|
+
// Reset session state for fresh connection
|
|
211
|
+
this.session = new shared_1.NetworkSession(this.options.mtu, this.options.debug);
|
|
212
|
+
this.session.send = this.send.bind(this);
|
|
213
|
+
this.session.handle = (data) => {
|
|
214
|
+
this.handleOnline(data);
|
|
215
|
+
};
|
|
210
216
|
return new Promise((resolve, reject) => {
|
|
211
217
|
let mtuIndex = 0;
|
|
212
218
|
let retryTimeout = null;
|
|
219
|
+
// Use configured MTU or fall back to default values
|
|
220
|
+
const mtuValues = this.options.mtu
|
|
221
|
+
? [this.options.mtu]
|
|
222
|
+
: Client.MTU_VALUES;
|
|
213
223
|
const sendRequest = () => {
|
|
214
|
-
if (mtuIndex >=
|
|
224
|
+
if (mtuIndex >= mtuValues.length) {
|
|
215
225
|
reject(new Error("Connection timed out, all MTU values exhausted"));
|
|
216
226
|
return;
|
|
217
227
|
}
|
|
218
|
-
const mtu =
|
|
228
|
+
const mtu = mtuValues[mtuIndex];
|
|
219
229
|
if (!mtu)
|
|
220
230
|
throw new Error("MTU value is undefined");
|
|
221
231
|
const request = new shared_1.OpenConnectionRequestOne();
|
|
@@ -330,6 +340,7 @@ class Client extends shared_1.EventEmitter {
|
|
|
330
340
|
case shared_1.Packets.OpenConnectionReply2: {
|
|
331
341
|
const reply2 = new shared_1.OpenConnectionReplyTwo(actualData).deserialize();
|
|
332
342
|
// Update session MTU with the negotiated value from server
|
|
343
|
+
shared_2.Logger.info(`MTU negotiated: ${reply2.mtu} (was ${this.session.mtu})`);
|
|
333
344
|
this.session.mtu = reply2.mtu;
|
|
334
345
|
const request = new shared_1.ConnectionRequest();
|
|
335
346
|
request.guid = this.options.guid;
|
|
@@ -396,7 +407,6 @@ class Client extends shared_1.EventEmitter {
|
|
|
396
407
|
this.lastPongTime = Date.now(); // Reset stale timer on connect
|
|
397
408
|
this.lastActivityTime = Date.now();
|
|
398
409
|
this.emit("connect");
|
|
399
|
-
console.log("Raknet Connected");
|
|
400
410
|
break;
|
|
401
411
|
}
|
|
402
412
|
default: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-options.d.ts","sourceRoot":"","sources":["../../../src/client/types/client-options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client-options.d.ts","sourceRoot":"","sources":["../../../src/client/types/client-options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,YAAY,QAAO,MAC0B,CAAC;AAE3D,eAAO,MAAM,0BAA0B,QAAO,aAS5C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/server/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAOR,YAAY,EACZ,KAAK,KAAK,EACV,KAAK,QAAQ,EAIb,QAAQ,EACR,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,qBAAa,UAAW,SAAQ,YAAY,CAAC,gBAAgB,CAAC;IAS5D,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,IAAI;IAXb,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAElD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAS;gBAGtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/server/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,GAAG,EAOR,YAAY,EACZ,KAAK,KAAK,EACV,KAAK,QAAQ,EAIb,QAAQ,EACR,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,qBAAa,UAAW,SAAQ,YAAY,CAAC,gBAAgB,CAAC;IAS5D,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,IAAI;IAXb,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAElD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,cAAc,CAAS;gBAGtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM;IAWd,MAAM,CAAC,IAAI,EAAE,MAAM;IAoBnB,UAAU,CAAC,MAAM,SAAiB,GAAG,IAAI;IAYzC,OAAO,IAAI,OAAO;IAIlB,UAAU,CAAC,QAAQ,EAAE,QAAQ;IAK7B,UAAU,IAAI,UAAU;IAIxB,SAAS,CAAC,IAAI,EAAE,MAAM;IA8DtB,KAAK,CAAC,OAAO,EAAE,GAAG;IAIzB,uFAAuF;IAChF,MAAM,CAAC,OAAO,EAAE,GAAG;IAInB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,QAAwB;IAI1D,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAwB;IAIhE,IAAI,CAAC,IAAI,EAAE,MAAM;CAGxB"}
|
|
@@ -18,7 +18,7 @@ class Connection extends shared_1.EventEmitter {
|
|
|
18
18
|
this.rinfo = rinfo;
|
|
19
19
|
this.mtu = mtu;
|
|
20
20
|
this.guid = guid;
|
|
21
|
-
this.session = new shared_1.NetworkSession(this.mtu);
|
|
21
|
+
this.session = new shared_1.NetworkSession(this.mtu, this.server.options.enableServerLogs);
|
|
22
22
|
this.session.send = this.send.bind(this);
|
|
23
23
|
this.session.handle = this.onMessage.bind(this);
|
|
24
24
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Ack, Frame, FrameSet, Priority } from "./proto";
|
|
2
2
|
export declare class NetworkSession {
|
|
3
3
|
mtu: number;
|
|
4
|
+
debug: boolean;
|
|
4
5
|
send: (data: Buffer) => void;
|
|
5
6
|
handle: (data: Buffer) => void;
|
|
6
7
|
outputReliableIndex: number;
|
|
@@ -27,13 +28,18 @@ export declare class NetworkSession {
|
|
|
27
28
|
private static readonly RELIABLE_WINDOW_SIZE;
|
|
28
29
|
private static readonly FRAGMENT_TIMEOUT_MS;
|
|
29
30
|
private static readonly ORDER_QUEUE_MAX_SIZE;
|
|
30
|
-
|
|
31
|
+
private static readonly ORDER_QUEUE_SKIP_THRESHOLD;
|
|
32
|
+
constructor(mtu: number, debug?: boolean);
|
|
31
33
|
onTick(_tick?: number): void;
|
|
32
34
|
onAck(ack: Ack): void;
|
|
33
35
|
onNack(nack: Ack): void;
|
|
34
36
|
frameAndSend(data: Buffer, priority?: Priority): void;
|
|
35
37
|
sendFrame(frame: Frame, priority?: Priority): void;
|
|
36
38
|
queueFrame(frame: Frame, priority: Priority): void;
|
|
39
|
+
/**
|
|
40
|
+
* Send split frames - each in its own frameset with small delays to ensure ordering
|
|
41
|
+
*/
|
|
42
|
+
sendSplitFrames(frames: Frame[], priority: Priority): void;
|
|
37
43
|
sendQueue(amount: number): void;
|
|
38
44
|
onFrameSet(frameSet: FrameSet): void;
|
|
39
45
|
handleFrame(frame: Frame): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network_session.d.ts","sourceRoot":"","sources":["../../src/shared/network_session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAW,QAAQ,EAAe,MAAM,SAAS,CAAC;AAK/E,qBAAa,cAAc;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,MAAM,EAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAGhC,mBAAmB,SAAK;IACxB,gBAAgB,SAAK;IAC5B,SAAS,CAAC,cAAc,SAAK;IACtB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IACrC,YAAY,uBAA8B;IAG1C,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAChD,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAC5C,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IACrC,iBAAiB,SAAM;IACvB,cAAc,EAAE,GAAG,CACzB,MAAM,EACN;QAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACjD,CAAa;IACP,yBAAyB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,SAAS,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAa;IAE1E,OAAO,CAAC,4BAA4B,CAA0B;IAC9D,OAAO,CAAC,oBAAoB,CAAM;IAGlC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAO;
|
|
1
|
+
{"version":3,"file":"network_session.d.ts","sourceRoot":"","sources":["../../src/shared/network_session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAW,QAAQ,EAAe,MAAM,SAAS,CAAC;AAK/E,qBAAa,cAAc;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,MAAM,EAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAGhC,mBAAmB,SAAK;IACxB,gBAAgB,SAAK;IAC5B,SAAS,CAAC,cAAc,SAAK;IACtB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IACrC,YAAY,uBAA8B;IAG1C,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAChD,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAC5C,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IACrC,iBAAiB,SAAM;IACvB,cAAc,EAAE,GAAG,CACzB,MAAM,EACN;QAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACjD,CAAa;IACP,yBAAyB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,SAAS,CAAC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAa;IAE1E,OAAO,CAAC,4BAA4B,CAA0B;IAC9D,OAAO,CAAC,oBAAoB,CAAM;IAGlC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAO;gBAE7C,GAAG,EAAE,MAAM,EAAE,KAAK,UAAQ;IAWtC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM;IAuDrB,KAAK,CAAC,GAAG,EAAE,GAAG;IAOd,MAAM,CAAC,IAAI,EAAE,GAAG;IAkBT,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,QAA0B;IAQ/D,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,QAA0B;IAmD5D,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IAclD;;OAEG;IACI,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ;IA6BnD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA0B/B,UAAU,CAAC,QAAQ,EAAE,QAAQ;IAkD7B,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAwB/B,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IA2CpC,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAenC,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;CAwDxC"}
|
|
@@ -7,6 +7,7 @@ const logger_1 = require("./logger");
|
|
|
7
7
|
const MTU_HEADER_SIZE = 36;
|
|
8
8
|
class NetworkSession {
|
|
9
9
|
mtu;
|
|
10
|
+
debug;
|
|
10
11
|
send;
|
|
11
12
|
handle;
|
|
12
13
|
// Output
|
|
@@ -32,9 +33,11 @@ class NetworkSession {
|
|
|
32
33
|
static RECEIVE_WINDOW_SIZE = 2048;
|
|
33
34
|
static RELIABLE_WINDOW_SIZE = 4096;
|
|
34
35
|
static FRAGMENT_TIMEOUT_MS = 30000;
|
|
35
|
-
static ORDER_QUEUE_MAX_SIZE =
|
|
36
|
-
|
|
36
|
+
static ORDER_QUEUE_MAX_SIZE = 1024;
|
|
37
|
+
static ORDER_QUEUE_SKIP_THRESHOLD = 512;
|
|
38
|
+
constructor(mtu, debug = false) {
|
|
37
39
|
this.mtu = mtu;
|
|
40
|
+
this.debug = debug;
|
|
38
41
|
this.outputOrderIndex = new Array(32).fill(0);
|
|
39
42
|
this.outputSequenceIndex = new Array(32).fill(0);
|
|
40
43
|
this.inputHighestSequenceIndex = Array.from({ length: 32 }).fill(0);
|
|
@@ -64,7 +67,8 @@ class NetworkSession {
|
|
|
64
67
|
}
|
|
65
68
|
for (const [splitId, entry] of this.fragmentsQueue) {
|
|
66
69
|
if (now - entry.timestamp > NetworkSession.FRAGMENT_TIMEOUT_MS) {
|
|
67
|
-
|
|
70
|
+
if (this.debug)
|
|
71
|
+
logger_1.Logger.warn(`Fragment queue ${splitId} timed out, dropping ${entry.frames.size} fragments`);
|
|
68
72
|
this.fragmentsQueue.delete(splitId);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
@@ -89,11 +93,13 @@ class NetworkSession {
|
|
|
89
93
|
this.sendQueue(size);
|
|
90
94
|
}
|
|
91
95
|
onAck(ack) {
|
|
96
|
+
logger_1.Logger.info(`ACK received: ${ack.sequences.join(", ")}`);
|
|
92
97
|
for (let i = 0, len = ack.sequences.length; i < len; i++) {
|
|
93
98
|
this.outputBackup.delete(ack.sequences[i]);
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
onNack(nack) {
|
|
102
|
+
logger_1.Logger.info(`Received NACK for sequences: ${nack.sequences.join(", ")} - resending`);
|
|
97
103
|
for (let i = 0, len = nack.sequences.length; i < len; i++) {
|
|
98
104
|
const seq = nack.sequences[i];
|
|
99
105
|
const lostFrames = this.outputBackup.get(seq);
|
|
@@ -129,6 +135,8 @@ class NetworkSession {
|
|
|
129
135
|
if (payloadSize > maxSize) {
|
|
130
136
|
const splitSize = Math.ceil(payloadSize / maxSize);
|
|
131
137
|
const splitId = this.outputSplitIndex++ & 0xffff;
|
|
138
|
+
// Queue all split frames first
|
|
139
|
+
const splitFrames = [];
|
|
132
140
|
for (let i = 0; i < splitSize; i++) {
|
|
133
141
|
const index = i * maxSize;
|
|
134
142
|
const nF = new proto_1.Frame();
|
|
@@ -143,8 +151,10 @@ class NetworkSession {
|
|
|
143
151
|
nF.splitFrameIndex = i;
|
|
144
152
|
nF.splitId = splitId;
|
|
145
153
|
nF.splitSize = splitSize;
|
|
146
|
-
|
|
154
|
+
splitFrames.push(nF);
|
|
147
155
|
}
|
|
156
|
+
// Send all split frames together in one batch
|
|
157
|
+
this.sendSplitFrames(splitFrames, priority);
|
|
148
158
|
}
|
|
149
159
|
else {
|
|
150
160
|
if (frame.isReliable()) {
|
|
@@ -155,13 +165,39 @@ class NetworkSession {
|
|
|
155
165
|
}
|
|
156
166
|
queueFrame(frame, priority) {
|
|
157
167
|
let length = 4;
|
|
158
|
-
for (const
|
|
159
|
-
length +=
|
|
168
|
+
for (const f of this.outputFrames)
|
|
169
|
+
length += f.getByteLength();
|
|
160
170
|
if (length + frame.getByteLength() > this.mtu - MTU_HEADER_SIZE)
|
|
161
171
|
this.sendQueue(this.outputFrames.size);
|
|
162
172
|
this.outputFrames.add(frame);
|
|
173
|
+
// For high priority, send all queued frames immediately (not just 1)
|
|
174
|
+
// This ensures split frames go out together
|
|
163
175
|
if (priority === proto_1.Priority.High)
|
|
164
|
-
this.sendQueue(
|
|
176
|
+
this.sendQueue(this.outputFrames.size);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Send split frames - each in its own frameset with small delays to ensure ordering
|
|
180
|
+
*/
|
|
181
|
+
sendSplitFrames(frames, priority) {
|
|
182
|
+
logger_1.Logger.info(`Sending ${frames.length} split frames`);
|
|
183
|
+
// Send first frame immediately
|
|
184
|
+
const sendFrame = (index) => {
|
|
185
|
+
if (index >= frames.length)
|
|
186
|
+
return;
|
|
187
|
+
const frame = frames[index];
|
|
188
|
+
const frameset = new proto_1.FrameSet();
|
|
189
|
+
frameset.sequence = this.outputSequence++;
|
|
190
|
+
frameset.frames = [frame];
|
|
191
|
+
this.outputBackup.set(frameset.sequence, [frame]);
|
|
192
|
+
const buffer = frameset.serialize();
|
|
193
|
+
logger_1.Logger.info(`Split ${index + 1}/${frames.length} sent (seq: ${frameset.sequence}, size: ${buffer.length})`);
|
|
194
|
+
this.send(buffer);
|
|
195
|
+
// Send next frame after a small delay to ensure ordering
|
|
196
|
+
if (index + 1 < frames.length) {
|
|
197
|
+
setTimeout(() => sendFrame(index + 1), 5);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
sendFrame(0);
|
|
165
201
|
}
|
|
166
202
|
sendQueue(amount) {
|
|
167
203
|
if (this.outputFrames.size === 0)
|
|
@@ -172,6 +208,12 @@ class NetworkSession {
|
|
|
172
208
|
this.outputBackup.set(frameset.sequence, frameset.frames);
|
|
173
209
|
for (const frame of frameset.frames)
|
|
174
210
|
this.outputFrames.delete(frame);
|
|
211
|
+
// Log what we're sending
|
|
212
|
+
for (const frame of frameset.frames) {
|
|
213
|
+
if (frame.isSplit()) {
|
|
214
|
+
logger_1.Logger.info(`Sending split frame ${frame.splitFrameIndex}/${frame.splitSize} (splitId: ${frame.splitId}, seq: ${frameset.sequence}, reliable: ${frame.reliableFrameIndex})`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
175
217
|
const buffer = frameset.serialize();
|
|
176
218
|
if (!this.send)
|
|
177
219
|
throw new Error("Send method was not initialized");
|
|
@@ -180,7 +222,8 @@ class NetworkSession {
|
|
|
180
222
|
onFrameSet(frameSet) {
|
|
181
223
|
// Already received this exact sequence so ignore duplicate
|
|
182
224
|
if (this.receivedFrameSequences.has(frameSet.sequence)) {
|
|
183
|
-
|
|
225
|
+
if (this.debug)
|
|
226
|
+
logger_1.Logger.warn(`Duplicate frame set received: sequence ${frameSet.sequence}`);
|
|
184
227
|
return;
|
|
185
228
|
}
|
|
186
229
|
// Remove from lost if we finally got it
|
|
@@ -204,7 +247,8 @@ class NetworkSession {
|
|
|
204
247
|
}
|
|
205
248
|
else {
|
|
206
249
|
// zOut-of-order packet (arrived late but still valid)
|
|
207
|
-
|
|
250
|
+
if (this.debug)
|
|
251
|
+
logger_1.Logger.warn(`Out-of-order frame set received: sequence ${frameSet.sequence} (expected > ${this.lastInputSequence})`);
|
|
208
252
|
}
|
|
209
253
|
// Process all frames
|
|
210
254
|
for (const frame of frameSet.frames) {
|
|
@@ -249,7 +293,8 @@ class NetworkSession {
|
|
|
249
293
|
for (let index = 0; index < frame.splitSize; index++) {
|
|
250
294
|
const sframe = entry.frames.get(index);
|
|
251
295
|
if (!sframe) {
|
|
252
|
-
|
|
296
|
+
if (this.debug)
|
|
297
|
+
logger_1.Logger.warn(`Missing fragment at index ${index} for splitId=${frame.splitId}`);
|
|
253
298
|
this.fragmentsQueue.delete(splitId);
|
|
254
299
|
return;
|
|
255
300
|
}
|
|
@@ -311,15 +356,28 @@ class NetworkSession {
|
|
|
311
356
|
}
|
|
312
357
|
}
|
|
313
358
|
else if (frame.orderedFrameIndex > expectedOrderIndex) {
|
|
314
|
-
|
|
359
|
+
const gap = frame.orderedFrameIndex - expectedOrderIndex;
|
|
360
|
+
// If we're way too far behind, skip ahead to avoid infinite queue buildup
|
|
361
|
+
if (gap > NetworkSession.ORDER_QUEUE_SKIP_THRESHOLD) {
|
|
362
|
+
if (this.debug)
|
|
363
|
+
logger_1.Logger.debug(`Order queue for channel ${channel} skipping ahead from ${expectedOrderIndex} to ${frame.orderedFrameIndex} (gap: ${gap})`);
|
|
364
|
+
// Clear the queue and skip to this frame
|
|
365
|
+
const outOfOrderQueue = this.inputOrderingQueue.get(channel);
|
|
366
|
+
if (outOfOrderQueue)
|
|
367
|
+
outOfOrderQueue.clear();
|
|
368
|
+
this.inputOrderIndex[channel] = frame.orderedFrameIndex + 1;
|
|
369
|
+
this.handle(frame.payload);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
// Future frame - queue it for later
|
|
315
373
|
const outOfOrderQueue = this.inputOrderingQueue.get(channel);
|
|
316
374
|
if (outOfOrderQueue) {
|
|
317
|
-
// Prevent unbounded queue growth
|
|
318
375
|
if (outOfOrderQueue.size < NetworkSession.ORDER_QUEUE_MAX_SIZE) {
|
|
319
376
|
outOfOrderQueue.set(frame.orderedFrameIndex, frame);
|
|
320
377
|
}
|
|
321
378
|
else {
|
|
322
|
-
|
|
379
|
+
if (this.debug)
|
|
380
|
+
logger_1.Logger.debug(`Order queue for channel ${channel} is full, dropping frame ${frame.orderedFrameIndex}`);
|
|
323
381
|
}
|
|
324
382
|
}
|
|
325
383
|
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"frame-reordering.d.ts","sourceRoot":"","sources":["../../src/tests/frame-reordering.ts"],"names":[],"mappings":""}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const shared_1 = require("../shared");
|
|
4
|
-
// Enable debug logging to see the reordering in action
|
|
5
|
-
shared_1.Logger.debugEnabled = true;
|
|
6
|
-
// Create a test network session``
|
|
7
|
-
const session = new shared_1.NetworkSession(1400);
|
|
8
|
-
session.send = (data) => {
|
|
9
|
-
console.log(`Would send ${data.length} bytes`);
|
|
10
|
-
};
|
|
11
|
-
session.handle = (data) => {
|
|
12
|
-
console.log(`✓ Processed frame: "${data.toString()}"`);
|
|
13
|
-
};
|
|
14
|
-
// Create test frame sets with out-of-order sequences
|
|
15
|
-
function createTestFrameSet(sequence, orderedIndex, message) {
|
|
16
|
-
const frameSet = new shared_1.FrameSet();
|
|
17
|
-
frameSet.sequence = sequence;
|
|
18
|
-
const frame = new shared_1.Frame();
|
|
19
|
-
frame.reliability = shared_1.Reliability.ReliableOrdered;
|
|
20
|
-
frame.orderChannel = 0;
|
|
21
|
-
frame.orderedFrameIndex = orderedIndex; // Set the ordered index
|
|
22
|
-
frame.reliableFrameIndex = sequence; // Set reliable index
|
|
23
|
-
frame.payload = Buffer.from(message);
|
|
24
|
-
frameSet.frames = [frame];
|
|
25
|
-
return frameSet;
|
|
26
|
-
}
|
|
27
|
-
console.log("Testing frame reordering system...\n");
|
|
28
|
-
// Simulate receiving frames out of order (starting from 0)
|
|
29
|
-
const frames = [
|
|
30
|
-
createTestFrameSet(0, 0, "First frame"),
|
|
31
|
-
createTestFrameSet(2, 2, "Third frame (out of order)"),
|
|
32
|
-
createTestFrameSet(1, 1, "Second frame (fills gap)"),
|
|
33
|
-
createTestFrameSet(4, 4, "Fifth frame (way ahead)"),
|
|
34
|
-
createTestFrameSet(3, 3, "Fourth frame (fills another gap)"),
|
|
35
|
-
];
|
|
36
|
-
// Process frames in the wrong order to test reordering
|
|
37
|
-
console.log("Receiving frames in order: 0, 2, 1, 4, 3");
|
|
38
|
-
console.log("Expected processing order: 0, 1, 2, 3, 4\n");
|
|
39
|
-
for (const frame of frames) {
|
|
40
|
-
console.log(`\n--- Receiving frame set ${frame.sequence} ---`);
|
|
41
|
-
try {
|
|
42
|
-
session.onFrameSet(frame);
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
console.error(`Error processing frame ${frame.sequence}:`, error);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
console.log("\nTest completed!");
|