@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.
@@ -129,7 +129,6 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
129
129
  changesNode?: ChangeTreeNode;
130
130
  unreliableChangesNode?: ChangeTreeNode;
131
131
  visibleViews?: number[];
132
- invisibleViews?: number[];
133
132
  tagViews?: Map<number, number[]>;
134
133
  /**
135
134
  * Per-view subscription bitmap — same layout as `visibleViews`. Set by
@@ -64,12 +64,6 @@ export declare class StateView {
64
64
  private _setSubscribed;
65
65
  /** Clear the subscription bit on `tree`. */
66
66
  private _clearSubscribed;
67
- /** True iff this view has previously marked `tree` as invisible. */
68
- isInvisible(tree: ChangeTree): boolean;
69
- /** Mark `tree` as invisible to this view (used by encode loop). */
70
- markInvisible(tree: ChangeTree): void;
71
- /** Clear invisible bit. */
72
- unmarkInvisible(tree: ChangeTree): void;
73
67
  /**
74
68
  * True iff this view shares at least one tag bit with `tree`.
75
69
  *
package/build/index.cjs CHANGED
@@ -2594,7 +2594,6 @@ class ChangeTree {
2594
2594
  // per-view WeakSet lookups with direct bitwise ops.
2595
2595
  // Lazy: undefined until the tree participates in any view.
2596
2596
  visibleViews;
2597
- invisibleViews;
2598
2597
  // Per-(view, tag) bitmap, indexed by tag. Custom tags only —
2599
2598
  // DEFAULT_VIEW_TAG visibility lives in `visibleViews`.
2600
2599
  tagViews;
@@ -2946,7 +2945,6 @@ class ChangeTree {
2946
2945
  // per-view visibility lives on the tree (NOT keyed by refId), so a
2947
2946
  // recycled tree must not inherit its previous life's view membership.
2948
2947
  this.visibleViews = undefined;
2949
- this.invisibleViews = undefined;
2950
2948
  this.tagViews = undefined;
2951
2949
  this.subscribedViews = undefined;
2952
2950
  }
@@ -8006,17 +8004,7 @@ function _fullSyncWalk(ctx, changeTree) {
8006
8004
  // Visibility gate: when a view is active, a non-visible tree contributes
8007
8005
  // nothing itself but we still recurse so descendants (possibly added to
8008
8006
  // the view explicitly) are reachable.
8009
- let visibleHere = true;
8010
- if (ctx.hasView) {
8011
- const view = ctx.view;
8012
- if (!view.isChangeTreeVisible(changeTree)) {
8013
- view.markInvisible(changeTree);
8014
- visibleHere = false;
8015
- }
8016
- else {
8017
- view.unmarkInvisible(changeTree);
8018
- }
8019
- }
8007
+ const visibleHere = !ctx.hasView || ctx.view.isChangeTreeVisible(changeTree);
8020
8008
  if (visibleHere) {
8021
8009
  const desc = changeTree.encDescriptor;
8022
8010
  ctx.changeTree = changeTree;
@@ -8165,12 +8153,8 @@ class Encoder {
8165
8153
  let current = queue;
8166
8154
  while (current = current.next) {
8167
8155
  const changeTree = current.changeTree;
8168
- if (hasView) {
8169
- if (!view.isChangeTreeVisible(changeTree)) {
8170
- view.markInvisible(changeTree);
8171
- continue;
8172
- }
8173
- view.unmarkInvisible(changeTree);
8156
+ if (hasView && !view.isChangeTreeVisible(changeTree)) {
8157
+ continue;
8174
8158
  }
8175
8159
  const recorder = unreliable ? changeTree.unreliableRecorder : changeTree;
8176
8160
  if (!recorder || !recorder.has()) {
@@ -9862,9 +9846,6 @@ function _clearViewBitFromAllTrees(root, slot, bit) {
9862
9846
  const v = tree.visibleViews;
9863
9847
  if (v !== undefined && slot < v.length)
9864
9848
  v[slot] &= clearMask;
9865
- const i = tree.invisibleViews;
9866
- if (i !== undefined && slot < i.length)
9867
- i[slot] &= clearMask;
9868
9849
  const s = tree.subscribedViews;
9869
9850
  if (s !== undefined && slot < s.length)
9870
9851
  s[slot] &= clearMask;
@@ -10016,32 +9997,6 @@ class StateView {
10016
9997
  if (slot < arr.length)
10017
9998
  arr[slot] &= ~this._bit;
10018
9999
  }
10019
- /** True iff this view has previously marked `tree` as invisible. */
10020
- isInvisible(tree) {
10021
- const arr = tree.invisibleViews;
10022
- const slot = this._slot;
10023
- return arr !== undefined && slot < arr.length && (arr[slot] & this._bit) !== 0;
10024
- }
10025
- /** Mark `tree` as invisible to this view (used by encode loop). */
10026
- markInvisible(tree) {
10027
- const slot = this._slot;
10028
- let arr = tree.invisibleViews;
10029
- if (arr === undefined) {
10030
- arr = tree.invisibleViews = [];
10031
- }
10032
- while (arr.length <= slot)
10033
- arr.push(0);
10034
- arr[slot] |= this._bit;
10035
- }
10036
- /** Clear invisible bit. */
10037
- unmarkInvisible(tree) {
10038
- const arr = tree.invisibleViews;
10039
- if (arr === undefined)
10040
- return;
10041
- const slot = this._slot;
10042
- if (slot < arr.length)
10043
- arr[slot] &= ~this._bit;
10044
- }
10045
10000
  // ──────────────────────────────────────────────────────────────────
