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

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 (116) hide show
  1. package/README.md +131 -61
  2. package/build/cjs/index.js +644 -283
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +643 -282
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +644 -283
  7. package/lib/Metadata.d.ts +2 -0
  8. package/lib/Metadata.js +39 -0
  9. package/lib/Metadata.js.map +1 -1
  10. package/lib/Reflection.d.ts +2 -3
  11. package/lib/Reflection.js +31 -26
  12. package/lib/Reflection.js.map +1 -1
  13. package/lib/Schema.d.ts +2 -2
  14. package/lib/Schema.js +2 -2
  15. package/lib/Schema.js.map +1 -1
  16. package/lib/annotations.d.ts +0 -19
  17. package/lib/annotations.js +15 -101
  18. package/lib/annotations.js.map +1 -1
  19. package/lib/bench_encode.d.ts +1 -0
  20. package/lib/bench_encode.js +120 -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 -7
  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/strategy/RawChanges.js +1 -2
  52. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  53. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  54. package/lib/decoder/strategy/StateCallbacks.js +72 -63
  55. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  56. package/lib/encoder/ChangeTree.d.ts +1 -12
  57. package/lib/encoder/ChangeTree.js +29 -58
  58. package/lib/encoder/ChangeTree.js.map +1 -1
  59. package/lib/encoder/EncodeOperation.d.ts +0 -1
  60. package/lib/encoder/EncodeOperation.js +29 -13
  61. package/lib/encoder/EncodeOperation.js.map +1 -1
  62. package/lib/encoder/Encoder.d.ts +10 -8
  63. package/lib/encoder/Encoder.js +78 -53
  64. package/lib/encoder/Encoder.js.map +1 -1
  65. package/lib/encoder/Root.d.ts +17 -0
  66. package/lib/encoder/Root.js +44 -0
  67. package/lib/encoder/Root.js.map +1 -0
  68. package/lib/encoder/StateView.d.ts +2 -2
  69. package/lib/encoder/StateView.js +48 -58
  70. package/lib/encoder/StateView.js.map +1 -1
  71. package/lib/encoding/assert.js +3 -3
  72. package/lib/encoding/assert.js.map +1 -1
  73. package/lib/encoding/decode.js +21 -22
  74. package/lib/encoding/decode.js.map +1 -1
  75. package/lib/encoding/encode.d.ts +2 -2
  76. package/lib/encoding/encode.js +40 -39
  77. package/lib/encoding/encode.js.map +1 -1
  78. package/lib/encoding/spec.d.ts +2 -1
  79. package/lib/encoding/spec.js +1 -0
  80. package/lib/encoding/spec.js.map +1 -1
  81. package/lib/index.d.ts +5 -1
  82. package/lib/index.js +8 -4
  83. package/lib/index.js.map +1 -1
  84. package/lib/types/TypeContext.d.ts +23 -0
  85. package/lib/types/TypeContext.js +109 -0
  86. package/lib/types/TypeContext.js.map +1 -0
  87. package/lib/types/custom/ArraySchema.d.ts +2 -2
  88. package/lib/types/custom/ArraySchema.js +0 -9
  89. package/lib/types/custom/ArraySchema.js.map +1 -1
  90. package/lib/types/registry.js +3 -4
  91. package/lib/types/registry.js.map +1 -1
  92. package/lib/types/utils.js +1 -2
  93. package/lib/types/utils.js.map +1 -1
  94. package/lib/utils.js +3 -4
  95. package/lib/utils.js.map +1 -1
  96. package/package.json +5 -5
  97. package/src/Metadata.ts +47 -0
  98. package/src/Reflection.ts +34 -26
  99. package/src/Schema.ts +2 -2
  100. package/src/annotations.ts +7 -109
  101. package/src/bench_encode.ts +97 -0
  102. package/src/debug.ts +56 -0
  103. package/src/decoder/DecodeOperation.ts +30 -7
  104. package/src/decoder/Decoder.ts +13 -11
  105. package/src/decoder/strategy/StateCallbacks.ts +149 -79
  106. package/src/encoder/ChangeTree.ts +36 -66
  107. package/src/encoder/EncodeOperation.ts +29 -12
  108. package/src/encoder/Encoder.ts +95 -61
  109. package/src/encoder/Root.ts +51 -0
  110. package/src/encoder/StateView.ts +51 -67
  111. package/src/encoding/decode.ts +1 -2
  112. package/src/encoding/encode.ts +25 -22
  113. package/src/encoding/spec.ts +1 -0
  114. package/src/index.ts +8 -11
  115. package/src/types/TypeContext.ts +127 -0
  116. package/src/types/custom/ArraySchema.ts +2 -2
