@colyseus/schema 4.0.29 → 4.0.31
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/encoder/ChangeTree.d.ts +9 -0
- package/build/index.cjs +100 -0
- package/build/index.cjs.map +1 -1
- package/build/index.js +100 -0
- package/build/index.mjs +100 -0
- package/build/index.mjs.map +1 -1
- package/build/types/custom/ArraySchema.d.ts +16 -0
- package/build/types/custom/MapSchema.d.ts +17 -0
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +27 -0
- package/src/types/custom/ArraySchema.ts +39 -0
- package/src/types/custom/MapSchema.ts +33 -0
|
@@ -110,6 +110,15 @@ export declare class ChangeTree<T extends Ref = any> {
|
|
|
110
110
|
* Add a parent to the chain
|
|
111
111
|
*/
|
|
112
112
|
addParent(parent: Ref, index: number): void;
|
|
113
|
+
/**
|
|
114
|
+
* Move `parent`'s existing chain entry to `index`, skipping the work
|
|
115
|
+
* `addParent` does. `parent` must already be a parent of this tree.
|
|
116
|
+
*
|
|
117
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
118
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
119
|
+
* element it names.
|
|
120
|
+
*/
|
|
121
|
+
setParentIndex(parent: Ref, index: number): void;
|
|
113
122
|
/**
|
|
114
123
|
* Remove a parent from the chain
|
|
115
124
|
* @param parent - The parent to remove
|
package/build/index.cjs
CHANGED
|
@@ -1452,6 +1452,33 @@ class ChangeTree {
|
|
|
1452
1452
|
next: this.parentChain
|
|
1453
1453
|
};
|
|
1454
1454
|
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Move `parent`'s existing chain entry to `index`, skipping the work
|
|
1457
|
+
* `addParent` does. `parent` must already be a parent of this tree.
|
|
1458
|
+
*
|
|
1459
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
1460
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
1461
|
+
* element it names.
|
|
1462
|
+
*/
|
|
1463
|
+
setParentIndex(parent, index) {
|
|
1464
|
+
const chain = this.parentChain;
|
|
1465
|
+
if (chain === undefined) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (chain.next === undefined) {
|
|
1469
|
+
chain.index = index; // sole parent, so it is `parent`
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
1473
|
+
// through `$changes` because ArraySchema arrives proxied (see
|
|
1474
|
+
// removeParent below).
|
|
1475
|
+
for (let entry = chain; entry !== undefined; entry = entry.next) {
|
|
1476
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
1477
|
+
entry.index = index;
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1455
1482
|
/**
|
|
1456
1483
|
* Remove a parent from the chain
|
|
1457
1484
|
* @param parent - The parent to remove
|
|
@@ -2088,6 +2115,39 @@ class ArraySchema {
|
|
|
2088
2115
|
get length() {
|
|
2089
2116
|
return this.items.length;
|
|
2090
2117
|
}
|
|
2118
|
+
/**
|
|
2119
|
+
* Re-point children at their wire slot. `ChangeTree.parentIndex` caches
|
|
2120
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
2121
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
2122
|
+
* at whichever element inherited the slot (issue #231).
|
|
2123
|
+
*
|
|
2124
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
2125
|
+
* the only reader, and an array without `filteredChanges` never has a slot
|
|
2126
|
+
* read back. Everything else stops at that check instead of walking its
|
|
2127
|
+
* children every tick.
|
|
2128
|
+
*
|
|
2129
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
2130
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
2131
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
2132
|
+
*/
|
|
2133
|
+
$reindexChildren(from, staged) {
|
|
2134
|
+
if (this[$changes].filteredChanges === undefined) {
|
|
2135
|
+
return;
|
|
2136
|
+
} // nothing will read the cache
|
|
2137
|
+
if (typeof this[$childType] === "string") {
|
|
2138
|
+
return;
|
|
2139
|
+
} // primitives have no child tree
|
|
2140
|
+
const tmpItems = this.tmpItems;
|
|
2141
|
+
const length = tmpItems.length;
|
|
2142
|
+
if (staged !== undefined) {
|
|
2143
|
+
while (from < length && tmpItems[from] === staged[from]) {
|
|
2144
|
+
from++;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
for (let i = from; i < length; i++) {
|
|
2148
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2091
2151
|
push(...values) {
|
|
2092
2152
|
let length = this.tmpItems.length;
|
|
2093
2153
|
const changeTree = this[$changes];
|
|
@@ -2220,6 +2280,7 @@ class ArraySchema {
|
|
|
2220
2280
|
this[$changes].operation(exports.OPERATION.REVERSE);
|
|
2221
2281
|
this.items.reverse();
|
|
2222
2282
|
this.tmpItems.reverse();
|
|
2283
|
+
this.$reindexChildren(0);
|
|
2223
2284
|
return this;
|
|
2224
2285
|
}
|
|
2225
2286
|
/**
|
|
@@ -2263,6 +2324,7 @@ class ArraySchema {
|
|
|
2263
2324
|
// wouldn't OPERATION.MOVE make more sense here?
|
|
2264
2325
|
sortedItems.forEach((_, i) => changeTree.change(i, exports.OPERATION.REPLACE));
|
|
2265
2326
|
this.tmpItems.sort(compareFn);
|
|
2327
|
+
this.$reindexChildren(0);
|
|
2266
2328
|
this.isMovingItems = false;
|
|
2267
2329
|
return this;
|
|
2268
2330
|
}
|
|
@@ -2358,6 +2420,7 @@ class ArraySchema {
|
|
|
2358
2420
|
changeTree.change(index, exports.OPERATION.ADD);
|
|
2359
2421
|
});
|
|
2360
2422
|
this.tmpItems.unshift(...items);
|
|
2423
|
+
this.$reindexChildren(0); // from 0: nothing above placed the new items either
|
|
2361
2424
|
return this.items.unshift(...items);
|
|
2362
2425
|
}
|
|
2363
2426
|
/**
|
|
@@ -2629,7 +2692,13 @@ class ArraySchema {
|
|
|
2629
2692
|
this.tmpItems[index] = undefined; // TODO: do not try to get "tmpItems" at decoding time.
|
|
2630
2693
|
}
|
|
2631
2694
|
[$onEncodeEnd]() {
|
|
2695
|
+
const staged = this.tmpItems;
|
|
2632
2696
|
this.tmpItems = this.items.slice();
|
|
2697
|
+
// Compaction just closed the staged holes — everything above the
|
|
2698
|
+
// lowest one slid down a slot. There is no cheap "were there any"
|
|
2699
|
+
// test to gate this on: `deletedIndexes` is an object here, and a
|
|
2700
|
+
// `for...in` probe measured slower than the prefix scan it skips.
|
|
2701
|
+
this.$reindexChildren(0, staged);
|
|
2633
2702
|
this.deletedIndexes = {};
|
|
2634
2703
|
}
|
|
2635
2704
|
[$onDecodeEnd]() {
|
|
@@ -2779,6 +2848,37 @@ class MapSchema {
|
|
|
2779
2848
|
get(key) {
|
|
2780
2849
|
return this.$items.get(key);
|
|
2781
2850
|
}
|
|
2851
|
+
/**
|
|
2852
|
+
* Returns the value for `key` if present. Otherwise inserts `defaultValue`
|
|
2853
|
+
* (tracked as an ADD change, like `set()`) and returns it.
|
|
2854
|
+
*
|
|
2855
|
+
* Mirrors `Map.prototype.getOrInsert` (TC39 "upsert" proposal, typed in
|
|
2856
|
+
* TypeScript 6's standard library).
|
|
2857
|
+
*/
|
|
2858
|
+
getOrInsert(key, defaultValue) {
|
|
2859
|
+
if (this.$items.has(key)) {
|
|
2860
|
+
return this.$items.get(key);
|
|
2861
|
+
}
|
|
2862
|
+
this.set(key, defaultValue);
|
|
2863
|
+
return defaultValue;
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* Returns the value for `key` if present. Otherwise computes a value via
|
|
2867
|
+
* `callbackfn(key)`, inserts it (tracked as an ADD change, like `set()`)
|
|
2868
|
+
* and returns it. The callback is only invoked when the key is missing.
|
|
2869
|
+
*
|
|
2870
|
+
* Mirrors `Map.prototype.getOrInsertComputed` (TC39 "upsert" proposal,
|
|
2871
|
+
* typed in TypeScript 6's standard library).
|
|
2872
|
+
*/
|
|
2873
|
+
getOrInsertComputed(key, callbackfn) {
|
|
2874
|
+
if (this.$items.has(key)) {
|
|
2875
|
+
return this.$items.get(key);
|
|
2876
|
+
}
|
|
2877
|
+
const value = callbackfn(key);
|
|
2878
|
+
// per spec: overwrites even if callbackfn itself inserted `key`
|
|
2879
|
+
this.set(key, value);
|
|
2880
|
+
return value;
|
|
2881
|
+
}
|
|
2782
2882
|
delete(key) {
|
|
2783
2883
|
if (!this.$items.has(key)) {
|
|
2784
2884
|
return false;
|