10046
10001
  // Per-tag, per-view bitmap. Replaces the legacy
10047
10002
  // `tags: WeakMap<ChangeTree, Set<number>>` storage. Hot read site is
@@ -10185,11 +10140,18 @@ class StateView {
10185
10140
  // below use `metadata?.[...]` null-safe access. Only Schema
10186
10141
  // subclasses yield a real Metadata object.
10187
10142
  const metadata = obj.constructor[Symbol.metadata];
10188
- this.markVisible(changeTree);
10189
- // add to iterable list (only the explicitly added items)
10190
- if (this.iterable && checkIncludeParent) {
10143
+ // Add to iterable list (only the explicitly added items), deduping
10144
+ // re-adds of an already-visible instance. isVisible must be read
10145
+ // BEFORE markVisible; indexOf runs only on the re-add path.
10146
+ // NOTE: dedup applies to `items` only — a re-add still re-queues the
10147
+ // full snapshot on purpose (shared-view bootstrap re-add: a
10148
+ // late-attached client may not have consumed earlier drains).
10149
+ // Callers wanting cheap idempotence can guard with `view.has(obj)`.
10150
+ if (this.iterable && checkIncludeParent
10151
+ && (!this.isVisible(changeTree) || this.items.indexOf(obj) === -1)) {
10191
10152
  this.items.push(obj);
10192
10153
  }
10154
+ this.markVisible(changeTree);
10193
10155
  // add parent ChangeTree's
10194
10156
  // - if it was invisible to this view
10195
10157
  // - if it were previously filtered out
@@ -10272,18 +10234,20 @@ class StateView {
10272
10234
  }
10273
10235
  else if (!changeTree.isNew || isChildAdded) {
10274
10236
  // new structures will be added as part of .encode() call, no need to force it to .encodeView()
10275
- const isInvisible = this.isInvisible(changeTree);
10276
10237
  // Full-sync snapshot: walk the live ref structurally instead of
10277
10238
  // iterating a cumulative recorder bucket. Every populated index
10278
10239
  // is emitted as ADD (matching the op-coercion previously done
10279
10240
  // at encode time). Per-field tags come from the descriptor's
10280
10241
  // precomputed `tags[]` array — direct index vs a metadata[i].tag
10281
10242
  // object hop.
10243
+ //
10244
+ // Non-matching custom-tagged fields are NEVER included here —
10245
+ // `view.changes` is drained without a per-field tag re-check,
10246
+ // so anything added leaks straight to the wire.
10282
10247
  const tags = changeTree.encDescriptor.tags;
10283
10248
  changeTree.forEachLive((index) => {
10284
10249
  const tagAtIndex = tags[index];
10285
- if (isInvisible || // if "invisible", include all
10286
- tagAtIndex === undefined || // "all change" with no tag
10250
+ if (tagAtIndex === undefined || // "all change" with no tag
10287
10251
  tagAtIndex === DEFAULT_VIEW_TAG || // visible to all clients
10288
10252
  (tag !== DEFAULT_VIEW_TAG && (tagAtIndex & tag) !== 0) // tag bits overlap
10289
10253
  ) {
@@ -10435,7 +10399,7 @@ class StateView {
10435
10399
  }
10436
10400
  // ── Streamable-collection unsubscribe (the stream itself) ─────
10437
10401
  // Flush DELETE for every sent position and drop pending. After
10438
- // this, the stream is marked invisible to this view — any future
10402
+ // this, the stream is no longer visible to this view — any future
10439
10403
  // `stream.add()` would still seed broadcast pending (if no views)
10440
10404
  // but would NOT re-seed per-view pending (user must re-subscribe).
10441
10405
  if (changeTree.isStreamCollection) {