@colyseus/schema 3.0.0-alpha.1 → 3.0.0-alpha.11

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.
Files changed (43) hide show
  1. package/build/cjs/index.js +113 -69
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +113 -69
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +113 -69
  6. package/lib/Reflection.d.ts +1 -1
  7. package/lib/Reflection.js +1 -2
  8. package/lib/Reflection.js.map +1 -1
  9. package/lib/decoder/DecodeOperation.js +2 -2
  10. package/lib/decoder/DecodeOperation.js.map +1 -1
  11. package/lib/decoder/Decoder.d.ts +1 -1
  12. package/lib/decoder/Decoder.js +4 -4
  13. package/lib/decoder/Decoder.js.map +1 -1
  14. package/lib/decoder/strategy/StateCallbacks.js +1 -1
  15. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  16. package/lib/encoder/ChangeTree.d.ts +0 -2
  17. package/lib/encoder/ChangeTree.js +2 -6
  18. package/lib/encoder/ChangeTree.js.map +1 -1
  19. package/lib/encoder/EncodeOperation.js +12 -0
  20. package/lib/encoder/EncodeOperation.js.map +1 -1
  21. package/lib/encoder/Encoder.d.ts +5 -4
  22. package/lib/encoder/Encoder.js +62 -48
  23. package/lib/encoder/Encoder.js.map +1 -1
  24. package/lib/encoder/StateView.d.ts +2 -0
  25. package/lib/encoder/StateView.js +4 -1
  26. package/lib/encoder/StateView.js.map +1 -1
  27. package/lib/encoding/decode.d.ts +21 -19
  28. package/lib/encoding/decode.js +6 -6
  29. package/lib/encoding/decode.js.map +1 -1
  30. package/lib/encoding/encode.d.ts +3 -2
  31. package/lib/encoding/encode.js +24 -22
  32. package/lib/encoding/encode.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/Reflection.ts +1 -2
  35. package/src/decoder/DecodeOperation.ts +3 -3
  36. package/src/decoder/Decoder.ts +5 -5
  37. package/src/decoder/strategy/StateCallbacks.ts +1 -1
  38. package/src/encoder/ChangeTree.ts +2 -6
  39. package/src/encoder/EncodeOperation.ts +13 -0
  40. package/src/encoder/Encoder.ts +69 -53
  41. package/src/encoder/StateView.ts +4 -0
  42. package/src/encoding/decode.ts +24 -25
  43. package/src/encoding/encode.ts +26 -23
@@ -164,7 +164,6 @@ class Root {
164
164
  // pending changes to be encoded
165
165
  this.changes = new Map();
166
166
  this.filteredChanges = new Map();
167
- this.views = [];
168
167
  }
