@colyseus/schema 3.0.0-alpha.35 → 3.0.0-alpha.36

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.
@@ -71,6 +71,102 @@ function getType(identifier) {
71
71
  return registeredTypes[identifier];
72
72
  }
73
73
 
74
+ class TypeContext {
75
+ /**
76
+ * For inheritance support
77
+ * Keeps track of which classes extends which. (parent -> children)
78
+ */
79
+ static { this.inheritedTypes = new Map(); }
80
+ static register(target) {
81
+ const parent = Object.getPrototypeOf(target);
82
+ if (parent !== Schema) {
83
+ let inherits = TypeContext.inheritedTypes.get(parent);
84
+ if (!inherits) {
85
+ inherits = new Set();
86
+ TypeContext.inheritedTypes.set(parent, inherits);
87
+ }
88
+ inherits.add(target);
89
+ }
90
+ }
91
+ constructor(rootClass) {
92
+ this.types = {};
93
+ this.schemas = new Map();
94
+ this.hasFilters = false;
95
+ this.parentFiltered = {};
96
+ if (rootClass) {
97
+ this.discoverTypes(rootClass);
98
+ }
99
+ }
100
+ has(schema) {
101
+ return this.schemas.has(schema);
102
+ }
103
+ get(typeid) {
104
+ return this.types[typeid];
105
+ }
106
+ add(schema, typeid = this.schemas.size) {
107
+ // skip if already registered
108
+ if (this.schemas.has(schema)) {
109
+ return false;
110
+ }
111
+ this.types[typeid] = schema;
112
+ //
113
+ // Workaround to allow using an empty Schema (with no `@type()` fields)
114
+ //
115
+ if (schema[Symbol.metadata] === undefined) {
116
+ Metadata.init(schema);
117
+ }
118
+ this.schemas.set(schema, typeid);
119
+ return true;
120
+ }
121
+ getTypeId(klass) {
122
+ return this.schemas.get(klass);
123
+ }
124
+ discoverTypes(klass, parentIndex, parentFieldViewTag) {
125
+ if (!this.add(klass)) {
126
+ return;
127
+ }
128
+ // add classes inherited from this base class
129
+ TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
130
+ this.discoverTypes(child, parentIndex, parentFieldViewTag);
131
+ });
132
+ const metadata = (klass[Symbol.metadata] ??= {});
133
+ // if any schema/field has filters, mark "context" as having filters.
134
+ if (metadata[$viewFieldIndexes]) {
135
+ this.hasFilters = true;
136
+ }
137
+ if (parentFieldViewTag !== undefined) {
138
+ this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
139
+ }
140
+ for (const fieldIndex in metadata) {
141
+ const index = fieldIndex;
142
+ const fieldType = metadata[index].type;
143
+ const viewTag = metadata[index].tag;
144
+ if (typeof (fieldType) === "string") {
145
+ continue;
146
+ }
147
+ if (Array.isArray(fieldType)) {
148
+ const type = fieldType[0];
149
+ // skip primitive types
150
+ if (type === "string") {
151
+ continue;
152
+ }
153
+ this.discoverTypes(type, index, viewTag);
154
+ }
155
+ else if (typeof (fieldType) === "function") {
156
+ this.discoverTypes(fieldType, viewTag);
157
+ }
158
+ else {
159
+ const type = Object.values(fieldType)[0];
160
+ // skip primitive types
161
+ if (typeof (type) === "string") {
162
+ continue;
163
+ }
164
+ this.discoverTypes(type, index, viewTag);
165
+ }
166
+ }
167
+ }
168
+ }
169
+
74
170
  const Metadata = {
75
171
  addField(metadata, index, name, type, descriptor) {
76
172
  if (index > 64) {
@@ -155,17 +251,42 @@ const Metadata = {
155
251
  metadata[$fieldIndexesByViewTag][tag].push(index);
156
252
  },
157
253
  setFields(target, fields) {
158
- const metadata = (target.prototype.constructor[Symbol.metadata] ??= {});
159
- let index = 0;
254
+ // for inheritance support
255
+ const constructor = target.prototype.constructor;
256
+ TypeContext.register(constructor);
257
+ const parentClass = Object.getPrototypeOf(constructor);
258
+ const parentMetadata = parentClass && parentClass[Symbol.metadata];
259
+ const metadata = Metadata.initialize(constructor, parentMetadata);
260
+ // Use Schema's methods if not defined in the class
261
+ if (!constructor[$track]) {
262
+ constructor[$track] = Schema[$track];
263
+ }
264
+ if (!constructor[$encoder]) {
265
+ constructor[$encoder] = Schema[$encoder];
266
+ }
267
+ if (!constructor[$decoder]) {
268
+ constructor[$decoder] = Schema[$decoder];
269
+ }
270
+ if (!constructor.prototype.toJSON) {
271
+ constructor.prototype.toJSON = Schema.prototype.toJSON;
272
+ }
273
+ //
274
+ // detect index for this field, considering inheritance
275
+ //
276
+ let fieldIndex = metadata[$numFields] // current structure already has fields defined
277
+ ?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
278
+ ?? -1; // no fields defined
279
+ fieldIndex++;
160
280
  for (const field in fields) {
161
281
  const type = fields[field];
162
282
  // FIXME: this code is duplicated from @type() annotation
163
283
  const complexTypeKlass = (Array.isArray(type))
164
284
  ? getType("array")
165
285
  : (typeof (Object.keys(type)[0]) === "string") && getType(Object.keys(type)[0]);
166
- Metadata.addField(metadata, index, field, type, getPropertyDescriptor(`_${field}`, index, type, complexTypeKlass));
167
- index++;
286
+ Metadata.addField(metadata, fieldIndex, field, type, getPropertyDescriptor(`_${field}`, fieldIndex, type, complexTypeKlass));
287
+ fieldIndex++;
168
288
  }
289
+ return target;
169
290
  },
170
291
  isDeprecated(metadata, field) {
171
292
  return metadata[field].deprecated === true;
@@ -2473,102 +2594,6 @@ class MapSchema {
2473
2594
  }
2474
2595
  registerType("map", { constructor: MapSchema });
2475
2596
 
2476
- class TypeContext {
2477
- /**
2478
- * For inheritance support
2479
- * Keeps track of which classes extends which. (parent -> children)
2480
- */
2481
- static { this.inheritedTypes = new Map(); }
2482
- static register(target) {
2483
- const parent = Object.getPrototypeOf(target);
2484
- if (parent !== Schema) {
2485
- let inherits = TypeContext.inheritedTypes.get(parent);
2486
- if (!inherits) {
2487
- inherits = new Set();
2488
- TypeContext.inheritedTypes.set(parent, inherits);
2489
- }
2490
- inherits.add(target);
2491
- }
2492
- }
2493
- constructor(rootClass) {
2494
- this.types = {};
2495
- this.schemas = new Map();
2496
- this.hasFilters = false;
2497
- this.parentFiltered = {};
2498
- if (rootClass) {
2499
- this.discoverTypes(rootClass);
2500
- }
2501
- }
2502
- has(schema) {
2503
- return this.schemas.has(schema);
2504
- }
2505
- get(typeid) {
2506
- return this.types[typeid];
2507
- }
2508
- add(schema, typeid = this.schemas.size) {
2509
- // skip if already registered
2510
- if (this.schemas.has(schema)) {
2511
- return false;
2512
- }
2513
- this.types[typeid] = schema;
2514
- //
2515
- // Workaround to allow using an empty Schema (with no `@type()` fields)
2516
- //
2517
- if (schema[Symbol.metadata] === undefined) {
2518
- Metadata.init(schema);
2519
- }
2520
- this.schemas.set(schema, typeid);
2521
- return true;
2522
- }
2523
- getTypeId(klass) {
2524
- return this.schemas.get(klass);
2525
- }
2526
- discoverTypes(klass, parentIndex, parentFieldViewTag) {
2527
- if (!this.add(klass)) {
2528
- return;
2529
- }
2530
- // add classes inherited from this base class
2531
- TypeContext.inheritedTypes.get(klass)?.forEach((child) => {
2532
- this.discoverTypes(child, parentIndex, parentFieldViewTag);
2533
- });
2534
- const metadata = (klass[Symbol.metadata] ??= {});
2535
- // if any schema/field has filters, mark "context" as having filters.
2536
- if (metadata[$viewFieldIndexes]) {
2537
- this.hasFilters = true;
2538
- }
2539
- if (parentFieldViewTag !== undefined) {
2540
- this.parentFiltered[`${this.schemas.get(klass)}-${parentIndex}`] = true;
2541
- }
2542
- for (const fieldIndex in metadata) {
2543
- const index = fieldIndex;
2544
- const fieldType = metadata[index].type;
2545
- const viewTag = metadata[index].tag;
2546
- if (typeof (fieldType) === "string") {
2547
- continue;
2548
- }
2549
- if (Array.isArray(fieldType)) {
2550
- const type = fieldType[0];
2551
- // skip primitive types
2552
- if (type === "string") {
2553
- continue;
2554
- }
2555
- this.discoverTypes(type, index, viewTag);
2556
- }
2557
- else if (typeof (fieldType) === "function") {
2558
- this.discoverTypes(fieldType, viewTag);
2559
- }
2560
- else {
2561
- const type = Object.values(fieldType)[0];
2562
- // skip primitive types
2563
- if (typeof (type) === "string") {
2564
- continue;
2565
- }
2566
- this.discoverTypes(type, index, viewTag);
2567
- }
2568
- }
2569
- }
2570
- }
2571
-
2572
2597
  const DEFAULT_VIEW_TAG = -1;
2573
2598
  /**
2574
2599
  * [See documentation](https://docs.colyseus.io/state/schema/)
@@ -3866,6 +3891,10 @@ class Encoder {
3866
3891
  tryEncodeTypeId(bytes, baseType, targetType, it) {
3867
3892
  const baseTypeId = this.context.getTypeId(baseType);
3868
3893
  const targetTypeId = this.context.getTypeId(targetType);
3894
+ if (targetTypeId === undefined) {
3895
+ console.warn(`@colyseus/schema WARNING: Class "${targetType.name}" is not registered on TypeRegistry - Please either tag the class with @entity or define a @type() field.`);
3896
+ return;
3897
+ }
3869
3898
  if (baseTypeId !== targetTypeId) {
3870
3899
  bytes[it.offset++] = TYPE_ID & 255;
3871
3900
  number$1(bytes, targetTypeId, it);
@@ -4135,10 +4164,16 @@ class Reflection extends Schema {
4135
4164
  super(...arguments);
4136
4165
  this.types = new ArraySchema();
4137
4166
  }
4138
- static encode(instance, context, it = { offset: 0 }) {
4139
- context ??= new TypeContext(instance.constructor);
4167
+ /**
4168
+ * Encodes the TypeContext of an Encoder into a buffer.
4169
+ *
4170
+ * @param context TypeContext instance
4171
+ * @param it
4172
+ * @returns
4173
+ */
4174
+ static encode(context, it = { offset: 0 }) {
4140
4175
  const reflection = new Reflection();
4141
- const encoder = new Encoder(reflection);
4176
+ const reflectionEncoder = new Encoder(reflection);
4142
4177
  const buildType = (currentType, metadata) => {
4143
4178
  for (const fieldIndex in metadata) {
4144
4179
  const index = Number(fieldIndex);
@@ -4192,9 +4227,16 @@ class Reflection extends Schema {
4192
4227
  }
4193
4228
  buildType(type, klass[Symbol.metadata]);
4194
4229
  }
4195
- const buf = encoder.encodeAll(it);
4230
+ const buf = reflectionEncoder.encodeAll(it);
4196
4231
  return Buffer.from(buf, 0, it.offset);
4197
4232
  }
4233
+ /**
4234
+ * Decodes the TypeContext from a buffer into a Decoder instance.
4235
+ *
4236
+ * @param bytes Reflection.encode() output
4237
+ * @param it
4238
+ * @returns Decoder instance
4239
+ */
4198
4240
  static decode(bytes, it) {
4199
4241
  const reflection = new Reflection();
4200
4242
  const reflectionDecoder = new Decoder(reflection);
@@ -4240,8 +4282,8 @@ class Reflection extends Schema {
4240
4282
  }
4241
4283
  });
4242
4284
  });
4243
- // @ts-ignore
4244
- return new (typeContext.get(0))();
4285
+ const state = new (typeContext.get(0))();
4286
+ return new Decoder(state, typeContext);
4245
4287
  }
4246
4288
  }
4247
4289
  __decorate([