@colyseus/schema 3.0.0-alpha.3 → 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 (134) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +966 -563
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +965 -562
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +966 -563
  7. package/lib/Metadata.d.ts +15 -4
  8. package/lib/Metadata.js +86 -18
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Reflection.d.ts +2 -3
  11. package/lib/Reflection.js +24 -28
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +2 -2
  14. package/lib/Schema.js +28 -41
  15. package/lib/Schema.js.map +1 -1
  16. package/lib/annotations.d.ts +1 -21
  17. package/lib/annotations.js +73 -153
  18. package/lib/annotations.js.map +1 -1
  19. package/lib/bench_encode.d.ts +1 -0
  20. package/lib/bench_encode.js +142 -0
  21. package/lib/bench_encode.js.map +1 -0
  22. package/lib/codegen/api.js +1 -2
  23. package/lib/codegen/api.js.map +1 -1
  24. package/lib/codegen/languages/cpp.js +1 -2
  25. package/lib/codegen/languages/cpp.js.map +1 -1
  26. package/lib/codegen/languages/csharp.js +1 -2
  27. package/lib/codegen/languages/csharp.js.map +1 -1
  28. package/lib/codegen/languages/haxe.js +1 -2
  29. package/lib/codegen/languages/haxe.js.map +1 -1
  30. package/lib/codegen/languages/java.js +1 -2
  31. package/lib/codegen/languages/java.js.map +1 -1
  32. package/lib/codegen/languages/js.js +1 -2
  33. package/lib/codegen/languages/js.js.map +1 -1
  34. package/lib/codegen/languages/lua.js +1 -2
  35. package/lib/codegen/languages/lua.js.map +1 -1
  36. package/lib/codegen/languages/ts.js +1 -2
  37. package/lib/codegen/languages/ts.js.map +1 -1
  38. package/lib/codegen/parser.js +2 -3
  39. package/lib/codegen/parser.js.map +1 -1
  40. package/lib/codegen/types.js +3 -3
  41. package/lib/codegen/types.js.map +1 -1
  42. package/lib/debug.d.ts +1 -0
  43. package/lib/debug.js +52 -0
  44. package/lib/debug.js.map +1 -0
  45. package/lib/decoder/DecodeOperation.d.ts +0 -1
  46. package/lib/decoder/DecodeOperation.js +23 -11
  47. package/lib/decoder/DecodeOperation.js.map +1 -1
  48. package/lib/decoder/Decoder.d.ts +6 -7
  49. package/lib/decoder/Decoder.js +8 -8
  50. package/lib/decoder/Decoder.js.map +1 -1
  51. package/lib/decoder/ReferenceTracker.js +3 -2
  52. package/lib/decoder/ReferenceTracker.js.map +1 -1
  53. package/lib/decoder/strategy/RawChanges.js +1 -2
  54. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  55. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  56. package/lib/decoder/strategy/StateCallbacks.js +75 -65
  57. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  58. package/lib/encoder/ChangeTree.d.ts +9 -19
  59. package/lib/encoder/ChangeTree.js +129 -145
  60. package/lib/encoder/ChangeTree.js.map +1 -1
  61. package/lib/encoder/EncodeOperation.d.ts +1 -5
  62. package/lib/encoder/EncodeOperation.js +74 -58
  63. package/lib/encoder/EncodeOperation.js.map +1 -1
  64. package/lib/encoder/Encoder.d.ts +10 -8
  65. package/lib/encoder/Encoder.js +89 -56
  66. package/lib/encoder/Encoder.js.map +1 -1
  67. package/lib/encoder/Root.d.ts +17 -0
  68. package/lib/encoder/Root.js +44 -0
  69. package/lib/encoder/Root.js.map +1 -0
  70. package/lib/encoder/StateView.d.ts +2 -2
  71. package/lib/encoder/StateView.js +49 -59
  72. package/lib/encoder/StateView.js.map +1 -1
  73. package/lib/encoding/assert.d.ts +2 -1
  74. package/lib/encoding/assert.js +5 -5
  75. package/lib/encoding/assert.js.map +1 -1
  76. package/lib/encoding/decode.js +21 -22
  77. package/lib/encoding/decode.js.map +1 -1
  78. package/lib/encoding/encode.d.ts +2 -2
  79. package/lib/encoding/encode.js +40 -39
  80. package/lib/encoding/encode.js.map +1 -1
  81. package/lib/encoding/spec.d.ts +2 -1
  82. package/lib/encoding/spec.js +1 -0
  83. package/lib/encoding/spec.js.map +1 -1
  84. package/lib/index.d.ts +6 -3
  85. package/lib/index.js +18 -13
  86. package/lib/index.js.map +1 -1
  87. package/lib/types/TypeContext.d.ts +23 -0
  88. package/lib/types/TypeContext.js +102 -0
  89. package/lib/types/TypeContext.js.map +1 -0
  90. package/lib/types/custom/ArraySchema.d.ts +2 -2
  91. package/lib/types/custom/ArraySchema.js +6 -9
  92. package/lib/types/custom/ArraySchema.js.map +1 -1
  93. package/lib/types/custom/CollectionSchema.js +1 -0
  94. package/lib/types/custom/CollectionSchema.js.map +1 -1
  95. package/lib/types/custom/MapSchema.js +5 -0
  96. package/lib/types/custom/MapSchema.js.map +1 -1
  97. package/lib/types/custom/SetSchema.js +1 -0
  98. package/lib/types/custom/SetSchema.js.map +1 -1
  99. package/lib/types/registry.js +3 -4
  100. package/lib/types/registry.js.map +1 -1
  101. package/lib/types/symbols.d.ts +1 -0
  102. package/lib/types/symbols.js +2 -1
  103. package/lib/types/symbols.js.map +1 -1
  104. package/lib/types/utils.js +1 -2
  105. package/lib/types/utils.js.map +1 -1
  106. package/lib/utils.js +3 -4
  107. package/lib/utils.js.map +1 -1
  108. package/package.json +5 -5
  109. package/src/Metadata.ts +104 -26
  110. package/src/Reflection.ts +26 -28
  111. package/src/Schema.ts +35 -47
  112. package/src/annotations.ts +82 -176
  113. package/src/bench_encode.ts +121 -0
  114. package/src/debug.ts +56 -0
  115. package/src/decoder/DecodeOperation.ts +28 -11
  116. package/src/decoder/Decoder.ts +13 -11
  117. package/src/decoder/ReferenceTracker.ts +3 -2
  118. package/src/decoder/strategy/StateCallbacks.ts +152 -81
  119. package/src/encoder/ChangeTree.ts +147 -166
  120. package/src/encoder/EncodeOperation.ts +93 -70
  121. package/src/encoder/Encoder.ts +111 -65
  122. package/src/encoder/Root.ts +51 -0
  123. package/src/encoder/StateView.ts +53 -69
  124. package/src/encoding/assert.ts +4 -3
  125. package/src/encoding/decode.ts +1 -2
  126. package/src/encoding/encode.ts +25 -22
  127. package/src/encoding/spec.ts +1 -0
  128. package/src/index.ts +8 -14
  129. package/src/types/TypeContext.ts +122 -0
  130. package/src/types/custom/ArraySchema.ts +10 -2
  131. package/src/types/custom/CollectionSchema.ts +1 -0
  132. package/src/types/custom/MapSchema.ts +6 -0
  133. package/src/types/custom/SetSchema.ts +1 -0
  134. package/src/types/symbols.ts +2 -0
