@colyseus/schema 4.0.20 → 5.0.0
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 +2 -0
- package/build/Metadata.d.ts +55 -2
- package/build/Reflection.d.ts +24 -30
- package/build/Schema.d.ts +70 -9
- package/build/annotations.d.ts +56 -13
- package/build/codegen/cli.cjs +84 -67
- package/build/codegen/cli.cjs.map +1 -1
- package/build/decoder/DecodeOperation.d.ts +48 -5
- package/build/decoder/Decoder.d.ts +2 -2
- package/build/decoder/strategy/Callbacks.d.ts +1 -1
- package/build/encoder/ChangeRecorder.d.ts +107 -0
- package/build/encoder/ChangeTree.d.ts +218 -69
- package/build/encoder/EncodeDescriptor.d.ts +63 -0
- package/build/encoder/EncodeOperation.d.ts +25 -2
- package/build/encoder/Encoder.d.ts +59 -3
- package/build/encoder/MapJournal.d.ts +62 -0
- package/build/encoder/RefIdAllocator.d.ts +35 -0
- package/build/encoder/Root.d.ts +94 -13
- package/build/encoder/StateView.d.ts +116 -8
- package/build/encoder/changeTree/inheritedFlags.d.ts +34 -0
- package/build/encoder/changeTree/liveIteration.d.ts +3 -0
- package/build/encoder/changeTree/parentChain.d.ts +24 -0
- package/build/encoder/changeTree/treeAttachment.d.ts +13 -0
- package/build/encoder/streaming.d.ts +73 -0
- package/build/encoder/subscriptions.d.ts +25 -0
- package/build/index.cjs +5202 -1552
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +7 -3
- package/build/index.js +5202 -1552
- package/build/index.mjs +5193 -1552
- package/build/index.mjs.map +1 -1
- package/build/input/InputDecoder.d.ts +32 -0
- package/build/input/InputEncoder.d.ts +117 -0
- package/build/input/index.cjs +7429 -0
- package/build/input/index.cjs.map +1 -0
- package/build/input/index.d.ts +3 -0
- package/build/input/index.mjs +7426 -0
- package/build/input/index.mjs.map +1 -0
- package/build/types/HelperTypes.d.ts +22 -8
- package/build/types/TypeContext.d.ts +9 -0
- package/build/types/builder.d.ts +162 -0
- package/build/types/custom/ArraySchema.d.ts +25 -4
- package/build/types/custom/CollectionSchema.d.ts +30 -2
- package/build/types/custom/MapSchema.d.ts +52 -3
- package/build/types/custom/SetSchema.d.ts +32 -2
- package/build/types/custom/StreamSchema.d.ts +114 -0
- package/build/types/symbols.d.ts +48 -5
- package/package.json +9 -3
- package/src/Metadata.ts +258 -31
- package/src/Reflection.ts +15 -13
- package/src/Schema.ts +176 -134
- package/src/annotations.ts +308 -236
- package/src/bench_bloat.ts +173 -0
- package/src/bench_decode.ts +221 -0
- package/src/bench_decode_mem.ts +165 -0
- package/src/bench_encode.ts +108 -0
- package/src/bench_init.ts +150 -0
- package/src/bench_static.ts +109 -0
- package/src/bench_stream.ts +295 -0
- package/src/bench_view_cmp.ts +142 -0
- package/src/codegen/languages/csharp.ts +0 -24
- package/src/codegen/parser.ts +83 -61
- package/src/decoder/DecodeOperation.ts +168 -63
- package/src/decoder/Decoder.ts +20 -10
- package/src/decoder/ReferenceTracker.ts +4 -0
- package/src/decoder/strategy/Callbacks.ts +30 -26
- package/src/decoder/strategy/getDecoderStateCallbacks.ts +16 -13
- package/src/encoder/ChangeRecorder.ts +276 -0
- package/src/encoder/ChangeTree.ts +674 -519
- package/src/encoder/EncodeDescriptor.ts +213 -0
- package/src/encoder/EncodeOperation.ts +107 -65
- package/src/encoder/Encoder.ts +630 -119
- package/src/encoder/MapJournal.ts +124 -0
- package/src/encoder/RefIdAllocator.ts +68 -0
- package/src/encoder/Root.ts +247 -120
- package/src/encoder/StateView.ts +592 -121
- package/src/encoder/changeTree/inheritedFlags.ts +217 -0
- package/src/encoder/changeTree/liveIteration.ts +74 -0
- package/src/encoder/changeTree/parentChain.ts +131 -0
- package/src/encoder/changeTree/treeAttachment.ts +171 -0
- package/src/encoder/streaming.ts +232 -0
- package/src/encoder/subscriptions.ts +71 -0
- package/src/index.ts +15 -3
- package/src/input/InputDecoder.ts +57 -0
- package/src/input/InputEncoder.ts +303 -0
- package/src/input/index.ts +3 -0
- package/src/types/HelperTypes.ts +21 -9
- package/src/types/TypeContext.ts +14 -2
- package/src/types/builder.ts +285 -0
- package/src/types/custom/ArraySchema.ts +210 -197
- package/src/types/custom/CollectionSchema.ts +115 -35
- package/src/types/custom/MapSchema.ts +162 -58
- package/src/types/custom/SetSchema.ts +128 -39
- package/src/types/custom/StreamSchema.ts +310 -0
- package/src/types/symbols.ts +54 -6
- package/src/utils.ts +4 -6
package/README.md
CHANGED
|
@@ -260,6 +260,8 @@ up-to-date version of the schema definitions.
|
|
|
260
260
|
## Limitations and best practices
|
|
261
261
|
|
|
262
262
|
- Each `Schema` structure can hold up to `64` fields. If you need more fields, use nested structures.
|
|
263
|
+
- Fields tagged with `@view`, `@unreliable`, or `@static` at field indexes `≥ 32` use a slower per-mutation classification path (linear scan over the tagged-field list instead of a single bitwise op). For schemas with more than 32 fields, declare frequently-mutated tagged fields earlier so they fall in the bitmask fast path.
|
|
264
|
+
- Schemas with `≤ 8` fields store per-field operation bytes inline in two numbers (no allocation per instance). Schemas with `> 8` fields allocate a small `Uint8Array` per instance for op storage. The difference is only material when allocating thousands of instances per tick — prefer narrower nested structures in that regime.
|
|
263
265
|
- `NaN` or `null` numbers are encoded as `0`
|
|
264
266
|
- `null` strings are encoded as `""`
|
|
265
267
|
- `Infinity` numbers are encoded as `Number.MAX_SAFE_INTEGER`
|
package/build/Metadata.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { DefinitionType } from "./annotations.js";
|
|
2
|
-
import {
|
|
2
|
+
import { TypeDefinition } from "./types/registry.js";
|
|
3
|
+
import { $descriptors, $encoders, $fieldIndexesByViewTag, $numFields, $refTypeFieldIndexes, $staticFieldIndexes, $streamFieldIndexes, $streamPriorities, $transientFieldIndexes, $unreliableFieldIndexes, $viewFieldIndexes } from "./types/symbols.js";
|
|
3
4
|
export type MetadataField = {
|
|
4
5
|
type: DefinitionType;
|
|
5
6
|
name: string;
|
|
6
7
|
index: number;
|
|
7
8
|
tag?: number;
|
|
8
9
|
unreliable?: boolean;
|
|
10
|
+
transient?: boolean;
|
|
9
11
|
deprecated?: boolean;
|
|
12
|
+
owned?: boolean;
|
|
13
|
+
static?: boolean;
|
|
14
|
+
stream?: boolean;
|
|
10
15
|
};
|
|
11
16
|
export type Metadata = {
|
|
12
17
|
[$numFields]: number;
|
|
@@ -22,6 +27,26 @@ export type Metadata = {
|
|
|
22
27
|
{
|
|
23
28
|
[$refTypeFieldIndexes]: number[];
|
|
24
29
|
} & // all field indexes containing Ref types (Schema, ArraySchema, MapSchema, etc)
|
|
30
|
+
{
|
|
31
|
+
[$unreliableFieldIndexes]: number[];
|
|
32
|
+
} & // all field indexes tagged with @unreliable
|
|
33
|
+
{
|
|
34
|
+
[$transientFieldIndexes]: number[];
|
|
35
|
+
} & // all field indexes tagged with @transient (not persisted to snapshots)
|
|
36
|
+
{
|
|
37
|
+
[$staticFieldIndexes]: number[];
|
|
38
|
+
} & // all field indexes tagged with @static (not tracked after assignment)
|
|
39
|
+
{
|
|
40
|
+
[$streamFieldIndexes]: number[];
|
|
41
|
+
} & // all field indexes holding a t.stream(...) collection
|
|
42
|
+
{
|
|
43
|
+
[$streamPriorities]: {
|
|
44
|
+
[field: number]: (view: any, element: any) => number;
|
|
45
|
+
};
|
|
46
|
+
} & // per-stream-field priority callback declared at schema definition time
|
|
47
|
+
{
|
|
48
|
+
[$encoders]: Array<(bytes: Uint8Array, value: any, it: any) => void>;
|
|
49
|
+
} & // pre-computed encoder fn per primitive field
|
|
25
50
|
{
|
|
26
51
|
[field: number]: MetadataField;
|
|
27
52
|
} & // index => field name
|
|
@@ -33,17 +58,45 @@ export type Metadata = {
|
|
|
33
58
|
[field: string]: PropertyDescriptor;
|
|
34
59
|
};
|
|
35
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Given a normalized field type (`"number"`, `{ map: Foo }`, `Player`,
|
|
63
|
+
* etc.), split into the collection-type descriptor (`{ constructor:
|
|
64
|
+
* MapSchema, ... }`) if applicable and the inner child type. Shared by
|
|
65
|
+
* `@type()` decoration and `Metadata.setFields` — both need to build a
|
|
66
|
+
* property accessor that knows whether the slot holds a collection.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveFieldType(type: any): {
|
|
69
|
+
complexTypeKlass: TypeDefinition | false;
|
|
70
|
+
childType: any;
|
|
71
|
+
};
|
|
36
72
|
export declare function getNormalizedType(type: any): DefinitionType;
|
|
37
73
|
export declare const Metadata: {
|
|
38
74
|
addField(metadata: any, index: number, name: string, type: DefinitionType, descriptor?: PropertyDescriptor): void;
|
|
39
75
|
setTag(metadata: Metadata, fieldName: string, tag: number): void;
|
|
76
|
+
setUnreliable(metadata: Metadata, fieldName: string): void;
|
|
77
|
+
setTransient(metadata: Metadata, fieldName: string): void;
|
|
78
|
+
setStatic(metadata: Metadata, fieldName: string): void;
|
|
79
|
+
setStream(metadata: Metadata, fieldName: string): void;
|
|
80
|
+
/**
|
|
81
|
+
* Attach a declaration-scope priority callback to a stream field.
|
|
82
|
+
* Called at schema definition time (via `t.stream(X).priority(fn)` or
|
|
83
|
+
* `@type({ stream: X, priority: fn })`), looked up at stream-attach
|
|
84
|
+
* time to seed the instance's `_stream.priority` slot. The callback
|
|
85
|
+
* signature is `(view: StateView, element: V) => number` — only fires
|
|
86
|
+
* during `encodeView`, broadcast mode emits FIFO regardless.
|
|
87
|
+
*/
|
|
88
|
+
setStreamPriority(metadata: Metadata, fieldName: string, fn: (view: any, element: any) => number): void;
|
|
89
|
+
getStreamPriority(metadata: Metadata | undefined, index: number): (view: any, element: any) => number;
|
|
40
90
|
setFields<T extends {
|
|
41
91
|
new (...args: any[]): InstanceType<T>;
|
|
42
92
|
} = any>(target: T, fields: { [field in keyof InstanceType<T>]?: DefinitionType; }): T;
|
|
43
93
|
isDeprecated(metadata: any, field: string): boolean;
|
|
44
|
-
init(klass: any): void;
|
|
45
94
|
initialize(constructor: any): Metadata;
|
|
46
95
|
isValidInstance(klass: any): boolean;
|
|
47
96
|
getFields(klass: any): any;
|
|
48
97
|
hasViewTagAtIndex(metadata: Metadata, index: number): boolean;
|
|
98
|
+
hasUnreliableAtIndex(metadata: Metadata, index: number): boolean;
|
|
99
|
+
hasTransientAtIndex(metadata: Metadata, index: number): boolean;
|
|
100
|
+
hasStaticAtIndex(metadata: Metadata, index: number): boolean;
|
|
101
|
+
hasStreamAtIndex(metadata: Metadata, index: number): boolean;
|
|
49
102
|
};
|
package/build/Reflection.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Iterator } from "./encoding/decode.js";
|
|
|
3
3
|
import { Encoder } from "./encoder/Encoder.js";
|
|
4
4
|
import { Decoder } from "./decoder/Decoder.js";
|
|
5
5
|
import { Schema } from "./Schema.js";
|
|
6
|
+
import { FieldBuilder } from "./types/builder.js";
|
|
7
|
+
import { ArraySchema } from "./types/custom/ArraySchema.js";
|
|
6
8
|
/**
|
|
7
9
|
* Static methods available on Reflection
|
|
8
10
|
*/
|
|
@@ -28,44 +30,36 @@ interface ReflectionStatic {
|
|
|
28
30
|
* Reflection
|
|
29
31
|
*/
|
|
30
32
|
export declare const ReflectionField: import("./annotations.js").SchemaWithExtendsConstructor<{
|
|
31
|
-
name:
|
|
32
|
-
type:
|
|
33
|
-
referencedType:
|
|
33
|
+
name: FieldBuilder<string>;
|
|
34
|
+
type: FieldBuilder<string>;
|
|
35
|
+
referencedType: FieldBuilder<number>;
|
|
34
36
|
}, import("./index.js").AssignableProps<import("./index.js").InferSchemaInstanceType<{
|
|
35
|
-
name:
|
|
36
|
-
type:
|
|
37
|
-
referencedType:
|
|
37
|
+
name: FieldBuilder<string>;
|
|
38
|
+
type: FieldBuilder<string>;
|
|
39
|
+
referencedType: FieldBuilder<number>;
|
|
38
40
|
}>>, typeof Schema>;
|
|
39
41
|
export type ReflectionField = SchemaType<typeof ReflectionField>;
|
|
40
42
|
export declare const ReflectionType: import("./annotations.js").SchemaWithExtendsConstructor<{
|
|
41
|
-
id:
|
|
42
|
-
extendsId:
|
|
43
|
-
fields:
|
|
44
|
-
name:
|
|
45
|
-
type:
|
|
46
|
-
referencedType:
|
|
47
|
-
}
|
|
48
|
-
name: "string";
|
|
49
|
-
type: "string";
|
|
50
|
-
referencedType: "number";
|
|
51
|
-
}>>, typeof Schema>[];
|
|
43
|
+
id: FieldBuilder<number>;
|
|
44
|
+
extendsId: FieldBuilder<number>;
|
|
45
|
+
fields: FieldBuilder<ArraySchema<{
|
|
46
|
+
name: string;
|
|
47
|
+
type: string;
|
|
48
|
+
referencedType: number;
|
|
49
|
+
} & Schema<any> & Schema<unknown>>>;
|
|
52
50
|
}, import("./index.js").AssignableProps<import("./index.js").InferSchemaInstanceType<{
|
|
53
|
-
id:
|
|
54
|
-
extendsId:
|
|
55
|
-
fields:
|
|
56
|
-
name:
|
|
57
|
-
type:
|
|
58
|
-
referencedType:
|
|
59
|
-
}
|
|
60
|
-
name: "string";
|
|
61
|
-
type: "string";
|
|
62
|
-
referencedType: "number";
|
|
63
|
-
}>>, typeof Schema>[];
|
|
51
|
+
id: FieldBuilder<number>;
|
|
52
|
+
extendsId: FieldBuilder<number>;
|
|
53
|
+
fields: FieldBuilder<ArraySchema<{
|
|
54
|
+
name: string;
|
|
55
|
+
type: string;
|
|
56
|
+
referencedType: number;
|
|
57
|
+
} & Schema<any> & Schema<unknown>>>;
|
|
64
58
|
}>>, typeof Schema>;
|
|
65
59
|
export type ReflectionType = SchemaType<typeof ReflectionType>;
|
|
66
60
|
export declare const Reflection: ReturnType<typeof schema<{
|
|
67
|
-
types:
|
|
68
|
-
rootType:
|
|
61
|
+
types: FieldBuilder<ArraySchema<ReflectionType>>;
|
|
62
|
+
rootType: FieldBuilder<number>;
|
|
69
63
|
}>> & ReflectionStatic;
|
|
70
64
|
export type Reflection = SchemaType<typeof Reflection>;
|
|
71
65
|
export {};
|
package/build/Schema.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { OPERATION } from './encoding/spec.js';
|
|
2
2
|
import { type DefinitionType } from "./annotations.js";
|
|
3
3
|
import { AssignableProps, NonFunctionPropNames, ToJSON } from './types/HelperTypes.js';
|
|
4
|
-
import {
|
|
5
|
-
import { $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $refId, $track } from './types/symbols.js';
|
|
4
|
+
import { ChangeTree, IRef, Ref } from './encoder/ChangeTree.js';
|
|
5
|
+
import { $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $refId, $track, $values } from './types/symbols.js';
|
|
6
6
|
import { StateView } from './encoder/StateView.js';
|
|
7
7
|
import type { Decoder } from './decoder/Decoder.js';
|
|
8
8
|
import type { Metadata } from './Metadata.js';
|
|
@@ -14,21 +14,58 @@ export declare class Schema<C = any> implements IRef {
|
|
|
14
14
|
static [$encoder]: import("./encoder/EncodeOperation.js").EncodeOperation<any>;
|
|
15
15
|
static [$decoder]: import("./decoder/DecodeOperation.js").DecodeOperation<any>;
|
|
16
16
|
[$refId]?: number;
|
|
17
|
+
[$values]: any[];
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Initialize change tracking on this instance.
|
|
20
|
+
* Field accessor descriptors (getter/setter) live on the prototype,
|
|
21
|
+
* installed once at class-definition time. Per-instance work is limited
|
|
22
|
+
* to allocating a ChangeTree and a values array.
|
|
20
23
|
*/
|
|
21
24
|
static initialize(instance: any): void;
|
|
25
|
+
/**
|
|
26
|
+
* Decoder-side factory. Skips the user subclass ctor entirely —
|
|
27
|
+
* decoder-built instances are passive mirrors of server state, so any
|
|
28
|
+
* field initializer / ctor body work would be overwritten by the
|
|
29
|
+
* decoded ADDs immediately after. Assignment order matches
|
|
30
|
+
* {@link Schema.initialize} so V8 assigns the same hidden class
|
|
31
|
+
* ($changes, then $values), keeping decode-path ICs monomorphic even
|
|
32
|
+
* when tracked and untracked instances coexist.
|
|
33
|
+
*
|
|
34
|
+
* The `this:` constraint pins the return type to the concrete subclass
|
|
35
|
+
* when called as `Player.initializeForDecoder()`, not the base Schema.
|
|
36
|
+
*/
|
|
37
|
+
static initializeForDecoder<T extends Schema = Schema>(this: {
|
|
38
|
+
prototype: T;
|
|
39
|
+
} & typeof Schema): T;
|
|
40
|
+
/**
|
|
41
|
+
* Check whether `type` describes a Schema *class* (a subclass
|
|
42
|
+
* constructor carrying `Symbol.metadata`, as installed by `@type`).
|
|
43
|
+
* Returns false for primitive type strings like `"number"`, descriptor
|
|
44
|
+
* objects like `{ map: Player }`, and Schema *instances*.
|
|
45
|
+
*
|
|
46
|
+
* For the instance-level check — "is this value a Schema instance?" —
|
|
47
|
+
* see {@link Schema.isSchema}.
|
|
48
|
+
*/
|
|
22
49
|
static is(type: DefinitionType): boolean;
|
|
23
50
|
/**
|
|
24
|
-
* Check if a value is an instance of Schema.
|
|
25
|
-
*
|
|
51
|
+
* Check if a value is an *instance* of Schema. Uses duck-typing on
|
|
52
|
+
* `.assign` to work across multiple `@colyseus/schema` versions that
|
|
53
|
+
* may be loaded in the same process (e.g. bundled server types vs.
|
|
54
|
+
* client types in a p2p setup).
|
|
55
|
+
*
|
|
56
|
+
* For the class-level check — "is this type a Schema subclass?" —
|
|
57
|
+
* see {@link Schema.is}.
|
|
58
|
+
*
|
|
26
59
|
* @param obj Value to check
|
|
27
60
|
* @returns true if the value is a Schema instance
|
|
28
61
|
*/
|
|
29
62
|
static isSchema(obj: any): obj is Schema;
|
|
30
63
|
/**
|
|
31
|
-
* Track property changes
|
|
64
|
+
* Track property changes. Exposed as an override point so downstream
|
|
65
|
+
* tools (debuggers, transparent proxies, custom instrumentation) can
|
|
66
|
+
* intercept per-field writes. Hot-path code in `annotations.ts` calls
|
|
67
|
+
* `(this.constructor as typeof Schema)[$track](...)` rather than
|
|
68
|
+
* `changeTree.change(...)` directly so any subclass override wins.
|
|
32
69
|
*/
|
|
33
70
|
static [$track](changeTree: ChangeTree, index: number, operation?: OPERATION): void;
|
|
34
71
|
/**
|
|
@@ -48,6 +85,12 @@ export declare class Schema<C = any> implements IRef {
|
|
|
48
85
|
* @returns
|
|
49
86
|
*/
|
|
50
87
|
assign<T extends Partial<this>>(props: AssignableProps<T>): this;
|
|
88
|
+
/**
|
|
89
|
+
* Metadata-driven property assignment.
|
|
90
|
+
* Reads tracked fields via property access (works with prototype accessors),
|
|
91
|
+
* then copies any remaining own properties for non-tracked fields.
|
|
92
|
+
*/
|
|
93
|
+
protected static assignProps(target: any, source: any): void;
|
|
51
94
|
/**
|
|
52
95
|
* Restore the instance from JSON data.
|
|
53
96
|
* @param jsonData JSON data to restore the instance from
|
|
@@ -61,6 +104,17 @@ export declare class Schema<C = any> implements IRef {
|
|
|
61
104
|
* @param operation OPERATION to perform (detected automatically)
|
|
62
105
|
*/
|
|
63
106
|
setDirty<K extends NonFunctionPropNames<this>>(property: K | number, operation?: OPERATION): void;
|
|
107
|
+
/** Stop recording mutations until resumeTracking() is called. */
|
|
108
|
+
pauseTracking(): void;
|
|
109
|
+
/** Re-enable automatic change tracking. */
|
|
110
|
+
resumeTracking(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Run `fn` with change tracking paused, then resume.
|
|
113
|
+
* Returns the function's return value. Safe to nest.
|
|
114
|
+
*/
|
|
115
|
+
untracked<T>(fn: () => T): T;
|
|
116
|
+
/** True while tracking is paused. */
|
|
117
|
+
get isTrackingPaused(): boolean;
|
|
64
118
|
clone(): this;
|
|
65
119
|
toJSON(this: any): ToJSON<this>;
|
|
66
120
|
/**
|
|
@@ -78,7 +132,15 @@ export declare class Schema<C = any> implements IRef {
|
|
|
78
132
|
* @returns
|
|
79
133
|
*/
|
|
80
134
|
static debugRefIds<T extends Schema>(ref: T, showContents?: boolean, level?: number, decoder?: Decoder, keyPrefix?: string): string;
|
|
81
|
-
|
|
135
|
+
/**
|
|
136
|
+
* @param changeSet
|
|
137
|
+
* - "changes": iterate the current-tick dirty queue (per-tick encode order)
|
|
138
|
+
* - "allChanges" / "allFilteredChanges" (legacy): structurally walk the
|
|
139
|
+
* tree in DFS preorder (matches the order in which full-sync emits
|
|
140
|
+
* trees). The two legacy modes differ by which side of the filter
|
|
141
|
+
* split they include.
|
|
142
|
+
*/
|
|
143
|
+
static debugRefIdEncodingOrder<T extends Ref>(ref: T, changeSet?: "changes" | "allChanges" | "allFilteredChanges"): number[];
|
|
82
144
|
static debugRefIdsFromDecoder(decoder: Decoder): string;
|
|
83
145
|
/**
|
|
84
146
|
* Return a string representation of the changes on a Schema instance.
|
|
@@ -89,5 +151,4 @@ export declare class Schema<C = any> implements IRef {
|
|
|
89
151
|
* @returns
|
|
90
152
|
*/
|
|
91
153
|
static debugChanges<T extends Ref>(instance: T, isEncodeAll?: boolean): string;
|
|
92
|
-
static debugChangesDeep<T extends Schema>(ref: T, changeSetName?: "changes" | "allChanges" | "allFilteredChanges" | "filteredChanges"): string;
|
|
93
154
|
}
|
package/build/annotations.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { Schema } from './Schema.js';
|
|
|
3
3
|
import { ArraySchema } from './types/custom/ArraySchema.js';
|
|
4
4
|
import { MapSchema } from './types/custom/MapSchema.js';
|
|
5
5
|
import { TypeDefinition } from "./types/registry.js";
|
|
6
|
-
import { OPERATION } from "./encoding/spec.js";
|
|
7
6
|
import type { InferValueType, InferSchemaInstanceType, AssignableProps } from "./types/HelperTypes.js";
|
|
8
7
|
import { CollectionSchema } from "./types/custom/CollectionSchema.js";
|
|
9
8
|
import { SetSchema } from "./types/custom/SetSchema.js";
|
|
9
|
+
import { StreamSchema } from "./types/custom/StreamSchema.js";
|
|
10
|
+
import { FieldBuilder } from "./types/builder.js";
|
|
10
11
|
export type RawPrimitiveType = "string" | "number" | "boolean" | "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "int64" | "uint64" | "float32" | "float64" | "bigint64" | "biguint64";
|
|
11
12
|
export type PrimitiveType = RawPrimitiveType | typeof Schema | object;
|
|
12
13
|
export type DefinitionType<T extends PrimitiveType = PrimitiveType> = T | T[] | {
|
|
@@ -14,26 +15,38 @@ export type DefinitionType<T extends PrimitiveType = PrimitiveType> = T | T[] |
|
|
|
14
15
|
default?: InferValueType<T>;
|
|
15
16
|
view?: boolean | number;
|
|
16
17
|
sync?: boolean;
|
|
18
|
+
owned?: boolean;
|
|
17
19
|
} | {
|
|
18
20
|
array: T;
|
|
19
21
|
default?: ArraySchema<InferValueType<T>>;
|
|
20
22
|
view?: boolean | number;
|
|
21
23
|
sync?: boolean;
|
|
24
|
+
owned?: boolean;
|
|
22
25
|
} | {
|
|
23
26
|
map: T;
|
|
24
27
|
default?: MapSchema<InferValueType<T>>;
|
|
25
28
|
view?: boolean | number;
|
|
26
29
|
sync?: boolean;
|
|
30
|
+
owned?: boolean;
|
|
27
31
|
} | {
|
|
28
32
|
collection: T;
|
|
29
33
|
default?: CollectionSchema<InferValueType<T>>;
|
|
30
34
|
view?: boolean | number;
|
|
31
35
|
sync?: boolean;
|
|
36
|
+
owned?: boolean;
|
|
32
37
|
} | {
|
|
33
38
|
set: T;
|
|
34
39
|
default?: SetSchema<InferValueType<T>>;
|
|
35
40
|
view?: boolean | number;
|
|
36
41
|
sync?: boolean;
|
|
42
|
+
owned?: boolean;
|
|
43
|
+
} | {
|
|
44
|
+
stream: T;
|
|
45
|
+
default?: StreamSchema<InferValueType<T>>;
|
|
46
|
+
view?: boolean | number;
|
|
47
|
+
sync?: boolean;
|
|
48
|
+
owned?: boolean;
|
|
49
|
+
priority?: (view: any, element: InferValueType<T>) => number;
|
|
37
50
|
};
|
|
38
51
|
export type Definition = {
|
|
39
52
|
[field: string]: DefinitionType;
|
|
@@ -60,10 +73,20 @@ export declare function entity(constructor: any): any;
|
|
|
60
73
|
* ```
|
|
61
74
|
*/
|
|
62
75
|
export declare function view<T>(tag?: number): (target: T, fieldName: string) => void;
|
|
76
|
+
export declare function owned<T>(target: T, field: string): void;
|
|
63
77
|
export declare function unreliable<T>(target: T, field: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* @transient — mark a field as not persisted to snapshots (encodeAll /
|
|
80
|
+
* encodeAllView). Transient fields are still emitted on per-tick patches
|
|
81
|
+
* (reliable or unreliable), but late-joining clients won't see them until
|
|
82
|
+
* the next mutation.
|
|
83
|
+
*
|
|
84
|
+
* Orthogonal to @unreliable: a field can be either, both, or neither.
|
|
85
|
+
*/
|
|
86
|
+
export declare function transient<T>(target: T, field: string): void;
|
|
64
87
|
export declare function type(type: DefinitionType, options?: TypeOptions): PropertyDecorator;
|
|
65
|
-
export declare function getPropertyDescriptor(
|
|
66
|
-
get: (this: Schema) =>
|
|
88
|
+
export declare function getPropertyDescriptor(fieldName: string, fieldIndex: number, type: DefinitionType, complexTypeKlass: TypeDefinition | false): {
|
|
89
|
+
get: (this: Schema) => any;
|
|
67
90
|
set: (this: Schema, value: any) => void;
|
|
68
91
|
enumerable: boolean;
|
|
69
92
|
configurable: boolean;
|
|
@@ -73,37 +96,57 @@ export declare function getPropertyDescriptor(fieldCached: string, fieldIndex: n
|
|
|
73
96
|
* The previous `@type()` annotation should remain along with this one.
|
|
74
97
|
*/
|
|
75
98
|
export declare function deprecated(throws?: boolean): PropertyDecorator;
|
|
76
|
-
export declare function defineTypes(target: typeof Schema, fields: Definition, options?: TypeOptions): typeof Schema;
|
|
77
99
|
type ExtractInitProps<T> = T extends {
|
|
78
100
|
initialize: (...args: infer P) => void;
|
|
79
|
-
} ? P extends readonly [] ? never : P extends readonly [infer First] ? First extends object ? First : P : P :
|
|
101
|
+
} ? P extends readonly [] ? never : P extends readonly [infer First] ? First extends object ? First : P : P : AssignableProps<InferSchemaInstanceType<T>>;
|
|
80
102
|
type IsInitPropsRequired<T> = T extends {
|
|
81
103
|
initialize: (props: any) => void;
|
|
82
104
|
} ? true : T extends {
|
|
83
105
|
initialize: (...args: infer P) => void;
|
|
84
106
|
} ? P extends readonly [] ? false : true : false;
|
|
85
|
-
|
|
86
|
-
|
|
107
|
+
/**
|
|
108
|
+
* A `schema()` field definition accepts a FieldBuilder, a Schema subclass
|
|
109
|
+
* (shorthand for `t.ref(Class)`), or a method (attached to the prototype).
|
|
110
|
+
*/
|
|
111
|
+
export type FieldsAndMethods = Record<string, FieldBuilder<any> | (new (...args: any[]) => Schema) | Function>;
|
|
112
|
+
export interface SchemaWithExtends<T, P extends typeof Schema> {
|
|
113
|
+
extend: <T2 extends FieldsAndMethods = FieldsAndMethods>(fields: T2 & ThisType<InferSchemaInstanceType<T & T2>>, name?: string) => SchemaWithExtendsConstructor<T & T2, ExtractInitProps<T2>, P>;
|
|
87
114
|
}
|
|
88
115
|
/**
|
|
89
|
-
* Get the type of the schema defined via `schema({...})` method.
|
|
116
|
+
* Get the type of the schema defined via `schema('Name', {...})` method.
|
|
90
117
|
*
|
|
91
118
|
* @example
|
|
92
|
-
* const Entity = schema({
|
|
93
|
-
* x:
|
|
94
|
-
* y:
|
|
119
|
+
* const Entity = schema('Entity', {
|
|
120
|
+
* x: t.number(),
|
|
121
|
+
* y: t.number(),
|
|
95
122
|
* });
|
|
96
123
|
* type Entity = SchemaType<typeof Entity>;
|
|
97
124
|
*/
|
|
98
125
|
export type SchemaType<T extends {
|
|
99
126
|
'~type': any;
|
|
100
127
|
}> = T['~type'];
|
|
101
|
-
export interface SchemaWithExtendsConstructor<T
|
|
128
|
+
export interface SchemaWithExtendsConstructor<T, InitProps, P extends typeof Schema> extends SchemaWithExtends<T, P> {
|
|
102
129
|
'~type': InferSchemaInstanceType<T>;
|
|
103
130
|
new (...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : IsInitPropsRequired<T> extends true ? [InitProps] : [InitProps?]): InferSchemaInstanceType<T> & InstanceType<P>;
|
|
104
131
|
prototype: InferSchemaInstanceType<T> & InstanceType<P> & {
|
|
105
132
|
initialize(...args: [InitProps] extends [never] ? [] : InitProps extends readonly any[] ? InitProps : [InitProps]): void;
|
|
106
133
|
};
|
|
107
134
|
}
|
|
108
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Define a Schema class declaratively.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* import { schema, t } from '@colyseus/schema';
|
|
140
|
+
*
|
|
141
|
+
* const Player = schema({
|
|
142
|
+
* hp: t.uint8().default(100),
|
|
143
|
+
* name: t.string().view(),
|
|
144
|
+
* takeDamage(n: number) { this.hp -= n; },
|
|
145
|
+
* }, 'Player');
|
|
146
|
+
*
|
|
147
|
+
* const Warrior = Player.extend({
|
|
148
|
+
* weapon: t.string(),
|
|
149
|
+
* }, 'Warrior');
|
|
150
|
+
*/
|
|
151
|
+
export declare function schema<T extends FieldsAndMethods, P extends typeof Schema = typeof Schema>(fieldsAndMethods: T & ThisType<InferSchemaInstanceType<T>>, name?: string, inherits?: P): SchemaWithExtendsConstructor<T, ExtractInitProps<T>, P>;
|
|
109
152
|
export {};
|
package/build/codegen/cli.cjs
CHANGED
|
@@ -213,7 +213,53 @@ function getInheritanceTree(klass, allClasses, includeSelf = true) {
|
|
|
213
213
|
let currentStructure;
|
|
214
214
|
let currentProperty;
|
|
215
215
|
let globalContext;
|
|
216
|
+
const BUILDER_COLLECTION_KINDS = new Set(["array", "map", "set", "collection"]);
|
|
217
|
+
/**
|
|
218
|
+
* For a t.*().chain().calls() expression, walk down to the base `t.X(...)`
|
|
219
|
+
* call and return its method name and first argument. Returns null if the
|
|
220
|
+
* node does not look like a builder chain.
|
|
221
|
+
*/
|
|
222
|
+
function extractBuilderBase(node) {
|
|
223
|
+
let current = node;
|
|
224
|
+
while (true) {
|
|
225
|
+
const expr = current.expression;
|
|
226
|
+
if (!ts__namespace.isPropertyAccessExpression(expr)) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (ts__namespace.isCallExpression(expr.expression)) {
|
|
230
|
+
// Chained modifier, e.g. .default() / .view() — walk deeper.
|
|
231
|
+
current = expr.expression;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
methodName: expr.name.text,
|
|
236
|
+
firstArg: current.arguments[0],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
216
240
|
function defineProperty(property, initializer) {
|
|
241
|
+
// Builder-style: t.number(), t.array(Item), t.map(Item).view(), etc.
|
|
242
|
+
if (ts__namespace.isCallExpression(initializer)) {
|
|
243
|
+
const base = extractBuilderBase(initializer);
|
|
244
|
+
if (base) {
|
|
245
|
+
if (BUILDER_COLLECTION_KINDS.has(base.methodName)) {
|
|
246
|
+
property.type = base.methodName;
|
|
247
|
+
if (base.firstArg) {
|
|
248
|
+
property.childType = base.firstArg.text ?? base.firstArg.getText();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else if (base.methodName === "ref") {
|
|
252
|
+
property.type = "ref";
|
|
253
|
+
if (base.firstArg) {
|
|
254
|
+
property.childType = base.firstArg.text ?? base.firstArg.getText();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
property.type = base.methodName;
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
217
263
|
if (ts__namespace.isIdentifier(initializer)) {
|
|
218
264
|
property.type = "ref";
|
|
219
265
|
property.childType = initializer.text;
|
|
@@ -377,30 +423,6 @@ function inspectNode(node, context, decoratorName) {
|
|
|
377
423
|
defineProperty(property, prop.initializer);
|
|
378
424
|
}
|
|
379
425
|
}
|
|
380
|
-
else if (node.getText() === "defineTypes" &&
|
|
381
|
-
(node.parent.kind === ts__namespace.SyntaxKind.CallExpression ||
|
|
382
|
-
node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)) {
|
|
383
|
-
/**
|
|
384
|
-
* JavaScript source file (`.js`)
|
|
385
|
-
* Using `defineTypes()`
|
|
386
|
-
*/
|
|
387
|
-
const callExpression = (node.parent.kind === ts__namespace.SyntaxKind.PropertyAccessExpression)
|
|
388
|
-
? node.parent.parent
|
|
389
|
-
: node.parent;
|
|
390
|
-
if (callExpression.kind !== ts__namespace.SyntaxKind.CallExpression) {
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
const className = callExpression.arguments[0].getText();
|
|
394
|
-
currentStructure.name = className;
|
|
395
|
-
const types = callExpression.arguments[1];
|
|
396
|
-
for (let i = 0; i < types.properties.length; i++) {
|
|
397
|
-
const prop = types.properties[i];
|
|
398
|
-
const property = currentProperty || new Property();
|
|
399
|
-
property.name = prop.name.escapedText;
|
|
400
|
-
currentStructure.addProperty(property);
|
|
401
|
-
defineProperty(property, prop.initializer);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
426
|
if (node.parent.kind === ts__namespace.SyntaxKind.ClassDeclaration) {
|
|
405
427
|
currentStructure.name = node.getText();
|
|
406
428
|
}
|
|
@@ -408,45 +430,63 @@ function inspectNode(node, context, decoratorName) {
|
|
|
408
430
|
break;
|
|
409
431
|
case ts__namespace.SyntaxKind.CallExpression:
|
|
410
432
|
/**
|
|
411
|
-
* Defining schema via
|
|
412
|
-
* - schema
|
|
413
|
-
* - schema({})
|
|
414
|
-
* -
|
|
433
|
+
* Defining schema via:
|
|
434
|
+
* - schema({ ... })
|
|
435
|
+
* - schema({ ... }, 'Name')
|
|
436
|
+
* - schema.schema({ ... }, 'Name')
|
|
437
|
+
* - ParentClass.extend({ ... }, 'Name')
|
|
415
438
|
*/
|
|
416
|
-
|
|
417
|
-
node.expression?.getText() === "schema") ||
|
|
418
|
-
(node.expression?.getText().indexOf(".extends") !== -1)) &&
|
|
419
|
-
node.arguments[0].kind === ts__namespace.SyntaxKind.ObjectLiteralExpression) {
|
|
439
|
+
{
|
|
420
440
|
const callExpression = node;
|
|
421
|
-
|
|
441
|
+
const callee = callExpression.expression?.getText?.();
|
|
442
|
+
if (!callee)
|
|
443
|
+
break;
|
|
444
|
+
const isSchemaCall = callee === "schema" || callee === "schema.schema";
|
|
445
|
+
const isExtendCall = callee.indexOf(".extend") !== -1 && !callee.endsWith(".extends");
|
|
446
|
+
if (!isSchemaCall && !isExtendCall)
|
|
447
|
+
break;
|
|
448
|
+
// Signature: (fields, name?)
|
|
449
|
+
const fieldsArg = callExpression.arguments[0];
|
|
450
|
+
const nameArg = callExpression.arguments[1];
|
|
451
|
+
if (!fieldsArg || fieldsArg.kind !== ts__namespace.SyntaxKind.ObjectLiteralExpression) {
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
let className;
|
|
455
|
+
if (nameArg) {
|
|
456
|
+
if (nameArg.kind === ts__namespace.SyntaxKind.StringLiteral) {
|
|
457
|
+
className = nameArg.text;
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
className = nameArg.getText();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
422
463
|
if (!className && callExpression.parent.kind === ts__namespace.SyntaxKind.VariableDeclaration) {
|
|
423
464
|
className = callExpression.parent.name?.getText();
|
|
424
465
|
}
|
|
425
|
-
|
|
426
|
-
if (!className) {
|
|
466
|
+
if (!className)
|
|
427
467
|
break;
|
|
428
|
-
}
|
|
429
468
|
if (currentStructure?.name !== className) {
|
|
430
469
|
currentStructure = new Class();
|
|
431
470
|
context.addStructure(currentStructure);
|
|
432
471
|
}
|
|
433
|
-
if (
|
|
434
|
-
// if it's using `.extends({})`
|
|
472
|
+
if (isExtendCall) {
|
|
435
473
|
const extendsClass = node.expression?.expression?.escapedText;
|
|
436
|
-
|
|
437
|
-
if (!extendsClass) {
|
|
474
|
+
if (!extendsClass)
|
|
438
475
|
break;
|
|
439
|
-
}
|
|
440
476
|
currentStructure.extends = extendsClass;
|
|
441
477
|
}
|
|
442
478
|
else {
|
|
443
|
-
|
|
444
|
-
currentStructure.extends = "Schema"; // force extends to Schema
|
|
479
|
+
currentStructure.extends = "Schema";
|
|
445
480
|
}
|
|
446
481
|
currentStructure.name = className;
|
|
447
|
-
const types =
|
|
482
|
+
const types = fieldsArg;
|
|
448
483
|
for (let i = 0; i < types.properties.length; i++) {
|
|
449
484
|
const prop = types.properties[i];
|
|
485
|
+
// Skip methods declared inside the fields object.
|
|
486
|
+
if (prop.kind === ts__namespace.SyntaxKind.MethodDeclaration)
|
|
487
|
+
continue;
|
|
488
|
+
if (!prop.initializer)
|
|
489
|
+
continue;
|
|
450
490
|
const property = currentProperty || new Property();
|
|
451
491
|
property.name = prop.name.escapedText;
|
|
452
492
|
currentStructure.addProperty(property);
|
|
@@ -643,33 +683,10 @@ ${generateClassBody$8(klass, indent)}
|
|
|
643
683
|
${namespace ? "}" : ""}
|
|
644
684
|
`;
|
|
645
685
|
}
|
|
646
|
-
/**
|
|
647
|
-
* Check if all enum members resolve to non-negative integers,
|
|
648
|
-
* allowing emission as a native C# `enum` (which only supports integral types).
|
|
649
|
-
*/
|
|
650
|
-
function canUseNativeEnum(_enum) {
|
|
651
|
-
return _enum.properties.every((prop) => {
|
|
652
|
-
if (!prop.type)
|
|
653
|
-
return true;
|
|
654
|
-
const n = Number(prop.type);
|
|
655
|
-
return Number.isInteger(n) && n >= 0;
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
686
|
/**
|
|
659
687
|
* Generate just the enum body (without imports/namespace) for bundling
|
|
660
688
|
*/
|
|
661
689
|
function generateEnumBody$1(_enum, indent = "") {
|
|
662
|
-
if (canUseNativeEnum(_enum)) {
|
|
663
|
-
const members = _enum.properties
|
|
664
|
-
.map((prop, i) => {
|
|
665
|
-
const value = prop.type ? Number(prop.type) : i;
|
|
666
|
-
return `${indent}\t${prop.name} = ${value},`;
|
|
667
|
-
})
|
|
668
|
-
.join("\n");
|
|
669
|
-
return `${indent}public enum ${_enum.name} : int {
|
|
670
|
-
${members}
|
|
671
|
-
${indent}}`;
|
|
672
|
-
}
|
|
673
690
|
return `${indent}public struct ${_enum.name} {
|
|
674
691
|
|
|
675
692
|
${_enum.properties
|