@colyseus/schema 4.0.23 → 4.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "4.0.23",
3
+ "version": "4.0.25",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,5 @@
1
1
  import { OPERATION } from "../encoding/spec.js";
2
+ import { DEFAULT_VIEW_TAG } from "../annotations.js";
2
3
  import { Schema } from "../Schema.js";
3
4
  import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $refId, $refTypeFieldIndexes, $viewFieldIndexes, type $deleteByIndex } from "../types/symbols.js";
4
5
 
@@ -403,13 +404,27 @@ export class ChangeTree<T extends Ref = any> {
403
404
  return;
404
405
  }
405
406
 
406
- const changeSet = (this.filteredChanges !== undefined)
407
+ // Mirror `change()`: a field's add/delete must target the same
408
+ // changeset family (filtered vs non-filtered). Otherwise
409
+ // `deleteOperationAtIndex` falls through to its "find last
410
+ // operation" branch and evicts an unrelated sibling field —
411
+ // surfaced as encodeAll() dropping a non-@view field after a
412
+ // sibling @view field is set to undefined on a Schema with
413
+ // mixed @view / non-@view fields (filteredChanges defined,
414
+ // isFiltered false).
415
+ const isFiltered = this.isFiltered || (this.metadata?.[index]?.tag !== undefined);
416
+ const changeSet = (isFiltered)
407
417
  ? this.filteredChanges
408
418
  : this.changes;
409
419
 
410
420
  this.indexedOperations[index] = operation ?? OPERATION.DELETE;
411
421
  setOperationAtIndex(changeSet, index);
412
- deleteOperationAtIndex(this.allChanges, allChangesIndex);
422
+
423
+ if (isFiltered) {
424
+ deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
425
+ } else {
426
+ deleteOperationAtIndex(this.allChanges, allChangesIndex);
427
+ }
413
428
 
414
429
  const previousValue = this.getValue(index);
415
430
 
@@ -428,13 +443,8 @@ export class ChangeTree<T extends Ref = any> {
428
443
  this.root?.remove(previousValue[$changes]);
429
444
  }
430
445
 
431
- //
432
- // FIXME: this is looking a ugly and repeated
433
- //
434
- if (this.filteredChanges !== undefined) {
435
- deleteOperationAtIndex(this.allFilteredChanges, allChangesIndex);
446
+ if (isFiltered) {
436
447
  this.root?.enqueueChangeTree(this, 'filteredChanges');
437
-
438
448
  } else {
439
449
  this.root?.enqueueChangeTree(this, 'changes');
440
450
  }
@@ -561,7 +571,8 @@ export class ChangeTree<T extends Ref = any> {
561
571
  }
562
572
  key += `-${parentIndex}`;
563
573
 
564
- const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentConstructor?.[Symbol.metadata], parentIndex);
574
+ const parentMetadata = parentConstructor?.[Symbol.metadata];
575
+ const fieldHasViewTag = Metadata.hasViewTagAtIndex(parentMetadata, parentIndex);
565
576
 
566
577
  this.isFiltered = parent[$changes].isFiltered // in case parent is already filtered
567
578
  || this.root.types.parentFiltered[key]
@@ -572,11 +583,23 @@ export class ChangeTree<T extends Ref = any> {
572
583
  // when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset.
573
584
  //
574
585
  if (this.isFiltered) {
575
-
586
+ //
587
+ // Children of a `@view(N)` collection (non-default tag) inherit
588
+ // visibility from their parent, so items pushed/set after the
589
+ // initial `view.add(state, N)` show up automatically.
590
+ //
591
+ // Default-tag `@view()` collections deliberately keep per-item
592
+ // gating — `view.add(item)` is required to opt each one in.
593
+ //
594
+ // The `parentMetadata[parentIndex].tag` access is safe inside
595
+ // this branch: the OR's short-circuit means we only reach it
596
+ // when `fieldHasViewTag` is true, which guarantees the metadata
597
+ // entry and its `tag` property exist.
598
+ //
576
599
  this.isVisibilitySharedWithParent = (
577
600
  parentChangeTree.isFiltered &&
578
601
  typeof (refType) !== "string" &&
579
- !fieldHasViewTag
602
+ (!fieldHasViewTag || (parentIsCollection && parentMetadata[parentIndex].tag !== DEFAULT_VIEW_TAG))
580
603
  );
581
604
 
582
605
  if (!this.filteredChanges) {