@colyseus/schema 3.0.0-alpha.14 → 3.0.0-alpha.16

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":"EncodeOperation.js","sourceRoot":"","sources":["../../src/encoder/EncodeOperation.ts"],"names":[],"mappings":"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","sourcesContent":["import { OPERATION } from \"../encoding/spec\";\nimport { $changes } from \"../types/symbols\";\nimport { getType } from \"../types/registry\";\n\nimport * as encode from \"../encoding/encode\";\nimport { EncodeSchemaError, assertInstanceType, assertType } from \"../encoding/assert\";\n\nimport type { ChangeTree, Ref } from \"./ChangeTree\";\nimport type { Encoder } from \"./Encoder\";\nimport type { Schema } from \"../Schema\";\nimport type { PrimitiveType } from \"../annotations\";\n\nimport type { Iterator } from \"../encoding/decode\";\nimport type { ArraySchema } from \"../types/custom/ArraySchema\";\n\nexport type EncodeOperation<T extends Ref = any> = (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<T>,\n index: number,\n operation: OPERATION,\n it: Iterator,\n isEncodeAll: boolean,\n hasView: boolean,\n) => void;\n\nexport function encodePrimitiveType(\n type: PrimitiveType,\n bytes: Buffer,\n value: any,\n klass: Schema,\n field: string | number,\n it: Iterator,\n) {\n assertType(value, type as string, klass, field);\n\n const encodeFunc = encode[type as string];\n\n if (encodeFunc) {\n encodeFunc(bytes, value, it);\n // encodeFunc(bytes, value);\n\n } else {\n throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);\n }\n};\n\nexport function encodeValue(\n encoder: Encoder,\n bytes: Buffer,\n ref: Ref,\n type: any,\n value: any,\n field: string | number,\n operation: OPERATION,\n it: Iterator,\n) {\n if (type[Symbol.metadata] !== undefined) {\n // TODO: move this to the `@type()` annotation\n assertInstanceType(value, type as typeof Schema, ref as Schema, field);\n\n //\n // Encode refId for this instance.\n // The actual instance is going to be encoded on next `changeTree` iteration.\n //\n encode.number(bytes, value[$changes].refId, it);\n\n // Try to encode inherited TYPE_ID if it's an ADD operation.\n if ((operation & OPERATION.ADD) === OPERATION.ADD) {\n encoder.tryEncodeTypeId(bytes, type as typeof Schema, value.constructor as typeof Schema, it);\n }\n\n } else if (typeof (type) === \"string\") {\n //\n // Primitive values\n //\n encodePrimitiveType(type as PrimitiveType, bytes, value, ref as Schema, field, it);\n\n } else {\n //\n // Custom type (MapSchema, ArraySchema, etc)\n //\n const definition = getType(Object.keys(type)[0]);\n\n //\n // ensure a ArraySchema has been provided\n //\n assertInstanceType(ref[field], definition.constructor, ref as Schema, field);\n\n //\n // Encode refId for this instance.\n // The actual instance is going to be encoded on next `changeTree` iteration.\n //\n encode.number(bytes, value[$changes].refId, it);\n }\n}\n\n/**\n * Used for Schema instances.\n * @private\n */\nexport const encodeSchemaOperation: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<Schema>,\n index: number,\n operation: OPERATION,\n it: Iterator,\n) {\n const ref = changeTree.ref;\n const metadata = ref['constructor'][Symbol.metadata];\n\n const field = metadata[index];\n const type = metadata[field].type;\n const value = ref[field];\n\n // \"compress\" field index + operation\n bytes[it.offset++] = (index | operation) & 255;\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}\n\n/**\n * Used for collections (MapSchema, CollectionSchema, SetSchema)\n * @private\n */\nexport const encodeKeyValueOperation: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree,\n field: number,\n operation: OPERATION,\n it: Iterator,\n) {\n const ref = changeTree.ref;\n\n // encode operation\n bytes[it.offset++] = operation & 255;\n\n // custom operations\n if (operation === OPERATION.CLEAR) {\n return;\n }\n\n // encode index\n encode.number(bytes, field, it);\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n //\n // encode \"alias\" for dynamic fields (maps)\n //\n if ((operation & OPERATION.ADD) == OPERATION.ADD) { // ADD or DELETE_AND_ADD\n if (typeof(ref['set']) === \"function\") {\n //\n // MapSchema dynamic key\n //\n const dynamicIndex = changeTree.ref['$indexes'].get(field);\n encode.string(bytes, dynamicIndex, it);\n }\n }\n\n const type = changeTree.getType(field);\n const value = changeTree.getValue(field);\n\n // try { throw new Error(); } catch (e) {\n // // only print if not coming from Reflection.ts\n // if (!e.stack.includes(\"src/Reflection.ts\")) {\n // console.log(\"encodeKeyValueOperation -> \", {\n // ref: changeTree.ref.constructor.name,\n // field,\n // operation: OPERATION[operation],\n // value: value?.toJSON(),\n // items: ref.toJSON(),\n // });\n // }\n // }\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}\n\n/**\n * Used for collections (MapSchema, ArraySchema, etc.)\n * @private\n */\nexport const encodeArray: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<ArraySchema>,\n field: number,\n operation: OPERATION,\n it: Iterator,\n isEncodeAll: boolean,\n hasView: boolean,\n) {\n const ref = changeTree.ref;\n\n if (\n hasView &&\n operation === OPERATION.DELETE &&\n typeof (changeTree.getType(field)) !== \"string\"\n ) {\n // encode delete by refId (array of schemas)\n bytes[it.offset++] = OPERATION.DELETE_BY_REFID;\n const value = ref['tmpItems'][field];\n const refId = value[$changes].refId;\n encode.number(bytes, refId, it);\n return;\n }\n\n // encode operation\n bytes[it.offset++] = operation & 255;\n\n // custom operations\n if (operation === OPERATION.CLEAR) {\n return;\n }\n\n // encode index\n encode.number(bytes, field, it);\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n const type = changeTree.getType(field);\n const value = changeTree.getValue(field, isEncodeAll);\n\n // console.log(\"encodeArray -> \", {\n // ref: changeTree.ref.constructor.name,\n // field,\n // operation: OPERATION[operation],\n // value: value?.toJSON(),\n // items: ref.toJSON(),\n // });\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}"]}
1
+ {"version":3,"file":"EncodeOperation.js","sourceRoot":"","sources":["../../src/encoder/EncodeOperation.ts"],"names":[],"mappings":"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","sourcesContent":["import { OPERATION } from \"../encoding/spec\";\nimport { $changes } from \"../types/symbols\";\nimport { getType } from \"../types/registry\";\n\nimport * as encode from \"../encoding/encode\";\nimport { EncodeSchemaError, assertInstanceType, assertType } from \"../encoding/assert\";\n\nimport type { ChangeTree, Ref } from \"./ChangeTree\";\nimport type { Encoder } from \"./Encoder\";\nimport type { Schema } from \"../Schema\";\nimport type { PrimitiveType } from \"../annotations\";\n\nimport type { Iterator } from \"../encoding/decode\";\nimport type { ArraySchema } from \"../types/custom/ArraySchema\";\n\nexport type EncodeOperation<T extends Ref = any> = (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<T>,\n index: number,\n operation: OPERATION,\n it: Iterator,\n isEncodeAll: boolean,\n hasView: boolean,\n) => void;\n\nexport function encodePrimitiveType(\n type: PrimitiveType,\n bytes: Buffer,\n value: any,\n klass: Schema,\n field: string | number,\n it: Iterator,\n) {\n assertType(value, type as string, klass, field);\n\n const encodeFunc = encode[type as string];\n\n if (encodeFunc) {\n encodeFunc(bytes, value, it);\n // encodeFunc(bytes, value);\n\n } else {\n throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);\n }\n};\n\nexport function encodeValue(\n encoder: Encoder,\n bytes: Buffer,\n ref: Ref,\n type: any,\n value: any,\n field: string | number,\n operation: OPERATION,\n it: Iterator,\n) {\n if (type[Symbol.metadata] !== undefined) {\n // TODO: move this to the `@type()` annotation\n assertInstanceType(value, type as typeof Schema, ref as Schema, field);\n\n //\n // Encode refId for this instance.\n // The actual instance is going to be encoded on next `changeTree` iteration.\n //\n encode.number(bytes, value[$changes].refId, it);\n\n // Try to encode inherited TYPE_ID if it's an ADD operation.\n if ((operation & OPERATION.ADD) === OPERATION.ADD) {\n encoder.tryEncodeTypeId(bytes, type as typeof Schema, value.constructor as typeof Schema, it);\n }\n\n } else if (typeof (type) === \"string\") {\n //\n // Primitive values\n //\n encodePrimitiveType(type as PrimitiveType, bytes, value, ref as Schema, field, it);\n\n } else {\n //\n // Custom type (MapSchema, ArraySchema, etc)\n //\n const definition = getType(Object.keys(type)[0]);\n\n //\n // ensure a ArraySchema has been provided\n //\n assertInstanceType(ref[field], definition.constructor, ref as Schema, field);\n\n //\n // Encode refId for this instance.\n // The actual instance is going to be encoded on next `changeTree` iteration.\n //\n encode.number(bytes, value[$changes].refId, it);\n }\n}\n\n/**\n * Used for Schema instances.\n * @private\n */\nexport const encodeSchemaOperation: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<Schema>,\n index: number,\n operation: OPERATION,\n it: Iterator,\n) {\n const ref = changeTree.ref;\n const metadata = ref['constructor'][Symbol.metadata];\n\n const field = metadata[index];\n const type = metadata[field].type;\n const value = ref[field];\n\n // \"compress\" field index + operation\n bytes[it.offset++] = (index | operation) & 255;\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}\n\n/**\n * Used for collections (MapSchema, CollectionSchema, SetSchema)\n * @private\n */\nexport const encodeKeyValueOperation: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree,\n field: number,\n operation: OPERATION,\n it: Iterator,\n) {\n const ref = changeTree.ref;\n\n // encode operation\n bytes[it.offset++] = operation & 255;\n\n // custom operations\n if (operation === OPERATION.CLEAR) {\n return;\n }\n\n // encode index\n encode.number(bytes, field, it);\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n //\n // encode \"alias\" for dynamic fields (maps)\n //\n if ((operation & OPERATION.ADD) == OPERATION.ADD) { // ADD or DELETE_AND_ADD\n if (typeof(ref['set']) === \"function\") {\n //\n // MapSchema dynamic key\n //\n const dynamicIndex = changeTree.ref['$indexes'].get(field);\n encode.string(bytes, dynamicIndex, it);\n }\n }\n\n const type = changeTree.getType(field);\n const value = changeTree.getValue(field);\n\n // try { throw new Error(); } catch (e) {\n // // only print if not coming from Reflection.ts\n // if (!e.stack.includes(\"src/Reflection.ts\")) {\n // console.log(\"encodeKeyValueOperation -> \", {\n // ref: changeTree.ref.constructor.name,\n // field,\n // operation: OPERATION[operation],\n // value: value?.toJSON(),\n // items: ref.toJSON(),\n // });\n // }\n // }\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}\n\n/**\n * Used for collections (MapSchema, ArraySchema, etc.)\n * @private\n */\nexport const encodeArray: EncodeOperation = function (\n encoder: Encoder,\n bytes: Buffer,\n changeTree: ChangeTree<ArraySchema>,\n field: number,\n operation: OPERATION,\n it: Iterator,\n isEncodeAll: boolean,\n hasView: boolean,\n) {\n const ref = changeTree.ref;\n const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== \"string\");\n\n let refOrIndex: number;\n\n if (useOperationByRefId) {\n refOrIndex = ref['tmpItems'][field][$changes].refId;\n\n if (operation === OPERATION.DELETE) {\n operation = OPERATION.DELETE_BY_REFID;\n\n } else if (operation === OPERATION.ADD) {\n operation = OPERATION.ADD_BY_REFID;\n }\n\n } else {\n refOrIndex = field;\n }\n\n // encode operation\n bytes[it.offset++] = operation & 255;\n\n // custom operations\n if (operation === OPERATION.CLEAR) {\n return;\n }\n\n // encode index\n encode.number(bytes, refOrIndex, it);\n\n // Do not encode value for DELETE operations\n if (operation === OPERATION.DELETE) {\n return;\n }\n\n const type = changeTree.getType(field);\n const value = changeTree.getValue(field, isEncodeAll);\n\n // console.log(\"encodeArray -> \", {\n // ref: changeTree.ref.constructor.name,\n // field,\n // operation: OPERATION[operation],\n // value: value?.toJSON(),\n // items: ref.toJSON(),\n // });\n\n // TODO: inline this function call small performance gain\n encodeValue(encoder, bytes, ref, type, value, field, operation, it);\n}"]}
@@ -21,5 +21,6 @@ export declare enum OPERATION {
21
21
  UNSHIFT = 12,
22
22
  REVERSE = 15,
23
23
  MOVE = 32,
24
- DELETE_BY_REFID = 33
24
+ DELETE_BY_REFID = 33,// This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE
25
+ ADD_BY_REFID = 129
25
26
  }
