@colyseus/schema 3.0.28 → 3.0.30

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.
@@ -959,6 +959,9 @@ const Metadata = {
959
959
  fields[metadata[i].name] = metadata[i].type;
960
960
  }
961
961
  return fields;
962
+ },
963
+ hasViewTagAtIndex(metadata, index) {
964
+ return metadata?.[$viewFieldIndexes]?.includes(index);
962
965
  }
963
966
  };
964
967
 
@@ -1379,7 +1382,7 @@ class ChangeTree {
1379
1382
  key += `-${this.root.types.schemas.get(parentConstructor)}`;
1380
1383
  }
1381
1384
  key += `-${parentIndex}`;
1382
- const fieldHasViewTag = parentConstructor?.[Symbol.metadata]?.[$viewFieldIndexes]?.includes(parentIndex);
1385
+ const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentConstructor?.[Symbol.metadata], parentIndex);
1383
1386
  this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
1384
1387
  || this.root.types.parentFiltered[key]
1385
1388
  || fieldHasViewTag;
@@ -1522,7 +1525,7 @@ const encodeArray = function (encoder, bytes, changeTree, field, operation, it,
1522
1525
  // encode index
1523
1526
  encode.number(bytes, refOrIndex, it);
1524
1527
  // Do not encode value for DELETE operations
1525
- if (operation === exports.OPERATION.DELETE) {
1528
+ if (operation === exports.OPERATION.DELETE || operation === exports.OPERATION.DELETE_BY_REFID) {
1526
1529
  return;
1527
1530
  }
1528
1531
  const type = changeTree.getType(field);
@@ -1743,10 +1746,14 @@ const decodeArray = function (decoder, bytes, it, ref, allChanges) {
1743
1746
  else if (operation === exports.OPERATION.ADD_BY_REFID) {
1744
1747
  const refId = decode.number(bytes, it);
1745
1748
  const itemByRefId = decoder.root.refs.get(refId);
1746
- // use existing index, or push new value
1747
- index = (itemByRefId)
1748
- ? ref.findIndex((value) => value === itemByRefId)
1749
- : ref.length;
1749
+ // if item already exists, use existing index
1750
+ if (itemByRefId) {
1751
+ index = ref.findIndex((value) => value === itemByRefId);
1752
+ }
1753
+ // fallback to use last index
1754
+ if (index === -1 || index === undefined) {
1755
+ index = ref.length;
1756
+ }
1750
1757
  }
1751
1758
  else {
1752
1759
  index = decode.number(bytes, it);
@@ -1865,6 +1872,7 @@ class ArraySchema {
1865
1872
  this.items = [];
1866
1873
  this.tmpItems = [];
1867
1874
  this.deletedIndexes = {};
1875
+ this.isMovingItems = false;
1868
1876
  Object.defineProperty(this, $childType, {
1869
1877
  value: undefined,
1870
1878
  enumerable: false,
@@ -1892,31 +1900,38 @@ class ArraySchema {
1892
1900
  if (setValue[$changes]) {
1893
1901
  assertInstanceType(setValue, obj[$childType], obj, key);
1894
1902
  const previousValue = obj.items[key];
1895
- if (previousValue !== undefined) {
1896
- if (setValue[$changes].isNew) {
1897
- this[$changes].indexedOperation(Number(key), exports.OPERATION.MOVE_AND_ADD);
1898
- }
1899
- else {
1900
- if ((obj[$changes].getChange(Number(key)) & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
1901
- this[$changes].indexedOperation(Number(key), exports.OPERATION.DELETE_AND_MOVE);
1903
+ if (!obj.isMovingItems) {
1904
+ obj.$changeAt(Number(key), setValue);
1905
+ }
1906
+ else {
1907
+ if (previousValue !== undefined) {
1908
+ if (setValue[$changes].isNew) {
1909
+ obj[$changes].indexedOperation(Number(key), exports.OPERATION.MOVE_AND_ADD);
1902
1910
  }
1903
1911
  else {
1904
- this[$changes].indexedOperation(Number(key), exports.OPERATION.MOVE);
1912
+ if ((obj[$changes].getChange(Number(key)) & exports.OPERATION.DELETE) === exports.OPERATION.DELETE) {
1913
+ obj[$changes].indexedOperation(Number(key), exports.OPERATION.DELETE_AND_MOVE);
1914
+ }
1915
+ else {
1916
+ obj[$changes].indexedOperation(Number(key), exports.OPERATION.MOVE);
1917
+ }
1905
1918
  }
1906
1919
  }
1920
+ else if (setValue[$changes].isNew) {
1921
+ obj[$changes].indexedOperation(Number(key), exports.OPERATION.ADD);
1922
+ }
1923
+ setValue[$changes].setParent(this, obj[$changes].root, key);
1924
+ }
1925
+ if (previousValue !== undefined) {
1907
1926
  // remove root reference from previous value
1908
1927
  previousValue[$changes].root?.remove(previousValue[$changes]);
1909
1928
  }
1910
- else if (setValue[$changes].isNew) {
1911
- this[$changes].indexedOperation(Number(key), exports.OPERATION.ADD);
1912
- }
1913
- setValue[$changes].setParent(this, obj[$changes].root, key);
1914
1929
  }
1915
1930
  else {
1916
1931
  obj.$changeAt(Number(key), setValue);
1917
1932
  }
1918
- this.items[key] = setValue;
1919
- this.tmpItems[key] = setValue;
1933
+ obj.items[key] = setValue;
1934
+ obj.tmpItems[key] = setValue;
1920
1935
  }
1921
1936
  return true;
1922
1937
  }
@@ -2024,8 +2039,12 @@ class ArraySchema {
2024
2039
  if (this.items[index] === value) {
2025
2040
  return;
2026
2041
  }
2042
+ const operation = (this.items[index] !== undefined)
2043
+ ? typeof (value) === "object"
2044
+ ? exports.OPERATION.DELETE_AND_ADD // schema child
2045
+ : exports.OPERATION.REPLACE // primitive
2046
+ : exports.OPERATION.ADD;
2027
2047
  const changeTree = this[$changes];
2028
- const operation = changeTree.indexes?.[index]?.op ?? exports.OPERATION.ADD;
2029
2048
  changeTree.change(index, operation);
2030
2049
  //
2031
2050
  // set value's parent after the value is set
@@ -2142,11 +2161,13 @@ class ArraySchema {
2142
2161
  * ```
2143
2162
  */
2144
2163
  sort(compareFn = DEFAULT_SORT) {
2164
+ this.isMovingItems = true;
2145
2165
  const changeTree = this[$changes];
2146
2166
  const sortedItems = this.items.sort(compareFn);
2147
2167
  // wouldn't OPERATION.MOVE make more sense here?
2148
2168
  sortedItems.forEach((_, i) => changeTree.change(i, exports.OPERATION.REPLACE));
2149
2169
  this.tmpItems.sort(compareFn);
2170
+ this.isMovingItems = false;
2150
2171
  return this;
2151
2172
  }
2152
2173
  /**
@@ -2459,6 +2480,37 @@ class ArraySchema {
2459
2480
  // @ts-ignore
2460
2481
  return this.items.toSpliced.apply(copy, arguments);
2461
2482
  }
2483
+ shuffle() {
2484
+ return this.move((_) => {
2485
+ let currentIndex = this.items.length;
2486
+ while (currentIndex != 0) {
2487
+ let randomIndex = Math.floor(Math.random() * currentIndex);
2488
+ currentIndex--;
2489
+ [this[currentIndex], this[randomIndex]] = [this[randomIndex], this[currentIndex]];
2490
+ }
2491
+ });
2492
+ }
2493
+ /**
2494
+ * Allows to move items around in the array.
2495
+ *
2496
+ * Example:
2497
+ * state.cards.move((cards) => {
2498
+ * [cards[4], cards[3]] = [cards[3], cards[4]];
2499
+ * [cards[3], cards[2]] = [cards[2], cards[3]];
2500
+ * [cards[2], cards[0]] = [cards[0], cards[2]];
2501
+ * [cards[1], cards[1]] = [cards[1], cards[1]];
2502
+ * [cards[0], cards[0]] = [cards[0], cards[0]];
2503
+ * })
2504
+ *
2505
+ * @param cb
2506
+ * @returns
2507
+ */
2508
+ move(cb) {
2509
+ this.isMovingItems = true;
2510
+ cb(this);
2511
+ this.isMovingItems = false;
2512
+ return this;
2513
+ }
2462
2514
  [($getByIndex)](index, isEncodeAll = false) {
2463
2515
  //
2464
2516
  // TODO: avoid unecessary `this.tmpItems` check during decoding.