169
168
  getNextUniqueId() {
170
169
  return this.nextUniqueId++;
@@ -259,14 +258,12 @@ class ChangeTree {
259
258
  this.checkIsFiltered(parent, parentIndex);
260
259
  if (!this.isFiltered) {
261
260
  this.root.changes.set(this, this.changes);
261
+ this.root.allChanges.set(this, this.allChanges);
262
262
  }
263
263
  if (this.isFiltered || this.isPartiallyFiltered) {
264
264
  this.root.filteredChanges.set(this, this.filteredChanges);
265
265
  this.root.allFilteredChanges.set(this, this.filteredChanges);
266
266
  }
267
- else {
268
- this.root.allChanges.set(this, this.allChanges);
269
- }
270
267
  this.ensureRefId();
271
268
  this.forEachChild((changeTree, atIndex) => {
272
269
  changeTree.setParent(this.ref, root, atIndex);
@@ -358,7 +355,6 @@ class ChangeTree {
358
355
  }
359
356
  _shiftAllChangeIndexes(shiftIndex, startIndex = 0, allChangeSet) {
360
357
  Array.from(allChangeSet.entries()).forEach(([index, op]) => {
361
- // console.log('shiftAllChangeIndexes', index >= startIndex, { index, op, shiftIndex, startIndex })
362
358
  if (index >= startIndex) {
363
359
  allChangeSet.delete(index);
364
360
  allChangeSet.set(index + shiftIndex, op);
@@ -499,7 +495,7 @@ class ChangeTree {
499
495
  }
500
496
  checkIsFiltered(parent, parentIndex) {
501
497
  // Detect if current structure has "filters" declared
502
- this.isPartiallyFiltered = this.ref['constructor']?.[Symbol.metadata]?.[-2];
498
+ this.isPartiallyFiltered = (this.ref['constructor']?.[Symbol.metadata]?.[-2] !== undefined);
503
499
  // TODO: support "partially filtered", where the instance is visible, but only a field is not.
504
500
  // Detect if parent has "filters" declared
505
501
  while (parent && !this.isFiltered) {
@@ -560,6 +556,29 @@ try {
560
556
  textEncoder = new TextEncoder();
561
557
  }
562
558
  catch (e) { }
559
+ const hasBufferByteLength = (typeof Buffer !== 'undefined' && Buffer.byteLength);
560
+ const utf8Length = (hasBufferByteLength)
561
+ ? Buffer.byteLength // node
562
+ : function (str, _) {
563
+ var c = 0, length = 0;
564
+ for (var i = 0, l = str.length; i < l; i++) {
565
+ c = str.charCodeAt(i);
566
+ if (c < 0x80) {
567
+ length += 1;
568
+ }
569
+ else if (c < 0x800) {
570
+ length += 2;
571
+ }
572
+ else if (c < 0xd800 || c >= 0xe000) {
573
+ length += 3;
574
+ }
575
+ else {
576
+ i++;
577
+ length += 4;
578
+ }
579
+ }
580
+ return length;
581
+ };
563
582
  function utf8Write(view, str, it) {
564
583
  var c = 0;
565
584
  for (var i = 0, l = str.length; i < l; i++) {
@@ -654,8 +673,7 @@ function string$1(bytes, value, it) {
654
673
  if (!value) {
655
674
  value = "";
656
675
  }
657
- // let length = utf8Length(value);
658
- let length = Buffer.byteLength(value, "utf8");
676
+ let length = utf8Length(value, "utf8");
659
677
  let size = 0;
660
678
  // fixstr
661
679
  if (length < 0x20) {
@@ -766,6 +784,7 @@ function number$1(bytes, value, it) {
766
784
 
767
785
  var encode = /*#__PURE__*/Object.freeze({
768
786
  __proto__: null,
787
+ utf8Length: utf8Length,
769
788
  utf8Write: utf8Write,
770
789
  int8: int8$1,
771
790
  uint8: uint8$1,
@@ -923,6 +942,18 @@ const encodeKeyValueOperation = function (encoder, bytes, changeTree, field, ope
923
942
  }
924
943
  const type = changeTree.getType(field);
925
944
  const value = changeTree.getValue(field);
945
+ // try { throw new Error(); } catch (e) {
946
+ // // only print if not coming from Reflection.ts
947
+ // if (!e.stack.includes("src/Reflection.ts")) {
948
+ // console.log("encodeKeyValueOperation -> ", {
949
+ // ref: changeTree.ref.constructor.name,
950
+ // field,
951
+ // operation: OPERATION[operation],
952
+ // value: value?.toJSON(),
953
+ // items: ref.toJSON(),
954
+ // });
955
+ // }
956
+ // }
926
957
  // TODO: inline this function call small performance gain
927
958
  encodeValue(encoder, bytes, ref, type, value, field, operation, it);
928
959
  };
@@ -989,9 +1020,9 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
989
1020
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
990
1021
  * SOFTWARE
991
1022
  */
992
- function utf8Read(bytes, offset, length) {
1023
+ function utf8Read(bytes, it, length) {
993
1024
  var string = '', chr = 0;
994
- for (var i = offset, end = offset + length; i < end; i++) {
1025
+ for (var i = it.offset, end = it.offset + length; i < end; i++) {
995
1026
  var byte = bytes[i];
996
1027
  if ((byte & 0x80) === 0x00) {
997
1028
  string += String.fromCharCode(byte);
@@ -1026,6 +1057,7 @@ function utf8Read(bytes, offset, length) {
1026
1057
  // (do not throw error to avoid server/client from crashing due to hack attemps)
1027
1058
  // throw new Error('Invalid byte ' + byte.toString(16));
1028
1059
  }
1060
+ it.offset += length;
1029
1061
  return string;
1030
1062
  }
1031
1063
  function int8(bytes, it) {
@@ -1093,9 +1125,7 @@ function string(bytes, it) {
1093
1125
  else if (prefix === 0xdb) {
1094
1126
  length = uint32(bytes, it);
1095
1127
  }
1096
- const value = utf8Read(bytes, it.offset, length);
1097
- it.offset += length;
1098
- return value;
1128
+ return utf8Read(bytes, it, length);
1099
1129
  }
1100
1130
  function stringCheck(bytes, it) {
1101
1131
  const prefix = bytes[it.offset];
@@ -1199,6 +1229,7 @@ function switchStructureCheck(bytes, it) {
1199
1229
 
1200
1230
  var decode = /*#__PURE__*/Object.freeze({
1201
1231
  __proto__: null,
1232
+ utf8Read: utf8Read,
1202
1233
  int8: int8,
1203
1234
  uint8: uint8,
1204
1235
  int16: int16,
@@ -1222,7 +1253,7 @@ var decode = /*#__PURE__*/Object.freeze({
1222
1253
 
1223
1254
  const DEFINITION_MISMATCH = -1;
1224
1255
  function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges) {
1225
- const $root = decoder.$root;
1256
+ const $root = decoder.root;
1226
1257
  const previousValue = ref[$getByIndex](index);
1227
1258
  let value;
1228
1259
  if ((operation & OPERATION.DELETE) === OPERATION.DELETE) {
@@ -1422,7 +1453,7 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1422
1453
  else if (operation === OPERATION.DELETE_BY_REFID) {
1423
1454
  // TODO: refactor here, try to follow same flow as below
1424
1455
  const refId = number(bytes, it);
1425
- const previousValue = decoder.$root.refs.get(refId);
1456
+ const previousValue = decoder.root.refs.get(refId);
1426
1457
  const index = ref.findIndex((value) => value === previousValue);
1427
1458
  ref[$deleteByIndex](index);
1428
1459
  allChanges.push({
@@ -3358,13 +3389,13 @@ class Encoder {
3358
3389
  // });
3359
3390
  }
3360
3391
  setRoot(state) {
3361
- this.$root = new Root();
3392
+ this.root = new Root();
3362
3393
  this.state = state;
3363
- state[$changes].setRoot(this.$root);
3394
+ state[$changes].setRoot(this.root);
3364
3395
  }
3365
- encode(it = { offset: 0 }, view, bytes = this.sharedBuffer, changeTrees = this.$root.changes) {
3396
+ encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeTrees = this.root.changes) {
3366
3397
  const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
3367
- const isEncodeAll = this.$root.allChanges === changeTrees;
3398
+ const isEncodeAll = this.root.allChanges === changeTrees;
3368
3399
  const hasView = (view !== undefined);
3369
3400
  const rootChangeTree = this.state[$changes];
3370
3401
  const changeTreesIterator = changeTrees.entries();
@@ -3373,6 +3404,12 @@ class Encoder {
3373
3404
  const ctor = ref['constructor'];
3374
3405
  const encoder = ctor[$encoder];
3375
3406
  const filter = ctor[$filter];
3407
+ // try { throw new Error(); } catch (e) {
3408
+ // // only print if not coming from Reflection.ts
3409
+ // if (!e.stack.includes("src/Reflection.ts")) {
3410
+ // console.log("ChangeTree:", { ref: ref.constructor.name, });
3411
+ // }
3412
+ // }
3376
3413
  if (hasView) {
3377
3414
  if (!view.items.has(changeTree)) {
3378
3415
  view.invisible.add(changeTree);
@@ -3384,8 +3421,8 @@ class Encoder {
3384
3421
  }
3385
3422
  // skip root `refId` if it's the first change tree
3386
3423
  if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
3387
- bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3388
- number$1(bytes, changeTree.refId, it);
3424
+ buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
3425
+ number$1(buffer, changeTree.refId, it);
3389
3426
  }
3390
3427
  const changesIterator = changes.entries();
3391
3428
  for (const [fieldIndex, operation] of changesIterator) {
@@ -3402,22 +3439,31 @@ class Encoder {
3402
3439
  // view?.invisible.add(changeTree);
3403
3440
  continue;
3404
3441
  }
3405
- // console.log("WILL ENCODE", {
3406
- // ref: changeTree.ref.constructor.name,
3407
- // fieldIndex,
3408
- // operation: OPERATION[operation],
3409
- // });
3410
- encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3442
+ // try { throw new Error(); } catch (e) {
3443
+ // // only print if not coming from Reflection.ts
3444
+ // if (!e.stack.includes("src/Reflection.ts")) {
3445
+ // // console.log("WILL ENCODE", {
3446
+ // // ref: changeTree.ref.constructor.name,
3447
+ // // fieldIndex,
3448
+ // // operation: OPERATION[operation],
3449
+ // // });
3450
+ // }
3451
+ // }
3452
+ encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
3411
3453
  }
3412
3454
  }
3413
- if (it.offset > bytes.byteLength) {
3414
- const newSize = getNextPowerOf2(this.sharedBuffer.byteLength * 2);
3415
- console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + bytes.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
3455
+ if (it.offset > buffer.byteLength) {
3456
+ const newSize = getNextPowerOf2(buffer.byteLength * 2);
3457
+ console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + buffer.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
3416
3458
  //
3417
3459
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
3418
3460
  //
3419
- this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);
3420
- return this.encode({ offset: initialOffset }, view);
3461
+ buffer = Buffer.allocUnsafeSlow(newSize);
3462
+ // assign resized buffer to local sharedBuffer
3463
+ if (buffer === this.sharedBuffer) {
3464
+ this.sharedBuffer = buffer;
3465
+ }
3466
+ return this.encode({ offset: initialOffset }, view, buffer);
3421
3467
  }
3422
3468
  else {
3423
3469
  //
@@ -3429,42 +3475,41 @@ class Encoder {
3429
3475
  //
3430
3476
  this.onEndEncode(changeTrees);
3431
3477
  }
3432
- // return bytes;
3433
- return bytes.slice(0, it.offset);
3478
+ return buffer.subarray(0, it.offset);
3434
3479
  }
3435
3480
  }
3436
- encodeAll(it = { offset: 0 }) {
3437
- // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);
3438
- // Array.from(this.$root.allChanges.entries()).map((item) => {
3439
- // console.log("->", item[0].refId, item[0].ref.toJSON());
3481
+ encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
3482
+ // console.log(`encodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
3483
+ // Array.from(this.root.allChanges.entries()).map((item) => {
3484
+ // console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
3440
3485
  // });
3441
- return this.encode(it, undefined, this.sharedBuffer, this.$root.allChanges);
3486
+ return this.encode(it, undefined, buffer, this.root.allChanges);
3442
3487
  }
3443
3488
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3444
3489
  const viewOffset = it.offset;
3445
- // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);
3490
+ // console.log(`encodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
3446
3491
  // this.debugAllFilteredChanges();
3447
3492
  // try to encode "filtered" changes
3448
- this.encode(it, view, bytes, this.$root.allFilteredChanges);
3493
+ this.encode(it, view, bytes, this.root.allFilteredChanges);
3449
3494
  return Buffer.concat([
3450
- bytes.slice(0, sharedOffset),
3451
- bytes.slice(viewOffset, it.offset)
3495
+ bytes.subarray(0, sharedOffset),
3496
+ bytes.subarray(viewOffset, it.offset)
3452
3497
  ]);
3453
3498
  }
3454
- // debugAllFilteredChanges() {
3455
- // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {
3456
- // console.log("->", { refId: item[0].refId }, item[0].ref.toJSON());
3457
- // if (Array.isArray(item[0].ref.toJSON())) {
3458
- // item[1].forEach((op, key) => {
3459
- // console.log(" ->", { key, op: OPERATION[op] });
3460
- // })
3461
- // }
3462
- // });
3463
- // }
3499
+ debugAllFilteredChanges() {
3500
+ Array.from(this.root.allFilteredChanges.entries()).map((item) => {
3501
+ console.log("->", { refId: item[0].refId, changes: item[1].size }, item[0].ref.toJSON());
3502
+ if (Array.isArray(item[0].ref.toJSON())) {
3503
+ item[1].forEach((op, key) => {
3504
+ console.log(" ->", { key, op: OPERATION[op] });
3505
+ });
3506
+ }
3507
+ });
3508
+ }
3464
3509
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
3465
3510
  const viewOffset = it.offset;
3466
3511
  // try to encode "filtered" changes
3467
- this.encode(it, view, bytes, this.$root.filteredChanges);
3512
+ this.encode(it, view, bytes, this.root.filteredChanges);
3468
3513
  // encode visibility changes (add/remove for this view)
3469
3514
  const viewChangesIterator = view.changes.entries();
3470
3515
  for (const [changeTree, changes] of viewChangesIterator) {
@@ -3492,11 +3537,11 @@ class Encoder {
3492
3537
  // clear "view" changes after encoding
3493
3538
  view.changes.clear();
3494
3539
  return Buffer.concat([
3495
- bytes.slice(0, sharedOffset),
3496
- bytes.slice(viewOffset, it.offset)
3540
+ bytes.subarray(0, sharedOffset),
3541
+ bytes.subarray(viewOffset, it.offset)
3497
3542
  ]);
3498
3543
  }
3499
- onEndEncode(changeTrees = this.$root.changes) {
3544
+ onEndEncode(changeTrees = this.root.changes) {
3500
3545
  const changeTreesIterator = changeTrees.entries();
3501
3546
  for (const [changeTree, _] of changeTreesIterator) {
3502
3547
  changeTree.endEncode();
@@ -3504,14 +3549,14 @@ class Encoder {
3504
3549
  }
3505
3550
  discardChanges() {
3506
3551
  // discard shared changes
3507
- if (this.$root.changes.size > 0) {
3508
- this.onEndEncode(this.$root.changes);
3509
- this.$root.changes.clear();
3552
+ if (this.root.changes.size > 0) {
3553
+ this.onEndEncode(this.root.changes);
3554
+ this.root.changes.clear();
3510
3555
  }
3511
3556
  // discard filtered changes
3512
- if (this.$root.filteredChanges.size > 0) {
3513
- this.onEndEncode(this.$root.filteredChanges);
3514
- this.$root.filteredChanges.clear();
3557
+ if (this.root.filteredChanges.size > 0) {
3558
+ this.onEndEncode(this.root.filteredChanges);
3559
+ this.root.filteredChanges.clear();
3515
3560
  }
3516
3561
  }
3517
3562
  tryEncodeTypeId(bytes, baseType, targetType, it) {
@@ -3672,12 +3717,12 @@ class Decoder {
3672
3717
  }
3673
3718
  setRoot(root) {
3674
3719
  this.state = root;
3675
- this.$root = new ReferenceTracker();
3676
- this.$root.addRef(0, root);
3720
+ this.root = new ReferenceTracker();
3721
+ this.root.addRef(0, root);
3677
3722
  }
3678
3723
  decode(bytes, it = { offset: 0 }, ref = this.state) {
3679
3724
  const allChanges = [];
3680
- const $root = this.$root;
3725
+ const $root = this.root;
3681
3726
  const totalBytes = bytes.byteLength;
3682
3727
  let decoder = ref['constructor'][$decoder];
3683
3728
  this.currentRefId = 0;
@@ -3758,7 +3803,7 @@ class Decoder {
3758
3803
  previousValue: value
3759
3804
  });
3760
3805
  if (needRemoveRef) {
3761
- this.$root.removeRef(this.$root.refIds.get(value));
3806
+ this.root.removeRef(this.root.refIds.get(value));
3762
3807
  }
3763
3808
  });
3764
3809
  }
@@ -3798,7 +3843,7 @@ class Reflection extends Schema {
3798
3843
  super(...arguments);
3799
3844
  this.types = new ArraySchema();
3800
3845
  }
3801
- static encode(instance, context) {
3846
+ static encode(instance, context, it = { offset: 0 }) {
3802
3847
  if (!context) {
3803
3848
  context = new TypeContext(instance.constructor);
3804
3849
  }
@@ -3855,7 +3900,6 @@ class Reflection extends Schema {
3855
3900
  }
3856
3901
  buildType(type, klass[Symbol.metadata]);
3857
3902
  }
3858
- const it = { offset: 0 };
3859
3903
  const buf = encoder.encodeAll(it);
3860
3904
  return Buffer.from(buf, 0, it.offset);
3861
3905
  }