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