package/src/Metadata.ts CHANGED
@@ -117,6 +117,53 @@ export const Metadata = {
117
117
  return metadata[field].deprecated === true;
118
118
  },
119
119
 
120
+ init(klass: any) {
121
+ //
122
+ // Used only to initialize an empty Schema (Encoder#constructor)
123
+ // TODO: remove/refactor this...
124
+ //
125
+ const metadata = {};
126
+ klass[Symbol.metadata] = metadata;
127
+ Object.defineProperty(metadata, -1, {
128
+ value: 0,
129
+ enumerable: false,
130
+ configurable: true,
131
+ });
132
+ },
133
+
134
+ initialize(constructor: any, parentMetadata?: any) {
135
+ let metadata: Metadata = constructor[Symbol.metadata] ?? Object.create(null);
136
+
137
+ // make sure inherited classes have their own metadata object.
138
+ if (constructor[Symbol.metadata] === parentMetadata) {
139
+ metadata = Object.create(null);
140
+
141
+ if (parentMetadata) {
142
+ // assign parent metadata to current
143
+ Object.assign(metadata, parentMetadata);
144
+
145
+ for (let i = 0; i <= parentMetadata[-1]; i++) {
146
+ Object.defineProperty(metadata, i, {
147
+ value: parentMetadata[i],
148
+ enumerable: false,
149
+ configurable: true,
150
+ });
151
+ }
152
+
153
+ Object.defineProperty(metadata, -1, {
154
+ value: parentMetadata[-1],
155
+ enumerable: false,
156
+ configurable: true,
157
+ writable: true,
158
+ });
159
+ }
160
+ }
161
+
162
+ constructor[Symbol.metadata] = metadata;
163
+
164
+ return metadata;
165
+ },
166
+
120
167
  isValidInstance(klass: any) {
121
168
  return (
122
169
  klass.constructor[Symbol.metadata] &&
package/src/Reflection.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type, PrimitiveType, DefinitionType, TypeContext } from "./annotations";
1
+ import { type, PrimitiveType, DefinitionType } from "./annotations";
2
+ import { TypeContext } from "./types/TypeContext";
2
3
  import { Metadata } from "./Metadata";
3
4
  import { ArraySchema } from "./types/custom/ArraySchema";
4
5
  import { Iterator } from "./encoding/decode";
@@ -24,10 +25,8 @@ export class ReflectionType extends Schema {
24
25
  export class Reflection extends Schema {
25
26
  @type([ ReflectionType ]) types: ArraySchema<ReflectionType> = new ArraySchema<ReflectionType>();
26
27
 
27
- static encode (instance: Schema, context?: TypeContext) {
28
- if (!context) {
29
- context = new TypeContext(instance.constructor as typeof Schema);
30
- }
28
+ static encode(instance: Schema, context?: TypeContext, it: Iterator = { offset: 0 }) {
29
+ context ??= new TypeContext(instance.constructor as typeof Schema);
31
30
 
32
31
  const reflection = new Reflection();
33
32
  const encoder = new Encoder(reflection);
@@ -96,7 +95,6 @@ export class Reflection extends Schema {
96
95
  buildType(type, klass[Symbol.metadata]);
97
96
  }
98
97
 
99
- const it = { offset: 0 };
100
98
  const buf = encoder.encodeAll(it);
101
99
  return Buffer.from(buf, 0, it.offset);
102
100
  }
@@ -107,62 +105,72 @@ export class Reflection extends Schema {
107
105
  const reflectionDecoder = new Decoder(reflection);
108
106
  reflectionDecoder.decode(bytes, it);
109
107
 
110
- const context = new TypeContext();
108
+ const typeContext = new TypeContext();
111
109
 
112
- const schemaTypes = reflection.types.reduce((types, reflectionType) => {
113
- const parentKlass: typeof Schema = types[reflectionType.extendsId] || Schema;
114
- const schema: typeof Schema = class _ extends parentKlass {};
110
+ // 1st pass, initialize metadata + inheritance
111
+ reflection.types.forEach((reflectionType) => {
112
+ const parentClass: typeof Schema = typeContext.get(reflectionType.extendsId) ?? Schema;
113
+ const schema: typeof Schema = class _ extends parentClass {};
115
114
 
116
- // const _metadata = Object.create(_classSuper[Symbol.metadata] ?? null);
117
- const _metadata = parentKlass && parentKlass[Symbol.metadata] || Object.create(null);
118
- Object.defineProperty(schema, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata })
115
+ const parentMetadata = parentClass[Symbol.metadata];
119
116
 
120
117
  // register for inheritance support
121
118
  TypeContext.register(schema);
122
119
 
123
- const typeid = reflectionType.id;
124
- types[typeid] = schema
125
- context.add(schema, typeid);
126
- return types;
120
+ // for inheritance support
121
+ Metadata.initialize(schema, parentMetadata);
122
+
123
+ typeContext.add(schema, reflectionType.id);
127
124
  }, {});
128
125
 
126
+ // 2nd pass, set fields
129
127
  reflection.types.forEach((reflectionType) => {
130
- const schemaType = schemaTypes[reflectionType.id];
128
+ const schemaType = typeContext.get(reflectionType.id);
131
129
  const metadata = schemaType[Symbol.metadata];
132
130
 
133
- const parentKlass = reflection.types[reflectionType.extendsId];
134
- const parentFieldIndex = parentKlass && parentKlass.fields.length || 0;
131
+ // FIXME: use metadata[-1] to get field count
132
+ const parentFieldIndex = 0;
133
+
134
+ // console.log("--------------------");
135
+ // // console.log("reflectionType", reflectionType.toJSON());
136
+ // console.log("reflectionType.fields", reflectionType.fields.toJSON());
137
+ // console.log("parentFieldIndex", parentFieldIndex);
138
+
139
+ //
140
+ // FIXME: set fields using parentKlass as well
141
+ // currently the fields are duplicated on inherited classes
142
+ //
143
+ // // const parentKlass = reflection.types[reflectionType.extendsId];
144
+ // // parentKlass.fields
135
145
 
136
146
  reflectionType.fields.forEach((field, i) => {
137
147
  const fieldIndex = parentFieldIndex + i;
138
148
 
139
149
  if (field.referencedType !== undefined) {
140
150
  let fieldType = field.type;
141
- let refType = schemaTypes[field.referencedType];
151
+ let refType: PrimitiveType = typeContext.get(field.referencedType);
142
152
 
143
153
  // map or array of primitive type (-1)
144
154
  if (!refType) {
145
155
  const typeInfo = field.type.split(":");
146
156
  fieldType = typeInfo[0];
147
- refType = typeInfo[1];
157
+ refType = typeInfo[1] as PrimitiveType; // string
148
158
  }
149
159
 
150
160
  if (fieldType === "ref") {
151
- // type(refType)(schemaType.prototype, field.name);
152
161
  Metadata.addField(metadata, fieldIndex, field.name, refType);
153
162
 
154
163
  } else {
155
- // type({ [fieldType]: refType } as DefinitionType)(schemaType.prototype, field.name);
156
164
  Metadata.addField(metadata, fieldIndex, field.name, { [fieldType]: refType } as DefinitionType);
157
165
  }
158
166
 
159
167
  } else {
160
- // type(field.type as PrimitiveType)(schemaType.prototype, field.name);
161
168
  Metadata.addField(metadata, fieldIndex, field.name, field.type as PrimitiveType);
162
169
  }
163
170
  });
164
171
  });
165
172
 
166
- return new (schemaTypes[0])();
173
+ // @ts-ignore
174
+ return new (typeContext.get(0))();
167
175
  }
168
176
  }
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];
@@ -6,6 +6,7 @@ import { Metadata } from "./Metadata";
6
6
  import { $changes, $childType, $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";
9
10
 
10
11
  /**
11
12
  * Data types
@@ -42,113 +43,6 @@ export interface TypeOptions {
42
43
 
43
44
  export const DEFAULT_VIEW_TAG = -1;
44
45
 
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
46
  export function entity(constructor, context: ClassDecoratorContext) {
153
47
  if (!constructor._definition) {
154
48
  // for inheritance support
@@ -330,6 +224,8 @@ export function view<T> (tag: number = DEFAULT_VIEW_TAG) {
330
224
 
331
225
  const parentClass = Object.getPrototypeOf(constructor);
332
226
  const parentMetadata = parentClass[Symbol.metadata];
227
+
228
+ // TODO: use Metadata.initialize()
333
229
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
334
230
 
335
231
  if (!metadata[fieldName]) {
@@ -356,6 +252,8 @@ export function unreliable<T> (target: T, field: string) {
356
252
 
357
253
  const parentClass = Object.getPrototypeOf(constructor);
358
254
  const parentMetadata = parentClass[Symbol.metadata];
255
+
256
+ // TODO: use Metadata.initialize()
359
257
  const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
360
258
 
361
259
  if (!metadata[field]) {
@@ -389,8 +287,8 @@ export function type (
389
287
  TypeContext.register(constructor);
390
288
 
391
289
  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)));
290
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
291
+ const metadata = Metadata.initialize(constructor, parentMetadata);
394
292
 
395
293
  let fieldIndex: number;
396
294
 
@@ -0,0 +1,97 @@
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
+ for (let i = 0; i < 50; i++) {
35
+ const player = new Player();
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 Item();
42
+ item.price = (i + 1) * 50;
43
+ for (let k = 0; k < 5; k++) {
44
+ const attr = new Attribute();
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 = 4096 * 4096;
56
+ const encoder = new Encoder(state);
57
+
58
+ // measure time to .encodeAll()
59
+
60
+ let now = Date.now();
61
+ for (let i = 0; i < 1000; i++) {
62
+ encoder.encodeAll();
63
+ }
64
+ console.log(Date.now() - now);
65
+
66
+ const allEncodes = Date.now();
67
+ for (let i = 0; i < 100; i++) {
68
+ now = Date.now();
69
+ for (let j = 0; j < 50; j++) {
70
+ const player = new Player();
71
+ state.players.set(`p-${nanoid()}`, player);
72
+
73
+ player.position.x = (j + 1) * 100;
74
+ player.position.y = (j + 1) * 100;
75
+ for (let k = 0; k < 10; k++) {
76
+ const item = new Item();
77
+ item.price = (j + 1) * 50;
78
+ for (let l = 0; l < 5; l++) {
79
+ const attr = new Attribute();
80
+ attr.name = `Attribute ${l}`;
81
+ attr.value = l;
82
+ item.attributes.push(attr);
83
+
84
+ }
85
+ player.items.set(`item-${k}`, item);
86
+ }
87
+ }
88
+ console.log("time to make changes:", Date.now() - now);
89
+
90
+ now = Date.now();
91
+ encoder.encode();
92
+ encoder.discardChanges();
93
+ console.log("time to encode:", Date.now() - now);
94
+ }
95
+ console.log("time for all encodes:", Date.now() - allEncodes);
96
+
97
+ 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;
@@ -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,
@@ -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,10 @@ 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
+
312
+ let isSchemaChild: boolean;
313
+ let index: number;
307
314
 
308
315
  if (operation === OPERATION.CLEAR) {
309
316
  //
@@ -318,8 +325,8 @@ export const decodeArray: DecodeOperation = function (
318
325
  } else if (operation === OPERATION.DELETE_BY_REFID) {
319
326
  // TODO: refactor here, try to follow same flow as below
320
327
  const refId = decode.number(bytes, it);
321
- const previousValue = decoder.$root.refs.get(refId);
322
- const index = ref.findIndex((value) => value === previousValue);
328
+ const previousValue = decoder.root.refs.get(refId);
329
+ index = ref.findIndex((value) => value === previousValue);
323
330
  ref[$deleteByIndex](index);
324
331
  allChanges.push({
325
332
  ref,
@@ -330,10 +337,26 @@ export const decodeArray: DecodeOperation = function (
330
337
  value: undefined,
331
338
  previousValue,
332
339
  });
340
+
333
341
  return;
342
+
343
+ } else if (operation === OPERATION.ADD_BY_REFID) {
344
+ isSchemaChild = true;
345
+ // operation = OPERATION.ADD;
346
+
347
+ const refId = decode.number(bytes, it);
348
+ const itemByRefId = decoder.root.refs.get(refId);
349
+
350
+ // use existing index, or push new value
351
+ index = (itemByRefId)
352
+ ? ref.findIndex((value) => value === itemByRefId)
353
+ : ref.length;
354
+
355
+ } else {
356
+ isSchemaChild = false;
357
+ index = decode.number(bytes, it);
334
358
  }
335
359
 
336
- const index = decode.number(bytes, it);
337
360
  const type = ref[$childType];
338
361
 
339
362
  let dynamicIndex: number | string = index;
@@ -1,27 +1,28 @@
1
- import { TypeContext } from "../annotations";
1
+ import { TypeContext } from "../types/TypeContext";
2
2
  import { $changes, $childType, $decoder, $onDecodeEnd } from "../types/symbols";
3
3
  import { Schema } from "../Schema";
4
4
 
5
5
  import * as decode from "../encoding/decode";
6
6
  import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
7
- import { Ref } from "../encoder/ChangeTree";
8
- import { Iterator } from "../encoding/decode";
7
+ import type { Ref } from "../encoder/ChangeTree";
8
+ import type { Iterator } from "../encoding/decode";
9
9
  import { ReferenceTracker } from "./ReferenceTracker";
10
- import { DEFINITION_MISMATCH, DataChange, DecodeOperation } from "./DecodeOperation";
10
+ import { DEFINITION_MISMATCH, type DataChange, type DecodeOperation } from "./DecodeOperation";
11
11
  import { Collection } from "../types/HelperTypes";
12
12
 
13
13
  export class Decoder<T extends Schema = any> {
14
14
  context: TypeContext;
15
15
 
16
16
  state: T;
17
- $root: ReferenceTracker;
17
+ root: ReferenceTracker;
18
18
 
19
19
  currentRefId: number = 0;
20
20
 
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,10 +31,10 @@ 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
- this.$root = new ReferenceTracker();
36
- this.$root.addRef(0, root);
36
+ this.root = new ReferenceTracker();
37
+ this.root.addRef(0, root);
37
38
  }
38
39
 
39
40
  decode(
@@ -43,7 +44,7 @@ export class Decoder<T extends Schema = any> {
43
44
  ) {
44
45
  const allChanges: DataChange[] = [];
45
46
 
46
- const $root = this.$root;
47
+ const $root = this.root;
47
48
  const totalBytes = bytes.byteLength;
48
49
 
49
50
  let decoder: DecodeOperation = ref['constructor'][$decoder];
@@ -66,6 +67,7 @@ export class Decoder<T extends Schema = any> {
66
67
  if (!nextRef) { throw new Error(`"refId" not found: ${this.currentRefId}`); }
67
68
  ref[$onDecodeEnd]?.()
68
69
  ref = nextRef;
70
+
69
71
  decoder = ref['constructor'][$decoder];
70
72
 
71
73
  continue;
@@ -146,7 +148,7 @@ export class Decoder<T extends Schema = any> {
146
148
  });
147
149
 
148
150
  if (needRemoveRef) {
149
- this.$root.removeRef(this.$root.refIds.get(value));
151
+ this.root.removeRef(this.root.refIds.get(value));
150
152
  }
151
153
  });
152
154
  }