@colyseus/schema 4.0.25 → 4.0.26
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/StateView.d.ts +1 -1
- package/build/index.cjs +64 -40
- package/build/index.cjs.map +1 -1
- package/build/index.js +64 -40
- package/build/index.mjs +64 -40
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Metadata.ts +17 -4
- package/src/Schema.ts +3 -2
- package/src/decoder/DecodeOperation.ts +13 -4
- package/src/encoder/StateView.ts +32 -32
package/build/index.js
CHANGED
|
@@ -821,10 +821,25 @@
|
|
|
821
821
|
});
|
|
822
822
|
}
|
|
823
823
|
metadata[$viewFieldIndexes].push(index);
|
|
824
|
-
|
|
825
|
-
|
|
824
|
+
// Populate $fieldIndexesByViewTag: for a bitmask tag, register the field
|
|
825
|
+
// index under each individual set bit so that view.add(obj, Tag.ONE) finds
|
|
826
|
+
// fields tagged @view(Tag.ONE|Tag.TWO).
|
|
827
|
+
// Negative tags (i.e. DEFAULT_VIEW_TAG = -1) are stored as-is.
|
|
828
|
+
if (tag < 0) {
|
|
829
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
830
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
831
|
+
}
|
|
832
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
for (let bits = tag; bits > 0; bits &= bits - 1) {
|
|
836
|
+
const bit = bits & (-bits); // isolate lowest set bit
|
|
837
|
+
if (!metadata[$fieldIndexesByViewTag][bit]) {
|
|
838
|
+
metadata[$fieldIndexesByViewTag][bit] = [];
|
|
839
|
+
}
|
|
840
|
+
metadata[$fieldIndexesByViewTag][bit].push(index);
|
|
841
|
+
}
|
|
826
842
|
}
|
|
827
|
-
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
828
843
|
},
|
|
829
844
|
setFields(target, fields) {
|
|
830
845
|
// for inheritance support
|
|
@@ -1680,17 +1695,25 @@
|
|
|
1680
1695
|
if (previousValue) {
|
|
1681
1696
|
let previousRefId = previousValue[$refId];
|
|
1682
1697
|
if (previousRefId !== undefined && refId !== previousRefId) {
|
|
1698
|
+
// Collection field replaced by a different instance.
|
|
1683
1699
|
//
|
|
1684
|
-
//
|
|
1685
|
-
//
|
|
1700
|
+
// Don't decrement children here: GC (`garbageCollectDeletedRefs`)
|
|
1701
|
+
// removes them once the previous collection's refId hits zero.
|
|
1702
|
+
// Doing it here too would double-decrement a *shared* child and
|
|
1703
|
+
// drop it while still referenced ("refId not found").
|
|
1704
|
+
if ((operation & exports.OPERATION.DELETE) !== exports.OPERATION.DELETE) {
|
|
1705
|
+
// Replacement not tagged DELETE (e.g. pending ADD not upgraded
|
|
1706
|
+
// to DELETE_AND_ADD), so the previous refId wasn't decremented
|
|
1707
|
+
// above. Release it here, else it never gets GC'd (leak).
|
|
1708
|
+
$root.removeRef(previousRefId);
|
|
1709
|
+
}
|
|
1710
|
+
// enqueue onRemove callbacks for the previous collection's children.
|
|
1686
1711
|
const entries = previousValue.entries();
|
|
1687
1712
|
let iter;
|
|
1688
1713
|
while ((iter = entries.next()) && !iter.done) {
|
|
1689
1714
|
const [key, value] = iter.value;
|
|
1690
|
-
// if value is a schema, remove its reference
|
|
1691
1715
|
if (typeof (value) === "object") {
|
|
1692
1716
|
previousRefId = value[$refId];
|
|
1693
|
-
$root.removeRef(previousRefId);
|
|
1694
1717
|
}
|
|
1695
1718
|
allChanges.push({
|
|
1696
1719
|
ref: previousValue,
|
|
@@ -3768,9 +3791,10 @@
|
|
|
3768
3791
|
return view.isChangeTreeVisible(ref[$changes]);
|
|
3769
3792
|
}
|
|
3770
3793
|
else {
|
|
3771
|
-
// view pass: custom tag
|
|
3794
|
+
// view pass: custom tag (bitmask)
|
|
3795
|
+
// tag is the field's stored bitmask; view.tags stores the accumulated bitmask of tags used in view.add().
|
|
3772
3796
|
const tags = view.tags?.get(ref[$changes]);
|
|
3773
|
-
return tags &&
|
|
3797
|
+
return tags != null && (tag & tags) !== 0;
|
|
3774
3798
|
}
|
|
3775
3799
|
}
|
|
3776
3800
|
// allow inherited classes to have a constructor
|
|
@@ -5584,7 +5608,7 @@
|
|
|
5584
5608
|
* List of ChangeTree's that are invisible to this view
|
|
5585
5609
|
*/
|
|
5586
5610
|
invisible = new WeakSet();
|
|
5587
|
-
tags; //
|
|
5611
|
+
tags; // bitmask of tags used to add each ChangeTree
|
|
5588
5612
|
/**
|
|
5589
5613
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
5590
5614
|
* (This is used to force encoding a property, even if it was not changed)
|
|
@@ -5672,9 +5696,16 @@
|
|
|
5672
5696
|
changeTree.forEachChild((change, index) => {
|
|
5673
5697
|
// Do not ADD children that don't have the same tag
|
|
5674
5698
|
if (metadata &&
|
|
5675
|
-
metadata[index].tag !== undefined
|
|
5676
|
-
metadata[index].tag
|
|
5677
|
-
|
|
5699
|
+
metadata[index].tag !== undefined) {
|
|
5700
|
+
const fieldTag = metadata[index].tag;
|
|
5701
|
+
// DEFAULT_VIEW_TAG fields are visible to all clients.
|
|
5702
|
+
// Custom-tagged fields are only visible when bits overlap,
|
|
5703
|
+
// and never to default-tag clients.
|
|
5704
|
+
const tagMatch = fieldTag === DEFAULT_VIEW_TAG ||
|
|
5705
|
+
(tag !== DEFAULT_VIEW_TAG && (fieldTag & tag) !== 0);
|
|
5706
|
+
if (!tagMatch) {
|
|
5707
|
+
return;
|
|
5708
|
+
}
|
|
5678
5709
|
}
|
|
5679
5710
|
if (this.add(change.ref, tag, false)) {
|
|
5680
5711
|
isChildAdded = true;
|
|
@@ -5685,15 +5716,9 @@
|
|
|
5685
5716
|
if (!this.tags) {
|
|
5686
5717
|
this.tags = new WeakMap();
|
|
5687
5718
|
}
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
this.tags.set(changeTree, tags);
|
|
5692
|
-
}
|
|
5693
|
-
else {
|
|
5694
|
-
tags = this.tags.get(changeTree);
|
|
5695
|
-
}
|
|
5696
|
-
tags.add(tag);
|
|
5719
|
+
// Add tag bits into the bitmask stored for this ChangeTree.
|
|
5720
|
+
const currentMask = this.tags.get(changeTree) ?? 0;
|
|
5721
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5697
5722
|
// Ref: add tagged properties
|
|
5698
5723
|
metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
|
|
5699
5724
|
if (changeTree.getChange(index) !== exports.OPERATION.DELETE) {
|
|
@@ -5717,7 +5742,7 @@
|
|
|
5717
5742
|
if (op !== exports.OPERATION.DELETE &&
|
|
5718
5743
|
(isInvisible || // if "invisible", include all
|
|
5719
5744
|
tagAtIndex === undefined || // "all change" with no tag
|
|
5720
|
-
tagAtIndex === tag // tagged property
|
|
5745
|
+
(tagAtIndex === DEFAULT_VIEW_TAG || (tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0)) // tagged property
|
|
5721
5746
|
)) {
|
|
5722
5747
|
changes[index] = op;
|
|
5723
5748
|
isChildAdded = true; // FIXME: assign only once
|
|
@@ -5743,18 +5768,16 @@
|
|
|
5743
5768
|
// add parent's tag properties
|
|
5744
5769
|
if (changeTree.getChange(parentIndex) !== exports.OPERATION.DELETE) {
|
|
5745
5770
|
const changes = this.touchChanges(changeTree.ref[$refId]);
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
tags = this.tags.get(changeTree);
|
|
5771
|
+
// Only accumulate positive (custom) tags in the bitmask.
|
|
5772
|
+
// DEFAULT_VIEW_TAG = -1 has all bits set and must not be OR'd in,
|
|
5773
|
+
// as it would make every custom-tagged field appear visible.
|
|
5774
|
+
if (tag !== DEFAULT_VIEW_TAG) {
|
|
5775
|
+
if (!this.tags) {
|
|
5776
|
+
this.tags = new WeakMap();
|
|
5777
|
+
}
|
|
5778
|
+
const currentMask = this.tags.has(changeTree) ? this.tags.get(changeTree) : 0;
|
|
5779
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5756
5780
|
}
|
|
5757
|
-
tags.add(tag);
|
|
5758
5781
|
changes[parentIndex] = exports.OPERATION.ADD;
|
|
5759
5782
|
}
|
|
5760
5783
|
}
|
|
@@ -5824,18 +5847,19 @@
|
|
|
5824
5847
|
}
|
|
5825
5848
|
// remove tag
|
|
5826
5849
|
if (this.tags && this.tags.has(changeTree)) {
|
|
5827
|
-
const tags = this.tags.get(changeTree);
|
|
5828
5850
|
if (tag === undefined) {
|
|
5829
5851
|
// delete all tags
|
|
5830
5852
|
this.tags.delete(changeTree);
|
|
5831
5853
|
}
|
|
5832
5854
|
else {
|
|
5833
|
-
//
|
|
5834
|
-
tags.
|
|
5835
|
-
|
|
5836
|
-
if (tags.size === 0) {
|
|
5855
|
+
// clear the tag's bits from the bitmask
|
|
5856
|
+
const newMask = this.tags.get(changeTree) & ~tag;
|
|
5857
|
+
if (newMask === 0) {
|
|
5837
5858
|
this.tags.delete(changeTree);
|
|
5838
5859
|
}
|
|
5860
|
+
else {
|
|
5861
|
+
this.tags.set(changeTree, newMask);
|
|
5862
|
+
}
|
|
5839
5863
|
}
|
|
5840
5864
|
}
|
|
5841
5865
|
return this;
|
|
@@ -5845,7 +5869,7 @@
|
|
|
5845
5869
|
}
|
|
5846
5870
|
hasTag(ob, tag = DEFAULT_VIEW_TAG) {
|
|
5847
5871
|
const tags = this.tags?.get(ob[$changes]);
|
|
5848
|
-
return tags
|
|
5872
|
+
return tags != null && (tags & tag) !== 0;
|
|
5849
5873
|
}
|
|
5850
5874
|
clear() {
|
|
5851
5875
|
if (!this.iterable) {
|
package/build/index.mjs
CHANGED
|
@@ -815,10 +815,25 @@ const Metadata = {
|
|
|
815
815
|
});
|
|
816
816
|
}
|
|
817
817
|
metadata[$viewFieldIndexes].push(index);
|
|
818
|
-
|
|
819
|
-
|
|
818
|
+
// Populate $fieldIndexesByViewTag: for a bitmask tag, register the field
|
|
819
|
+
// index under each individual set bit so that view.add(obj, Tag.ONE) finds
|
|
820
|
+
// fields tagged @view(Tag.ONE|Tag.TWO).
|
|
821
|
+
// Negative tags (i.e. DEFAULT_VIEW_TAG = -1) are stored as-is.
|
|
822
|
+
if (tag < 0) {
|
|
823
|
+
if (!metadata[$fieldIndexesByViewTag][tag]) {
|
|
824
|
+
metadata[$fieldIndexesByViewTag][tag] = [];
|
|
825
|
+
}
|
|
826
|
+
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
827
|
+
}
|
|
828
|
+
else {
|
|
829
|
+
for (let bits = tag; bits > 0; bits &= bits - 1) {
|
|
830
|
+
const bit = bits & (-bits); // isolate lowest set bit
|
|
831
|
+
if (!metadata[$fieldIndexesByViewTag][bit]) {
|
|
832
|
+
metadata[$fieldIndexesByViewTag][bit] = [];
|
|
833
|
+
}
|
|
834
|
+
metadata[$fieldIndexesByViewTag][bit].push(index);
|
|
835
|
+
}
|
|
820
836
|
}
|
|
821
|
-
metadata[$fieldIndexesByViewTag][tag].push(index);
|
|
822
837
|
},
|
|
823
838
|
setFields(target, fields) {
|
|
824
839
|
// for inheritance support
|
|
@@ -1674,17 +1689,25 @@ function decodeValue(decoder, operation, ref, index, type, bytes, it, allChanges
|
|
|
1674
1689
|
if (previousValue) {
|
|
1675
1690
|
let previousRefId = previousValue[$refId];
|
|
1676
1691
|
if (previousRefId !== undefined && refId !== previousRefId) {
|
|
1692
|
+
// Collection field replaced by a different instance.
|
|
1677
1693
|
//
|
|
1678
|
-
//
|
|
1679
|
-
//
|
|
1694
|
+
// Don't decrement children here: GC (`garbageCollectDeletedRefs`)
|
|
1695
|
+
// removes them once the previous collection's refId hits zero.
|
|
1696
|
+
// Doing it here too would double-decrement a *shared* child and
|
|
1697
|
+
// drop it while still referenced ("refId not found").
|
|
1698
|
+
if ((operation & OPERATION.DELETE) !== OPERATION.DELETE) {
|
|
1699
|
+
// Replacement not tagged DELETE (e.g. pending ADD not upgraded
|
|
1700
|
+
// to DELETE_AND_ADD), so the previous refId wasn't decremented
|
|
1701
|
+
// above. Release it here, else it never gets GC'd (leak).
|
|
1702
|
+
$root.removeRef(previousRefId);
|
|
1703
|
+
}
|
|
1704
|
+
// enqueue onRemove callbacks for the previous collection's children.
|
|
1680
1705
|
const entries = previousValue.entries();
|
|
1681
1706
|
let iter;
|
|
1682
1707
|
while ((iter = entries.next()) && !iter.done) {
|
|
1683
1708
|
const [key, value] = iter.value;
|
|
1684
|
-
// if value is a schema, remove its reference
|
|
1685
1709
|
if (typeof (value) === "object") {
|
|
1686
1710
|
previousRefId = value[$refId];
|
|
1687
|
-
$root.removeRef(previousRefId);
|
|
1688
1711
|
}
|
|
1689
1712
|
allChanges.push({
|
|
1690
1713
|
ref: previousValue,
|
|
@@ -3762,9 +3785,10 @@ class Schema {
|
|
|
3762
3785
|
return view.isChangeTreeVisible(ref[$changes]);
|
|
3763
3786
|
}
|
|
3764
3787
|
else {
|
|
3765
|
-
// view pass: custom tag
|
|
3788
|
+
// view pass: custom tag (bitmask)
|
|
3789
|
+
// tag is the field's stored bitmask; view.tags stores the accumulated bitmask of tags used in view.add().
|
|
3766
3790
|
const tags = view.tags?.get(ref[$changes]);
|
|
3767
|
-
return tags &&
|
|
3791
|
+
return tags != null && (tag & tags) !== 0;
|
|
3768
3792
|
}
|
|
3769
3793
|
}
|
|
3770
3794
|
// allow inherited classes to have a constructor
|
|
@@ -5578,7 +5602,7 @@ class StateView {
|
|
|
5578
5602
|
* List of ChangeTree's that are invisible to this view
|
|
5579
5603
|
*/
|
|
5580
5604
|
invisible = new WeakSet();
|
|
5581
|
-
tags; //
|
|
5605
|
+
tags; // bitmask of tags used to add each ChangeTree
|
|
5582
5606
|
/**
|
|
5583
5607
|
* Manual "ADD" operations for changes per ChangeTree, specific to this view.
|
|
5584
5608
|
* (This is used to force encoding a property, even if it was not changed)
|
|
@@ -5666,9 +5690,16 @@ class StateView {
|
|
|
5666
5690
|
changeTree.forEachChild((change, index) => {
|
|
5667
5691
|
// Do not ADD children that don't have the same tag
|
|
5668
5692
|
if (metadata &&
|
|
5669
|
-
metadata[index].tag !== undefined
|
|
5670
|
-
metadata[index].tag
|
|
5671
|
-
|
|
5693
|
+
metadata[index].tag !== undefined) {
|
|
5694
|
+
const fieldTag = metadata[index].tag;
|
|
5695
|
+
// DEFAULT_VIEW_TAG fields are visible to all clients.
|
|
5696
|
+
// Custom-tagged fields are only visible when bits overlap,
|
|
5697
|
+
// and never to default-tag clients.
|
|
5698
|
+
const tagMatch = fieldTag === DEFAULT_VIEW_TAG ||
|
|
5699
|
+
(tag !== DEFAULT_VIEW_TAG && (fieldTag & tag) !== 0);
|
|
5700
|
+
if (!tagMatch) {
|
|
5701
|
+
return;
|
|
5702
|
+
}
|
|
5672
5703
|
}
|
|
5673
5704
|
if (this.add(change.ref, tag, false)) {
|
|
5674
5705
|
isChildAdded = true;
|
|
@@ -5679,15 +5710,9 @@ class StateView {
|
|
|
5679
5710
|
if (!this.tags) {
|
|
5680
5711
|
this.tags = new WeakMap();
|
|
5681
5712
|
}
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
this.tags.set(changeTree, tags);
|
|
5686
|
-
}
|
|
5687
|
-
else {
|
|
5688
|
-
tags = this.tags.get(changeTree);
|
|
5689
|
-
}
|
|
5690
|
-
tags.add(tag);
|
|
5713
|
+
// Add tag bits into the bitmask stored for this ChangeTree.
|
|
5714
|
+
const currentMask = this.tags.get(changeTree) ?? 0;
|
|
5715
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5691
5716
|
// Ref: add tagged properties
|
|
5692
5717
|
metadata?.[$fieldIndexesByViewTag]?.[tag]?.forEach((index) => {
|
|
5693
5718
|
if (changeTree.getChange(index) !== OPERATION.DELETE) {
|
|
@@ -5711,7 +5736,7 @@ class StateView {
|
|
|
5711
5736
|
if (op !== OPERATION.DELETE &&
|
|
5712
5737
|
(isInvisible || // if "invisible", include all
|
|
5713
5738
|
tagAtIndex === undefined || // "all change" with no tag
|
|
5714
|
-
tagAtIndex === tag // tagged property
|
|
5739
|
+
(tagAtIndex === DEFAULT_VIEW_TAG || (tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0)) // tagged property
|
|
5715
5740
|
)) {
|
|
5716
5741
|
changes[index] = op;
|
|
5717
5742
|
isChildAdded = true; // FIXME: assign only once
|
|
@@ -5737,18 +5762,16 @@ class StateView {
|
|
|
5737
5762
|
// add parent's tag properties
|
|
5738
5763
|
if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
|
|
5739
5764
|
const changes = this.touchChanges(changeTree.ref[$refId]);
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
tags = this.tags.get(changeTree);
|
|
5765
|
+
// Only accumulate positive (custom) tags in the bitmask.
|
|
5766
|
+
// DEFAULT_VIEW_TAG = -1 has all bits set and must not be OR'd in,
|
|
5767
|
+
// as it would make every custom-tagged field appear visible.
|
|
5768
|
+
if (tag !== DEFAULT_VIEW_TAG) {
|
|
5769
|
+
if (!this.tags) {
|
|
5770
|
+
this.tags = new WeakMap();
|
|
5771
|
+
}
|
|
5772
|
+
const currentMask = this.tags.has(changeTree) ? this.tags.get(changeTree) : 0;
|
|
5773
|
+
this.tags.set(changeTree, currentMask | tag);
|
|
5750
5774
|
}
|
|
5751
|
-
tags.add(tag);
|
|
5752
5775
|
changes[parentIndex] = OPERATION.ADD;
|
|
5753
5776
|
}
|
|
5754
5777
|
}
|
|
@@ -5818,18 +5841,19 @@ class StateView {
|
|
|
5818
5841
|
}
|
|
5819
5842
|
// remove tag
|
|
5820
5843
|
if (this.tags && this.tags.has(changeTree)) {
|
|
5821
|
-
const tags = this.tags.get(changeTree);
|
|
5822
5844
|
if (tag === undefined) {
|
|
5823
5845
|
// delete all tags
|
|
5824
5846
|
this.tags.delete(changeTree);
|
|
5825
5847
|
}
|
|
5826
5848
|
else {
|
|
5827
|
-
//
|
|
5828
|
-
tags.
|
|
5829
|
-
|
|
5830
|
-
if (tags.size === 0) {
|
|
5849
|
+
// clear the tag's bits from the bitmask
|
|
5850
|
+
const newMask = this.tags.get(changeTree) & ~tag;
|
|
5851
|
+
if (newMask === 0) {
|
|
5831
5852
|
this.tags.delete(changeTree);
|
|
5832
5853
|
}
|
|
5854
|
+
else {
|
|
5855
|
+
this.tags.set(changeTree, newMask);
|
|
5856
|
+
}
|
|
5833
5857
|
}
|
|
5834
5858
|
}
|
|
5835
5859
|
return this;
|
|
@@ -5839,7 +5863,7 @@ class StateView {
|
|
|
5839
5863
|
}
|
|
5840
5864
|
hasTag(ob, tag = DEFAULT_VIEW_TAG) {
|
|
5841
5865
|
const tags = this.tags?.get(ob[$changes]);
|
|
5842
|
-
return tags
|
|
5866
|
+
return tags != null && (tags & tag) !== 0;
|
|
5843
5867
|
}
|
|
5844
5868
|
clear() {
|
|
5845
5869
|
if (!this.iterable) {
|