@@ -26,5 +26,6 @@ var OPERATION;
26
26
  OPERATION[OPERATION["REVERSE"] = 15] = "REVERSE";
27
27
  OPERATION[OPERATION["MOVE"] = 32] = "MOVE";
28
28
  OPERATION[OPERATION["DELETE_BY_REFID"] = 33] = "DELETE_BY_REFID";
29
+ OPERATION[OPERATION["ADD_BY_REFID"] = 129] = "ADD_BY_REFID";
29
30
  })(OPERATION || (exports.OPERATION = OPERATION = {}));
30
31
  //# sourceMappingURL=spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/encoding/spec.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAAG,GAAG,CAAC,CAAC,4DAA4D;AACvF,QAAA,OAAO,GAAG,GAAG,CAAC;AAE3B;;GAEG;AACH,IAAY,SAsBX;AAtBD,WAAY,SAAS;IACjB,yCAAS,CAAA;IACT,+CAAW,CAAA;IACX,8CAAW,CAAA;IACX,gEAAoB,CAAA;IACpB,2DAAkB,CAAA;IAClB,+DAAoB,CAAA;IAEpB;;OAEG;IACH,4CAAU,CAAA;IAEV;;OAEG;IACH,0CAAS,CAAA;IACT,gDAAY,CAAA;IACZ,gDAAY,CAAA;IACZ,0CAAS,CAAA;IACT,gEAAoB,CAAA;AAExB,CAAC,EAtBW,SAAS,yBAAT,SAAS,QAsBpB","sourcesContent":["export const SWITCH_TO_STRUCTURE = 255; // (decoding collides with DELETE_AND_ADD + fieldIndex = 63)\nexport const TYPE_ID = 213;\n\n/**\n * Encoding Schema field operations.\n */\nexport enum OPERATION {\n ADD = 128, // (10000000) add new structure/primitive\n REPLACE = 0, // (00000001) replace structure/primitive\n DELETE = 64, // (01000000) delete field\n DELETE_AND_MOVE = 96, // () add new structure/primitive\n MOVE_AND_ADD = 160, // () add new structure/primitive\n DELETE_AND_ADD = 192, // (11000000) DELETE field, followed by an ADD\n\n /**\n * Collection operations\n */\n CLEAR = 10,\n\n /**\n * ArraySchema operations\n */\n PUSH = 11,\n UNSHIFT = 12,\n REVERSE = 15,\n MOVE = 32,\n DELETE_BY_REFID = 33, // This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE\n\n}\n"]}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/encoding/spec.ts"],"names":[],"mappings":";;;AAAa,QAAA,mBAAmB,GAAG,GAAG,CAAC,CAAC,4DAA4D;AACvF,QAAA,OAAO,GAAG,GAAG,CAAC;AAE3B;;GAEG;AACH,IAAY,SAuBX;AAvBD,WAAY,SAAS;IACjB,yCAAS,CAAA;IACT,+CAAW,CAAA;IACX,8CAAW,CAAA;IACX,gEAAoB,CAAA;IACpB,2DAAkB,CAAA;IAClB,+DAAoB,CAAA;IAEpB;;OAEG;IACH,4CAAU,CAAA;IAEV;;OAEG;IACH,0CAAS,CAAA;IACT,gDAAY,CAAA;IACZ,gDAAY,CAAA;IACZ,0CAAS,CAAA;IACT,gEAAoB,CAAA;IACpB,2DAAkB,CAAA;AAEtB,CAAC,EAvBW,SAAS,yBAAT,SAAS,QAuBpB","sourcesContent":["export const SWITCH_TO_STRUCTURE = 255; // (decoding collides with DELETE_AND_ADD + fieldIndex = 63)\nexport const TYPE_ID = 213;\n\n/**\n * Encoding Schema field operations.\n */\nexport enum OPERATION {\n ADD = 128, // (10000000) add new structure/primitive\n REPLACE = 0, // (00000001) replace structure/primitive\n DELETE = 64, // (01000000) delete field\n DELETE_AND_MOVE = 96, // () add new structure/primitive\n MOVE_AND_ADD = 160, // () add new structure/primitive\n DELETE_AND_ADD = 192, // (11000000) DELETE field, followed by an ADD\n\n /**\n * Collection operations\n */\n CLEAR = 10,\n\n /**\n * ArraySchema operations\n */\n PUSH = 11,\n UNSHIFT = 12,\n REVERSE = 15,\n MOVE = 32,\n DELETE_BY_REFID = 33, // This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE\n ADD_BY_REFID = 129,\n\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "3.0.0-alpha.14",
