@colyseus/schema 3.0.0-alpha.30 → 3.0.0-alpha.31

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.
Files changed (73) hide show
  1. package/build/cjs/index.js +383 -341
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/index.mjs +383 -341
  4. package/build/esm/index.mjs.map +1 -1
  5. package/build/umd/index.js +383 -341
  6. package/lib/Metadata.d.ts +14 -5
  7. package/lib/Metadata.js +49 -20
  8. package/lib/Metadata.js.map +1 -1
  9. package/lib/Reflection.js +4 -13
  10. package/lib/Reflection.js.map +1 -1
  11. package/lib/Schema.js +26 -39
  12. package/lib/Schema.js.map +1 -1
  13. package/lib/annotations.d.ts +1 -2
  14. package/lib/annotations.js +58 -52
  15. package/lib/annotations.js.map +1 -1
  16. package/lib/bench_encode.js +33 -11
  17. package/lib/bench_encode.js.map +1 -1
  18. package/lib/decoder/DecodeOperation.js +4 -8
  19. package/lib/decoder/DecodeOperation.js.map +1 -1
  20. package/lib/decoder/ReferenceTracker.js +3 -2
  21. package/lib/decoder/ReferenceTracker.js.map +1 -1
  22. package/lib/decoder/strategy/StateCallbacks.js +4 -3
  23. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  24. package/lib/encoder/ChangeTree.d.ts +8 -7
  25. package/lib/encoder/ChangeTree.js +128 -115
  26. package/lib/encoder/ChangeTree.js.map +1 -1
  27. package/lib/encoder/EncodeOperation.d.ts +1 -4
  28. package/lib/encoder/EncodeOperation.js +46 -46
  29. package/lib/encoder/EncodeOperation.js.map +1 -1
  30. package/lib/encoder/Encoder.js +11 -3
  31. package/lib/encoder/Encoder.js.map +1 -1
  32. package/lib/encoder/StateView.js +3 -3
  33. package/lib/encoder/StateView.js.map +1 -1
  34. package/lib/encoding/assert.d.ts +2 -1
  35. package/lib/encoding/assert.js +2 -2
  36. package/lib/encoding/assert.js.map +1 -1
  37. package/lib/index.d.ts +1 -2
  38. package/lib/index.js +11 -10
  39. package/lib/index.js.map +1 -1
  40. package/lib/types/TypeContext.js +7 -14
  41. package/lib/types/TypeContext.js.map +1 -1
  42. package/lib/types/custom/ArraySchema.js +6 -0
  43. package/lib/types/custom/ArraySchema.js.map +1 -1
  44. package/lib/types/custom/CollectionSchema.js +1 -0
  45. package/lib/types/custom/CollectionSchema.js.map +1 -1
  46. package/lib/types/custom/MapSchema.js +5 -0
  47. package/lib/types/custom/MapSchema.js.map +1 -1
  48. package/lib/types/custom/SetSchema.js +1 -0
  49. package/lib/types/custom/SetSchema.js.map +1 -1
  50. package/lib/types/symbols.d.ts +1 -0
  51. package/lib/types/symbols.js +2 -1
  52. package/lib/types/symbols.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/Metadata.ts +60 -29
  55. package/src/Reflection.ts +5 -15
  56. package/src/Schema.ts +33 -45
  57. package/src/annotations.ts +75 -67
  58. package/src/bench_encode.ts +37 -13
  59. package/src/decoder/DecodeOperation.ts +4 -10
  60. package/src/decoder/ReferenceTracker.ts +3 -2
  61. package/src/decoder/strategy/StateCallbacks.ts +4 -3
  62. package/src/encoder/ChangeTree.ts +146 -135
  63. package/src/encoder/EncodeOperation.ts +64 -58
  64. package/src/encoder/Encoder.ts +16 -4
  65. package/src/encoder/StateView.ts +4 -4
  66. package/src/encoding/assert.ts +4 -3
  67. package/src/index.ts +1 -4
  68. package/src/types/TypeContext.ts +10 -15
  69. package/src/types/custom/ArraySchema.ts +8 -0
  70. package/src/types/custom/CollectionSchema.ts +1 -0
  71. package/src/types/custom/MapSchema.ts +6 -0
  72. package/src/types/custom/SetSchema.ts +1 -0
  73. package/src/types/symbols.ts +2 -0