@@ -3,9 +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
+ import { TypeContext } from "./types/TypeContext";
10
+ import { assertInstanceType, assertType } from "./encoding/assert";
11
+ import type { Ref } from "./encoder/ChangeTree";
9
12
 
10
13
  /**
11
14
  * Data types
@@ -42,113 +45,6 @@ export interface TypeOptions {
42
45
 
43
46
  export const DEFAULT_VIEW_TAG = -1;
44
47
 
45
- export class TypeContext {
46
- types: {[id: number]: typeof Schema} = {};
47
- schemas = new Map<typeof Schema, number>();
48
-
49
- hasFilters: boolean = false;
50
-
51
- /**
52
- * For inheritance support
53
- * Keeps track of which classes extends which. (parent -> children)
54
- */
55
- static inheritedTypes = new Map<typeof Schema, Set<typeof Schema>>();
56
-
57
- static register(target: typeof Schema) {
58
- const parent = Object.getPrototypeOf(target);
59
- if (parent !== Schema) {
60
- let inherits = TypeContext.inheritedTypes.get(parent);
61
- if (!inherits) {
62
- inherits = new Set<typeof Schema>();
63
- TypeContext.inheritedTypes.set(parent, inherits);
64
- }
65
- inherits.add(target);
66
- }
67
- }
68
-
69
- constructor(rootClass?: typeof Schema) {
70
- if (rootClass) {
71
- this.discoverTypes(rootClass);
72
- }
73
- }
74
-
75
- has(schema: typeof Schema) {
76
- return this.schemas.has(schema);
77
- }
78
-
79
- get(typeid: number) {
80
- return this.types[typeid];
81
- }
82
-
83
- add(schema: typeof Schema, typeid: number = this.schemas.size) {
84
- // skip if already registered
85
- if (this.schemas.has(schema)) {
86
- return false;
87
- }
88
-
89
- this.types[typeid] = schema;
90
- this.schemas.set(schema, typeid);
91
- return true;
92
- }
93
-
94
- getTypeId(klass: typeof Schema) {
95
- return this.schemas.get(klass);
96
- }
97
-
98
- private discoverTypes(klass: typeof Schema) {
99
- if (!this.add(klass)) {
100
- return;
101
- }
102
-
103
- // add classes inherited from this base class
104
- TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
105
- this.discoverTypes(child);
106
- });
107
-
108
- // skip if no fields are defined for this class.
109
- if (klass[Symbol.metadata] === undefined) {
110
- klass[Symbol.metadata] = {};
111
- }
112
-
113
- // const metadata = Metadata.getFor(klass);
114
- const metadata = klass[Symbol.metadata];
115
-
116
- // if any schema/field has filters, mark "context" as having filters.
117
- if (metadata[-2]) {
118
- this.hasFilters = true;
119
- }
120
-
121
- for (const field in metadata) {
122
- const fieldType = metadata[field].type;
123
-
124
- if (typeof(fieldType) === "string") {
125
- continue;
126
- }
127
-
128
- if (Array.isArray(fieldType)) {
129
- const type = fieldType[0];
130
- if (type === "string") {
131
- continue;
132
- }
133
- this.discoverTypes(type as typeof Schema);
134
-
135
- } else if (typeof(fieldType) === "function") {
136
- this.discoverTypes(fieldType);
137
-
138
- } else {
139
- const type = Object.values(fieldType)[0];
140
-
141
- // skip primitive types
142
- if (typeof(type) === "string") {
143
- continue;
144
- }
145
-
146
- this.discoverTypes(type as typeof Schema);
147
- }
148
- }
149
- }
150
- }
151
-
152
48
  export function entity(constructor, context: ClassDecoratorContext) {
153
49
  if (!constructor._definition) {
154
50
  // for inheritance support
@@ -330,19 +226,22 @@ export function view<T> (tag: number = DEFAULT_VIEW_TAG) {
330
226
 
331
227
  const parentClass = Object.getPrototypeOf(constructor);
332
228
  const parentMetadata = parentClass[Symbol.metadata];
333
- const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
334
229
 
335
- if (!metadata[fieldName]) {
336
- //
337
- // detect index for this field, considering inheritance
338
- //
339
- metadata[fieldName] = {
340
- type: undefined,
341
- index: (metadata[-1] // current structure already has fields defined
342
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
343
- ?? -1) + 1 // no fields defined
344
- }
345
- }
230
+ // TODO: use Metadata.initialize()
231
+ const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
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
+ // }
346
245
 
347
246
  Metadata.setTag(metadata, fieldName, tag);
348
247
  }
@@ -356,22 +255,24 @@ export function unreliable<T> (target: T, field: string) {
356
255
 
357
256
  const parentClass = Object.getPrototypeOf(constructor);
358
257
  const parentMetadata = parentClass[Symbol.metadata];
258
+
259
+ // TODO: use Metadata.initialize()
359
260
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
360
261
 
361
- if (!metadata[field]) {
362
- //
363
- // detect index for this field, considering inheritance
364
- //
365
- metadata[field] = {
366
- type: undefined,
367
- index: (metadata[-1] // current structure already has fields defined
368
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
369
- ?? -1) + 1 // no fields defined
370
- }
371
- }
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
+ // }
372
273
 
373
274
  // add owned flag to the field
374
- metadata[field].unreliable = true;
275
+ metadata[metadata[field]].unreliable = true;
375
276
  }
376
277
 
377
278
  export function type (
@@ -389,20 +290,20 @@ export function type (
389
290
  TypeContext.register(constructor);
390
291
 
391
292
  const parentClass = Object.getPrototypeOf(constructor);
392
- const parentMetadata = parentClass[Symbol.metadata];
393
- const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
293
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
294
+ const metadata = Metadata.initialize(constructor, parentMetadata);
394
295
 
395
- let fieldIndex: number;
296
+ let fieldIndex: number = metadata[field];
396
297
 
397
298
  /**
398
299
  * skip if descriptor already exists for this field (`@deprecated()`)
399
300
  */
400
- if (metadata[field]) {
401
- if (metadata[field].deprecated) {
301
+ if (metadata[fieldIndex]) {
302
+ if (metadata[fieldIndex].deprecated) {
402
303
  // do not create accessors for deprecated properties.
403
304
  return;
404
305
 
405
- } else if (metadata[field].descriptor !== undefined) {
306
+ } else if (metadata[fieldIndex].type !== undefined) {
406
307
  // trying to define same property multiple times across inheritance.
407
308
  // https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
408
309
  try {
@@ -412,9 +313,6 @@ export function type (
412
313
  const definitionAtLine = e.stack.split("\n")[4].trim();
413
314
  throw new Error(`${e.message} ${definitionAtLine}`);
414
315
  }
415
-
416
- } else {
417
- fieldIndex = metadata[field].index;
418
316
  }
419
317
 
420
318
  } else {
@@ -428,12 +326,18 @@ export function type (
428
326
  }
429
327
 
430
328
  if (options && options.manual) {
431
- Metadata.addField(metadata, fieldIndex, field, type, {
432
- // do not declare getter/setter descriptor
433
- enumerable: true,
434
- configurable: true,
435
- writable: true,
436
- });
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
+ );
437
341
 
438
342
  } else {
439
343
  const complexTypeKlass = (Array.isArray(type))
@@ -449,7 +353,7 @@ export function type (
449
353
  fieldIndex,
450
354
  field,
451
355
  type,
452
- getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass, metadata, field)
356
+ getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
453
357
  );
454
358
  }
455
359
  }
@@ -460,8 +364,6 @@ export function getPropertyDescriptor(
460
364
  fieldIndex: number,
461
365
  type: DefinitionType,
462
366
  complexTypeKlass: TypeDefinition,
463
- metadata: Metadata,
464
- field: string,
465
367
  ) {
466
368
  return {
467
369
  get: function () { return this[fieldCached]; },
@@ -487,30 +389,32 @@ export function getPropertyDescriptor(
487
389
  }
488
390
 
489
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));
490
398
  }
491
399
 
400
+ const changeTree = this[$changes];
401
+
492
402
  //
493
403
  // Replacing existing "ref", remove it from root.
494
404
  // TODO: if there are other references to this instance, we should not remove it from root.
495
405
  //
496
406
  if (previousValue !== undefined && previousValue[$changes]) {
497
- this[$changes].root?.remove(previousValue[$changes]);
407
+ changeTree.root?.remove(previousValue[$changes]);
498
408
  }
499
409
 
500
410
  // flag the change for encoding.
501
- this.constructor[$track](this[$changes], fieldIndex, OPERATION.ADD);
411
+ this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
502
412
 
503
413
  //
504
414
  // call setParent() recursively for this and its child
505
415
  // structures.
506
416
  //
507
- if (value[$changes]) {
508
- value[$changes].setParent(
509
- this,
510
- this[$changes].root,
511
- metadata[field].index,
512
- );
513
- }
417
+ (value as Ref)[$changes]?.setParent(this, changeTree.root, fieldIndex);
514
418
 
515
419
  } else if (previousValue !== undefined) {
516
420
  //
@@ -542,23 +446,25 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
542
446
  const parentClass = Object.getPrototypeOf(constructor);
543
447
  const parentMetadata = parentClass[Symbol.metadata];
544
448
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
545
-
546
- if (!metadata[field]) {
547
- //
548
- // detect index for this field, considering inheritance
549
- //
550
- metadata[field] = {
551
- type: undefined,
552
- index: (metadata[-1] // current structure already has fields defined
553
- ?? (parentMetadata && parentMetadata[-1]) // parent structure has fields defined
554
- ?? -1) + 1 // no fields defined
555
- }
556
- }
557
-
558
- 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;
559
464
 
560
465
  if (throws) {
561
- metadata[field].descriptor = {
466
+ metadata[$descriptors] ??= {};
467
+ metadata[$descriptors][field] = {
562
468
  get: function () { throw new Error(`${field} is deprecated.`); },
563
469
  set: function (this: Schema, value: any) { /* throw new Error(`${field} is deprecated.`); */ },
564
470
  enumerable: false,
@@ -567,8 +473,8 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
567
473
  }
568
474
 
569
475
  // flag metadata[field] as non-enumerable
570
- Object.defineProperty(metadata, field, {
571
- value: metadata[field],
476
+ Object.defineProperty(metadata, fieldIndex, {
477
+ value: metadata[fieldIndex],
572
478
  enumerable: false,
573
479
  configurable: true
574
480
  });
@@ -0,0 +1,121 @@
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 Attribute extends Schema {
8
+ @type("string") name: string;
9
+ @type("number") value: number;
10
+ }
11
+
12
+ class Item extends Schema {
13
+ @type("number") price: number;
14
+ @type([ Attribute ]) attributes = new ArraySchema<Attribute>();
15
+ }
16
+
17
+ class Position extends Schema {
18
+ @type("number") x: number;
19
+ @type("number") y: number;
20
+ }
21
+
22
+ class Player extends Schema {
23
+ @type(Position) position = new Position();
24
+ @type({ map: Item }) items = new MapSchema<Item>();
25
+ }
26
+
27
+ class State extends Schema {
28
+ @type({ map: Player }) players = new MapSchema<Player>();
29
+ @type("string") currentTurn;
30
+ }
31
+
32
+ const state = new State();
33
+
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++) {
61
+ const player = new Player();
62
+ state.players.set(`p-${nanoid()}`, player);
63
+
64
+ player.position.x = (i + 1) * 100;
65
+ player.position.y = (i + 1) * 100;
66
+ for (let j = 0; j < 10; j++) {
67
+ const item = new Item();
68
+ player.items.set(`item-${j}`, item);
69
+ item.price = (i + 1) * 50;
70
+ for (let k = 0; k < 5; k++) {
71
+ const attr = new Attribute();
72
+ attr.name = `Attribute ${k}`;
73
+ attr.value = k;
74
+ item.attributes.push(attr);
75
+ }
76
+ }
77
+ }
78
+ console.log("time to make changes:", Date.now() - now);
79
+
80
+ // process.exit();
81
+
82
+
83
+ // measure time to .encodeAll()
84
+
85
+ now = Date.now();
86
+ // for (let i = 0; i < 1000; i++) {
87
+ // encoder.encodeAll();
88
+ // }
89
+ // console.log(Date.now() - now);
90
+
91
+ const allEncodes = Date.now();
92
+ for (let i = 0; i < 100; i++) {
93
+ now = Date.now();
94
+ for (let j = 0; j < 50; j++) {
95
+ const player = new Player();
96
+ state.players.set(`p-${nanoid()}`, player);
97
+
98
+ player.position.x = (j + 1) * 100;
99
+ player.position.y = (j + 1) * 100;
100
+ for (let k = 0; k < 10; k++) {
101
+ const item = new Item();
102
+ item.price = (j + 1) * 50;
103
+ for (let l = 0; l < 5; l++) {
104
+ const attr = new Attribute();
105
+ attr.name = `Attribute ${l}`;
106
+ attr.value = l;
107
+ item.attributes.push(attr);
108
+ }
109
+ player.items.set(`item-${k}`, item);
110
+ }
111
+ }
112
+ console.log("time to make changes:", Date.now() - now);
113
+
114
+ now = Date.now();
115
+ // encoder.encode();
116
+ encoder.discardChanges();
117
+ console.log("time to encode:", Date.now() - now);
118
+ }
119
+ console.log("time for all encodes:", Date.now() - allEncodes);
120
+
121
+ // console.log(Array.from(encoder.encodeAll()).length, "bytes");
package/src/debug.ts ADDED
@@ -0,0 +1,56 @@
1
+ import * as fs from "fs";
2
+ import { Reflection, Decoder } from "./index";
3
+
4
+ const contents = fs.readFileSync("/Users/endel/Projects/colyseus/clients/bubbits/project/@bubbits/backend/schema-debug.txt", { encoding: "utf8" }).toString();
5
+
6
+ let isCommentBlock = false;
7
+ let lastComment = "";
8
+
9
+ let decoder: Decoder;
10
+
11
+ function getBuffer(line: string) {
12
+ const start = line.lastIndexOf(":");
13
+ const buffer = Buffer.from(new Uint8Array(line.substring(start + 1).split(",").map(n => Number(n))));
14
+ console.log(`(${buffer.byteLength}) ${Array.from(buffer).join(",")}`)
15
+ // console.log("");
16
+ // console.log("");
17
+ // console.log("> ", line);
18
+ // console.log("> substring:", line.substring(start + 1))
19
+ return buffer;
20
+ }
21
+
22
+ function decode(buffer: Buffer) {
23
+ try {
24
+ decoder.decode(buffer);
25
+ } catch (e) {
26
+ console.error(e);
27
+ console.log("Last log:\n\n")
28
+ console.log(lastComment);
29
+ }
30
+ }
31
+
32
+ contents.split("\n").forEach((line) => {
33
+ if (line.startsWith("#")) {
34
+ // reset last comment.
35
+ if (isCommentBlock === false) { lastComment = ""; }
36
+
37
+ isCommentBlock = true;
38
+ lastComment += line.substring(line.indexOf(":") + 1) + "\n";
39
+ return;
40
+ }
41
+
42
+ isCommentBlock = false;
43
+
44
+ if (line.startsWith("handshake:") && !decoder) {
45
+ const state = Reflection.decode(getBuffer(line));
46
+ decoder = new Decoder(state);
47
+
48
+ } else if (line.startsWith("state:")) {
49
+ decode(getBuffer(line));
50
+
51
+ } else if (line.startsWith("patch:")) {
52
+ decode(getBuffer(line));
53
+ }
54
+ });
55
+
56
+ console.log(decoder.state.toJSON());
@@ -4,7 +4,7 @@ import { Schema } from "../Schema";
4
4
  import type { Ref } from "../encoder/ChangeTree";
5
5
  import type { Decoder } from "./Decoder";
6
6
  import * as decode from "../encoding/decode";
7
- import { $changes, $childType, $deleteByIndex, $getByIndex } from "../types/symbols";
7
+ import { $childType, $deleteByIndex, $getByIndex } from "../types/symbols";
8
8
 
9
9
  import type { MapSchema } from "../types/custom/MapSchema";
10
10
  import type { ArraySchema } from "../types/custom/ArraySchema";
@@ -43,7 +43,7 @@ export function decodeValue(
43
43
  it: decode.Iterator,
44
44
  allChanges: DataChange[],
45
45
  ) {
46
- const $root = decoder.$root;
46
+ const $root = decoder.root;
47
47
  const previousValue = ref[$getByIndex](index);
48
48
 
49
49
  let value: any;
@@ -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
@@ -182,21 +182,24 @@ 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,
189
192
  operation,
190
193
  ref,
191
194
  index,
192
- metadata[field].type,
195
+ field.type,
193
196
  bytes,
194
197
  it,
195
198
  allChanges,
196
199
  );
197
200
 
198
201
  if (value !== null && value !== undefined) {
199
- ref[field] = value;
202
+ ref[field.name] = value;
200
203
  }
201
204
 
202
205
  // add change
@@ -205,7 +208,7 @@ export const decodeSchemaOperation: DecodeOperation = function (
205
208
  ref,
206
209
  refId: decoder.currentRefId,
207
210
  op: operation,
208
- field: field,
211
+ field: field.name,
209
212
  value,
210
213
  previousValue,
211
214
  });
@@ -251,6 +254,7 @@ export const decodeKeyValueOperation: DecodeOperation = function (
251
254
  dynamicIndex = ref['getIndex'](index);
252
255
  }
253
256
 
257
+
254
258
  const { value, previousValue } = decodeValue(
255
259
  decoder,
256
260
  operation,
@@ -303,7 +307,8 @@ export const decodeArray: DecodeOperation = function (
303
307
  allChanges: DataChange[]
304
308
  ) {
305
309
  // "uncompressed" index + operation (array/map items)
306
- const operation = bytes[it.offset++];
310
+ let operation = bytes[it.offset++];
311
+ let index: number;
307
312
 
308
313
  if (operation === OPERATION.CLEAR) {
309
314
  //
@@ -318,8 +323,8 @@ export const decodeArray: DecodeOperation = function (
318
323
  } else if (operation === OPERATION.DELETE_BY_REFID) {
319
324
  // TODO: refactor here, try to follow same flow as below
320
325
  const refId = decode.number(bytes, it);
321
- const previousValue = decoder.$root.refs.get(refId);
322
- const index = ref.findIndex((value) => value === previousValue);
326
+ const previousValue = decoder.root.refs.get(refId);
327
+ index = ref.findIndex((value) => value === previousValue);
323
328
  ref[$deleteByIndex](index);
324
329
  allChanges.push({
325
330
  ref,
@@ -330,10 +335,22 @@ export const decodeArray: DecodeOperation = function (
330
335
  value: undefined,
331
336
  previousValue,
332
337
  });
338
+
333
339
  return;
340
+
341
+ } else if (operation === OPERATION.ADD_BY_REFID) {
342
+ const refId = decode.number(bytes, it);
343
+ const itemByRefId = decoder.root.refs.get(refId);
344
+
345
+ // use existing index, or push new value
346
+ index = (itemByRefId)
347
+ ? ref.findIndex((value) => value === itemByRefId)
348
+ : ref.length;
349
+
350
+ } else {
351
+ index = decode.number(bytes, it);
334
352
  }
335
353
 
336
- const index = decode.number(bytes, it);
337
354
  const type = ref[$childType];
338
355
 
339
356
  let dynamicIndex: number | string = index;