3
+ "version": "3.0.0-alpha.16",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "bin": {
6
6
  "schema-codegen": "./bin/schema-codegen"
@@ -78,7 +78,7 @@
78
78
  "source-map-support": "^0.5.13",
79
79
  "ts-node": "^10.9.2",
80
80
  "tslib": "^2.1.0",
81
- "tsx": "^4.7.0",
81
+ "tsx": "^4.15.7",
82
82
  "typescript": "^5.3.3"
83
83
  },
84
84
  "nyc": {
@@ -0,0 +1,66 @@
1
+ import { nanoid } from "nanoid";
2
+ import { Schema, type, MapSchema, ArraySchema, Encoder } from ".";
3
+ import * as benchmark from "benchmark";
4
+
5
+ const suite = new benchmark.Suite();
6
+
7
+ class AttributeNew extends Schema {
8
+ @type("string") name: string;
9
+ @type("number") value: number;
10
+ }
11
+
12
+ class ItemNew extends Schema {
13
+ @type("number") price: number;
14
+ @type([ AttributeNew ]) attributes = new ArraySchema<AttributeNew>();
15
+ }
16
+
17
+ class PositionNew extends Schema {
18
+ @type("number") x: number;
19
+ @type("number") y: number;
20
+ }
21
+
22
+ class PlayerNew extends Schema {
23
+ @type(PositionNew) position = new PositionNew();
24
+ @type({ map: ItemNew }) items = new MapSchema<ItemNew>();
25
+ }
26
+
27
+ class StateNew extends Schema {
28
+ @type({ map: PlayerNew }) players = new MapSchema<PlayerNew>();
29
+ @type("string") currentTurn;
30
+ }
31
+
32
+ const state = new StateNew();
33
+
34
+ for (let i = 0; i < 50; i++) {
35
+ const player = new PlayerNew();
36
+ state.players.set(`p-${nanoid()}`, player);
37
+
38
+ player.position.x = (i + 1) * 100;
39
+ player.position.y = (i + 1) * 100;
40
+ for (let j = 0; j < 10; j++) {
41
+ const item = new ItemNew();
42
+ item.price = (i + 1) * 50;
43
+ for (let k = 0; k < 5; k++) {
44
+ const attr = new AttributeNew();
45
+ attr.name = `Attribute ${k}`;
46
+ attr.value = k;
47
+ item.attributes.push(attr);
48
+
49
+ }
50
+ player.items.set(`item-${j}`, item);
51
+ }
52
+ }
53
+
54
+
55
+ Encoder.BUFFER_SIZE = 1024 * 128;
56
+ const encoder = new Encoder(state);
57
+
58
+ // measure time to .encodeAll()
59
+
60
+ const now = Date.now();
61
+ for (let i = 0; i < 1000; i++) {
62
+ encoder.encodeAll();
63
+ }
64
+ console.log(Date.now() - now);
65
+
66
+ console.log(Array.from(encoder.encodeAll()).join(","));
@@ -251,6 +251,7 @@ export const decodeKeyValueOperation: DecodeOperation = function (
251
251
  dynamicIndex = ref['getIndex'](index);
252
252
  }
253
253
 
254
+
254
255
  const { value, previousValue } = decodeValue(
255
256
  decoder,
256
257
  operation,
@@ -303,7 +304,10 @@ export const decodeArray: DecodeOperation = function (
303
304
  allChanges: DataChange[]
304
305
  ) {
305
306
  // "uncompressed" index + operation (array/map items)
306
- const operation = bytes[it.offset++];
307
+ let operation = bytes[it.offset++];
308
+
309
+ let isSchemaChild: boolean;
310
+ let index: number;
307
311
 
308
312
  if (operation === OPERATION.CLEAR) {
309
313
  //
@@ -319,7 +323,7 @@ export const decodeArray: DecodeOperation = function (
319
323
  // TODO: refactor here, try to follow same flow as below
320
324
  const refId = decode.number(bytes, it);
321
325
  const previousValue = decoder.root.refs.get(refId);
322
- const index = ref.findIndex((value) => value === previousValue);
326
+ index = ref.findIndex((value) => value === previousValue);
323
327
  ref[$deleteByIndex](index);
324
328
  allChanges.push({
325
329
  ref,
@@ -330,10 +334,26 @@ export const decodeArray: DecodeOperation = function (
330
334
  value: undefined,
331
335
  previousValue,
332
336
  });
337
+
333
338
  return;
339
+
340
+ } else if (operation === OPERATION.ADD_BY_REFID) {
341
+ isSchemaChild = true;
342
+ // operation = OPERATION.ADD;
343
+
344
+ const refId = decode.number(bytes, it);
345
+ const itemByRefId = decoder.root.refs.get(refId);
346
+
347
+ // use existing index, or push new value
348
+ index = (itemByRefId)
349
+ ? ref.findIndex((value) => value === itemByRefId)
350
+ : ref.length;
351
+
352
+ } else {
353
+ isSchemaChild = false;
354
+ index = decode.number(bytes, it);
334
355
  }
335
356
 
336
- const index = decode.number(bytes, it);
337
357
  const type = ref[$childType];
338
358
 
339
359
  let dynamicIndex: number | string = index;
@@ -17,6 +17,8 @@ import type { ArraySchema } from "../../types/custom/ArraySchema";
17
17
  // - Avoid closures by allowing to pass a context. (https://github.com/colyseus/schema/issues/155#issuecomment-1804694081)
18
18
  //
19
19
 
20
+ export type GetCallbackProxy = (<T extends Schema>(instance: T) => CallbackProxy<T>);
21
+
20
22
  export type CallbackProxy<T> = unknown extends T // is "any"?
21
23
  ? InstanceCallback<T> & CollectionCallback<any, any>
22
24
  : T extends Collection<infer K, infer V, infer _>
@@ -88,12 +90,8 @@ type CallContext = {
88
90
  onInstanceAvailable?: OnInstanceAvailableCallback,
89
91
  }
90
92
 
91
- export function getStateCallbacks<T extends Schema>(
92
- decoder: Decoder<T>
93
- ): {
94
- $: (<F extends Schema>(instance: F) => CallbackProxy<F>),
95
- $state: CallbackProxy<T>,
96
- } {
93
+
94
+ export function getStateCallbacks<T extends Schema>(decoder: Decoder<T>): GetCallbackProxy {
97
95
  const $root = decoder.root;
98
96
  const callbacks = $root.callbacks;
99
97
 
@@ -157,17 +155,7 @@ export function getStateCallbacks<T extends Schema>(
157
155
  // Handle collection of items
158
156
  //
159
157
 
160
- if (change.op === OPERATION.ADD && change.previousValue === undefined) {
161
- // triger onAdd
162
-
163
- isTriggeringOnAdd = true;
164
- const addCallbacks = $callbacks[OPERATION.ADD];
165
- for (let i = addCallbacks?.length - 1; i >= 0; i--) {
166
- addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
167
- }
168
- isTriggeringOnAdd = false;
169
-
170
- } else if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
158
+ if ((change.op & OPERATION.DELETE) === OPERATION.DELETE) {
171
159
  //
172
160
  // FIXME: `previousValue` should always be available.
173
161
  //
@@ -187,6 +175,16 @@ export function getStateCallbacks<T extends Schema>(
187
175
  addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
188
176
  }
189
177
  }
178
+
179
+ } else if ((change.op & OPERATION.ADD) === OPERATION.ADD && change.previousValue === undefined) {
180
+ // triger onAdd
181
+
182
+ isTriggeringOnAdd = true;
183
+ const addCallbacks = $callbacks[OPERATION.ADD];
184
+ for (let i = addCallbacks?.length - 1; i >= 0; i--) {
185
+ addCallbacks[i](change.value, change.dynamicIndex ?? change.field);
186
+ }
187
+ isTriggeringOnAdd = false;
190
188
  }
191
189
 
192
190
  // trigger onChange
@@ -364,8 +362,5 @@ export function getStateCallbacks<T extends Schema>(
364
362
  return getProxy(undefined, { instance }) as CallbackProxy<T>;
365
363
  }
366
364
 
367
- return {
368
- $,
369
- $state: $(decoder.state),
370
- };
365
+ return $;
371
366
  }
@@ -204,18 +204,22 @@ export const encodeArray: EncodeOperation = function (
204
204
  hasView: boolean,
205
205
  ) {
206
206
  const ref = changeTree.ref;
207
+ const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
207
208
 
208
- if (
209
- hasView &&
210
- operation === OPERATION.DELETE &&
211
- typeof (changeTree.getType(field)) !== "string"
212
- ) {
213
- // encode delete by refId (array of schemas)
214
- bytes[it.offset++] = OPERATION.DELETE_BY_REFID;
215
- const value = ref['tmpItems'][field];
216
- const refId = value[$changes].refId;
217
- encode.number(bytes, refId, it);
218
- return;
209
+ let refOrIndex: number;
210
+
211
+ if (useOperationByRefId) {
212
+ refOrIndex = ref['tmpItems'][field][$changes].refId;
213
+
214
+ if (operation === OPERATION.DELETE) {
215
+ operation = OPERATION.DELETE_BY_REFID;
216
+
217
+ } else if (operation === OPERATION.ADD) {
218
+ operation = OPERATION.ADD_BY_REFID;
219
+ }
220
+
221
+ } else {
222
+ refOrIndex = field;
219
223
  }
220
224
 
221
225
  // encode operation
@@ -227,7 +231,7 @@ export const encodeArray: EncodeOperation = function (
227
231
  }
228
232
 
229
233
  // encode index
230
- encode.number(bytes, field, it);
234
+ encode.number(bytes, refOrIndex, it);
231
235
 
232
236
  // Do not encode value for DELETE operations
233
237
  if (operation === OPERATION.DELETE) {
@@ -25,5 +25,6 @@ export enum OPERATION {
25
25
  REVERSE = 15,
26
26
  MOVE = 32,
27
27
  DELETE_BY_REFID = 33, // This operation is only used at ENCODING time. During DECODING, DELETE_BY_REFID is converted to DELETE
28
+ ADD_BY_REFID = 129,
28
29
 
29
30
  }