@hytopia.com/server-protocol 1.4.34 → 1.4.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hytopia.com/server-protocol",
3
- "version": "1.4.34",
3
+ "version": "1.4.35",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -104,13 +104,12 @@ export function createPacket<TId extends PacketId, TSchema>(
104
104
  return packet;
105
105
  }
106
106
 
107
- export function createPacketBufferUnframer(): (chunk: Uint8Array) => Uint8Array[] {
107
+ export function createPacketBufferUnframer(onMessage: (message: Uint8Array) => void): (chunk: Uint8Array) => void {
108
108
  let buffer = new Uint8Array(512 * 1024); // 512KB initial buffer
109
+ let view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
109
110
  let used = 0;
110
111
 
111
- return (chunk: Uint8Array): Uint8Array[] => {
112
- const messages: Uint8Array[] = [];
113
-
112
+ return (chunk: Uint8Array): void => {
114
113
  // Grow buffer if needed
115
114
  if (used + chunk.length > buffer.length) {
116
115
  const requiredSize = Math.max(buffer.length * 2, used + chunk.length);
@@ -118,39 +117,48 @@ export function createPacketBufferUnframer(): (chunk: Uint8Array) => Uint8Array[
118
117
  // Enforce max buffer size cap
119
118
  if (requiredSize > MAX_FRAME_BUFFER_SIZE) {
120
119
  console.error(`Unframer packet buffer exceeded maximum size of ${MAX_FRAME_BUFFER_SIZE} bytes, discarding packet...`);
121
- used = 0; // Reset buffer state to discard oversized packet and continue processing future packets
122
- return [];
120
+ used = 0;
121
+ return;
123
122
  }
124
123
 
125
124
  const grown = new Uint8Array(requiredSize);
126
125
  grown.set(buffer.subarray(0, used));
127
126
  buffer = grown;
127
+ view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
128
128
  }
129
129
 
130
130
  // Append new chunk
131
131
  buffer.set(chunk, used);
132
132
  used += chunk.length;
133
133
 
134
- // Extract complete messages
135
- while (used >= FRAME_HEADER_SIZE) {
136
- const view = new DataView(buffer.buffer, buffer.byteOffset);
137
- const length = view.getUint32(0, false); // false = big-endian
134
+ // Extract and process complete messages
135
+ let readOffset = 0;
136
+
137
+ while (used - readOffset >= FRAME_HEADER_SIZE) {
138
+ const length = view.getUint32(readOffset, false); // big-endian
139
+ const totalFrameSize = FRAME_HEADER_SIZE + length;
138
140
 
139
141
  // Wait for complete message
140
- if (used < FRAME_HEADER_SIZE + length) {
142
+ if (used - readOffset < totalFrameSize) {
141
143
  break;
142
144
  }
143
145
 
144
- // Extract message (skip 4-byte header)
145
- const message = buffer.slice(FRAME_HEADER_SIZE, FRAME_HEADER_SIZE + length);
146
- messages.push(message);
147
-
148
- // Shift buffer
149
- buffer.copyWithin(0, FRAME_HEADER_SIZE + length, used);
150
- used -= FRAME_HEADER_SIZE + length;
146
+ // Zero-copy: subarray() returns a view, not a copy
147
+ // BUT! Callback must process immediately before buffer is modified
148
+ const messageStart = readOffset + FRAME_HEADER_SIZE;
149
+ onMessage(buffer.subarray(messageStart, messageStart + length));
150
+
151
+ readOffset += totalFrameSize;
151
152
  }
152
153
 
153
- return messages;
154
+ // Shift remaining data to start (after all callbacks complete)
155
+ if (readOffset > 0) {
156
+ if (used > readOffset) {
157
+ buffer.copyWithin(0, readOffset, used);
158
+ }
159
+
160
+ used -= readOffset;
161
+ }
154
162
  };
155
163
  }
156
164