@colyseus/schema 4.0.30 → 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 +69 -0
- package/build/index.cjs.map +1 -1
- package/build/index.js +69 -0
- package/build/index.mjs +69 -0
- package/build/index.mjs.map +1 -1
- package/build/types/custom/ArraySchema.d.ts +16 -0
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +27 -0
- package/src/types/custom/ArraySchema.ts +39 -0
package/build/index.js
CHANGED
|
@@ -1456,6 +1456,33 @@
|
|
|
1456
1456
|
next: this.parentChain
|
|
1457
1457
|
};
|
|
1458
1458
|
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Move `parent`'s existing chain entry to `index`, skipping the work
|
|
1461
|
+
* `addParent` does. `parent` must already be a parent of this tree.
|
|
1462
|
+
*
|
|
1463
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
1464
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
1465
|
+
* element it names.
|
|
1466
|
+
*/
|
|
1467
|
+
setParentIndex(parent, index) {
|
|
1468
|
+
const chain = this.parentChain;
|
|
1469
|
+
if (chain === undefined) {
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
if (chain.next === undefined) {
|
|
1473
|
+
chain.index = index; // sole parent, so it is `parent`
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
1477
|
+
// through `$changes` because ArraySchema arrives proxied (see
|
|
1478
|
+
// removeParent below).
|
|
1479
|
+
for (let entry = chain; entry !== undefined; entry = entry.next) {
|
|
1480
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
1481
|
+
entry.index = index;
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1459
1486
|
/**
|
|
1460
1487
|
* Remove a parent from the chain
|
|
1461
1488
|
* @param parent - The parent to remove
|
|
@@ -2092,6 +2119,39 @@
|
|
|
2092
2119
|
get length() {
|
|
2093
2120
|
return this.items.length;
|
|
2094
2121
|
}
|
|
2122
|
+
/**
|
|
2123
|
+
* Re-point children at their wire slot. `ChangeTree.parentIndex` caches
|
|
2124
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
2125
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
2126
|
+
* at whichever element inherited the slot (issue #231).
|
|
2127
|
+
*
|
|
2128
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
2129
|
+
* the only reader, and an array without `filteredChanges` never has a slot
|
|
2130
|
+
* read back. Everything else stops at that check instead of walking its
|
|
2131
|
+
* children every tick.
|
|
2132
|
+
*
|
|
2133
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
2134
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
2135
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
2136
|
+
*/
|
|
2137
|
+
$reindexChildren(from, staged) {
|
|
2138
|
+
if (this[$changes].filteredChanges === undefined) {
|
|
2139
|
+
return;
|
|
2140
|
+
} // nothing will read the cache
|
|
2141
|
+
if (typeof this[$childType] === "string") {
|
|
2142
|
+
return;
|
|
2143
|
+
} // primitives have no child tree
|
|
2144
|
+
const tmpItems = this.tmpItems;
|
|
2145
|
+
const length = tmpItems.length;
|
|
2146
|
+
if (staged !== undefined) {
|
|
2147
|
+
while (from < length && tmpItems[from] === staged[from]) {
|
|
2148
|
+
from++;
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
for (let i = from; i < length; i++) {
|
|
2152
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2095
2155
|
push(...values) {
|
|
2096
2156
|
let length = this.tmpItems.length;
|
|
2097
2157
|
const changeTree = this[$changes];
|
|
@@ -2224,6 +2284,7 @@
|
|
|
2224
2284
|
this[$changes].operation(exports.OPERATION.REVERSE);
|
|
2225
2285
|
this.items.reverse();
|
|
2226
2286
|
this.tmpItems.reverse();
|
|
2287
|
+
this.$reindexChildren(0);
|
|
2227
2288
|
return this;
|
|
2228
2289
|
}
|
|
2229
2290
|
/**
|
|
@@ -2267,6 +2328,7 @@
|
|
|
2267
2328
|
// wouldn't OPERATION.MOVE make more sense here?
|
|
2268
2329
|
sortedItems.forEach((_, i) => changeTree.change(i, exports.OPERATION.REPLACE));
|
|
2269
2330
|
this.tmpItems.sort(compareFn);
|
|
2331
|
+
this.$reindexChildren(0);
|
|
2270
2332
|
this.isMovingItems = false;
|
|
2271
2333
|
return this;
|
|
2272
2334
|
}
|
|
@@ -2362,6 +2424,7 @@
|
|
|
2362
2424
|
changeTree.change(index, exports.OPERATION.ADD);
|
|
2363
2425
|
});
|
|
2364
2426
|
this.tmpItems.unshift(...items);
|
|
2427
|
+
this.$reindexChildren(0); // from 0: nothing above placed the new items either
|
|
2365
2428
|
return this.items.unshift(...items);
|
|
2366
2429
|
}
|
|
2367
2430
|
/**
|
|
@@ -2633,7 +2696,13 @@
|
|
|
2633
2696
|
this.tmpItems[index] = undefined; // TODO: do not try to get "tmpItems" at decoding time.
|
|
2634
2697
|
}
|
|
2635
2698
|
[$onEncodeEnd]() {
|
|
2699
|
+
const staged = this.tmpItems;
|
|
2636
2700
|
this.tmpItems = this.items.slice();
|
|
2701
|
+
// Compaction just closed the staged holes — everything above the
|
|
2702
|
+
// lowest one slid down a slot. There is no cheap "were there any"
|
|
2703
|
+
// test to gate this on: `deletedIndexes` is an object here, and a
|
|
2704
|
+
// `for...in` probe measured slower than the prefix scan it skips.
|
|
2705
|
+
this.$reindexChildren(0, staged);
|
|
2637
2706
|
this.deletedIndexes = {};
|
|
2638
2707
|
}
|
|
2639
2708
|
[$onDecodeEnd]() {
|
package/build/index.mjs
CHANGED
|
@@ -1450,6 +1450,33 @@ class ChangeTree {
|
|
|
1450
1450
|
next: this.parentChain
|
|
1451
1451
|
};
|
|
1452
1452
|
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Move `parent`'s existing chain entry to `index`, skipping the work
|
|
1455
|
+
* `addParent` does. `parent` must already be a parent of this tree.
|
|
1456
|
+
*
|
|
1457
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
1458
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
1459
|
+
* element it names.
|
|
1460
|
+
*/
|
|
1461
|
+
setParentIndex(parent, index) {
|
|
1462
|
+
const chain = this.parentChain;
|
|
1463
|
+
if (chain === undefined) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (chain.next === undefined) {
|
|
1467
|
+
chain.index = index; // sole parent, so it is `parent`
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
1471
|
+
// through `$changes` because ArraySchema arrives proxied (see
|
|
1472
|
+
// removeParent below).
|
|
1473
|
+
for (let entry = chain; entry !== undefined; entry = entry.next) {
|
|
1474
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
1475
|
+
entry.index = index;
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1453
1480
|
/**
|
|
1454
1481
|
* Remove a parent from the chain
|
|
1455
1482
|
* @param parent - The parent to remove
|
|
@@ -2086,6 +2113,39 @@ class ArraySchema {
|
|
|
2086
2113
|
get length() {
|
|
2087
2114
|
return this.items.length;
|
|
2088
2115
|
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Re-point children at their wire slot. `ChangeTree.parentIndex` caches
|
|
2118
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
2119
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
2120
|
+
* at whichever element inherited the slot (issue #231).
|
|
2121
|
+
*
|
|
2122
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
2123
|
+
* the only reader, and an array without `filteredChanges` never has a slot
|
|
2124
|
+
* read back. Everything else stops at that check instead of walking its
|
|
2125
|
+
* children every tick.
|
|
2126
|
+
*
|
|
2127
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
2128
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
2129
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
2130
|
+
*/
|
|
2131
|
+
$reindexChildren(from, staged) {
|
|
2132
|
+
if (this[$changes].filteredChanges === undefined) {
|
|
2133
|
+
return;
|
|
2134
|
+
} // nothing will read the cache
|
|
2135
|
+
if (typeof this[$childType] === "string") {
|
|
2136
|
+
return;
|
|
2137
|
+
} // primitives have no child tree
|
|
2138
|
+
const tmpItems = this.tmpItems;
|
|
2139
|
+
const length = tmpItems.length;
|
|
2140
|
+
if (staged !== undefined) {
|
|
2141
|
+
while (from < length && tmpItems[from] === staged[from]) {
|
|
2142
|
+
from++;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
for (let i = from; i < length; i++) {
|
|
2146
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2089
2149
|
push(...values) {
|
|
2090
2150
|
let length = this.tmpItems.length;
|
|
2091
2151
|
const changeTree = this[$changes];
|
|
@@ -2218,6 +2278,7 @@ class ArraySchema {
|
|
|
2218
2278
|
this[$changes].operation(OPERATION.REVERSE);
|
|
2219
2279
|
this.items.reverse();
|
|
2220
2280
|
this.tmpItems.reverse();
|
|
2281
|
+
this.$reindexChildren(0);
|
|
2221
2282
|
return this;
|
|
2222
2283
|
}
|
|
2223
2284
|
/**
|
|
@@ -2261,6 +2322,7 @@ class ArraySchema {
|
|
|
2261
2322
|
// wouldn't OPERATION.MOVE make more sense here?
|
|
2262
2323
|
sortedItems.forEach((_, i) => changeTree.change(i, OPERATION.REPLACE));
|
|
2263
2324
|
this.tmpItems.sort(compareFn);
|
|
2325
|
+
this.$reindexChildren(0);
|
|
2264
2326
|
this.isMovingItems = false;
|
|
2265
2327
|
return this;
|
|
2266
2328
|
}
|
|
@@ -2356,6 +2418,7 @@ class ArraySchema {
|
|
|
2356
2418
|
changeTree.change(index, OPERATION.ADD);
|
|
2357
2419
|
});
|
|
2358
2420
|
this.tmpItems.unshift(...items);
|
|
2421
|
+
this.$reindexChildren(0); // from 0: nothing above placed the new items either
|
|
2359
2422
|
return this.items.unshift(...items);
|
|
2360
2423
|
}
|
|
2361
2424
|
/**
|
|
@@ -2627,7 +2690,13 @@ class ArraySchema {
|
|
|
2627
2690
|
this.tmpItems[index] = undefined; // TODO: do not try to get "tmpItems" at decoding time.
|
|
2628
2691
|
}
|
|
2629
2692
|
[$onEncodeEnd]() {
|
|
2693
|
+
const staged = this.tmpItems;
|
|
2630
2694
|
this.tmpItems = this.items.slice();
|
|
2695
|
+
// Compaction just closed the staged holes — everything above the
|
|
2696
|
+
// lowest one slid down a slot. There is no cheap "were there any"
|
|
2697
|
+
// test to gate this on: `deletedIndexes` is an object here, and a
|
|
2698
|
+
// `for...in` probe measured slower than the prefix scan it skips.
|
|
2699
|
+
this.$reindexChildren(0, staged);
|
|
2631
2700
|
this.deletedIndexes = {};
|
|
2632
2701
|
}
|
|
2633
2702
|
[$onDecodeEnd]() {
|