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