@colyseus/schema 3.0.0-alpha.25 → 3.0.0-alpha.27

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.
@@ -1 +1 @@
1
- {"version":3,"file":"StateView.js","sourceRoot":"","sources":["../../src/encoder/StateView.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeTree, Ref } from \"./ChangeTree\";\nimport { $changes } from \"../types/symbols\";\nimport { DEFAULT_VIEW_TAG } from \"../annotations\";\nimport { OPERATION } from \"../encoding/spec\";\nimport { Metadata } from \"../Metadata\";\nimport type { Schema } from \"../Schema\";\n\nexport function createView(root: Schema) {\n}\n\nexport class StateView {\n /**\n * List of ChangeTree's that are visible to this view\n */\n items: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();\n\n /**\n * List of ChangeTree's that are invisible to this view\n */\n invisible: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();\n\n tags?: WeakMap<ChangeTree, Set<number>>; // TODO: use bit manipulation instead of Set<number> ()\n\n /**\n * Manual \"ADD\" operations for changes per ChangeTree, specific to this view.\n * (This is used to force encoding a property, even if it was not changed)\n */\n changes = new Map<ChangeTree, Map<number, OPERATION>>();\n\n // TODO: allow to set multiple tags at once\n add(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {\n if (!obj[$changes]) {\n console.warn(\"StateView#add(), invalid object:\", obj);\n return this;\n }\n\n // FIXME: ArraySchema/MapSchema does not have metadata\n const metadata: Metadata = obj.constructor[Symbol.metadata];\n\n let changeTree: ChangeTree = obj[$changes];\n this.items.add(changeTree);\n\n // Add children of this ChangeTree to this view\n changeTree.forEachChild((change, index) => {\n // Do not ADD children that don't have the same tag\n if (metadata && metadata[metadata[index]].tag !== tag) {\n return;\n }\n this.add(change.ref, tag);\n });\n\n // add parent ChangeTree's, if they are invisible to this view\n // TODO: REFACTOR addParent()\n this.addParent(changeTree, tag);\n\n //\n // TODO: when adding an item of a MapSchema, the changes may not\n // be set (only the parent's changes are set)\n //\n let changes = this.changes.get(changeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(changeTree, changes)\n }\n\n // set tag\n if (tag !== DEFAULT_VIEW_TAG) {\n if (!this.tags) {\n this.tags = new WeakMap<ChangeTree, Set<number>>();\n }\n let tags: Set<number>;\n if (!this.tags.has(changeTree)) {\n tags = new Set<number>();\n this.tags.set(changeTree, tags);\n } else {\n tags = this.tags.get(changeTree);\n }\n tags.add(tag);\n\n // Ref: add tagged properties\n metadata?.[-3]?.[tag]?.forEach((index) => {\n if (changeTree.getChange(index) !== OPERATION.DELETE) {\n changes.set(index, OPERATION.ADD)\n }\n });\n\n } else {\n\n // console.log(\"DEFAULT TAG\", changeTree.allChanges);\n\n // // add default tag properties\n // metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {\n // if (changeTree.getChange(index) !== OPERATION.DELETE) {\n // changes.set(index, OPERATION.ADD);\n // }\n // });\n\n const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)\n ? changeTree.allFilteredChanges\n : changeTree.allChanges;\n const it = allChangesSet.keys();\n const isInvisible = this.invisible.has(changeTree);\n\n for (const index of it) {\n if (\n (isInvisible || metadata?.[metadata?.[index]].tag === tag) &&\n changeTree.getChange(index) !== OPERATION.DELETE\n ) {\n changes.set(index, OPERATION.ADD);\n }\n }\n }\n\n // TODO: avoid unnecessary iteration here\n while (\n changeTree.parent &&\n (changeTree = changeTree.parent[$changes]) &&\n (changeTree.isFiltered || changeTree.isPartiallyFiltered)\n ) {\n this.items.add(changeTree);\n }\n\n return this;\n }\n\n protected addParent(changeTree: ChangeTree, tag: number) {\n const parentRef = changeTree.parent;\n if (!parentRef) { return; }\n\n const parentChangeTree = parentRef[$changes];\n const parentIndex = changeTree.parentIndex;\n\n if (!this.invisible.has(parentChangeTree)) {\n // parent is already available, no need to add it!\n return;\n }\n\n this.addParent(parentChangeTree, tag);\n\n // add parent's tag properties\n if (parentChangeTree.getChange(parentIndex) !== OPERATION.DELETE) {\n\n let parentChanges = this.changes.get(parentChangeTree);\n if (parentChanges === undefined) {\n parentChanges = new Map<number, OPERATION>();\n this.changes.set(parentChangeTree, parentChanges);\n }\n\n // console.log(\"add parent change\", {\n // parentIndex,\n // parentChanges,\n // parentChange: (\n // parentChangeTree.getChange(parentIndex) &&\n // OPERATION[parentChangeTree.getChange(parentIndex)]\n // ),\n // })\n\n if (!this.tags) { this.tags = new WeakMap<ChangeTree, Set<number>>(); }\n let tags: Set<number>;\n if (!this.tags.has(parentChangeTree)) {\n tags = new Set<number>();\n this.tags.set(parentChangeTree, tags);\n } else {\n tags = this.tags.get(parentChangeTree);\n }\n tags.add(tag);\n\n parentChanges.set(parentIndex, OPERATION.ADD);\n }\n\n }\n\n remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {\n const changeTree = obj[$changes];\n if (!changeTree) {\n console.warn(\"StateView#remove(), invalid object:\", obj);\n return this;\n }\n\n this.items.delete(changeTree);\n\n const ref = changeTree.ref;\n const metadata: Metadata = ref.constructor[Symbol.metadata];\n\n let changes = this.changes.get(changeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(changeTree, changes)\n }\n\n if (tag === DEFAULT_VIEW_TAG) {\n // parent is collection (Map/Array)\n const parent = changeTree.parent;\n if (!Metadata.isValidInstance(parent)) {\n const parentChangeTree = parent[$changes];\n let changes = this.changes.get(parentChangeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(parentChangeTree, changes)\n }\n // DELETE / DELETE BY REF ID\n changes.set(changeTree.parentIndex, OPERATION.DELETE);\n\n } else {\n // delete all \"tagged\" properties.\n metadata[-2].forEach((index) =>\n changes.set(index, OPERATION.DELETE));\n }\n\n\n } else {\n // delete only tagged properties\n metadata[-3][tag].forEach((index) =>\n changes.set(index, OPERATION.DELETE));\n }\n\n // remove tag\n if (this.tags && this.tags.has(changeTree)) {\n const tags = this.tags.get(changeTree);\n if (tag === undefined) {\n // delete all tags\n this.tags.delete(changeTree);\n } else {\n // delete specific tag\n tags.delete(tag);\n\n // if tag set is empty, delete it entirely\n if (tags.size === 0) {\n this.tags.delete(changeTree);\n }\n }\n }\n\n return this;\n }\n}"]}
1
+ {"version":3,"file":"StateView.js","sourceRoot":"","sources":["../../src/encoder/StateView.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeTree, Ref } from \"./ChangeTree\";\nimport { $changes } from \"../types/symbols\";\nimport { DEFAULT_VIEW_TAG } from \"../annotations\";\nimport { OPERATION } from \"../encoding/spec\";\nimport { Metadata } from \"../Metadata\";\nimport type { Schema } from \"../Schema\";\n\nexport function createView(root: Schema) {\n}\n\nexport class StateView {\n /**\n * List of ChangeTree's that are visible to this view\n */\n items: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();\n\n /**\n * List of ChangeTree's that are invisible to this view\n */\n invisible: WeakSet<ChangeTree> = new WeakSet<ChangeTree>();\n\n tags?: WeakMap<ChangeTree, Set<number>>; // TODO: use bit manipulation instead of Set<number> ()\n\n /**\n * Manual \"ADD\" operations for changes per ChangeTree, specific to this view.\n * (This is used to force encoding a property, even if it was not changed)\n */\n changes = new Map<ChangeTree, Map<number, OPERATION>>();\n\n // TODO: allow to set multiple tags at once\n add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {\n if (!obj[$changes]) {\n console.warn(\"StateView#add(), invalid object:\", obj);\n return this;\n }\n\n // FIXME: ArraySchema/MapSchema does not have metadata\n const metadata: Metadata = obj.constructor[Symbol.metadata];\n const changeTree: ChangeTree = obj[$changes];\n this.items.add(changeTree);\n\n // add parent ChangeTree's\n // - if it was invisible to this view\n // - if it were previously filtered out\n if (checkIncludeParent && changeTree.parent) {\n this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);\n }\n\n //\n // TODO: when adding an item of a MapSchema, the changes may not\n // be set (only the parent's changes are set)\n //\n let changes = this.changes.get(changeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(changeTree, changes)\n }\n\n // set tag\n if (tag !== DEFAULT_VIEW_TAG) {\n if (!this.tags) {\n this.tags = new WeakMap<ChangeTree, Set<number>>();\n }\n let tags: Set<number>;\n if (!this.tags.has(changeTree)) {\n tags = new Set<number>();\n this.tags.set(changeTree, tags);\n } else {\n tags = this.tags.get(changeTree);\n }\n tags.add(tag);\n\n // Ref: add tagged properties\n metadata?.[-3]?.[tag]?.forEach((index) => {\n if (changeTree.getChange(index) !== OPERATION.DELETE) {\n changes.set(index, OPERATION.ADD)\n }\n });\n\n } else {\n const isInvisible = this.invisible.has(changeTree);\n const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)\n ? changeTree.allFilteredChanges\n : changeTree.allChanges;\n\n changeSet.forEach((op, index) => {\n const tagAtIndex = metadata?.[metadata?.[index]].tag;\n if (\n (\n isInvisible || // if \"invisible\", include all\n tagAtIndex === undefined || // \"all change\" with no tag\n tagAtIndex === tag // tagged property\n ) &&\n op !== OPERATION.DELETE\n ) {\n changes.set(index, op);\n }\n });\n }\n\n // Add children of this ChangeTree to this view\n changeTree.forEachChild((change, index) => {\n // Do not ADD children that don't have the same tag\n if (metadata && metadata[metadata[index]].tag !== tag) {\n return;\n }\n this.add(change.ref, tag, false);\n });\n\n return this;\n }\n\n protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number) {\n // view must have all \"changeTree\" parent tree\n this.items.add(changeTree);\n\n // add parent's parent\n const parentChangeTree = changeTree.parent?.[$changes];\n if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {\n this.addParent(parentChangeTree, changeTree.parentIndex, tag);\n }\n\n // parent is already available, no need to add it!\n if (!this.invisible.has(changeTree)) {\n return;\n }\n\n // add parent's tag properties\n if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {\n\n let changes = this.changes.get(changeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(changeTree, changes);\n }\n\n if (!this.tags) {\n this.tags = new WeakMap<ChangeTree, Set<number>>();\n }\n\n let tags: Set<number>;\n if (!this.tags.has(changeTree)) {\n tags = new Set<number>();\n this.tags.set(changeTree, tags);\n } else {\n tags = this.tags.get(changeTree);\n }\n tags.add(tag);\n\n changes.set(parentIndex, OPERATION.ADD);\n }\n }\n\n remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {\n const changeTree = obj[$changes];\n if (!changeTree) {\n console.warn(\"StateView#remove(), invalid object:\", obj);\n return this;\n }\n\n this.items.delete(changeTree);\n\n const ref = changeTree.ref;\n const metadata: Metadata = ref.constructor[Symbol.metadata];\n\n let changes = this.changes.get(changeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(changeTree, changes)\n }\n\n if (tag === DEFAULT_VIEW_TAG) {\n // parent is collection (Map/Array)\n const parent = changeTree.parent;\n if (!Metadata.isValidInstance(parent)) {\n const parentChangeTree = parent[$changes];\n let changes = this.changes.get(parentChangeTree);\n if (changes === undefined) {\n changes = new Map<number, OPERATION>();\n this.changes.set(parentChangeTree, changes)\n }\n // DELETE / DELETE BY REF ID\n changes.set(changeTree.parentIndex, OPERATION.DELETE);\n\n } else {\n // delete all \"tagged\" properties.\n metadata[-2].forEach((index) =>\n changes.set(index, OPERATION.DELETE));\n }\n\n\n } else {\n // delete only tagged properties\n metadata[-3][tag].forEach((index) =>\n changes.set(index, OPERATION.DELETE));\n }\n\n // remove tag\n if (this.tags && this.tags.has(changeTree)) {\n const tags = this.tags.get(changeTree);\n if (tag === undefined) {\n // delete all tags\n this.tags.delete(changeTree);\n } else {\n // delete specific tag\n tags.delete(tag);\n\n // if tag set is empty, delete it entirely\n if (tags.size === 0) {\n this.tags.delete(changeTree);\n }\n }\n }\n\n return this;\n }\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "3.0.0-alpha.25",
3
+ "version": "3.0.0-alpha.27",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "bin": {
6
6
  "schema-codegen": "./bin/schema-codegen"
package/src/Reflection.ts CHANGED
@@ -25,9 +25,7 @@ export class Reflection extends Schema {
25
25
  @type([ ReflectionType ]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
26
26
 
27
27
  static encode(instance: Schema, context?: TypeContext, it: Iterator = { offset: 0 }) {
28
- if (!context) {
29
- context = new TypeContext(instance.constructor as typeof Schema);
30
- }
28
+ context ??= new TypeContext(instance.constructor as typeof Schema);
31
29
 
32
30
  const reflection = new Reflection();
33
31
  const encoder = new Encoder(reflection);
package/src/Schema.ts CHANGED
@@ -248,7 +248,7 @@ export abstract class Schema {
248
248
  return output;
249
249
  }
250
250
 
251
- static debugChangesDeep(ref: Ref) {
251
+ static debugChangesDeep(ref: Ref, changeSetName: "changes" | "allChanges" | "allFilteredChanges" | "filteredChanges" = "changes") {
252
252
  let output = "";
253
253
 
254
254
  const rootChangeTree = ref[$changes];
@@ -257,7 +257,7 @@ export abstract class Schema {
257
257
  let totalInstances = 0;
258
258
  let totalOperations = 0;
259
259
 
260
- for (const [changeTree, changes] of (rootChangeTree.root.changes.entries())) {
260
+ for (const [changeTree, changes] of (rootChangeTree.root[changeSetName].entries())) {
261
261
  let includeChangeTree = false;
262
262
  let parentChangeTrees: ChangeTree[] = [];
263
263
  let parentChangeTree = changeTree.parent?.[$changes];
@@ -103,14 +103,14 @@ export class TypeContext {
103
103
  return this.schemas.get(klass);
104
104
  }
105
105
 
106
- private discoverTypes(klass: typeof Schema) {
106
+ private discoverTypes(klass: typeof Schema, parentFieldViewTag?: number) {
107
107
  if (!this.add(klass)) {
108
108
  return;
109
109
  }
110
110
 
111
111
  // add classes inherited from this base class
112
112
  TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
113
- this.discoverTypes(child);
113
+ this.discoverTypes(child, parentFieldViewTag);
114
114
  });
115
115
 
116
116
  // skip if no fields are defined for this class.
@@ -127,7 +127,18 @@ export class TypeContext {
127
127
  }
128
128
 
129
129
  for (const field in metadata) {
130
+ //
131
+ // Modify the field's metadata to include the parent field's view tag
132
+ //
133
+ if (
134
+ parentFieldViewTag !== undefined &&
135
+ metadata[field].tag === undefined
136
+ ) {
137
+ metadata[field].tag = parentFieldViewTag;
138
+ }
139
+
130
140
  const fieldType = metadata[field].type;
141
+ const viewTag = metadata[field].tag;
131
142
 
132
143
  if (typeof(fieldType) === "string") {
133
144
  continue;
@@ -138,10 +149,10 @@ export class TypeContext {
138
149
  if (type === "string") {
139
150
  continue;
140
151
  }
141
- this.discoverTypes(type as typeof Schema);
152
+ this.discoverTypes(type as typeof Schema, viewTag);
142
153
 
143
154
  } else if (typeof(fieldType) === "function") {
144
- this.discoverTypes(fieldType);
155
+ this.discoverTypes(fieldType, viewTag);
145
156
 
146
157
  } else {
147
158
  const type = Object.values(fieldType)[0];
@@ -151,7 +162,7 @@ export class TypeContext {
151
162
  continue;
152
163
  }
153
164
 
154
- this.discoverTypes(type as typeof Schema);
165
+ this.discoverTypes(type as typeof Schema, viewTag);
155
166
  }
156
167
  }
157
168
  }
@@ -182,7 +182,10 @@ export const decodeSchemaOperation: DecodeOperation = function (
182
182
 
183
183
  // skip early if field is not defined
184
184
  const field = metadata[index];
185
- if (field === undefined) { return DEFINITION_MISMATCH; }
185
+ if (field === undefined) {
186
+ console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
187
+ return DEFINITION_MISMATCH;
188
+ }
186
189
 
187
190
  const { value, previousValue } = decodeValue(
188
191
  decoder,
@@ -21,7 +21,8 @@ export class Decoder<T extends Schema = any> {
21
21
  triggerChanges?: (allChanges: DataChange[]) => void;
22
22
 
23
23
  constructor(root: T, context?: TypeContext) {
24
- this.setRoot(root);
24
+ this.setState(root);
25
+
25
26
  this.context = context || new TypeContext(root.constructor as typeof Schema);
26
27
 
27
28
  // console.log(">>>>>>>>>>>>>>>> Decoder types");
@@ -30,7 +31,7 @@ export class Decoder<T extends Schema = any> {
30
31
  // });
31
32
  }
32
33
 
33
- protected setRoot(root: T) {
34
+ protected setState(root: T) {
34
35
  this.state = root;
35
36
  this.root = new ReferenceTracker();
36
37
  this.root.addRef(0, root);
@@ -6,7 +6,7 @@ import { DataChange } from "../DecodeOperation";
6
6
  import { OPERATION } from "../../encoding/spec";
7
7
  import { DefinitionType } from "../../annotations";
8
8
  import { Schema } from "../../Schema";
9
- import type { ArraySchema } from "../../types/custom/ArraySchema";
9
+ import type { CollectionSchema } from "../../types/custom/CollectionSchema";
10
10
 
11
11
  //
12
12
  // Discussion: https://github.com/colyseus/schema/issues/155
@@ -100,7 +100,8 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
100
100
  const $root = decoder.root;
101
101
  const callbacks = $root.callbacks;
102
102
 
103
- let isTriggeringOnAdd = false;
103
+ const onAddCalls: WeakMap<Function, boolean> = new WeakMap();
104
+ let currentOnAddCallback: Function | undefined;
104
105
 
105
106
  decoder.triggerChanges = function (allChanges: DataChange[]) {
106
107
  const uniqueRefIds = new Set<number>();
@@ -173,7 +174,6 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
173
174
  }
174
175
 
175
176
  // Handle DELETE_AND_ADD operations
176
- // FIXME: should we set "isTriggeringOnAdd" here?
177
177
  if ((change.op & OPERATION.ADD) === OPERATION.ADD) {
178
178
  const addCallbacks = $callbacks[OPERATION.ADD];
179
179
  for (let i = addCallbacks?.length - 1; i >= 0; i--) {
@@ -183,13 +183,10 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
183
183
 
184
184
  } else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
185
185
  // triger onAdd
186
-
187
- isTriggeringOnAdd = true;
188
186
  const addCallbacks = $callbacks[OPERATION.ADD];
189
187
  for (let i = addCallbacks?.length - 1; i >= 0; i--) {
190
188
  addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
191
189
  }
192
- isTriggeringOnAdd = false;
193
190
  }
194
191
 
195
192
  // trigger onChange
@@ -205,7 +202,10 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
205
202
  }
206
203
  };
207
204
 
208
- function getProxy(metadataOrType: Metadata | DefinitionType, context: CallContext) {
205
+ function getProxy(
206
+ metadataOrType: Metadata | DefinitionType,
207
+ context: CallContext
208
+ ) {
209
209
  let metadata: Metadata = context.instance?.constructor[Symbol.metadata] || metadataOrType;
210
210
  let isCollection = (
211
211
  (context.instance && typeof (context.instance['forEach']) === "function") ||
@@ -223,7 +223,7 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
223
223
  if (
224
224
  immediate &&
225
225
  context.instance[prop] !== undefined &&
226
- !isTriggeringOnAdd // FIXME: This is a workaround (https://github.com/colyseus/schema/issues/147)
226
+ !onAddCalls.has(callback) // Workaround for https://github.com/colyseus/schema/issues/147
227
227
  ) {
228
228
  callback(context.instance[prop], undefined);
229
229
  }
@@ -249,6 +249,7 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
249
249
  return () => detachCallback();
250
250
  }
251
251
  },
252
+
252
253
  onChange: function onChange(callback: () => void) {
253
254
  return $root.addCallback(
254
255
  $root.refIds.get(context.instance),
@@ -256,10 +257,12 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
256
257
  callback
257
258
  );
258
259
  },
260
+
261
+ //
262
+ // TODO: refactor `bindTo()` implementation.
263
+ // There is room for improvement.
264
+ //
259
265
  bindTo: function bindTo(targetObject: any, properties?: string[]) {
260
- //
261
- // TODO: refactor this implementation. There is room for improvement here.
262
- //
263
266
  if (!properties) {
264
267
  properties = Object.keys(metadata);
265
268
  }
@@ -295,7 +298,8 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
295
298
  }
296
299
  );
297
300
  return getProxy(metadata[prop].type, {
298
- instance,
301
+ // make sure refId is available, otherwise need to wait for the instance to be available.
302
+ instance: ($root.refIds.get(instance) && instance),
299
303
  parentInstance: context.instance,
300
304
  onInstanceAvailable,
301
305
  });
@@ -318,9 +322,15 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
318
322
  const onAdd = function (ref: Ref, callback: (value: any, key: any) => void, immediate: boolean) {
319
323
  // Trigger callback on existing items
320
324
  if (immediate) {
321
- (ref as ArraySchema).forEach((v, k) => callback(v, k));
325
+ (ref as CollectionSchema).forEach((v, k) => callback(v, k));
322
326
  }
323
- return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, callback);
327
+
328
+ return $root.addCallback($root.refIds.get(ref), OPERATION.ADD, (value, key) => {
329
+ onAddCalls.set(callback, true);
330
+ currentOnAddCallback = callback;
331
+ callback(value, key);
332
+ onAddCalls.delete(callback)
333
+ });
324
334
  };
325
335
 
326
336
  const onRemove = function (ref: Ref, callback: (value: any, key: any) => void) {
@@ -333,19 +343,19 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
333
343
  // https://github.com/colyseus/schema/issues/147
334
344
  // If parent instance has "onAdd" registered, avoid triggering immediate callback.
335
345
  //
336
- // FIXME: "isTriggeringOnAdd" is a workaround. We should find a better way to handle this.
337
- //
338
- if (context.onInstanceAvailable) {
346
+
347
+ if (context.instance) {
348
+ return onAdd(context.instance, callback, immediate && !onAddCalls.has(currentOnAddCallback));
349
+
350
+ } else if (context.onInstanceAvailable) {
339
351
  // collection instance not received yet
340
352
  let detachCallback = () => {};
341
353
 
342
354
  context.onInstanceAvailable((ref: Ref, existing: boolean) => {
343
- detachCallback = onAdd(ref, callback, immediate && existing && !isTriggeringOnAdd);
355
+ detachCallback = onAdd(ref, callback, immediate && existing && !onAddCalls.has(currentOnAddCallback));
344
356
  });
345
357
 
346
358
  return () => detachCallback();
347
- } else if (context.instance) {
348
- return onAdd(context.instance, callback, immediate && !isTriggeringOnAdd);
349
359
  }
350
360
  },
351
361
  onRemove: function(callback: (value, key) => void) {
@@ -204,7 +204,7 @@ export class ChangeTree<T extends Ref=any> {
204
204
  // MapSchema / ArraySchema, etc.
205
205
  (this.ref as MapSchema).forEach((value, key) => {
206
206
  if (Metadata.isValidInstance(value)) {
207
- callback(value[$changes], this.ref[$changes].indexes[key]);
207
+ callback(value[$changes], this.ref[$changes].indexes[key] ?? key);
208
208
  }
209
209
  });
210
210
  }
@@ -238,9 +238,11 @@ export class ChangeTree<T extends Ref=any> {
238
238
  //
239
239
 
240
240
  if (isFiltered) {
241
- this.allFilteredChanges.set(index, OPERATION.ADD);
242
241
  this.root?.filteredChanges.set(this, this.filteredChanges);
243
242
 
243
+ this.allFilteredChanges.set(index, OPERATION.ADD);
244
+ this.root?.allFilteredChanges.set(this, this.allFilteredChanges);
245
+
244
246
  } else {
245
247
  this.allChanges.set(index, OPERATION.ADD);
246
248
  this.root?.changes.set(this, this.changes);
@@ -20,14 +20,16 @@ export class Encoder<T extends Schema = any> {
20
20
 
21
21
  root: Root;
22
22
 
23
- constructor(root: T) {
23
+ constructor(state: T) {
24
+ this.root = new Root();
25
+
24
26
  //
25
27
  // TODO: cache and restore "Context" based on root schema
26
28
  // (to avoid creating a new context for every new room)
27
29
  //
28
- this.context = new TypeContext(root.constructor as typeof Schema);
30
+ this.context = new TypeContext(state.constructor as typeof Schema);
29
31
 
30
- this.setRoot(root);
32
+ this.setState(state);
31
33
 
32
34
  // console.log(">>>>>>>>>>>>>>>> Encoder types");
33
35
  // this.context.schemas.forEach((id, schema) => {
@@ -35,10 +37,9 @@ export class Encoder<T extends Schema = any> {
35
37
  // });
36
38
  }
37
39
 
38
- protected setRoot(state: T) {
39
- this.root = new Root();
40
+ protected setState(state: T) {
40
41
  this.state = state;
41
- state[$changes].setRoot(this.root);
42
+ this.state[$changes].setRoot(this.root);
42
43
  }
43
44
 
44
45
  encode(
@@ -47,9 +48,8 @@ export class Encoder<T extends Schema = any> {
47
48
  buffer = this.sharedBuffer,
48
49
  changeTrees = this.root.changes,
49
50
  isEncodeAll = this.root.allChanges === changeTrees,
51
+ initialOffset = it.offset // cache current offset in case we need to resize the buffer
50
52
  ): Buffer {
51
- const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
52
-
53
53
  const hasView = (view !== undefined);
54
54
  const rootChangeTree = this.state[$changes];
55
55
 
@@ -96,8 +96,6 @@ export class Encoder<T extends Schema = any> {
96
96
  // TODO: avoid checking if no view tags were defined
97
97
  //
98
98
  if (filter && !filter(ref, fieldIndex, view)) {
99
- // console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
100
-
101
99
  // console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
102
100
  // view?.invisible.add(changeTree);
103
101
  continue;
@@ -194,9 +192,6 @@ export class Encoder<T extends Schema = any> {
194
192
  encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
195
193
  const viewOffset = it.offset;
196
194
 
197
- // try to encode "filtered" changes
198
- this.encode(it, view, bytes, this.root.filteredChanges);
199
-
200
195
  // encode visibility changes (add/remove for this view)
201
196
  const viewChangesIterator = view.changes.entries();
202
197
  for (const [changeTree, changes] of viewChangesIterator) {
@@ -230,6 +225,9 @@ export class Encoder<T extends Schema = any> {
230
225
  // clear "view" changes after encoding
231
226
  view.changes.clear();
232
227
 
228
+ // try to encode "filtered" changes
229
+ this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
230
+
233
231
  return Buffer.concat([
234
232
  bytes.subarray(0, sharedOffset),
235
233
  bytes.subarray(viewOffset, it.offset)
@@ -28,7 +28,7 @@ export class StateView {
28
28
  changes = new Map<ChangeTree, Map<number, OPERATION>>();
29
29
 
30
30
  // TODO: allow to set multiple tags at once
31
- add(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {
31
+ add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
32
32
  if (!obj[$changes]) {
33
33
  console.warn("StateView#add(), invalid object:", obj);
34
34
  return this;
@@ -36,22 +36,15 @@ export class StateView {
36
36
 
37
37
  // FIXME: ArraySchema/MapSchema does not have metadata
38
38
  const metadata: Metadata = obj.constructor[Symbol.metadata];
39
-
40
- let changeTree: ChangeTree = obj[$changes];
39
+ const changeTree: ChangeTree = obj[$changes];
41
40
  this.items.add(changeTree);
42
41
 
43
- // Add children of this ChangeTree to this view
44
- changeTree.forEachChild((change, index) => {
45
- // Do not ADD children that don't have the same tag
46
- if (metadata && metadata[metadata[index]].tag !== tag) {
47
- return;
48
- }
49
- this.add(change.ref, tag);
50
- });
51
-
52
- // add parent ChangeTree's, if they are invisible to this view
53
- // TODO: REFACTOR addParent()
54
- this.addParent(changeTree, tag);
42
+ // add parent ChangeTree's
43
+ // - if it was invisible to this view
44
+ // - if it were previously filtered out
45
+ if (checkIncludeParent && changeTree.parent) {
46
+ this.addParent(changeTree.parent[$changes], changeTree.parentIndex, tag);
47
+ }
55
48
 
56
49
  //
57
50
  // TODO: when adding an item of a MapSchema, the changes may not
@@ -85,89 +78,77 @@ export class StateView {
85
78
  });
86
79
 
87
80
  } else {
88
-
89
- // console.log("DEFAULT TAG", changeTree.allChanges);
90
-
91
- // // add default tag properties
92
- // metadata?.[-3]?.[DEFAULT_VIEW_TAG]?.forEach((index) => {
93
- // if (changeTree.getChange(index) !== OPERATION.DELETE) {
94
- // changes.set(index, OPERATION.ADD);
95
- // }
96
- // });
97
-
98
- const allChangesSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
81
+ const isInvisible = this.invisible.has(changeTree);
82
+ const changeSet = (changeTree.isFiltered || changeTree.isPartiallyFiltered)
99
83
  ? changeTree.allFilteredChanges
100
84
  : changeTree.allChanges;
101
- const it = allChangesSet.keys();
102
- const isInvisible = this.invisible.has(changeTree);
103
85
 
104
- for (const index of it) {
86
+ changeSet.forEach((op, index) => {
87
+ const tagAtIndex = metadata?.[metadata?.[index]].tag;
105
88
  if (
106
- (isInvisible || metadata?.[metadata?.[index]].tag === tag) &&
107
- changeTree.getChange(index) !== OPERATION.DELETE
89
+ (
90
+ isInvisible || // if "invisible", include all
91
+ tagAtIndex === undefined || // "all change" with no tag
92
+ tagAtIndex === tag // tagged property
93
+ ) &&
94
+ op !== OPERATION.DELETE
108
95
  ) {
109
- changes.set(index, OPERATION.ADD);
96
+ changes.set(index, op);
110
97
  }
111
- }
98
+ });
112
99
  }
113
100
 
114
- // TODO: avoid unnecessary iteration here
115
- while (
116
- changeTree.parent &&
117
- (changeTree = changeTree.parent[$changes]) &&
118
- (changeTree.isFiltered || changeTree.isPartiallyFiltered)
119
- ) {
120
- this.items.add(changeTree);
121
- }
101
+ // Add children of this ChangeTree to this view
102
+ changeTree.forEachChild((change, index) => {
103
+ // Do not ADD children that don't have the same tag
104
+ if (metadata && metadata[metadata[index]].tag !== tag) {
105
+ return;
106
+ }
107
+ this.add(change.ref, tag, false);
108
+ });
122
109
 
123
110
  return this;
124
111
  }
125
112
 
126
- protected addParent(changeTree: ChangeTree, tag: number) {
127
- const parentRef = changeTree.parent;
128
- if (!parentRef) { return; }
113
+ protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number) {
114
+ // view must have all "changeTree" parent tree
115
+ this.items.add(changeTree);
129
116
 
130
- const parentChangeTree = parentRef[$changes];
131
- const parentIndex = changeTree.parentIndex;
117
+ // add parent's parent
118
+ const parentChangeTree = changeTree.parent?.[$changes];
119
+ if (parentChangeTree && (parentChangeTree.isFiltered || parentChangeTree.isPartiallyFiltered)) {
120
+ this.addParent(parentChangeTree, changeTree.parentIndex, tag);
121
+ }
132
122
 
133
- if (!this.invisible.has(parentChangeTree)) {
134
- // parent is already available, no need to add it!
123
+ // parent is already available, no need to add it!
124
+ if (!this.invisible.has(changeTree)) {
135
125
  return;
136
126
  }
137
127
 
138
- this.addParent(parentChangeTree, tag);
139
-
140
128
  // add parent's tag properties
141
- if (parentChangeTree.getChange(parentIndex) !== OPERATION.DELETE) {
129
+ if (changeTree.getChange(parentIndex) !== OPERATION.DELETE) {
142
130
 
143
- let parentChanges = this.changes.get(parentChangeTree);
144
- if (parentChanges === undefined) {
145
- parentChanges = new Map<number, OPERATION>();
146
- this.changes.set(parentChangeTree, parentChanges);
131
+ let changes = this.changes.get(changeTree);
132
+ if (changes === undefined) {
133
+ changes = new Map<number, OPERATION>();
134
+ this.changes.set(changeTree, changes);
147
135
  }
148
136
 
149
- // console.log("add parent change", {
150
- // parentIndex,
151
- // parentChanges,
152
- // parentChange: (
153
- // parentChangeTree.getChange(parentIndex) &&
154
- // OPERATION[parentChangeTree.getChange(parentIndex)]
155
- // ),
156
- // })
137
+ if (!this.tags) {
138
+ this.tags = new WeakMap<ChangeTree, Set<number>>();
139
+ }
157
140
 
158
- if (!this.tags) { this.tags = new WeakMap<ChangeTree, Set<number>>(); }
159
141
  let tags: Set<number>;
160
- if (!this.tags.has(parentChangeTree)) {
142
+ if (!this.tags.has(changeTree)) {
161
143
  tags = new Set<number>();
162
- this.tags.set(parentChangeTree, tags);
144
+ this.tags.set(changeTree, tags);
163
145
  } else {
164
- tags = this.tags.get(parentChangeTree);
146
+ tags = this.tags.get(changeTree);
165
147
  }
166
148
  tags.add(tag);
167
149
 
168
- parentChanges.set(parentIndex, OPERATION.ADD);
150
+ changes.set(parentIndex, OPERATION.ADD);
169
151
  }
170
-
171
152
  }
172
153
 
173
154
  remove(obj: Ref, tag: number = DEFAULT_VIEW_TAG) {