@@ -3,10 +3,12 @@ import { Schema } from './Schema';
3
3
  import { ArraySchema } from './types/custom/ArraySchema';
4
4
  import { MapSchema } from './types/custom/MapSchema';
5
5
  import { Metadata } from "./Metadata";
6
- import { $changes, $childType, $track } from "./types/symbols";
6
+ import { $changes, $childType, $descriptors, $track } from "./types/symbols";
7
7
  import { TypeDefinition, getType } from "./types/registry";
8
8
  import { OPERATION } from "./encoding/spec";
9
9
  import { TypeContext } from "./types/TypeContext";
10
+ import { assertInstanceType, assertType } from "./encoding/assert";
11
+ import type { Ref } from "./encoder/ChangeTree";
10
12
 
11
13
  /**
12
14
  * Data types
@@ -227,18 +229,19 @@ export function view<T> (tag: number = DEFAULT_VIEW_TAG) {
227
229
 
228
230
  // TODO: use Metadata.initialize()
229
231
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
230
-
231
- if (!metadata[fieldName]) {
232
- //
233
- // detect index for this field, considering inheritance
234
- //
235
- metadata[fieldName] = {
236
- type: undefined,
237
- index: (metadata[-1] // current structure already has fields defined
238
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
239
- ?? -1) + 1 // no fields defined
240
- }
241
- }
232
+ // const fieldIndex = metadata[fieldName];
233
+
234
+ // if (!metadata[fieldIndex]) {
235
+ // //
236
+ // // detect index for this field, considering inheritance
237
+ // //
238
+ // metadata[fieldIndex] = {
239
+ // type: undefined,
240
+ // index: (metadata[-1] // current structure already has fields defined
241
+ // ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
242
+ // ?? -1) + 1 // no fields defined
243
+ // }
244
+ // }
242
245
 
243
246
  Metadata.setTag(metadata, fieldName, tag);
244
247
  }
@@ -256,20 +259,20 @@ export function unreliable<T> (target: T, field: string) {
256
259
  // TODO: use Metadata.initialize()
257
260
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
258
261
 
259
- if (!metadata[field]) {
260
- //
261
- // detect index for this field, considering inheritance
262
- //
263
- metadata[field] = {
264
- type: undefined,
265
- index: (metadata[-1] // current structure already has fields defined
266
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
267
- ?? -1) + 1 // no fields defined
268
- }
269
- }
262
+ // if (!metadata[field]) {
263
+ // //
264
+ // // detect index for this field, considering inheritance
265
+ // //
266
+ // metadata[field] = {
267
+ // type: undefined,
268
+ // index: (metadata[-1] // current structure already has fields defined
269
+ // ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
270
+ // ?? -1) + 1 // no fields defined
271
+ // }
272
+ // }
270
273
 
271
274
  // add owned flag to the field
272
- metadata[field].unreliable = true;
275
+ metadata[metadata[field]].unreliable = true;
273
276
  }
274
277
 
275
278
  export function type (
@@ -290,17 +293,17 @@ export function type (
290
293
  const parentMetadata = parentClass && parentClass[Symbol.metadata];
291
294
  const metadata = Metadata.initialize(constructor, parentMetadata);
292
295
 
293
- let fieldIndex: number;
296
+ let fieldIndex: number = metadata[field];
294
297
 
295
298
  /**
296
299
  * skip if descriptor already exists for this field (`@deprecated()`)
297
300
  */
