@colyseus/schema 3.0.57 → 3.0.60

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.
@@ -988,7 +988,7 @@ function createChangeSet(queueRootNode) {
988
988
  }
989
989
  // Linked list helper functions
990
990
  function createChangeTreeList() {
991
- return { next: undefined, tail: undefined, length: 0 };
991
+ return { next: undefined, tail: undefined };
992
992
  }
993
993
  function setOperationAtIndex(changeSet, index) {
994
994
  const operationsIndex = changeSet.indexes[index];
@@ -1080,11 +1080,11 @@ class ChangeTree {
1080
1080
  this.forEachChild((child, index) => {
1081
1081
  if (child.root === root) {
1082
1082
  //
1083
- // re-assigning a child of the same root, move it to the end
1084
- // of the changes queue so encoding order is preserved
1083
+ // re-assigning a child of the same root, move it next to parent
1084
+ // so encoding order is preserved
1085
1085
  //
1086
1086
  root.add(child);
1087
- root.moveToEndOfChanges(child);
1087
+ root.moveNextToParent(child);
1088
1088
  return;
1089
1089
  }
1090
1090
  child.setParent(this.ref, root, index);
@@ -2797,10 +2797,15 @@ class MapSchema {
2797
2797
  }
2798
2798
  [$onEncodeEnd]() {
2799
2799
  const changeTree = this[$changes];
2800
- // cleanup changeTree.indexes of deleted keys
2800
+ // - cleanup changeTree.indexes
2801
+ // - cleanup $indexes
2801
2802
  for (const indexStr in this.deletedItems) {
2802
- const key = this.$indexes.get(parseInt(indexStr));
2803
+ const index = parseInt(indexStr);
2804
+ const key = this.$indexes.get(index);
2805
+ // TODO: refactor this.
2806
+ // it shouldn't be necessary to keep track of indexes both on changeTree and on $indexes
2803
2807
  delete changeTree.indexes[key];
2808
+ this.$indexes.delete(index);
2804
2809
  }
2805
2810
  this.deletedItems = {};
2806
2811
  }
@@ -4011,8 +4016,8 @@ class Root {
4011
4016
  this.remove(child);
4012
4017
  }
4013
4018
  else if (child.parentChain) {
4014
- // re-assigning a child of the same root, move it to the end
4015
- this.moveToEndOfChanges(child);
4019
+ // re-assigning a child of the same root, move it next to parent
4020
+ this.moveNextToParent(child);
4016
4021
  }
4017
4022
  }
4018
4023
  });
@@ -4021,34 +4026,52 @@ class Root {
4021
4026
  this.refCount[changeTree.refId] = refCount;
4022
4027
  //
4023
4028
  // When losing a reference to an instance, it is best to move the
4024
- // ChangeTree to the end of the encoding queue.
4029
+ // ChangeTree next to its parent in the encoding queue.
4025
4030
  //
4026
4031
  // This way, at decoding time, the instance that contains the
4027
4032
  // ChangeTree will be available before the ChangeTree itself. If the
4028
4033
  // containing instance is not available, the Decoder will throw
4029
4034
  // "refId not found" error.
4030
4035
  //
4031
- this.moveToEndOfChanges(changeTree);
4032
- changeTree.forEachChild((child, _) => this.moveToEndOfChanges(child));
4036
+ this.recursivelyMoveNextToParent(changeTree);
4033
4037
  }
4034
4038
  return refCount;
4035
4039
  }
