@colyseus/schema 4.0.26 → 4.0.27

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.
@@ -15,6 +15,8 @@ export declare class Encoder<T extends Schema = any> {
15
15
  encode(it?: Iterator, view?: StateView, buffer?: Uint8Array, changeSetName?: ChangeSetName, isEncodeAll?: boolean, initialOffset?: number): Uint8Array;
16
16
  encodeAll(it?: Iterator, buffer?: Uint8Array): Uint8Array<ArrayBufferLike>;
17
17
  encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes?: Uint8Array): Uint8Array<ArrayBufferLike>;
18
+ /** Grow `buffer` to keep BUFFER_SIZE free bytes past `offset`, preserving `[0, offset)`. */
19
+ protected ensureCapacity(buffer: Uint8Array, offset: number): Uint8Array;
18
20
  encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes?: Uint8Array): Uint8Array<ArrayBufferLike>;
19
21
  /**
20
22
  * Produce a topological ordering of `view.changes` keys so each refId
package/build/index.cjs CHANGED
@@ -4400,41 +4400,43 @@ class Encoder {
4400
4400
  encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView, metadata);
4401
4401
  }
4402
4402
  }
4403
- if (it.offset > buffer.byteLength) {
4404
- // we can assume that n + 1 BUFFER_SIZE will suffice given that we are likely done with encoding at this point
4405
- // multiples of BUFFER_SIZE are faster to allocate than arbitrary sizes
4406
- const newSize = Math.ceil(it.offset / Encoder.BUFFER_SIZE) * Encoder.BUFFER_SIZE;
4407
- console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
4403
+ if (it.offset <= buffer.byteLength) {
4404
+ return buffer.subarray(0, it.offset);
4405
+ }
4406
+ // Overflowed: grow and re-encode. Reuse the same iterator so `it.offset`
4407
+ // ends accurate a fresh one strands it at the overflow value and
4408
+ // corrupts the next view's `viewOffset` in a multi-view encode.
4409
+ buffer = this.ensureCapacity(buffer, it.offset);
4410
+ console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
4408
4411
 
4409
4412
  import { Encoder } from "@colyseus/schema";
4410
- Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
4413
+ Encoder.BUFFER_SIZE = ${Math.round(buffer.byteLength / 1024)} * 1024; // ${Math.round(buffer.byteLength / 1024)} KB
4411
4414
  `);
4412
- //
4413
- // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
4414
- // -> No we probably can't unless we catch the need for resize before encoding which is likely more computationally expensive than resizing on demand
4415
- //
4416
- const newBuffer = new Uint8Array(newSize);
4417
- newBuffer.set(buffer); // copy previous encoding steps beyond the initialOffset
4418
- buffer = newBuffer;
4419
- // assign resized buffer to local sharedBuffer
4420
- if (buffer === this.sharedBuffer) {
4421
- this.sharedBuffer = buffer;
4422
- }
4423
- return this.encode({ offset: initialOffset }, view, buffer, changeSetName, isEncodeAll);
4424
- }
4425
- else {
4426
- return buffer.subarray(0, it.offset);
4427
- }
4415
+ it.offset = initialOffset;
4416
+ return this.encode(it, view, buffer, changeSetName, isEncodeAll);
4428
4417
  }
4429
4418
  encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
4430
4419
  return this.encode(it, undefined, buffer, "allChanges", true);
4431
4420
  }
4432
4421
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
4433
4422
  const viewOffset = it.offset;
4434
- // try to encode "filtered" changes
4435
- this.encode(it, view, bytes, "allFilteredChanges", true, viewOffset);
4423
+ // encode() may reallocate the buffer — keep its return, not the stale `bytes`.
4424
+ bytes = this.encode(it, view, bytes, "allFilteredChanges", true, viewOffset);
4436
4425
  return concatBytes(bytes.subarray(0, sharedOffset), bytes.subarray(viewOffset, it.offset));
4437
4426
  }
4427
+ /** Grow `buffer` to keep BUFFER_SIZE free bytes past `offset`, preserving `[0, offset)`. */
4428
+ ensureCapacity(buffer, offset) {
4429
+ if (offset + Encoder.BUFFER_SIZE <= buffer.byteLength) {
4430
+ return buffer;
4431
+ }
4432
+ const size = Math.ceil((offset + Encoder.BUFFER_SIZE) / Encoder.BUFFER_SIZE) * Encoder.BUFFER_SIZE;
4433
+ const grown = new Uint8Array(size);
4434
+ grown.set(buffer.subarray(0, offset));
4435
+ if (buffer === this.sharedBuffer) {
4436
+ this.sharedBuffer = grown;
4437
+ }
4438
+ return grown;
4439
+ }
4438
4440
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
4439
4441
  const viewOffset = it.offset;
4440
4442
  //
@@ -4473,6 +4475,9 @@ class Encoder {
4473
4475
  const ctor = ref.constructor;
4474
4476
  const encoder = ctor[$encoder];
4475
4477
  const metadata = ctor[Symbol.metadata];
4478
+ // These writes are unguarded and unrecoverable (view.changes is cleared
4479
+ // below), so unlike encode() they can't re-encode on overflow — grow ahead.
4480
+ bytes = this.ensureCapacity(bytes, it.offset);
4476
4481
  bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
4477
4482
  encode.number(bytes, ref[$refId], it);
4478
4483
  for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
@@ -4492,8 +4497,10 @@ class Encoder {
4492
4497
  // clear "view" changes after encoding
4493
4498
  view.changes.clear();
4494
4499
  view.changesOutOfOrder = false;
4495
- // try to encode "filtered" changes
4496
- this.encode(it, view, bytes, "filteredChanges", false, viewOffset);
4500
+ // encode() may reallocate the buffer — keep its return, not the stale `bytes`.
4501
+ // Anchor the re-encode at the current offset (default), not `viewOffset`: a
4502
+ // resize must not clobber the view.changes already written at [viewOffset, ).
4503
+ bytes = this.encode(it, view, bytes, "filteredChanges", false);
4497
4504
  return concatBytes(bytes.subarray(0, sharedOffset), bytes.subarray(viewOffset, it.offset));
4498
4505
  }
4499
4506
  /**