@colyseus/schema 5.0.12 → 5.0.13
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 +3 -0
- package/build/encoder/changeTree/parentChain.d.ts +9 -0
- package/build/index.cjs +75 -1
- package/build/index.cjs.map +1 -1
- package/build/index.js +75 -1
- package/build/index.mjs +75 -1
- package/build/index.mjs.map +1 -1
- package/build/types/custom/ArraySchema.d.ts +17 -0
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +5 -0
- package/src/encoder/changeTree/parentChain.ts +29 -0
- package/src/types/custom/ArraySchema.ts +40 -1
package/build/index.js
CHANGED
|
@@ -1918,6 +1918,34 @@
|
|
|
1918
1918
|
tree._parentIndex = index;
|
|
1919
1919
|
}
|
|
1920
1920
|
}
|
|
1921
|
+
/**
|
|
1922
|
+
* Move `parent`'s existing chain entry to `index`, skipping the attachment
|
|
1923
|
+
* work `addParent` does. `parent` must already be a parent of `tree`.
|
|
1924
|
+
*
|
|
1925
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
1926
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
1927
|
+
* element it names.
|
|
1928
|
+
*/
|
|
1929
|
+
function setParentIndex(tree, parent, index) {
|
|
1930
|
+
if (tree.extraParents === undefined) {
|
|
1931
|
+
tree._parentIndex = index; // sole parent, so it is `parent`
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
1935
|
+
// through `$changes` because ArraySchema arrives proxied (see removeParent
|
|
1936
|
+
// below), and `extraParents` only ever fills by demoting `parentRef`, so
|
|
1937
|
+
// the inline parent is set here.
|
|
1938
|
+
if (tree.parentRef[$changes] === parent[$changes]) {
|
|
1939
|
+
tree._parentIndex = index;
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
for (let entry = tree.extraParents; entry !== undefined; entry = entry.next) {
|
|
1943
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
1944
|
+
entry.index = index;
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1921
1949
|
/**
|
|
1922
1950
|
* Remove a parent from the chain.
|
|
1923
1951
|
* @returns true if parent was found and removed (Root.remove relies on this).
|
|
@@ -3162,6 +3190,8 @@
|
|
|
3162
3190
|
get parent() { return this.parentRef; }
|
|
3163
3191
|
get parentIndex() { return this._parentIndex; }
|
|
3164
3192
|
addParent(parent, index) { addParent(this, parent, index); }
|
|
3193
|
+
/** Re-point an existing parent's cached index after the parent reindexed. */
|
|
3194
|
+
setParentIndex(parent, index) { setParentIndex(this, parent, index); }
|
|
3165
3195
|
/** @returns true if parent was found and removed */
|
|
3166
3196
|
removeParent(parent = this.parent) { return removeParent(this, parent); }
|
|
3167
3197
|
findParent(predicate) {
|
|
@@ -3206,6 +3236,7 @@
|
|
|
3206
3236
|
operation() { }
|
|
3207
3237
|
setParent() { }
|
|
3208
3238
|
addParent() { }
|
|
3239
|
+
setParentIndex() { }
|
|
3209
3240
|
removeParent() { return false; }
|
|
3210
3241
|
getChange() { return 0; }
|
|
3211
3242
|
discard() { }
|
|
@@ -4261,6 +4292,40 @@
|
|
|
4261
4292
|
// beyond the live range: appends land after the staged tmpItems tail
|
|
4262
4293
|
return tmpItems.length + (index - live);
|
|
4263
4294
|
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Re-point children at their wire slot. `ChangeTree._parentIndex` caches
|
|
4297
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
4298
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
4299
|
+
* at whichever element inherited the slot (issue #231).
|
|
4300
|
+
*
|
|
4301
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
4302
|
+
* the only reader and reaches the index only through a filtered array
|
|
4303
|
+
* (`addParentOf` bails on `hasFilteredFields`, `remove` on the child's
|
|
4304
|
+
* `isFiltered`). Everything else stops at the flag read instead of walking
|
|
4305
|
+
* its children every tick.
|
|
4306
|
+
*
|
|
4307
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
4308
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
4309
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
4310
|
+
*/
|
|
4311
|
+
$reindexChildren(from, staged) {
|
|
4312
|
+
if (!this[$changes].hasFilteredFields) {
|
|
4313
|
+
return;
|
|
4314
|
+
} // nothing will read the cache
|
|
4315
|
+
if (typeof this[$childType] === "string") {
|
|
4316
|
+
return;
|
|
4317
|
+
} // primitives have no child tree
|
|
4318
|
+
const tmpItems = this.tmpItems;
|
|
4319
|
+
const length = tmpItems.length;
|
|
4320
|
+
if (staged !== undefined) {
|
|
4321
|
+
while (from < length && tmpItems[from] === staged[from]) {
|
|
4322
|
+
from++;
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
for (let i = from; i < length; i++) {
|
|
4326
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4264
4329
|
// encoding only. Returns the wire index the change was recorded at
|
|
4265
4330
|
// (undefined when nothing was recorded).
|
|
4266
4331
|
$changeAt(index, value) {
|
|
@@ -4372,6 +4437,7 @@
|
|
|
4372
4437
|
self[$changes].operation(exports.OPERATION.REVERSE);
|
|
4373
4438
|
self.items.reverse();
|
|
4374
4439
|
self.tmpItems.reverse();
|
|
4440
|
+
self.$reindexChildren(0);
|
|
4375
4441
|
return this;
|
|
4376
4442
|
}
|
|
4377
4443
|
/**
|
|
@@ -4423,6 +4489,7 @@
|
|
|
4423
4489
|
// wouldn't OPERATION.MOVE make more sense here?
|
|
4424
4490
|
sortedItems.forEach((_, i) => changeTree.change(i, exports.OPERATION.REPLACE));
|
|
4425
4491
|
self.tmpItems.sort(compareFn);
|
|
4492
|
+
self.$reindexChildren(0);
|
|
4426
4493
|
self.isMovingItems = false;
|
|
4427
4494
|
return this;
|
|
4428
4495
|
}
|
|
@@ -4504,6 +4571,7 @@
|
|
|
4504
4571
|
deletedIndexes.unshift(...new Array(items.length).fill(false));
|
|
4505
4572
|
}
|
|
4506
4573
|
self.tmpItems.unshift(...items);
|
|
4574
|
+
self.$reindexChildren(items.length); // survivors only — the loop above placed the new items
|
|
4507
4575
|
return self.items.unshift(...items);
|
|
4508
4576
|
}
|
|
4509
4577
|
/**
|
|
@@ -4772,8 +4840,14 @@
|
|
|
4772
4840
|
}
|
|
4773
4841
|
[$onEncodeEnd]() {
|
|
4774
4842
|
const self = this[$proxyTarget] ?? this;
|
|
4843
|
+
const staged = self.tmpItems;
|
|
4775
4844
|
self.tmpItems = self.items.slice();
|
|
4776
|
-
self.deletedIndexes.length
|
|
4845
|
+
if (self.deletedIndexes.length > 0) {
|
|
4846
|
+
// compaction just closed the staged holes — everything above the
|
|
4847
|
+
// lowest one slid down a slot
|
|
4848
|
+
self.$reindexChildren(0, staged);
|
|
4849
|
+
self.deletedIndexes.length = 0;
|
|
4850
|
+
}
|
|
4777
4851
|
}
|
|
4778
4852
|
[$onDecodeEnd]() {
|
|
4779
4853
|
const self = this[$proxyTarget] ?? this;
|
package/build/index.mjs
CHANGED
|
@@ -1912,6 +1912,34 @@ function addParent(tree, parent, index) {
|
|
|
1912
1912
|
tree._parentIndex = index;
|
|
1913
1913
|
}
|
|
1914
1914
|
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Move `parent`'s existing chain entry to `index`, skipping the attachment
|
|
1917
|
+
* work `addParent` does. `parent` must already be a parent of `tree`.
|
|
1918
|
+
*
|
|
1919
|
+
* Called by collections whose wire slots shift (ArraySchema): StateView
|
|
1920
|
+
* addresses per-view ADD/DELETE by that index, so it has to follow the
|
|
1921
|
+
* element it names.
|
|
1922
|
+
*/
|
|
1923
|
+
function setParentIndex(tree, parent, index) {
|
|
1924
|
+
if (tree.extraParents === undefined) {
|
|
1925
|
+
tree._parentIndex = index; // sole parent, so it is `parent`
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
// Shared instance — move only the entry `parent` owns. Matching goes
|
|
1929
|
+
// through `$changes` because ArraySchema arrives proxied (see removeParent
|
|
1930
|
+
// below), and `extraParents` only ever fills by demoting `parentRef`, so
|
|
1931
|
+
// the inline parent is set here.
|
|
1932
|
+
if (tree.parentRef[$changes] === parent[$changes]) {
|
|
1933
|
+
tree._parentIndex = index;
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
for (let entry = tree.extraParents; entry !== undefined; entry = entry.next) {
|
|
1937
|
+
if (entry.ref[$changes] === parent[$changes]) {
|
|
1938
|
+
entry.index = index;
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1915
1943
|
/**
|
|
1916
1944
|
* Remove a parent from the chain.
|
|
1917
1945
|
* @returns true if parent was found and removed (Root.remove relies on this).
|
|
@@ -3156,6 +3184,8 @@ class ChangeTree {
|
|
|
3156
3184
|
get parent() { return this.parentRef; }
|
|
3157
3185
|
get parentIndex() { return this._parentIndex; }
|
|
3158
3186
|
addParent(parent, index) { addParent(this, parent, index); }
|
|
3187
|
+
/** Re-point an existing parent's cached index after the parent reindexed. */
|
|
3188
|
+
setParentIndex(parent, index) { setParentIndex(this, parent, index); }
|
|
3159
3189
|
/** @returns true if parent was found and removed */
|
|
3160
3190
|
removeParent(parent = this.parent) { return removeParent(this, parent); }
|
|
3161
3191
|
findParent(predicate) {
|
|
@@ -3200,6 +3230,7 @@ class UntrackedChangeTree {
|
|
|
3200
3230
|
operation() { }
|
|
3201
3231
|
setParent() { }
|
|
3202
3232
|
addParent() { }
|
|
3233
|
+
setParentIndex() { }
|
|
3203
3234
|
removeParent() { return false; }
|
|
3204
3235
|
getChange() { return 0; }
|
|
3205
3236
|
discard() { }
|
|
@@ -4255,6 +4286,40 @@ class ArraySchema {
|
|
|
4255
4286
|
// beyond the live range: appends land after the staged tmpItems tail
|
|
4256
4287
|
return tmpItems.length + (index - live);
|
|
4257
4288
|
}
|
|
4289
|
+
/**
|
|
4290
|
+
* Re-point children at their wire slot. `ChangeTree._parentIndex` caches
|
|
4291
|
+
* the slot a child holds in `tmpItems`, and StateView addresses per-view
|
|
4292
|
+
* ADD/DELETE with it — so a reorder that leaves it behind aims those ops
|
|
4293
|
+
* at whichever element inherited the slot (issue #231).
|
|
4294
|
+
*
|
|
4295
|
+
* The filter check is a correctness boundary, not a tunable: StateView is
|
|
4296
|
+
* the only reader and reaches the index only through a filtered array
|
|
4297
|
+
* (`addParentOf` bails on `hasFilteredFields`, `remove` on the child's
|
|
4298
|
+
* `isFiltered`). Everything else stops at the flag read instead of walking
|
|
4299
|
+
* its children every tick.
|
|
4300
|
+
*
|
|
4301
|
+
* Callers name the lowest slot that moved as `from`. Compaction cannot, so
|
|
4302
|
+
* it hands over the pre-compaction layout as `staged` and the unchanged
|
|
4303
|
+
* prefix is skipped instead. Either way tail churn walks nothing.
|
|
4304
|
+
*/
|
|
4305
|
+
$reindexChildren(from, staged) {
|
|
4306
|
+
if (!this[$changes].hasFilteredFields) {
|
|
4307
|
+
return;
|
|
4308
|
+
} // nothing will read the cache
|
|
4309
|
+
if (typeof this[$childType] === "string") {
|
|
4310
|
+
return;
|
|
4311
|
+
} // primitives have no child tree
|
|
4312
|
+
const tmpItems = this.tmpItems;
|
|
4313
|
+
const length = tmpItems.length;
|
|
4314
|
+
if (staged !== undefined) {
|
|
4315
|
+
while (from < length && tmpItems[from] === staged[from]) {
|
|
4316
|
+
from++;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
for (let i = from; i < length; i++) {
|
|
4320
|
+
tmpItems[i]?.[$changes]?.setParentIndex(this, i);
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4258
4323
|
// encoding only. Returns the wire index the change was recorded at
|
|
4259
4324
|
// (undefined when nothing was recorded).
|
|
4260
4325
|
$changeAt(index, value) {
|
|
@@ -4366,6 +4431,7 @@ class ArraySchema {
|
|
|
4366
4431
|
self[$changes].operation(OPERATION.REVERSE);
|
|
4367
4432
|
self.items.reverse();
|
|
4368
4433
|
self.tmpItems.reverse();
|
|
4434
|
+
self.$reindexChildren(0);
|
|
4369
4435
|
return this;
|
|
4370
4436
|
}
|
|
4371
4437
|
/**
|
|
@@ -4417,6 +4483,7 @@ class ArraySchema {
|
|
|
4417
4483
|
// wouldn't OPERATION.MOVE make more sense here?
|
|
4418
4484
|
sortedItems.forEach((_, i) => changeTree.change(i, OPERATION.REPLACE));
|
|
4419
4485
|
self.tmpItems.sort(compareFn);
|
|
4486
|
+
self.$reindexChildren(0);
|
|
4420
4487
|
self.isMovingItems = false;
|
|
4421
4488
|
return this;
|
|
4422
4489
|
}
|
|
@@ -4498,6 +4565,7 @@ class ArraySchema {
|
|
|
4498
4565
|
deletedIndexes.unshift(...new Array(items.length).fill(false));
|
|
4499
4566
|
}
|
|
4500
4567
|
self.tmpItems.unshift(...items);
|
|
4568
|
+
self.$reindexChildren(items.length); // survivors only — the loop above placed the new items
|
|
4501
4569
|
return self.items.unshift(...items);
|
|
4502
4570
|
}
|
|
4503
4571
|
/**
|
|
@@ -4766,8 +4834,14 @@ class ArraySchema {
|
|
|
4766
4834
|
}
|
|
4767
4835
|
[$onEncodeEnd]() {
|
|
4768
4836
|
const self = this[$proxyTarget] ?? this;
|
|
4837
|
+
const staged = self.tmpItems;
|
|
4769
4838
|
self.tmpItems = self.items.slice();
|
|
4770
|
-
self.deletedIndexes.length
|
|
4839
|
+
if (self.deletedIndexes.length > 0) {
|
|
4840
|
+
// compaction just closed the staged holes — everything above the
|
|
4841
|
+
// lowest one slid down a slot
|
|
4842
|
+
self.$reindexChildren(0, staged);
|
|
4843
|
+
self.deletedIndexes.length = 0;
|
|
4844
|
+
}
|
|
4771
4845
|
}
|
|
4772
4846
|
[$onDecodeEnd]() {
|
|
4773
4847
|
const self = this[$proxyTarget] ?? this;
|