@colyseus/schema 3.0.11 → 3.0.12
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/cjs/index.js +75 -84
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +75 -84
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +75 -84
- package/lib/Metadata.js +1 -2
- package/lib/Metadata.js.map +1 -1
- package/lib/annotations.js +4 -3
- package/lib/annotations.js.map +1 -1
- package/lib/decoder/DecodeOperation.js +0 -13
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +0 -1
- package/lib/encoder/Encoder.js +22 -30
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +1 -1
- package/lib/encoder/Root.js +1 -0
- package/lib/encoder/Root.js.map +1 -1
- package/lib/encoder/StateView.d.ts +2 -4
- package/lib/encoder/StateView.js +19 -13
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +5 -1
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +23 -22
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +1 -2
- package/src/annotations.ts +4 -4
- package/src/decoder/DecodeOperation.ts +0 -14
- package/src/encoder/ChangeTree.ts +0 -2
- package/src/encoder/Encoder.ts +25 -41
- package/src/encoder/Root.ts +1 -0
- package/src/encoder/StateView.ts +22 -15
- package/src/types/custom/ArraySchema.ts +8 -1
- package/src/types/custom/MapSchema.ts +24 -27
package/build/esm/index.mjs
CHANGED
|
@@ -857,14 +857,13 @@ const Metadata = {
|
|
|
857
857
|
fieldIndex++;
|
|
858
858
|
for (const field in fields) {
|
|
859
859
|
const type = fields[field];
|
|
860
|
-
const normalizedType = getNormalizedType(type);
|
|
861
860
|
// FIXME: this code is duplicated from @type() annotation
|
|
862
861
|
const complexTypeKlass = (Array.isArray(type))
|
|
863
862
|
? getType("array")
|
|
864
863
|
: (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
|
|
865
864
|
const childType = (complexTypeKlass)
|
|
866
865
|
? Object.values(type)[0]
|
|
867
|
-
:
|
|
866
|
+
: getNormalizedType(type);
|
|
868
867
|
Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass));
|
|
869
868
|
fieldIndex++;
|
|
870
869
|
}
|
|
@@ -1531,19 +1530,6 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1531
1530
|
//
|
|
1532
1531
|
if (operation !== OPERATION.DELETE_AND_ADD) {
|
|
1533
1532
|
ref[$deleteByIndex](index);
|
|
1534
|
-
// //
|
|
1535
|
-
// // FIXME: is this in the correct place?
|
|
1536
|
-
// // (This is sounding like a workaround just for ArraySchema, see
|
|
1537
|
-
// // "should splice and move" test on ArraySchema.test.ts)
|
|
1538
|
-
// //
|
|
1539
|
-
// allChanges.push({
|
|
1540
|
-
// ref,
|
|
1541
|
-
// refId: decoder.currentRefId,
|
|
1542
|
-
// op: OPERATION.DELETE,
|
|
1543
|
-
// field: index as unknown as string,
|
|
1544
|
-
// value: undefined,
|
|
1545
|
-
// previousValue,
|
|
1546
|
-
// });
|
|
1547
1533
|
}
|
|
1548
1534
|
value = undefined;
|
|
1549
1535
|
}
|
|
@@ -1891,7 +1877,8 @@ class ArraySchema {
|
|
|
1891
1877
|
else {
|
|
1892
1878
|
if (setValue[$changes]) {
|
|
1893
1879
|
assertInstanceType(setValue, obj[$childType], obj, key);
|
|
1894
|
-
|
|
1880
|
+
const previousValue = obj.items[key];
|
|
1881
|
+
if (previousValue !== undefined) {
|
|
1895
1882
|
if (setValue[$changes].isNew) {
|
|
1896
1883
|
this[$changes].indexedOperation(Number(key), OPERATION.MOVE_AND_ADD);
|
|
1897
1884
|
}
|
|
@@ -1903,10 +1890,13 @@ class ArraySchema {
|
|
|
1903
1890
|
this[$changes].indexedOperation(Number(key), OPERATION.MOVE);
|
|
1904
1891
|
}
|
|
1905
1892
|
}
|
|
1893
|
+
// remove root reference from previous value
|
|
1894
|
+
previousValue[$changes].root?.remove(previousValue[$changes]);
|
|
1906
1895
|
}
|
|
1907
1896
|
else if (setValue[$changes].isNew) {
|
|
1908
1897
|
this[$changes].indexedOperation(Number(key), OPERATION.ADD);
|
|
1909
1898
|
}
|
|
1899
|
+
setValue[$changes].setParent(this, obj[$changes].root, key);
|
|
1910
1900
|
}
|
|
1911
1901
|
else {
|
|
1912
1902
|
obj.$changeAt(Number(key), setValue);
|
|
@@ -2544,33 +2534,34 @@ class MapSchema {
|
|
|
2544
2534
|
// See: https://github.com/colyseus/colyseus/issues/561#issuecomment-1646733468
|
|
2545
2535
|
key = key.toString();
|
|
2546
2536
|
const changeTree = this[$changes];
|
|
2547
|
-
// get "index" for this value.
|
|
2548
|
-
const isReplace = typeof (changeTree.indexes[key]) !== "undefined";
|
|
2549
|
-
const index = (isReplace)
|
|
2550
|
-
? changeTree.indexes[key]
|
|
2551
|
-
: changeTree.indexes[$numFields] ?? 0;
|
|
2552
|
-
let operation = (isReplace)
|
|
2553
|
-
? OPERATION.REPLACE
|
|
2554
|
-
: OPERATION.ADD;
|
|
2555
2537
|
const isRef = (value[$changes]) !== undefined;
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
//
|
|
2559
|
-
|
|
2560
|
-
|
|
2538
|
+
let index;
|
|
2539
|
+
let operation;
|
|
2540
|
+
// IS REPLACE?
|
|
2541
|
+
if (typeof (changeTree.indexes[key]) !== "undefined") {
|
|
2542
|
+
index = changeTree.indexes[key];
|
|
2543
|
+
operation = OPERATION.REPLACE;
|
|
2544
|
+
const previousValue = this.$items.get(key);
|
|
2545
|
+
if (previousValue === value) {
|
|
2546
|
+
// if value is the same, avoid re-encoding it.
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
else if (isRef) {
|
|
2550
|
+
// if is schema, force ADD operation if value differ from previous one.
|
|
2551
|
+
operation = OPERATION.DELETE_AND_ADD;
|
|
2552
|
+
// remove reference from previous value
|
|
2553
|
+
if (previousValue !== undefined) {
|
|
2554
|
+
previousValue[$changes].root?.remove(previousValue[$changes]);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
else {
|
|
2559
|
+
index = changeTree.indexes[$numFields] ?? 0;
|
|
2560
|
+
operation = OPERATION.ADD;
|
|
2561
2561
|
this.$indexes.set(index, key);
|
|
2562
2562
|
changeTree.indexes[key] = index;
|
|
2563
2563
|
changeTree.indexes[$numFields] = index + 1;
|
|
2564
2564
|
}
|
|
2565
|
-
else if (!isRef &&
|
|
2566
|
-
this.$items.get(key) === value) {
|
|
2567
|
-
// if value is the same, avoid re-encoding it.
|
|
2568
|
-
return;
|
|
2569
|
-
}
|
|
2570
|
-
else if (isRef && // if is schema, force ADD operation if value differ from previous one.
|
|
2571
|
-
this.$items.get(key) !== value) {
|
|
2572
|
-
operation = OPERATION.ADD;
|
|
2573
|
-
}
|
|
2574
2565
|
this.$items.set(key, value);
|
|
2575
2566
|
changeTree.change(index, operation);
|
|
2576
2567
|
//
|
|
@@ -2942,13 +2933,14 @@ function getPropertyDescriptor(fieldCached, fieldIndex, type, complexTypeKlass)
|
|
|
2942
2933
|
const changeTree = this[$changes];
|
|
2943
2934
|
//
|
|
2944
2935
|
// Replacing existing "ref", remove it from root.
|
|
2945
|
-
// TODO: if there are other references to this instance, we should not remove it from root.
|
|
2946
2936
|
//
|
|
2947
2937
|
if (previousValue !== undefined && previousValue[$changes]) {
|
|
2948
2938
|
changeTree.root?.remove(previousValue[$changes]);
|
|
2939
|
+
this.constructor[$track](changeTree, fieldIndex, OPERATION.DELETE_AND_ADD);
|
|
2940
|
+
}
|
|
2941
|
+
else {
|
|
2942
|
+
this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
|
|
2949
2943
|
}
|
|
2950
|
-
// flag the change for encoding.
|
|
2951
|
-
this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
|
|
2952
2944
|
//
|
|
2953
2945
|
// call setParent() recursively for this and its child
|
|
2954
2946
|
// structures.
|
|
@@ -3771,6 +3763,7 @@ class Root {
|
|
|
3771
3763
|
if (spliceOne(changeSet, changeSet.indexOf(changeTree))) {
|
|
3772
3764
|
changeTree[changeSetName].queueRootIndex = -1;
|
|
3773
3765
|
// changeSet[index] = undefined;
|
|
3766
|
+
return true;
|
|
3774
3767
|
}
|
|
3775
3768
|
}
|
|
3776
3769
|
clear() {
|
|
@@ -3806,12 +3799,6 @@ class Encoder {
|
|
|
3806
3799
|
const changeTrees = this.root[changeSetName];
|
|
3807
3800
|
for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {
|
|
3808
3801
|
const changeTree = changeTrees[i];
|
|
3809
|
-
const operations = changeTree[changeSetName];
|
|
3810
|
-
const ref = changeTree.ref;
|
|
3811
|
-
const ctor = ref.constructor;
|
|
3812
|
-
const encoder = ctor[$encoder];
|
|
3813
|
-
const filter = ctor[$filter];
|
|
3814
|
-
const metadata = ctor[Symbol.metadata];
|
|
3815
3802
|
if (hasView) {
|
|
3816
3803
|
if (!view.items.has(changeTree)) {
|
|
3817
3804
|
view.invisible.add(changeTree);
|
|
@@ -3821,13 +3808,24 @@ class Encoder {
|
|
|
3821
3808
|
view.invisible.delete(changeTree); // remove from invisible list
|
|
3822
3809
|
}
|
|
3823
3810
|
}
|
|
3811
|
+
const operations = changeTree[changeSetName];
|
|
3812
|
+
const ref = changeTree.ref;
|
|
3813
|
+
// TODO: avoid iterating over change tree if no changes were made
|
|
3814
|
+
const numChanges = operations.operations.length;
|
|
3815
|
+
if (numChanges === 0) {
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
const ctor = ref.constructor;
|
|
3819
|
+
const encoder = ctor[$encoder];
|
|
3820
|
+
const filter = ctor[$filter];
|
|
3821
|
+
const metadata = ctor[Symbol.metadata];
|
|
3824
3822
|
// skip root `refId` if it's the first change tree
|
|
3825
3823
|
// (unless it "hasView", which will need to revisit the root)
|
|
3826
3824
|
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
3827
3825
|
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3828
3826
|
encode.number(buffer, changeTree.refId, it);
|
|
3829
3827
|
}
|
|
3830
|
-
for (let j = 0
|
|
3828
|
+
for (let j = 0; j < numChanges; j++) {
|
|
3831
3829
|
const fieldIndex = operations.operations[j];
|
|
3832
3830
|
const operation = (fieldIndex < 0)
|
|
3833
3831
|
? Math.abs(fieldIndex) // "pure" operation without fieldIndex (e.g. CLEAR, REVERSE, etc.)
|
|
@@ -3924,14 +3922,16 @@ class Encoder {
|
|
|
3924
3922
|
encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
|
|
3925
3923
|
const viewOffset = it.offset;
|
|
3926
3924
|
// encode visibility changes (add/remove for this view)
|
|
3927
|
-
const
|
|
3928
|
-
for (let i = 0, numRefIds = refIds.length; i < numRefIds; i++) {
|
|
3929
|
-
const refId = refIds[i];
|
|
3930
|
-
const changes = view.changes[refId];
|
|
3925
|
+
for (const [refId, changes] of view.changes) {
|
|
3931
3926
|
const changeTree = this.root.changeTrees[refId];
|
|
3932
|
-
if (changeTree === undefined
|
|
3933
|
-
|
|
3934
|
-
|
|
3927
|
+
if (changeTree === undefined) {
|
|
3928
|
+
// detached instance, remove from view and skip.
|
|
3929
|
+
view.changes.delete(refId);
|
|
3930
|
+
continue;
|
|
3931
|
+
}
|
|
3932
|
+
const keys = Object.keys(changes);
|
|
3933
|
+
if (keys.length === 0) {
|
|
3934
|
+
// FIXME: avoid having empty changes if no changes were made
|
|
3935
3935
|
// console.log("changes.size === 0, skip", changeTree.ref.constructor.name);
|
|
3936
3936
|
continue;
|
|
3937
3937
|
}
|
|
@@ -3941,7 +3941,6 @@ class Encoder {
|
|
|
3941
3941
|
const metadata = ctor[Symbol.metadata];
|
|
3942
3942
|
bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
3943
3943
|
encode.number(bytes, changeTree.refId, it);
|
|
3944
|
-
const keys = Object.keys(changes);
|
|
3945
3944
|
for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
|
|
3946
3945
|
const key = keys[i];
|
|
3947
3946
|
const operation = changes[key];
|
|
@@ -3955,7 +3954,7 @@ class Encoder {
|
|
|
3955
3954
|
// (to allow re-using StateView's for multiple clients)
|
|
3956
3955
|
//
|
|
3957
3956
|
// clear "view" changes after encoding
|
|
3958
|
-
view.changes
|
|
3957
|
+
view.changes.clear();
|
|
3959
3958
|
// try to encode "filtered" changes
|
|
3960
3959
|
this.encode(it, view, bytes, "filteredChanges", false, viewOffset);
|
|
3961
3960
|
return Buffer.concat([
|
|
@@ -3963,20 +3962,6 @@ class Encoder {
|
|
|
3963
3962
|
bytes.subarray(viewOffset, it.offset)
|
|
3964
3963
|
]);
|
|
3965
3964
|
}
|
|
3966
|
-
onEndEncode(changeTrees = this.root.changes) {
|
|
3967
|
-
// changeTrees.forEach(function(changeTree) {
|
|
3968
|
-
// changeTree.endEncode();
|
|
3969
|
-
// });
|
|
3970
|
-
// for (const refId in changeTrees) {
|
|
3971
|
-
// const changeTree = this.root.changeTrees[refId];
|
|
3972
|
-
// changeTree.endEncode();
|
|
3973
|
-
// // changeTree.changes.clear();
|
|
3974
|
-
// // // ArraySchema and MapSchema have a custom "encode end" method
|
|
3975
|
-
// // changeTree.ref[$onEncodeEnd]?.();
|
|
3976
|
-
// // // Not a new instance anymore
|
|
3977
|
-
// // delete changeTree[$isNew];
|
|
3978
|
-
// }
|
|
3979
|
-
}
|
|
3980
3965
|
discardChanges() {
|
|
3981
3966
|
// discard shared changes
|
|
3982
3967
|
let length = this.root.changes.length;
|
|
@@ -4708,7 +4693,8 @@ class StateView {
|
|
|
4708
4693
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
4709
4694
|
* (This is used to force encoding a property, even if it was not changed)
|
|
4710
4695
|
*/
|
|
4711
|
-
|
|
4696
|
+
// TODO: use map here!? may fix encode ordering issue
|
|
4697
|
+
this.changes = new Map();
|
|
4712
4698
|
}
|
|
4713
4699
|
// TODO: allow to set multiple tags at once
|
|
4714
4700
|
add(obj, tag = DEFAULT_VIEW_TAG, checkIncludeParent = true) {
|
|
@@ -4724,16 +4710,16 @@ class StateView {
|
|
|
4724
4710
|
// - if it was invisible to this view
|
|
4725
4711
|
// - if it were previously filtered out
|
|
4726
4712
|
if (checkIncludeParent && changeTree.parent) {
|
|
4727
|
-
this.
|
|
4713
|
+
this.addParentOf(changeTree, tag);
|
|
4728
4714
|
}
|
|
4729
4715
|
//
|
|
4730
4716
|
// TODO: when adding an item of a MapSchema, the changes may not
|
|
4731
4717
|
// be set (only the parent's changes are set)
|
|
4732
4718
|
//
|
|
4733
|
-
let changes = this.changes
|
|
4719
|
+
let changes = this.changes.get(changeTree.refId);
|
|
4734
4720
|
if (changes === undefined) {
|
|
4735
4721
|
changes = {};
|
|
4736
|
-
this.changes
|
|
4722
|
+
this.changes.set(changeTree.refId, changes);
|
|
4737
4723
|
}
|
|
4738
4724
|
// set tag
|
|
4739
4725
|
if (tag !== DEFAULT_VIEW_TAG) {
|
|
@@ -4789,24 +4775,29 @@ class StateView {
|
|
|
4789
4775
|
});
|
|
4790
4776
|
return this;
|
|
4791
4777
|
}
|
|
4792
|
-
|
|
4778
|
+
addParentOf(childChangeTree, tag) {
|
|
4779
|
+
const changeTree = childChangeTree.parent[$changes];
|
|
4780
|
+
const parentIndex = childChangeTree.parentIndex;
|
|
4793
4781
|
// view must have all "changeTree" parent tree
|
|
4794
4782
|
this.items.add(changeTree);
|
|
4795
4783
|
// add parent's parent
|
|
4796
4784
|
const parentChangeTree = changeTree.parent?.[$changes];
|
|
4797
4785
|
if (parentChangeTree && (parentChangeTree.filteredChanges !== undefined)) {
|
|
4798
|
-
this.
|
|
4786
|
+
this.addParentOf(changeTree, tag);
|
|
4799
4787
|
}
|
|
4788
|
+
if (
|
|
4800
4789
|
// parent is already available, no need to add it!
|
|
4801
|
-
|
|
4790
|
+
!this.invisible.has(changeTree) &&
|
|
4791
|
+
// item is being replaced, no need to add parent
|
|
4792
|
+
changeTree.indexedOperations[parentIndex] !== OPERATION.DELETE_AND_ADD) {
|
|
4802
4793
|
return;
|
|
4803
4794
|
}
|
|
4804
4795
|
// add parent's tag properties
|
|
4805
4796
|
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
4806
|
-
let changes = this.changes
|
|
4797
|
+
let changes = this.changes.get(changeTree.refId);
|
|
4807
4798
|
if (changes === undefined) {
|
|
4808
4799
|
changes = {};
|
|
4809
|
-
this.changes
|
|
4800
|
+
this.changes.set(changeTree.refId, changes);
|
|
4810
4801
|
}
|
|
4811
4802
|
if (!this.tags) {
|
|
4812
4803
|
this.tags = new WeakMap();
|
|
@@ -4832,20 +4823,20 @@ class StateView {
|
|
|
4832
4823
|
this.items.delete(changeTree);
|
|
4833
4824
|
const ref = changeTree.ref;
|
|
4834
4825
|
const metadata = ref.constructor[Symbol.metadata];
|
|
4835
|
-
let changes = this.changes
|
|
4826
|
+
let changes = this.changes.get(changeTree.refId);
|
|
4836
4827
|
if (changes === undefined) {
|
|
4837
4828
|
changes = {};
|
|
4838
|
-
this.changes
|
|
4829
|
+
this.changes.set(changeTree.refId, changes);
|
|
4839
4830
|
}
|
|
4840
4831
|
if (tag === DEFAULT_VIEW_TAG) {
|
|
4841
4832
|
// parent is collection (Map/Array)
|
|
4842
4833
|
const parent = changeTree.parent;
|
|
4843
4834
|
if (!Metadata.isValidInstance(parent)) {
|
|
4844
4835
|
const parentChangeTree = parent[$changes];
|
|
4845
|
-
let changes = this.changes
|
|
4836
|
+
let changes = this.changes.get(parentChangeTree.refId);
|
|
4846
4837
|
if (changes === undefined) {
|
|
4847
4838
|
changes = {};
|
|
4848
|
-
this.changes
|
|
4839
|
+
this.changes.set(parentChangeTree.refId, changes);
|
|
4849
4840
|
}
|
|
4850
4841
|
// DELETE / DELETE BY REF ID
|
|
4851
4842
|
changes[changeTree.parentIndex] = OPERATION.DELETE;
|