@colyseus/schema 5.0.10 → 5.0.11
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 +0 -1
- package/build/encoder/StateView.d.ts +0 -6
- package/build/index.cjs +19 -55
- package/build/index.cjs.map +1 -1
- package/build/index.js +19 -55
- package/build/index.mjs +19 -55
- package/build/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +0 -2
- package/src/encoder/Encoder.ts +3 -16
- package/src/encoder/StateView.ts +16 -35
package/build/index.js
CHANGED
|
@@ -2598,7 +2598,6 @@
|
|
|
2598
2598
|
// per-view WeakSet lookups with direct bitwise ops.
|
|
2599
2599
|
// Lazy: undefined until the tree participates in any view.
|
|
2600
2600
|
visibleViews;
|
|
2601
|
-
invisibleViews;
|
|
2602
2601
|
// Per-(view, tag) bitmap, indexed by tag. Custom tags only —
|
|
2603
2602
|
// DEFAULT_VIEW_TAG visibility lives in `visibleViews`.
|
|
2604
2603
|
tagViews;
|
|
@@ -2950,7 +2949,6 @@
|
|
|
2950
2949
|
// per-view visibility lives on the tree (NOT keyed by refId), so a
|
|
2951
2950
|
// recycled tree must not inherit its previous life's view membership.
|
|
2952
2951
|
this.visibleViews = undefined;
|
|
2953
|
-
this.invisibleViews = undefined;
|
|
2954
2952
|
this.tagViews = undefined;
|
|
2955
2953
|
this.subscribedViews = undefined;
|
|
2956
2954
|
}
|
|
@@ -8010,17 +8008,7 @@
|
|
|
8010
8008
|
// Visibility gate: when a view is active, a non-visible tree contributes
|
|
8011
8009
|
// nothing itself but we still recurse so descendants (possibly added to
|
|
8012
8010
|
// the view explicitly) are reachable.
|
|
8013
|
-
|
|
8014
|
-
if (ctx.hasView) {
|
|
8015
|
-
const view = ctx.view;
|
|
8016
|
-
if (!view.isChangeTreeVisible(changeTree)) {
|
|
8017
|
-
view.markInvisible(changeTree);
|
|
8018
|
-
visibleHere = false;
|
|
8019
|
-
}
|
|
8020
|
-
else {
|
|
8021
|
-
view.unmarkInvisible(changeTree);
|
|
8022
|
-
}
|
|
8023
|
-
}
|
|
8011
|
+
const visibleHere = !ctx.hasView || ctx.view.isChangeTreeVisible(changeTree);
|
|
8024
8012
|
if (visibleHere) {
|
|
8025
8013
|
const desc = changeTree.encDescriptor;
|
|
8026
8014
|
ctx.changeTree = changeTree;
|
|
@@ -8169,12 +8157,8 @@
|
|
|
8169
8157
|
let current = queue;
|
|
8170
8158
|
while (current = current.next) {
|
|
8171
8159
|
const changeTree = current.changeTree;
|
|
8172
|
-
if (hasView) {
|
|
8173
|
-
|
|
8174
|
-
view.markInvisible(changeTree);
|
|
8175
|
-
continue;
|
|
8176
|
-
}
|
|
8177
|
-
view.unmarkInvisible(changeTree);
|
|
8160
|
+
if (hasView && !view.isChangeTreeVisible(changeTree)) {
|
|
8161
|
+
continue;
|
|
8178
8162
|
}
|
|
8179
8163
|
const recorder = unreliable ? changeTree.unreliableRecorder : changeTree;
|
|
8180
8164
|
if (!recorder || !recorder.has()) {
|
|
@@ -9866,9 +9850,6 @@
|
|
|
9866
9850
|
const v = tree.visibleViews;
|
|
9867
9851
|
if (v !== undefined && slot < v.length)
|
|
9868
9852
|
v[slot] &= clearMask;
|
|
9869
|
-
const i = tree.invisibleViews;
|
|
9870
|
-
if (i !== undefined && slot < i.length)
|
|
9871
|
-
i[slot] &= clearMask;
|
|
9872
9853
|
const s = tree.subscribedViews;
|
|
9873
9854
|
if (s !== undefined && slot < s.length)
|
|
9874
9855
|
s[slot] &= clearMask;
|
|
@@ -10020,32 +10001,6 @@
|
|
|
10020
10001
|
if (slot < arr.length)
|
|
10021
10002
|
arr[slot] &= ~this._bit;
|
|
10022
10003
|
}
|
|
10023
|
-
/** True iff this view has previously marked `tree` as invisible. */
|
|
10024
|
-
isInvisible(tree) {
|
|
10025
|
-
const arr = tree.invisibleViews;
|
|
10026
|
-
const slot = this._slot;
|
|
10027
|
-
return arr !== undefined && slot < arr.length && (arr[slot] & this._bit) !== 0;
|
|
10028
|
-
}
|
|
10029
|
-
/** Mark `tree` as invisible to this view (used by encode loop). */
|
|
10030
|
-
markInvisible(tree) {
|
|
10031
|
-
const slot = this._slot;
|
|
10032
|
-
let arr = tree.invisibleViews;
|
|
10033
|
-
if (arr === undefined) {
|
|
10034
|
-
arr = tree.invisibleViews = [];
|
|
10035
|
-
}
|
|
10036
|
-
while (arr.length <= slot)
|
|
10037
|
-
arr.push(0);
|
|
10038
|
-
arr[slot] |= this._bit;
|
|
10039
|
-
}
|
|
10040
|
-
/** Clear invisible bit. */
|
|
10041
|
-
unmarkInvisible(tree) {
|
|
10042
|
-
const arr = tree.invisibleViews;
|
|
10043
|
-
if (arr === undefined)
|
|
10044
|
-
return;
|
|
10045
|
-
const slot = this._slot;
|
|
10046
|
-
if (slot < arr.length)
|
|
10047
|
-
arr[slot] &= ~this._bit;
|
|
10048
|
-
}
|
|
10049
10004
|
// ──────────────────────────────────────────────────────────────────
|
|
10050
10005
|
// Per-tag, per-view bitmap. Replaces the legacy
|
|
10051
10006
|
// `tags: WeakMap<ChangeTree, Set<number>>` storage. Hot read site is
|
|
@@ -10189,11 +10144,18 @@
|
|
|
10189
10144
|
// below use `metadata?.[...]` null-safe access. Only Schema
|
|
10190
10145
|
// subclasses yield a real Metadata object.
|
|
10191
10146
|
const metadata = obj.constructor[Symbol.metadata];
|
|
10192
|
-
|
|
10193
|
-
//
|
|
10194
|
-
|
|
10147
|
+
// Add to iterable list (only the explicitly added items), deduping
|
|
10148
|
+
// re-adds of an already-visible instance. isVisible must be read
|
|
10149
|
+
// BEFORE markVisible; indexOf runs only on the re-add path.
|
|
10150
|
+
// NOTE: dedup applies to `items` only — a re-add still re-queues the
|
|
10151
|
+
// full snapshot on purpose (shared-view bootstrap re-add: a
|
|
10152
|
+
// late-attached client may not have consumed earlier drains).
|
|
10153
|
+
// Callers wanting cheap idempotence can guard with `view.has(obj)`.
|
|
10154
|
+
if (this.iterable && checkIncludeParent
|
|
10155
|
+
&& (!this.isVisible(changeTree) || this.items.indexOf(obj) === -1)) {
|
|
10195
10156
|
this.items.push(obj);
|
|
10196
10157
|
}
|
|
10158
|
+
this.markVisible(changeTree);
|
|
10197
10159
|
// add parent ChangeTree's
|
|
10198
10160
|
// - if it was invisible to this view
|
|
10199
10161
|
// - if it were previously filtered out
|
|
@@ -10276,18 +10238,20 @@
|
|
|
10276
10238
|
}
|
|
10277
10239
|
else if (!changeTree.isNew || isChildAdded) {
|
|
10278
10240
|
// new structures will be added as part of .encode() call, no need to force it to .encodeView()
|
|
10279
|
-
const isInvisible = this.isInvisible(changeTree);
|
|
10280
10241
|
// Full-sync snapshot: walk the live ref structurally instead of
|
|
10281
10242
|
// iterating a cumulative recorder bucket. Every populated index
|
|
10282
10243
|
// is emitted as ADD (matching the op-coercion previously done
|
|
10283
10244
|
// at encode time). Per-field tags come from the descriptor's
|
|
10284
10245
|
// precomputed `tags[]` array — direct index vs a metadata[i].tag
|
|
10285
10246
|
// object hop.
|
|
10247
|
+
//
|
|
10248
|
+
// Non-matching custom-tagged fields are NEVER included here —
|
|
10249
|
+
// `view.changes` is drained without a per-field tag re-check,
|
|
10250
|
+
// so anything added leaks straight to the wire.
|
|
10286
10251
|
const tags = changeTree.encDescriptor.tags;
|
|
10287
10252
|
changeTree.forEachLive((index) => {
|
|
10288
10253
|
const tagAtIndex = tags[index];
|
|
10289
|
-
if (
|
|
10290
|
-
tagAtIndex === undefined || // "all change" with no tag
|
|
10254
|
+
if (tagAtIndex === undefined || // "all change" with no tag
|
|
10291
10255
|
tagAtIndex === DEFAULT_VIEW_TAG || // visible to all clients
|
|
10292
10256
|
(tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0) // tag bits overlap
|
|
10293
10257
|
) {
|
|
@@ -10439,7 +10403,7 @@
|
|
|
10439
10403
|
}
|
|
10440
10404
|
// ── Streamable-collection unsubscribe (the stream itself) ─────
|
|
10441
10405
|
// Flush DELETE for every sent position and drop pending. After
|
|
10442
|
-
// this, the stream is
|
|
10406
|
+
// this, the stream is no longer visible to this view — any future
|
|
10443
10407
|
// `stream.add()` would still seed broadcast pending (if no views)
|
|
10444
10408
|
// but would NOT re-seed per-view pending (user must re-subscribe).
|
|
10445
10409
|
if (changeTree.isStreamCollection) {
|
package/build/index.mjs
CHANGED
|
@@ -2592,7 +2592,6 @@ class ChangeTree {
|
|
|
2592
2592
|
// per-view WeakSet lookups with direct bitwise ops.
|
|
2593
2593
|
// Lazy: undefined until the tree participates in any view.
|
|
2594
2594
|
visibleViews;
|
|
2595
|
-
invisibleViews;
|
|
2596
2595
|
// Per-(view, tag) bitmap, indexed by tag. Custom tags only —
|
|
2597
2596
|
// DEFAULT_VIEW_TAG visibility lives in `visibleViews`.
|
|
2598
2597
|
tagViews;
|
|
@@ -2944,7 +2943,6 @@ class ChangeTree {
|
|
|
2944
2943
|
// per-view visibility lives on the tree (NOT keyed by refId), so a
|
|
2945
2944
|
// recycled tree must not inherit its previous life's view membership.
|
|
2946
2945
|
this.visibleViews = undefined;
|
|
2947
|
-
this.invisibleViews = undefined;
|
|
2948
2946
|
this.tagViews = undefined;
|
|
2949
2947
|
this.subscribedViews = undefined;
|
|
2950
2948
|
}
|
|
@@ -8004,17 +8002,7 @@ function _fullSyncWalk(ctx, changeTree) {
|
|
|
8004
8002
|
// Visibility gate: when a view is active, a non-visible tree contributes
|
|
8005
8003
|
// nothing itself but we still recurse so descendants (possibly added to
|
|
8006
8004
|
// the view explicitly) are reachable.
|
|
8007
|
-
|
|
8008
|
-
if (ctx.hasView) {
|
|
8009
|
-
const view = ctx.view;
|
|
8010
|
-
if (!view.isChangeTreeVisible(changeTree)) {
|
|
8011
|
-
view.markInvisible(changeTree);
|
|
8012
|
-
visibleHere = false;
|
|
8013
|
-
}
|
|
8014
|
-
else {
|
|
8015
|
-
view.unmarkInvisible(changeTree);
|
|
8016
|
-
}
|
|
8017
|
-
}
|
|
8005
|
+
const visibleHere = !ctx.hasView || ctx.view.isChangeTreeVisible(changeTree);
|
|
8018
8006
|
if (visibleHere) {
|
|
8019
8007
|
const desc = changeTree.encDescriptor;
|
|
8020
8008
|
ctx.changeTree = changeTree;
|
|
@@ -8163,12 +8151,8 @@ class Encoder {
|
|
|
8163
8151
|
let current = queue;
|
|
8164
8152
|
while (current = current.next) {
|
|
8165
8153
|
const changeTree = current.changeTree;
|
|
8166
|
-
if (hasView) {
|
|
8167
|
-
|
|
8168
|
-
view.markInvisible(changeTree);
|
|
8169
|
-
continue;
|
|
8170
|
-
}
|
|
8171
|
-
view.unmarkInvisible(changeTree);
|
|
8154
|
+
if (hasView && !view.isChangeTreeVisible(changeTree)) {
|
|
8155
|
+
continue;
|
|
8172
8156
|
}
|
|
8173
8157
|
const recorder = unreliable ? changeTree.unreliableRecorder : changeTree;
|
|
8174
8158
|
if (!recorder || !recorder.has()) {
|
|
@@ -9860,9 +9844,6 @@ function _clearViewBitFromAllTrees(root, slot, bit) {
|
|
|
9860
9844
|
const v = tree.visibleViews;
|
|
9861
9845
|
if (v !== undefined && slot < v.length)
|
|
9862
9846
|
v[slot] &= clearMask;
|
|
9863
|
-
const i = tree.invisibleViews;
|
|
9864
|
-
if (i !== undefined && slot < i.length)
|
|
9865
|
-
i[slot] &= clearMask;
|
|
9866
9847
|
const s = tree.subscribedViews;
|
|
9867
9848
|
if (s !== undefined && slot < s.length)
|
|
9868
9849
|
s[slot] &= clearMask;
|
|
@@ -10014,32 +9995,6 @@ class StateView {
|
|
|
10014
9995
|
if (slot < arr.length)
|
|
10015
9996
|
arr[slot] &= ~this._bit;
|
|
10016
9997
|
}
|
|
10017
|
-
/** True iff this view has previously marked `tree` as invisible. */
|
|
10018
|
-
isInvisible(tree) {
|
|
10019
|
-
const arr = tree.invisibleViews;
|
|
10020
|
-
const slot = this._slot;
|
|
10021
|
-
return arr !== undefined && slot < arr.length && (arr[slot] & this._bit) !== 0;
|
|
10022
|
-
}
|
|
10023
|
-
/** Mark `tree` as invisible to this view (used by encode loop). */
|
|
10024
|
-
markInvisible(tree) {
|
|
10025
|
-
const slot = this._slot;
|
|
10026
|
-
let arr = tree.invisibleViews;
|
|
10027
|
-
if (arr === undefined) {
|
|
10028
|
-
arr = tree.invisibleViews = [];
|
|
10029
|
-
}
|
|
10030
|
-
while (arr.length <= slot)
|
|
10031
|
-
arr.push(0);
|
|
10032
|
-
arr[slot] |= this._bit;
|
|
10033
|
-
}
|
|
10034
|
-
/** Clear invisible bit. */
|
|
10035
|
-
unmarkInvisible(tree) {
|
|
10036
|
-
const arr = tree.invisibleViews;
|
|
10037
|
-
if (arr === undefined)
|
|
10038
|
-
return;
|
|
10039
|
-
const slot = this._slot;
|
|
10040
|
-
if (slot < arr.length)
|
|
10041
|
-
arr[slot] &= ~this._bit;
|
|
10042
|
-
}
|
|
10043
9998
|
// ──────────────────────────────────────────────────────────────────
|
|
10044
9999
|
// Per-tag, per-view bitmap. Replaces the legacy
|
|
10045
10000
|
// `tags: WeakMap<ChangeTree, Set<number>>` storage. Hot read site is
|
|
@@ -10183,11 +10138,18 @@ class StateView {
|
|
|
10183
10138
|
// below use `metadata?.[...]` null-safe access. Only Schema
|
|
10184
10139
|
// subclasses yield a real Metadata object.
|
|
10185
10140
|
const metadata = obj.constructor[Symbol.metadata];
|
|
10186
|
-
|
|
10187
|
-
//
|
|
10188
|
-
|
|
10141
|
+
// Add to iterable list (only the explicitly added items), deduping
|
|
10142
|
+
// re-adds of an already-visible instance. isVisible must be read
|
|
10143
|
+
// BEFORE markVisible; indexOf runs only on the re-add path.
|
|
10144
|
+
// NOTE: dedup applies to `items` only — a re-add still re-queues the
|
|
10145
|
+
// full snapshot on purpose (shared-view bootstrap re-add: a
|
|
10146
|
+
// late-attached client may not have consumed earlier drains).
|
|
10147
|
+
// Callers wanting cheap idempotence can guard with `view.has(obj)`.
|
|
10148
|
+
if (this.iterable && checkIncludeParent
|
|
10149
|
+
&& (!this.isVisible(changeTree) || this.items.indexOf(obj) === -1)) {
|
|
10189
10150
|
this.items.push(obj);
|
|
10190
10151
|
}
|
|
10152
|
+
this.markVisible(changeTree);
|
|
10191
10153
|
// add parent ChangeTree's
|
|
10192
10154
|
// - if it was invisible to this view
|
|
10193
10155
|
// - if it were previously filtered out
|
|
@@ -10270,18 +10232,20 @@ class StateView {
|
|
|
10270
10232
|
}
|
|
10271
10233
|
else if (!changeTree.isNew || isChildAdded) {
|
|
10272
10234
|
// new structures will be added as part of .encode() call, no need to force it to .encodeView()
|
|
10273
|
-
const isInvisible = this.isInvisible(changeTree);
|
|
10274
10235
|
// Full-sync snapshot: walk the live ref structurally instead of
|
|
10275
10236
|
// iterating a cumulative recorder bucket. Every populated index
|
|
10276
10237
|
// is emitted as ADD (matching the op-coercion previously done
|
|
10277
10238
|
// at encode time). Per-field tags come from the descriptor's
|
|
10278
10239
|
// precomputed `tags[]` array — direct index vs a metadata[i].tag
|
|
10279
10240
|
// object hop.
|
|
10241
|
+
//
|
|
10242
|
+
// Non-matching custom-tagged fields are NEVER included here —
|
|
10243
|
+
// `view.changes` is drained without a per-field tag re-check,
|
|
10244
|
+
// so anything added leaks straight to the wire.
|
|
10280
10245
|
const tags = changeTree.encDescriptor.tags;
|
|
10281
10246
|
changeTree.forEachLive((index) => {
|
|
10282
10247
|
const tagAtIndex = tags[index];
|
|
10283
|
-
if (
|
|
10284
|
-
tagAtIndex === undefined || // "all change" with no tag
|
|
10248
|
+
if (tagAtIndex === undefined || // "all change" with no tag
|
|
10285
10249
|
tagAtIndex === DEFAULT_VIEW_TAG || // visible to all clients
|
|
10286
10250
|
(tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0) // tag bits overlap
|
|
10287
10251
|
) {
|
|
@@ -10433,7 +10397,7 @@ class StateView {
|
|
|
10433
10397
|
}
|
|
10434
10398
|
// ── Streamable-collection unsubscribe (the stream itself) ─────
|
|
10435
10399
|
// Flush DELETE for every sent position and drop pending. After
|
|
10436
|
-
// this, the stream is
|
|
10400
|
+
// this, the stream is no longer visible to this view — any future
|
|
10437
10401
|
// `stream.add()` would still seed broadcast pending (if no views)
|
|
10438
10402
|
// but would NOT re-seed per-view pending (user must re-subscribe).
|
|
10439
10403
|
if (changeTree.isStreamCollection) {
|