@sanctumterra/raknet 1.4.13 → 1.4.15

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.
@@ -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;IAiD9B,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;IAmEhD,YAAY,CAAC,IAAI,EAAE,MAAM;IAkDzB,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"}
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;YA0BlC,UAAU;IAoGxB,OAAO,CAAC,wBAAwB;IAIhC,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,oBAAoB;IA2Bf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwE9B,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;IA0EhD,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"}
@@ -8,7 +8,7 @@ const shared_2 = require("../shared");
8
8
  const node_net_1 = require("node:net");
9
9
  const promises_1 = require("node:dns/promises");
10
10
  class Client extends shared_1.EventEmitter {
11
- static MTU_VALUES = [1492, 1400, 1028, 1200, 576];
11
+ static MTU_VALUES = [1400, 1200, 1028, 576, 1492];
12
12
  static MTU_RETRY_INTERVAL = 500;
13
13
  static STALE_TIMEOUT_MS = 10000; // 10 seconds without pong = stale
14
14
  static PING_INTERVAL_TICKS = 100; // Send connected ping every ~2 seconds at 50 tick rate
@@ -33,14 +33,20 @@ class Client extends shared_1.EventEmitter {
33
33
  this.status = shared_1.ConnectionStatus.Disconnected;
34
34
  this.tick = 0;
35
35
  this.socket = (0, node_dgram_1.createSocket)("udp4");
36
- this.socket.bind();
37
36
  this.interval = setInterval(this.onTick.bind(this), 1000 / this.options.tickRate);
38
37
  this.socket.on("message", this.onMessage.bind(this));
38
+ this.socket.on("error", (err) => {
39
+ shared_2.Logger.error(`Socket error: ${err.message}`);
40
+ });
39
41
  this.session = new shared_1.NetworkSession(this.options.mtu, this.options.debug);
40
42
  this.session.send = this.send.bind(this);
41
43
  this.session.handle = (data) => {
42
44
  this.handleOnline(data);
43
45
  };
46
+ // Enable debug logging if debug option is set
47
+ if (this.options.debug) {
48
+ shared_2.Logger.debugEnabled = true;
49
+ }
44
50
  }
45
51
  async setupProxy() {
46
52
  if (!this.options.proxy)
@@ -202,28 +208,47 @@ class Client extends shared_1.EventEmitter {
202
208
  return null;
203
209
  }
204
210
  async connect() {
211
+ // Ensure socket is bound and ready
212
+ await new Promise((resolve) => {
213
+ this.socket.once("listening", () => resolve());
214
+ this.socket.bind();
215
+ });
205
216
  if (this.options.proxy) {
206
217
  await this.setupProxy();
207
218
  }
208
219
  this.status = shared_1.ConnectionStatus.Connecting;
209
220
  this.gotReply1 = false;
221
+ // Reset session state for fresh connection
222
+ this.session = new shared_1.NetworkSession(this.options.mtu, this.options.debug);
223
+ this.session.send = this.send.bind(this);
224
+ this.session.handle = (data) => {
225
+ this.handleOnline(data);
226
+ };
210
227
  return new Promise((resolve, reject) => {
211
228
  let mtuIndex = 0;
212
229
  let retryTimeout = null;
230
+ // Use configured MTU or fall back to default values
231
+ const mtuValues = this.options.mtu && this.options.mtu > 0
232
+ ? [this.options.mtu]
233
+ : Client.MTU_VALUES;
213
234
  const sendRequest = () => {
214
- if (mtuIndex >= Client.MTU_VALUES.length) {
235
+ if (mtuIndex >= mtuValues.length) {
215
236
  reject(new Error("Connection timed out, all MTU values exhausted"));
216
237
  return;
217
238
  }
218
- const mtu = Client.MTU_VALUES[mtuIndex];
239
+ const mtu = mtuValues[mtuIndex];
219
240
  if (!mtu)
220
241
  throw new Error("MTU value is undefined");
221
242
  const request = new shared_1.OpenConnectionRequestOne();
222
243
  request.mtu = mtu;
223
244
  request.protocol = 11;
245
+ if (this.options.debug) {
246
+ shared_2.Logger.debug(`Sending OpenConnectionRequestOne with MTU ${mtu}`);
247
+ }
224
248
  this.send(request.serialize());
225
249
  retryTimeout = setTimeout(() => {
226
250
  if (!this.gotReply1) {
251
+ shared_2.Logger.warn(`No reply for MTU ${mtu}, trying next value...`);
227
252
  mtuIndex++;
228
253
  sendRequest();
229
254
  }
@@ -309,6 +334,9 @@ class Client extends shared_1.EventEmitter {
309
334
  const isOnline = (id & 0xf0) === 0x80;
310
335
  if (isOnline)
311
336
  id = 0x80;
337
+ if (this.options.debug) {
338
+ shared_2.Logger.debug(`Received packet ID: 0x${id?.toString(16).padStart(2, "0")} from ${rinfo.address}:${rinfo.port}`);
339
+ }
312
340
  switch (id) {
313
341
  case shared_1.Packets.UnconnectedPong: {
314
342
  const pong = new shared_1.UnconnectedPong(actualData).deserialize();
@@ -330,6 +358,7 @@ class Client extends shared_1.EventEmitter {
330
358
  case shared_1.Packets.OpenConnectionReply2: {
331
359
  const reply2 = new shared_1.OpenConnectionReplyTwo(actualData).deserialize();
332
360
  // Update session MTU with the negotiated value from server
361
+ shared_2.Logger.info(`MTU negotiated: ${reply2.mtu} (was ${this.session.mtu})`);
333
362
  this.session.mtu = reply2.mtu;
334
363
  const request = new shared_1.ConnectionRequest();
335
364
  request.guid = this.options.guid;
@@ -396,7 +425,6 @@ class Client extends shared_1.EventEmitter {
396
425
  this.lastPongTime = Date.now(); // Reset stale timer on connect
397
426
  this.lastActivityTime = Date.now();
398
427
  this.emit("connect");
399
- console.log("Raknet Connected");
400
428
  break;
401
429
  }
402
430
  default: {
@@ -4,7 +4,7 @@ exports.createDefaultClientOptions = exports.generateGuid = void 0;
4
4
  const generateGuid = () => BigInt(Math.floor(Date.now() + Math.random() * 10000000));
5
5
  exports.generateGuid = generateGuid;
6
6
  const createDefaultClientOptions = () => ({
7
- mtu: 1492,
7
+ mtu: 0, // 0 means use MTU_VALUES array
8
8
  address: "127.0.0.1",
9
9
  port: 19132,
10
10
  guid: (0, exports.generateGuid)(),
@@ -28,6 +28,7 @@ export declare class NetworkSession {
28
28
  private static readonly RELIABLE_WINDOW_SIZE;
29
29
  private static readonly FRAGMENT_TIMEOUT_MS;
30
30
  private static readonly ORDER_QUEUE_MAX_SIZE;
31
+ private static readonly ORDER_QUEUE_SKIP_THRESHOLD;
31
32
  constructor(mtu: number, debug?: boolean);
32
33
  onTick(_tick?: number): void;
33
34
  onAck(ack: Ack): void;
@@ -35,6 +36,10 @@ export declare class NetworkSession {
35
36
  frameAndSend(data: Buffer, priority?: Priority): void;
36
37
  sendFrame(frame: Frame, priority?: Priority): void;
37
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;
38
43
  sendQueue(amount: number): void;
39
44
  onFrameSet(frameSet: FrameSet): void;
40
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,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,CAAO;gBAEvC,GAAG,EAAE,MAAM,EAAE,KAAK,UAAQ;IAWtC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM;IAuDrB,KAAK,CAAC,GAAG,EAAE,GAAG;IAMd,MAAM,CAAC,IAAI,EAAE,GAAG;IAeT,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,QAA0B;IAQ/D,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,QAA0B;IA8C5D,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IAW3C,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAiB/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;CAyCxC"}
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"}
@@ -33,7 +33,8 @@ class NetworkSession {
33
33
  static RECEIVE_WINDOW_SIZE = 2048;
34
34
  static RELIABLE_WINDOW_SIZE = 4096;
35
35
  static FRAGMENT_TIMEOUT_MS = 30000;
36
- static ORDER_QUEUE_MAX_SIZE = 256;
36
+ static ORDER_QUEUE_MAX_SIZE = 1024;
37
+ static ORDER_QUEUE_SKIP_THRESHOLD = 512;
37
38
  constructor(mtu, debug = false) {
38
39
  this.mtu = mtu;
39
40
  this.debug = debug;
@@ -92,11 +93,13 @@ class NetworkSession {
92
93
  this.sendQueue(size);
93
94
  }
94
95
  onAck(ack) {
96
+ logger_1.Logger.info(`ACK received: ${ack.sequences.join(", ")}`);
95
97
  for (let i = 0, len = ack.sequences.length; i < len; i++) {
96
98
  this.outputBackup.delete(ack.sequences[i]);
97
99
  }
98
100
  }
99
101
  onNack(nack) {
102
+ logger_1.Logger.info(`Received NACK for sequences: ${nack.sequences.join(", ")} - resending`);
100
103
  for (let i = 0, len = nack.sequences.length; i < len; i++) {
101
104
  const seq = nack.sequences[i];
102
105
  const lostFrames = this.outputBackup.get(seq);
@@ -132,6 +135,8 @@ class NetworkSession {
132
135
  if (payloadSize > maxSize) {
133
136
  const splitSize = Math.ceil(payloadSize / maxSize);
134
137
  const splitId = this.outputSplitIndex++ & 0xffff;
138
+ // Queue all split frames first
139
+ const splitFrames = [];
135
140
  for (let i = 0; i < splitSize; i++) {
136
141
  const index = i * maxSize;
137
142
  const nF = new proto_1.Frame();
@@ -146,8 +151,10 @@ class NetworkSession {
146
151
  nF.splitFrameIndex = i;
147
152
  nF.splitId = splitId;
148
153
  nF.splitSize = splitSize;
149
- this.queueFrame(nF, priority);
154
+ splitFrames.push(nF);
150
155
  }
156
+ // Send all split frames together in one batch
157
+ this.sendSplitFrames(splitFrames, priority);
151
158
  }
152
159
  else {
153
160
  if (frame.isReliable()) {
@@ -158,13 +165,39 @@ class NetworkSession {
158
165
  }
159
166
  queueFrame(frame, priority) {
160
167
  let length = 4;
161
- for (const frame of this.outputFrames)
162
- length += frame.getByteLength();
168
+ for (const f of this.outputFrames)
169
+ length += f.getByteLength();
163
170
  if (length + frame.getByteLength() > this.mtu - MTU_HEADER_SIZE)
164
171
  this.sendQueue(this.outputFrames.size);
165
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
166
175
  if (priority === proto_1.Priority.High)
167
- this.sendQueue(1);
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);
168
201
  }
169
202
  sendQueue(amount) {
170
203
  if (this.outputFrames.size === 0)
@@ -175,6 +208,12 @@ class NetworkSession {
175
208
  this.outputBackup.set(frameset.sequence, frameset.frames);
176
209
  for (const frame of frameset.frames)
177
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
+ }
178
217
  const buffer = frameset.serialize();
179
218
  if (!this.send)
180
219
  throw new Error("Send method was not initialized");
@@ -317,10 +356,22 @@ class NetworkSession {
317
356
  }
318
357
  }
319
358
  else if (frame.orderedFrameIndex > expectedOrderIndex) {
320
- // Future frame n queue it for later
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
321
373
  const outOfOrderQueue = this.inputOrderingQueue.get(channel);
322
374
  if (outOfOrderQueue) {
323
- // Prevent unbounded queue growth
324
375
  if (outOfOrderQueue.size < NetworkSession.ORDER_QUEUE_MAX_SIZE) {
325
376
  outOfOrderQueue.set(frame.orderedFrameIndex, frame);
326
377
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanctumterra/raknet",
3
- "version": "1.4.13",
3
+ "version": "1.4.15",
4
4
  "description": "",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=frame-reordering.d.ts.map
@@ -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!");