4036
- moveToEndOfChanges(changeTree) {
4040
+ recursivelyMoveNextToParent(changeTree) {
4041
+ this.moveNextToParent(changeTree);
4042
+ changeTree.forEachChild((child, _) => this.recursivelyMoveNextToParent(child));
4043
+ }
4044
+ moveNextToParent(changeTree) {
4037
4045
  if (changeTree.filteredChanges) {
4038
- this.moveToEndOfChangeTreeList("filteredChanges", changeTree);
4039
- this.moveToEndOfChangeTreeList("allFilteredChanges", changeTree);
4046
+ this.moveNextToParentInChangeTreeList("filteredChanges", changeTree);
4047
+ this.moveNextToParentInChangeTreeList("allFilteredChanges", changeTree);
4040
4048
  }
4041
4049
  else {
4042
- this.moveToEndOfChangeTreeList("changes", changeTree);
4043
- this.moveToEndOfChangeTreeList("allChanges", changeTree);
4050
+ this.moveNextToParentInChangeTreeList("changes", changeTree);
4051
+ this.moveNextToParentInChangeTreeList("allChanges", changeTree);
4044
4052
  }
4045
4053
  }
4046
- moveToEndOfChangeTreeList(changeSetName, changeTree) {
4054
+ moveNextToParentInChangeTreeList(changeSetName, changeTree) {
4047
4055
  const changeSet = this[changeSetName];
4048
4056
  const node = changeTree[changeSetName].queueRootNode;
4049
- if (!node || node === changeSet.tail)
4057
+ if (!node)
4058
+ return;
4059
+ // Find the parent in the linked list
4060
+ const parent = changeTree.parent;
4061
+ if (!parent || !parent[$changes])
4062
+ return;
4063
+ const parentNode = parent[$changes][changeSetName]?.queueRootNode;
4064
+ if (!parentNode || parentNode === node)
4065
+ return;
4066
+ // Use cached positions - no iteration needed!
4067
+ const parentPosition = parentNode.position;
4068
+ const childPosition = node.position;
4069
+ // If child is already after parent, no need to move
4070
+ if (childPosition > parentPosition)
4050
4071
  return;
4051
- // Remove from current position
4072
+ // Child is before parent, so we need to move it after parent
4073
+ // This maintains decoding order (parent before child)
4074
+ // Remove node from current position
4052
4075
  if (node.prev) {
4053
4076
  node.prev.next = node.next;
4054
4077
  }
@@ -4061,16 +4084,18 @@ class Root {
4061
4084
  else {
4062
4085
  changeSet.tail = node.prev;
4063
4086
  }
4064
- // Add to end
4065
- node.prev = changeSet.tail;
4066
- node.next = undefined;
4067
- if (changeSet.tail) {
4068
- changeSet.tail.next = node;
4087
+ // Insert node right after parent
4088
+ node.prev = parentNode;
4089
+ node.next = parentNode.next;
4090
+ if (parentNode.next) {
4091
+ parentNode.next.prev = node;
4069
4092
  }
4070
4093
  else {
4071
- changeSet.next = node;
4094
+ changeSet.tail = node;
4072
4095
  }
4073
- changeSet.tail = node;
4096
+ parentNode.next = node;
4097
+ // Update positions after the move
4098
+ this.updatePositionsAfterMove(changeSet, node, parentPosition + 1);
4074
4099
  }
4075
4100
  enqueueChangeTree(changeTree, changeSet, queueRootNode = changeTree[changeSet].queueRootNode) {
4076
4101
  // skip
@@ -4081,7 +4106,12 @@ class Root {
4081
4106
  changeTree[changeSet].queueRootNode = this.addToChangeTreeList(this[changeSet], changeTree);
4082
4107
  }
4083
4108
  addToChangeTreeList(list, changeTree) {
4084
- const node = { changeTree, next: undefined, prev: undefined };
4109
+ const node = {
4110
+ changeTree,
4111
+ next: undefined,
4112
+ prev: undefined,
4113
+ position: list.tail ? list.tail.position + 1 : 0
4114
+ };
4085
4115
  if (!list.next) {
4086
4116
  list.next = node;
4087
4117
  list.tail = node;
@@ -4091,13 +4121,35 @@ class Root {
4091
4121
  list.tail.next = node;
4092
4122
  list.tail = node;
4093
4123
  }
4094
- list.length++;
4095
4124
  return node;
4096
4125
  }
4126
+ updatePositionsAfterRemoval(list, removedPosition) {
4127
+ // Update positions for all nodes after the removed position
4128
+ let current = list.next;
4129
+ let position = 0;
4130
+ while (current) {
4131
+ if (position >= removedPosition) {
4132
+ current.position = position;
4133
+ }
4134
+ current = current.next;
4135
+ position++;
4136
+ }
4137
+ }
4138
+ updatePositionsAfterMove(list, node, newPosition) {
4139
+ // Recalculate all positions - this is more reliable than trying to be clever
4140
+ let current = list.next;
4141
+ let position = 0;
4142
+ while (current) {
4143
+ current.position = position;
4144
+ current = current.next;
4145
+ position++;
4146
+ }
4147
+ }
4097
4148
  removeChangeFromChangeSet(changeSetName, changeTree) {
4098
4149
  const changeSet = this[changeSetName];
4099
4150
  const node = changeTree[changeSetName].queueRootNode;
4100
4151
  if (node && node.changeTree === changeTree) {
4152
+ const removedPosition = node.position;
4101
4153
  // Remove the node from the linked list
4102
4154
  if (node.prev) {
4103
4155
  node.prev.next = node.next;
@@ -4111,7 +4163,8 @@ class Root {
4111
4163
  else {
4112
4164
  changeSet.tail = node.prev;
4113
4165
  }
4114
- changeSet.length--;
4166
+ // Update positions for nodes that came after the removed node
4167
+ this.updatePositionsAfterRemoval(changeSet, removedPosition);
4115
4168
  // Clear ChangeTree reference
4116
4169
  changeTree[changeSetName].queueRootNode = undefined;
4117
4170
  return true;
@@ -4333,8 +4386,8 @@ class Encoder {
4333
4386
  }
4334
4387
  }
4335
4388
  get hasChanges() {
4336
- return (this.root.changes.length > 0 ||
4337
- this.root.filteredChanges.length > 0);
4389
+ return (this.root.changes.next !== undefined ||
4390
+ this.root.filteredChanges.next !== undefined);
4338
4391
  }
4339
4392
  }
4340
4393
 
@@ -4517,8 +4570,7 @@ class Decoder {
4517
4570
  if (!nextRef) {
4518
4571
  // throw new Error(`"refId" not found: ${nextRefId}`);
4519
4572
  console.error(`"refId" not found: ${nextRefId}`, { previousRef: ref, previousRefId: this.currentRefId });
4520
- console.warn("Please report this to the developers. All refIds =>");
4521
- console.warn(Schema.debugRefIdsFromDecoder(this));
4573
+ console.warn("Please report this issue to the developers.");
4522
4574
  this.skipCurrentStructure(bytes, it, totalBytes);
4523
4575
  }
4524
4576
  else {