@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.
- package/README.md +131 -61
- package/build/cjs/index.js +966 -563
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +965 -562
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +966 -563
- package/lib/Metadata.d.ts +15 -4
- package/lib/Metadata.js +86 -18
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +2 -3
- package/lib/Reflection.js +24 -28
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +2 -2
- package/lib/Schema.js +28 -41
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +1 -21
- package/lib/annotations.js +73 -153
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +142 -0
- package/lib/bench_encode.js.map +1 -0
- package/lib/codegen/api.js +1 -2
- package/lib/codegen/api.js.map +1 -1
- package/lib/codegen/languages/cpp.js +1 -2
- package/lib/codegen/languages/cpp.js.map +1 -1
- package/lib/codegen/languages/csharp.js +1 -2
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +1 -2
- package/lib/codegen/languages/haxe.js.map +1 -1
- package/lib/codegen/languages/java.js +1 -2
- package/lib/codegen/languages/java.js.map +1 -1
- package/lib/codegen/languages/js.js +1 -2
- package/lib/codegen/languages/js.js.map +1 -1
- package/lib/codegen/languages/lua.js +1 -2
- package/lib/codegen/languages/lua.js.map +1 -1
- package/lib/codegen/languages/ts.js +1 -2
- package/lib/codegen/languages/ts.js.map +1 -1
- package/lib/codegen/parser.js +2 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +3 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +52 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +23 -11
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +8 -8
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +3 -2
- package/lib/decoder/ReferenceTracker.js.map +1 -1
- package/lib/decoder/strategy/RawChanges.js +1 -2
- package/lib/decoder/strategy/RawChanges.js.map +1 -1
- package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
- package/lib/decoder/strategy/StateCallbacks.js +75 -65
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +9 -19
- package/lib/encoder/ChangeTree.js +129 -145
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +1 -5
- package/lib/encoder/EncodeOperation.js +74 -58
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +10 -8
- package/lib/encoder/Encoder.js +89 -56
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +17 -0
- package/lib/encoder/Root.js +44 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +2 -2
- package/lib/encoder/StateView.js +49 -59
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.d.ts +2 -1
- package/lib/encoding/assert.js +5 -5
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.js +21 -22
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +40 -39
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +2 -1
- package/lib/encoding/spec.js +1 -0
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +6 -3
- package/lib/index.js +18 -13
- package/lib/index.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +102 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +6 -9
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/lib/types/custom/CollectionSchema.js +1 -0
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +5 -0
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.js +1 -0
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/lib/types/registry.js +3 -4
- package/lib/types/registry.js.map +1 -1
- package/lib/types/symbols.d.ts +1 -0
- package/lib/types/symbols.js +2 -1
- package/lib/types/symbols.js.map +1 -1
- package/lib/types/utils.js +1 -2
- package/lib/types/utils.js.map +1 -1
- package/lib/utils.js +3 -4
- package/lib/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/Metadata.ts +104 -26
- package/src/Reflection.ts +26 -28
- package/src/Schema.ts +35 -47
- package/src/annotations.ts +82 -176
- package/src/bench_encode.ts +121 -0
- package/src/debug.ts +56 -0
- package/src/decoder/DecodeOperation.ts +28 -11
- package/src/decoder/Decoder.ts +13 -11
- package/src/decoder/ReferenceTracker.ts +3 -2
- package/src/decoder/strategy/StateCallbacks.ts +152 -81
- package/src/encoder/ChangeTree.ts +147 -166
- package/src/encoder/EncodeOperation.ts +93 -70
- package/src/encoder/Encoder.ts +111 -65
- package/src/encoder/Root.ts +51 -0
- package/src/encoder/StateView.ts +53 -69
- package/src/encoding/assert.ts +4 -3
- package/src/encoding/decode.ts +1 -2
- package/src/encoding/encode.ts +25 -22
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -14
- package/src/types/TypeContext.ts +122 -0
- package/src/types/custom/ArraySchema.ts +10 -2
- package/src/types/custom/CollectionSchema.ts +1 -0
- package/src/types/custom/MapSchema.ts +6 -0
- package/src/types/custom/SetSchema.ts +1 -0
- package/src/types/symbols.ts +2 -0
package/src/annotations.ts
CHANGED
|
@@ -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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
|
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[
|
|
401
|
-
if (metadata[
|
|
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[
|
|
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(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
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
|
-
|
|
407
|
+
changeTree.root?.remove(previousValue[$changes]);
|
|
498
408
|
}
|
|
499
409
|
|
|
500
410
|
// flag the change for encoding.
|
|
501
|
-
this.constructor[$track](
|
|
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
|
-
|
|
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
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
|
|
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[
|
|
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,
|
|
571
|
-
value: metadata[
|
|
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 { $
|
|
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
|
|
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
|
|
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) {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
322
|
-
|
|
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;
|