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