298
- if (metadata[field]) {
299
- if (metadata[field].deprecated) {
301
+ if (metadata[fieldIndex]) {
302
+ if (metadata[fieldIndex].deprecated) {
300
303
  // do not create accessors for deprecated properties.
301
304
  return;
302
305
 
303
- } else if (metadata[field].descriptor !== undefined) {
306
+ } else if (metadata[fieldIndex].type !== undefined) {
304
307
  // trying to define same property multiple times across inheritance.
305
308
  // https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
306
309
  try {
@@ -310,9 +313,6 @@ export function type (
310
313
  const definitionAtLine = e.stack.split("\n")[4].trim();
311
314
  throw new Error(`${e.message} ${definitionAtLine}`);
312
315
  }
313
-
314
- } else {
315
- fieldIndex = metadata[field].index;
316
316
  }
317
317
 
318
318
  } else {
@@ -326,12 +326,18 @@ export function type (
326
326
  }
327
327
 
328
328
  if (options && options.manual) {
329
- Metadata.addField(metadata, fieldIndex, field, type, {
330
- // do not declare getter/setter descriptor
331
- enumerable: true,
332
- configurable: true,
333
- writable: true,
334
- });
329
+ Metadata.addField(
330
+ metadata,
331
+ fieldIndex,
332
+ field,
333
+ type,
334
+ {
335
+ // do not declare getter/setter descriptor
336
+ enumerable: true,
337
+ configurable: true,
338
+ writable: true,
339
+ }
340
+ );
335
341
 
336
342
  } else {
337
343
  const complexTypeKlass = (Array.isArray(type))
@@ -347,7 +353,7 @@ export function type (
347
353
  fieldIndex,
348
354
  field,
349
355
  type,
350
- getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass, metadata, field)
356
+ getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
351
357
  );
352
358
  }
353
359
  }
@@ -358,8 +364,6 @@ export function getPropertyDescriptor(
358
364
  fieldIndex: number,
359
365
  type: DefinitionType,
360
366
  complexTypeKlass: TypeDefinition,
361
- metadata: Metadata,
362
- field: string,
363
367
  ) {
364
368
  return {
365
369
  get: function () { return this[fieldCached]; },
@@ -385,30 +389,32 @@ export function getPropertyDescriptor(
385
389
  }
386
390
 
387
391
  value[$childType] = type;
392
+
393
+ } else if (typeof (type) !== "string") {
394
+ assertInstanceType(value, type as typeof Schema, this, fieldCached.substring(1));
395
+
396
+ } else {
397
+ assertType(value, type, this, fieldCached.substring(1));
388
398
  }
389
399
 
400
+ const changeTree = this[$changes];
401
+
390
402
  //
391
403
  // Replacing existing "ref", remove it from root.
392
404
  // TODO: if there are other references to this instance, we should not remove it from root.
393
405
  //
394
406
  if (previousValue !== undefined && previousValue[$changes]) {
395
- this[$changes].root?.remove(previousValue[$changes]);
407
+ changeTree.root?.remove(previousValue[$changes]);
396
408
  }
397
409
 
398
410
  // flag the change for encoding.
399
- this.constructor[$track](this[$changes], fieldIndex, OPERATION.ADD);
411
+ this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
400
412
 
401
413
  //
402
414
  // call setParent() recursively for this and its child
403
415
  // structures.
404
416
  //
405
- if (value[$changes]) {
406
- value[$changes].setParent(
407
- this,
408
- this[$changes].root,
409
- metadata[field].index,
410
- );
411
- }
417
+ (value as Ref)[$changes]?.setParent(this, changeTree.root, fieldIndex);
412
418
 
413
419
  } else if (previousValue !== undefined) {
414
420
  //
@@ -440,23 +446,25 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
440
446
  const parentClass = Object.getPrototypeOf(constructor);
441
447
  const parentMetadata = parentClass[Symbol.metadata];
442
448
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
443
-
444
- if (!metadata[field]) {
445
- //
446
- // detect index for this field, considering inheritance
447
- //
448
- metadata[field] = {
449
- type: undefined,
450
- index: (metadata[-1] // current structure already has fields defined
451
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
452
- ?? -1) + 1 // no fields defined
453
- }
454
- }
455
-
456
- metadata[field].deprecated = true;
449
+ const fieldIndex = metadata[field];
450
+
451
+ // if (!metadata[field]) {
452
+ // //
453
+ // // detect index for this field, considering inheritance
454
+ // //
455
+ // metadata[field] = {
456
+ // type: undefined,
457
+ // index: (metadata[-1] // current structure already has fields defined
458
+ // ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
459
+ // ?? -1) + 1 // no fields defined
460
+ // }
461
+ // }
462
+
463
+ metadata[fieldIndex].deprecated = true;
457
464
 
458
465
  if (throws) {
459
- metadata[field].descriptor = {
466
+ metadata[$descriptors] ??= {};
467
+ metadata[$descriptors][field] = {
460
468
  get: function () { throw new Error(`${field} is deprecated.`); },
461
469
  set: function (this: Schema, value: any) { /* throw new Error(`${field} is deprecated.`); */ },
462
470
  enumerable: false,
@@ -465,8 +473,8 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
465
473
  }
466
474
 
467
475
  // flag metadata[field] as non-enumerable
468
- Object.defineProperty(metadata, field, {
469
- value: metadata[field],
476
+ Object.defineProperty(metadata, fieldIndex, {
477
+ value: metadata[fieldIndex],
470
478
  enumerable: false,
471
479
  configurable: true
472
480
  });
@@ -31,7 +31,33 @@ class State extends Schema {
31
31
 
32
32
  const state = new State();
33
33
 
34
- for (let i = 0; i < 50; i++) {
34
+ Encoder.BUFFER_SIZE = 4096 * 4096;
35
+ const encoder = new Encoder(state);
36
+
37
+ // // SINGLE TESTING
38
+ // console.log("> Will create player...");
39
+ // const player = new Player();
40
+ // console.log("... players.set()")
41
+ // state.players.set(`p-${nanoid()}`, player);
42
+ // player.position.x = 100;
43
+ // player.position.y = 100;
44
+ // console.log("> Will create Item...");
45
+ // const item = new Item();
46
+ // item.price = 50;
47
+
48
+ // console.log("> Will create Attribute...");
49
+ // const attr = new Attribute();
50
+ // attr.name = `Attribute 1`;
51
+ // attr.value = 1;
52
+ // console.log("... item.attributes.push()")
53
+ // item.attributes.push(attr);
54
+
55
+ // console.log("... player.items.set()")
56
+ // player.items.set(`item-1`, item);
57
+
58
+ let now = Date.now();
59
+ for (let i = 0; i < 10000; i++) {
60
+ // for (let i = 0; i < 10; i++) {
35
61
  const player = new Player();
36
62
  state.players.set(`p-${nanoid()}`, player);
37
63
 
@@ -39,29 +65,28 @@ for (let i = 0; i < 50; i++) {
39
65
  player.position.y = (i + 1) * 100;
40
66
  for (let j = 0; j < 10; j++) {
41
67
  const item = new Item();
68
+ player.items.set(`item-${j}`, item);
42
69
  item.price = (i + 1) * 50;
43
70
  for (let k = 0; k < 5; k++) {
44
71
  const attr = new Attribute();
45
72
  attr.name = `Attribute ${k}`;
46
73
  attr.value = k;
47
74
  item.attributes.push(attr);
48
-
49
75
  }
50
- player.items.set(`item-${j}`, item);
51
76
  }
52
77
  }
78
+ console.log("time to make changes:", Date.now() - now);
53
79
 
80
+ // process.exit();
54
81
 
55
- Encoder.BUFFER_SIZE = 4096 * 4096;
56
- const encoder = new Encoder(state);
57
82
 
58
83
  // measure time to .encodeAll()
59
84
 
60
- let now = Date.now();
61
- for (let i = 0; i < 1000; i++) {
62
- encoder.encodeAll();
63
- }
64
- console.log(Date.now() - now);
85
+ now = Date.now();
86
+ // for (let i = 0; i < 1000; i++) {
87
+ // encoder.encodeAll();
88
+ // }
89
+ // console.log(Date.now() - now);
65
90
 
66
91
  const allEncodes = Date.now();
67
92
  for (let i = 0; i < 100; i++) {
@@ -80,7 +105,6 @@ for (let i = 0; i < 100; i++) {
80
105
  attr.name = `Attribute ${l}`;
81
106
  attr.value = l;
82
107
  item.attributes.push(attr);
83
-
84
108
  }
85
109
  player.items.set(`item-${k}`, item);
86
110
  }
@@ -88,10 +112,10 @@ for (let i = 0; i < 100; i++) {
88
112
  console.log("time to make changes:", Date.now() - now);
89
113
 
90
114
  now = Date.now();
91
- encoder.encode();
115
+ // encoder.encode();
92
116
  encoder.discardChanges();
93
117
  console.log("time to encode:", Date.now() - now);
94
118
  }
95
119
  console.log("time for all encodes:", Date.now() - allEncodes);
96
120
 
97
- console.log(Array.from(encoder.encodeAll()).length, "bytes");
121
+ // console.log(Array.from(encoder.encodeAll()).length, "bytes");
@@ -174,7 +174,7 @@ export const decodeSchemaOperation: DecodeOperation = function (
174
174
  allChanges: DataChange[],
175
175
  ) {
176
176
  const first_byte = bytes[it.offset++];
177
- const metadata: Metadata = ref['constructor'][Symbol.metadata];
177
+ const metadata: Metadata = ref.constructor[Symbol.metadata];
178
178
 
179
179
  // "compressed" index + operation
180
180
  const operation = (first_byte >> 6) << 6
@@ -192,14 +192,14 @@ export const decodeSchemaOperation: DecodeOperation = function (
192
192
  operation,
193
193
  ref,
194
194
  index,
195
- metadata[field].type,
195
+ field.type,
196
196
  bytes,
197
197
  it,
198
198
  allChanges,
199
199
  );
200
200
 
201
201
  if (value !== null && value !== undefined) {
202
- ref[field] = value;
202
+ ref[field.name] = value;
203
203
  }
204
204
 
205
205
  // add change
@@ -208,7 +208,7 @@ export const decodeSchemaOperation: DecodeOperation = function (
208
208
  ref,
209
209
  refId: decoder.currentRefId,
210
210
  op: operation,
211
- field: field,
211
+ field: field.name,
212
212
  value,
213
213
  previousValue,
214
214
  });
@@ -308,8 +308,6 @@ export const decodeArray: DecodeOperation = function (
308
308
  ) {
309
309
  // "uncompressed" index + operation (array/map items)
310
310
  let operation = bytes[it.offset++];
311
-
312
- let isSchemaChild: boolean;
313
311
  let index: number;
314
312
 
315
313
  if (operation === OPERATION.CLEAR) {
@@ -341,9 +339,6 @@ export const decodeArray: DecodeOperation = function (
341
339
  return;
342
340
 
343
341
  } else if (operation === OPERATION.ADD_BY_REFID) {
344
- isSchemaChild = true;
345
- // operation = OPERATION.ADD;
346
-
347
342
  const refId = decode.number(bytes, it);
348
343
  const itemByRefId = decoder.root.refs.get(refId);
349
344
 
@@ -353,7 +348,6 @@ export const decodeArray: DecodeOperation = function (
353
348
  : ref.length;
354
349
 
355
350
  } else {
356
- isSchemaChild = false;
357
351
  index = decode.number(bytes, it);
358
352
  }
359
353
 
@@ -98,8 +98,9 @@ export class ReferenceTracker {
98
98
  // Ensure child schema instances have their references removed as well.
99
99
  //
100
100
  if (Metadata.isValidInstance(ref)) {
101
- const metadata: Metadata = ref['constructor'][Symbol.metadata];
102
- for (const field in metadata) {
101
+ const metadata: Metadata = ref.constructor[Symbol.metadata];
102
+ for (const index in metadata) {
103
+ const field = metadata[index as any as number].name;
103
104
  const childRefId = typeof(ref[field]) === "object" && this.refIds.get(ref[field]);
104
105
  if (childRefId) {
105
106
  this.removeRef(childRefId);
@@ -264,7 +264,7 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
264
264
  //
265
265
  bindTo: function bindTo(targetObject: any, properties?: string[]) {
266
266
  if (!properties) {
267
- properties = Object.keys(metadata);
267
+ properties = Object.keys(metadata).map((index) => metadata[index as any as number].name);
268
268
  }
269
269
  return $root.addCallback(
270
270
  $root.refIds.get(context.instance),
@@ -277,7 +277,8 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
277
277
  }
278
278
  }, {
279
279
  get(target, prop: string) {
280
- if (metadata[prop]) {
280
+ const metadataField = metadata[metadata[prop]];
281
+ if (metadataField) {
281
282
  const instance = context.instance?.[prop];
282
283
  const onInstanceAvailable: OnInstanceAvailableCallback = (
283
284
  (callback: (ref: Ref, existing: boolean) => void) => {
@@ -297,7 +298,7 @@ export function getDecoderStateCallbacks<T extends Schema>(decoder: Decoder<T>):
297
298
  }
298
299
  }
299
300
  );
300
- return getProxy(metadata[prop].type, {
301
+ return getProxy(metadataField.type, {
301
302
  // make sure refId is available, otherwise need to wait for the instance to be available.
302
303
  instance: ($root.refIds.get(instance) && instance),
303
304
  parentInstance: context.instance,