@colyseus/schema 3.0.0-alpha.4 → 3.0.0-alpha.40
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/bin/schema-debug +94 -0
- package/build/cjs/index.js +1521 -809
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +1519 -808
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +1528 -816
- package/lib/Metadata.d.ts +21 -9
- package/lib/Metadata.js +169 -32
- package/lib/Metadata.js.map +1 -1
- package/lib/Reflection.d.ts +19 -4
- package/lib/Reflection.js +66 -32
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +4 -4
- package/lib/Schema.js +44 -50
- package/lib/Schema.js.map +1 -1
- package/lib/annotations.d.ts +31 -34
- package/lib/annotations.js +110 -160
- package/lib/annotations.js.map +1 -1
- package/lib/bench_encode.d.ts +1 -0
- package/lib/bench_encode.js +130 -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 +85 -3
- package/lib/codegen/parser.js.map +1 -1
- package/lib/codegen/types.js +6 -3
- package/lib/codegen/types.js.map +1 -1
- package/lib/debug.d.ts +1 -0
- package/lib/debug.js +51 -0
- package/lib/debug.js.map +1 -0
- package/lib/decoder/DecodeOperation.d.ts +0 -1
- package/lib/decoder/DecodeOperation.js +30 -12
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +6 -7
- package/lib/decoder/Decoder.js +9 -9
- 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 +27 -21
- package/lib/encoder/ChangeTree.js +246 -186
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +3 -6
- package/lib/encoder/EncodeOperation.js +47 -61
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +9 -8
- package/lib/encoder/Encoder.js +165 -88
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/Root.d.ts +22 -0
- package/lib/encoder/Root.js +81 -0
- package/lib/encoder/Root.js.map +1 -0
- package/lib/encoder/StateView.d.ts +7 -7
- package/lib/encoder/StateView.js +70 -74
- 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 +20 -21
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +2 -2
- package/lib/encoding/encode.js +52 -48
- 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 +19 -13
- package/lib/index.js.map +1 -1
- package/lib/types/HelperTypes.d.ts +34 -2
- package/lib/types/HelperTypes.js.map +1 -1
- package/lib/types/TypeContext.d.ts +23 -0
- package/lib/types/TypeContext.js +111 -0
- package/lib/types/TypeContext.js.map +1 -0
- package/lib/types/custom/ArraySchema.d.ts +2 -2
- package/lib/types/custom/ArraySchema.js +33 -22
- 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.d.ts +3 -1
- package/lib/types/custom/MapSchema.js +12 -4
- 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 +8 -5
- package/lib/types/symbols.js +9 -6
- 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 +9 -7
- package/lib/utils.js.map +1 -1
- package/package.json +7 -6
- package/src/Metadata.ts +190 -42
- package/src/Reflection.ts +77 -39
- package/src/Schema.ts +59 -64
- package/src/annotations.ts +155 -203
- package/src/bench_encode.ts +108 -0
- package/src/codegen/parser.ts +107 -0
- package/src/codegen/types.ts +1 -0
- package/src/debug.ts +55 -0
- package/src/decoder/DecodeOperation.ts +40 -12
- package/src/decoder/Decoder.ts +14 -12
- package/src/decoder/ReferenceTracker.ts +3 -2
- package/src/decoder/strategy/StateCallbacks.ts +153 -82
- package/src/encoder/ChangeTree.ts +286 -202
- package/src/encoder/EncodeOperation.ts +77 -77
- package/src/encoder/Encoder.ts +201 -96
- package/src/encoder/Root.ts +93 -0
- package/src/encoder/StateView.ts +76 -88
- package/src/encoding/assert.ts +4 -3
- package/src/encoding/encode.ts +37 -31
- package/src/encoding/spec.ts +1 -0
- package/src/index.ts +8 -14
- package/src/types/HelperTypes.ts +54 -2
- package/src/types/TypeContext.ts +133 -0
- package/src/types/custom/ArraySchema.ts +49 -19
- package/src/types/custom/CollectionSchema.ts +1 -0
- package/src/types/custom/MapSchema.ts +18 -5
- package/src/types/custom/SetSchema.ts +1 -0
- package/src/types/symbols.ts +10 -7
- package/src/utils.ts +7 -3
package/src/annotations.ts
CHANGED
|
@@ -3,15 +3,17 @@ 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, $numFields, $track } from "./types/symbols";
|
|
7
7
|
import { TypeDefinition, getType } from "./types/registry";
|
|
8
8
|
import { OPERATION } from "./encoding/spec";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
import { TypeContext } from "./types/TypeContext";
|
|
10
|
+
import { assertInstanceType, assertType } from "./encoding/assert";
|
|
11
|
+
import type { Ref } from "./encoder/ChangeTree";
|
|
12
|
+
import type { DefinedSchemaType, InferValueType } from "./types/HelperTypes";
|
|
13
|
+
import type { CollectionSchema } from "./types/custom/CollectionSchema";
|
|
14
|
+
import type { SetSchema } from "./types/custom/SetSchema";
|
|
15
|
+
|
|
16
|
+
export type RawPrimitiveType = "string" |
|
|
15
17
|
"number" |
|
|
16
18
|
"boolean" |
|
|
17
19
|
"int8" |
|
|
@@ -23,16 +25,18 @@ export type PrimitiveType =
|
|
|
23
25
|
"int64" |
|
|
24
26
|
"uint64" |
|
|
25
27
|
"float32" |
|
|
26
|
-
"float64"
|
|
27
|
-
typeof Schema |
|
|
28
|
-
object;
|
|
28
|
+
"float64";
|
|
29
29
|
|
|
30
|
-
export type
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
| {
|
|
30
|
+
export type PrimitiveType = RawPrimitiveType | typeof Schema | object;
|
|
31
|
+
|
|
32
|
+
// TODO: infer "default" value type correctly.
|
|
33
|
+
export type DefinitionType<T extends PrimitiveType = PrimitiveType> = T
|
|
34
|
+
| T[]
|
|
35
|
+
| { type: T, default?: InferValueType<T>, view?: boolean | number }
|
|
36
|
+
| { array: T, default?: ArraySchema<InferValueType<T>>, view?: boolean | number }
|
|
37
|
+
| { map: T, default?: MapSchema<InferValueType<T>>, view?: boolean | number }
|
|
38
|
+
| { collection: T, default?: CollectionSchema<InferValueType<T>>, view?: boolean | number }
|
|
39
|
+
| { set: T, default?: SetSchema<InferValueType<T>>, view?: boolean | number };
|
|
36
40
|
|
|
37
41
|
export type Definition = { [field: string]: DefinitionType };
|
|
38
42
|
|
|
@@ -42,119 +46,8 @@ export interface TypeOptions {
|
|
|
42
46
|
|
|
43
47
|
export const DEFAULT_VIEW_TAG = -1;
|
|
44
48
|
|
|
45
|
-
export
|
|
46
|
-
|
|
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
|
-
export function entity(constructor, context: ClassDecoratorContext) {
|
|
153
|
-
if (!constructor._definition) {
|
|
154
|
-
// for inheritance support
|
|
155
|
-
TypeContext.register(constructor);
|
|
156
|
-
}
|
|
157
|
-
|
|
49
|
+
export function entity(constructor) {
|
|
50
|
+
TypeContext.register(constructor);
|
|
158
51
|
return constructor;
|
|
159
52
|
}
|
|
160
53
|
|
|
@@ -186,8 +79,8 @@ export function entity(constructor, context: ClassDecoratorContext) {
|
|
|
186
79
|
// // detect index for this field, considering inheritance
|
|
187
80
|
// //
|
|
188
81
|
// const parent = Object.getPrototypeOf(context.metadata);
|
|
189
|
-
// let fieldIndex: number = context.metadata[
|
|
190
|
-
// ?? (parent && parent[
|
|
82
|
+
// let fieldIndex: number = context.metadata[$numFields] // current structure already has fields defined
|
|
83
|
+
// ?? (parent && parent[$numFields]) // parent structure has fields defined
|
|
191
84
|
// ?? -1; // no fields defined
|
|
192
85
|
// fieldIndex++;
|
|
193
86
|
|
|
@@ -330,19 +223,22 @@ export function view<T> (tag: number = DEFAULT_VIEW_TAG) {
|
|
|
330
223
|
|
|
331
224
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
332
225
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
333
|
-
const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
334
226
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
227
|
+
// TODO: use Metadata.initialize()
|
|
228
|
+
const metadata: Metadata = (constructor[Symbol.metadata] ??= Object.assign({}, constructor[Symbol.metadata], parentMetadata ?? Object.create(null)));
|
|
229
|
+
// const fieldIndex = metadata[fieldName];
|
|
230
|
+
|
|
231
|
+
// if (!metadata[fieldIndex]) {
|
|
232
|
+
// //
|
|
233
|
+
// // detect index for this field, considering inheritance
|
|
234
|
+
// //
|
|
235
|
+
// metadata[fieldIndex] = {
|
|
236
|
+
// type: undefined,
|
|
237
|
+
// index: (metadata[$numFields] // current structure already has fields defined
|
|
238
|
+
// ?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
239
|
+
// ?? -1) + 1 // no fields defined
|
|
240
|
+
// }
|
|
241
|
+
// }
|
|
346
242
|
|
|
347
243
|
Metadata.setTag(metadata, fieldName, tag);
|
|
348
244
|
}
|
|
@@ -356,22 +252,24 @@ 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
|
-
if (!metadata[field]) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
259
|
+
// if (!metadata[field]) {
|
|
260
|
+
// //
|
|
261
|
+
// // detect index for this field, considering inheritance
|
|
262
|
+
// //
|
|
263
|
+
// metadata[field] = {
|
|
264
|
+
// type: undefined,
|
|
265
|
+
// index: (metadata[$numFields] // current structure already has fields defined
|
|
266
|
+
// ?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
267
|
+
// ?? -1) + 1 // no fields defined
|
|
268
|
+
// }
|
|
269
|
+
// }
|
|
372
270
|
|
|
373
271
|
// add owned flag to the field
|
|
374
|
-
metadata[field].unreliable = true;
|
|
272
|
+
metadata[metadata[field]].unreliable = true;
|
|
375
273
|
}
|
|
376
274
|
|
|
377
275
|
export function type (
|
|
@@ -389,20 +287,20 @@ export function type (
|
|
|
389
287
|
TypeContext.register(constructor);
|
|
390
288
|
|
|
391
289
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
392
|
-
const parentMetadata =
|
|
393
|
-
const metadata
|
|
290
|
+
const parentMetadata = parentClass[Symbol.metadata];
|
|
291
|
+
const metadata = Metadata.initialize(constructor);
|
|
394
292
|
|
|
395
|
-
let fieldIndex: number;
|
|
293
|
+
let fieldIndex: number = metadata[field];
|
|
396
294
|
|
|
397
295
|
/**
|
|
398
296
|
* skip if descriptor already exists for this field (`@deprecated()`)
|
|
399
297
|
*/
|
|
400
|
-
if (metadata[
|
|
401
|
-
if (metadata[
|
|
298
|
+
if (metadata[fieldIndex] !== undefined) {
|
|
299
|
+
if (metadata[fieldIndex].deprecated) {
|
|
402
300
|
// do not create accessors for deprecated properties.
|
|
403
301
|
return;
|
|
404
302
|
|
|
405
|
-
} else if (metadata[
|
|
303
|
+
} else if (metadata[fieldIndex].type !== undefined) {
|
|
406
304
|
// trying to define same property multiple times across inheritance.
|
|
407
305
|
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
|
|
408
306
|
try {
|
|
@@ -412,28 +310,31 @@ export function type (
|
|
|
412
310
|
const definitionAtLine = e.stack.split("\n")[4].trim();
|
|
413
311
|
throw new Error(`${e.message} ${definitionAtLine}`);
|
|
414
312
|
}
|
|
415
|
-
|
|
416
|
-
} else {
|
|
417
|
-
fieldIndex = metadata[field].index;
|
|
418
313
|
}
|
|
419
314
|
|
|
420
315
|
} else {
|
|
421
316
|
//
|
|
422
317
|
// detect index for this field, considering inheritance
|
|
423
318
|
//
|
|
424
|
-
fieldIndex = metadata[
|
|
425
|
-
?? (parentMetadata && parentMetadata[
|
|
319
|
+
fieldIndex = metadata[$numFields] // current structure already has fields defined
|
|
320
|
+
?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
426
321
|
?? -1; // no fields defined
|
|
427
322
|
fieldIndex++;
|
|
428
323
|
}
|
|
429
324
|
|
|
430
325
|
if (options && options.manual) {
|
|
431
|
-
Metadata.addField(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
326
|
+
Metadata.addField(
|
|
327
|
+
metadata,
|
|
328
|
+
fieldIndex,
|
|
329
|
+
field,
|
|
330
|
+
type,
|
|
331
|
+
{
|
|
332
|
+
// do not declare getter/setter descriptor
|
|
333
|
+
enumerable: true,
|
|
334
|
+
configurable: true,
|
|
335
|
+
writable: true,
|
|
336
|
+
}
|
|
337
|
+
);
|
|
437
338
|
|
|
438
339
|
} else {
|
|
439
340
|
const complexTypeKlass = (Array.isArray(type))
|
|
@@ -449,7 +350,7 @@ export function type (
|
|
|
449
350
|
fieldIndex,
|
|
450
351
|
field,
|
|
451
352
|
type,
|
|
452
|
-
getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass
|
|
353
|
+
getPropertyDescriptor(`_${field}`, fieldIndex, childType, complexTypeKlass)
|
|
453
354
|
);
|
|
454
355
|
}
|
|
455
356
|
}
|
|
@@ -460,13 +361,11 @@ export function getPropertyDescriptor(
|
|
|
460
361
|
fieldIndex: number,
|
|
461
362
|
type: DefinitionType,
|
|
462
363
|
complexTypeKlass: TypeDefinition,
|
|
463
|
-
metadata: Metadata,
|
|
464
|
-
field: string,
|
|
465
364
|
) {
|
|
466
365
|
return {
|
|
467
366
|
get: function () { return this[fieldCached]; },
|
|
468
367
|
set: function (this: Schema, value: any) {
|
|
469
|
-
const previousValue = this[fieldCached]
|
|
368
|
+
const previousValue = this[fieldCached] ?? undefined;
|
|
470
369
|
|
|
471
370
|
// skip if value is the same as cached.
|
|
472
371
|
if (value === previousValue) { return; }
|
|
@@ -487,30 +386,32 @@ export function getPropertyDescriptor(
|
|
|
487
386
|
}
|
|
488
387
|
|
|
489
388
|
value[$childType] = type;
|
|
389
|
+
|
|
390
|
+
} else if (typeof (type) !== "string") {
|
|
391
|
+
assertInstanceType(value, type as typeof Schema, this, fieldCached.substring(1));
|
|
392
|
+
|
|
393
|
+
} else {
|
|
394
|
+
assertType(value, type, this, fieldCached.substring(1));
|
|
490
395
|
}
|
|
491
396
|
|
|
397
|
+
const changeTree = this[$changes];
|
|
398
|
+
|
|
492
399
|
//
|
|
493
400
|
// Replacing existing "ref", remove it from root.
|
|
494
401
|
// TODO: if there are other references to this instance, we should not remove it from root.
|
|
495
402
|
//
|
|
496
403
|
if (previousValue !== undefined && previousValue[$changes]) {
|
|
497
|
-
|
|
404
|
+
changeTree.root?.remove(previousValue[$changes]);
|
|
498
405
|
}
|
|
499
406
|
|
|
500
407
|
// flag the change for encoding.
|
|
501
|
-
this.constructor[$track](
|
|
408
|
+
this.constructor[$track](changeTree, fieldIndex, OPERATION.ADD);
|
|
502
409
|
|
|
503
410
|
//
|
|
504
411
|
// call setParent() recursively for this and its child
|
|
505
412
|
// structures.
|
|
506
413
|
//
|
|
507
|
-
|
|
508
|
-
value[$changes].setParent(
|
|
509
|
-
this,
|
|
510
|
-
this[$changes].root,
|
|
511
|
-
metadata[field].index,
|
|
512
|
-
);
|
|
513
|
-
}
|
|
414
|
+
(value as Ref)[$changes]?.setParent(this, changeTree.root, fieldIndex);
|
|
514
415
|
|
|
515
416
|
} else if (previousValue !== undefined) {
|
|
516
417
|
//
|
|
@@ -542,23 +443,25 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
|
|
|
542
443
|
const parentClass = Object.getPrototypeOf(constructor);
|
|
543
444
|
const parentMetadata = parentClass[Symbol.metadata];
|
|
544
445
|
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
|
-
|
|
446
|
+
const fieldIndex = metadata[field];
|
|
447
|
+
|
|
448
|
+
// if (!metadata[field]) {
|
|
449
|
+
// //
|
|
450
|
+
// // detect index for this field, considering inheritance
|
|
451
|
+
// //
|
|
452
|
+
// metadata[field] = {
|
|
453
|
+
// type: undefined,
|
|
454
|
+
// index: (metadata[$numFields] // current structure already has fields defined
|
|
455
|
+
// ?? (parentMetadata && parentMetadata[$numFields]) // parent structure has fields defined
|
|
456
|
+
// ?? -1) + 1 // no fields defined
|
|
457
|
+
// }
|
|
458
|
+
// }
|
|
459
|
+
|
|
460
|
+
metadata[fieldIndex].deprecated = true;
|
|
559
461
|
|
|
560
462
|
if (throws) {
|
|
561
|
-
metadata[
|
|
463
|
+
metadata[$descriptors] ??= {};
|
|
464
|
+
metadata[$descriptors][field] = {
|
|
562
465
|
get: function () { throw new Error(`${field} is deprecated.`); },
|
|
563
466
|
set: function (this: Schema, value: any) { /* throw new Error(`${field} is deprecated.`); */ },
|
|
564
467
|
enumerable: false,
|
|
@@ -567,8 +470,8 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
|
|
|
567
470
|
}
|
|
568
471
|
|
|
569
472
|
// flag metadata[field] as non-enumerable
|
|
570
|
-
Object.defineProperty(metadata,
|
|
571
|
-
value: metadata[
|
|
473
|
+
Object.defineProperty(metadata, fieldIndex, {
|
|
474
|
+
value: metadata[fieldIndex],
|
|
572
475
|
enumerable: false,
|
|
573
476
|
configurable: true
|
|
574
477
|
});
|
|
@@ -577,7 +480,7 @@ export function deprecated(throws: boolean = true): PropertyDecorator {
|
|
|
577
480
|
|
|
578
481
|
export function defineTypes(
|
|
579
482
|
target: typeof Schema,
|
|
580
|
-
fields:
|
|
483
|
+
fields: Definition,
|
|
581
484
|
options?: TypeOptions
|
|
582
485
|
) {
|
|
583
486
|
for (let field in fields) {
|
|
@@ -585,3 +488,52 @@ export function defineTypes(
|
|
|
585
488
|
}
|
|
586
489
|
return target;
|
|
587
490
|
}
|
|
491
|
+
|
|
492
|
+
export interface SchemaWithExtends<T extends Definition, P extends typeof Schema> extends DefinedSchemaType<T, P> {
|
|
493
|
+
extends: <T2 extends Definition>(
|
|
494
|
+
fields: T2,
|
|
495
|
+
name?: string
|
|
496
|
+
) => SchemaWithExtends<T & T2, typeof this>;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function schema<T extends Definition, P extends typeof Schema = typeof Schema>(
|
|
500
|
+
fields: T,
|
|
501
|
+
name?: string,
|
|
502
|
+
inherits: P = Schema as P
|
|
503
|
+
): SchemaWithExtends<T, P> {
|
|
504
|
+
const defaultValues: any = {};
|
|
505
|
+
const viewTagFields: any = {};
|
|
506
|
+
|
|
507
|
+
for (let fieldName in fields) {
|
|
508
|
+
const field = fields[fieldName] as DefinitionType;
|
|
509
|
+
if (typeof (field) === "object") {
|
|
510
|
+
if (field['default'] !== undefined) {
|
|
511
|
+
defaultValues[fieldName] = field['default'];
|
|
512
|
+
}
|
|
513
|
+
if (field['view'] !== undefined) {
|
|
514
|
+
viewTagFields[fieldName] = (typeof (field['view']) === "boolean")
|
|
515
|
+
? DEFAULT_VIEW_TAG
|
|
516
|
+
: field['view'];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const klass = Metadata.setFields(class extends inherits {
|
|
522
|
+
constructor (...args: any[]) {
|
|
523
|
+
args[0] = Object.assign({}, defaultValues, args[0]);
|
|
524
|
+
super(...args);
|
|
525
|
+
}
|
|
526
|
+
}, fields) as SchemaWithExtends<T, P>;
|
|
527
|
+
|
|
528
|
+
for (let fieldName in viewTagFields) {
|
|
529
|
+
view(viewTagFields[fieldName])(klass.prototype, fieldName);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (name) {
|
|
533
|
+
Object.defineProperty(klass, "name", { value: name });
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
klass.extends = (fields, name) => schema(fields, name, klass);
|
|
537
|
+
|
|
538
|
+
return klass;
|
|
539
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { nanoid } from "nanoid";
|
|
2
|
+
import { Schema, type, MapSchema, ArraySchema, Encoder } from ".";
|
|
3
|
+
|
|
4
|
+
class Attribute extends Schema {
|
|
5
|
+
@type("string") name: string;
|
|
6
|
+
@type("number") value: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class Item extends Schema {
|
|
10
|
+
@type("number") price: number;
|
|
11
|
+
@type([ Attribute ]) attributes = new ArraySchema<Attribute>();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class Position extends Schema {
|
|
15
|
+
@type("number") x: number;
|
|
16
|
+
@type("number") y: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class Player extends Schema {
|
|
20
|
+
@type(Position) position = new Position();
|
|
21
|
+
@type({ map: Item }) items = new MapSchema<Item>();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class State extends Schema {
|
|
25
|
+
@type({ map: Player }) players = new MapSchema<Player>();
|
|
26
|
+
@type("string") currentTurn;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const state = new State();
|
|
30
|
+
|
|
31
|
+
Encoder.BUFFER_SIZE = 4096 * 4096;
|
|
32
|
+
const encoder = new Encoder(state);
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
let now = Date.now();
|
|
36
|
+
|
|
37
|
+
// for (let i = 0; i < 10000; i++) {
|
|
38
|
+
// const player = new Player();
|
|
39
|
+
// state.players.set(`p-${nanoid()}`, player);
|
|
40
|
+
//
|
|
41
|
+
// player.position.x = (i + 1) * 100;
|
|
42
|
+
// player.position.y = (i + 1) * 100;
|
|
43
|
+
// for (let j = 0; j < 10; j++) {
|
|
44
|
+
// const item = new Item();
|
|
45
|
+
// player.items.set(`item-${j}`, item);
|
|
46
|
+
// item.price = (i + 1) * 50;
|
|
47
|
+
// for (let k = 0; k < 5; k++) {
|
|
48
|
+
// const attr = new Attribute();
|
|
49
|
+
// attr.name = `Attribute ${k}`;
|
|
50
|
+
// attr.value = k;
|
|
51
|
+
// item.attributes.push(attr);
|
|
52
|
+
// }
|
|
53
|
+
// }
|
|
54
|
+
// }
|
|
55
|
+
// console.log("time to make changes:", Date.now() - now);
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// measure time to .encodeAll()
|
|
59
|
+
|
|
60
|
+
now = Date.now();
|
|
61
|
+
// for (let i = 0; i < 1000; i++) {
|
|
62
|
+
// encoder.encodeAll();
|
|
63
|
+
// }
|
|
64
|
+
// console.log(Date.now() - now);
|
|
65
|
+
|
|
66
|
+
const total = 100;
|
|
67
|
+
const allEncodes = Date.now();
|
|
68
|
+
|
|
69
|
+
let avgTimeToEncode = 0;
|
|
70
|
+
let avgTimeToMakeChanges = 0;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < total; i++) {
|
|
73
|
+
now = Date.now();
|
|
74
|
+
for (let j = 0; j < 50; j++) {
|
|
75
|
+
const player = new Player();
|
|
76
|
+
state.players.set(`p-${nanoid()}`, player);
|
|
77
|
+
|
|
78
|
+
player.position.x = (j + 1) * 100;
|
|
79
|
+
player.position.y = (j + 1) * 100;
|
|
80
|
+
for (let k = 0; k < 10; k++) {
|
|
81
|
+
const item = new Item();
|
|
82
|
+
item.price = (j + 1) * 50;
|
|
83
|
+
for (let l = 0; l < 5; l++) {
|
|
84
|
+
const attr = new Attribute();
|
|
85
|
+
attr.name = `Attribute ${l}`;
|
|
86
|
+
attr.value = l;
|
|
87
|
+
item.attributes.push(attr);
|
|
88
|
+
}
|
|
89
|
+
player.items.set(`item-${k}`, item);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const timeToMakeChanges = Date.now() - now;
|
|
93
|
+
console.log("time to make changes:", timeToMakeChanges);
|
|
94
|
+
avgTimeToMakeChanges += timeToMakeChanges;
|
|
95
|
+
|
|
96
|
+
now = Date.now();
|
|
97
|
+
encoder.encode();
|
|
98
|
+
encoder.discardChanges();
|
|
99
|
+
|
|
100
|
+
const timeToEncode = Date.now() - now;
|
|
101
|
+
console.log("time to encode:", timeToEncode);
|
|
102
|
+
avgTimeToEncode += timeToEncode;
|
|
103
|
+
}
|
|
104
|
+
console.log("avg time to encode:", (avgTimeToEncode) / total);
|
|
105
|
+
console.log("avg time to make changes:", (avgTimeToMakeChanges) / total);
|
|
106
|
+
console.log("time for all encodes:", Date.now() - allEncodes);
|
|
107
|
+
|
|
108
|
+
console.log(Array.from(encoder.encodeAll()).length, "bytes");
|