@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
|
@@ -3,7 +3,7 @@ import { $changes } from "../types/symbols";
|
|
|
3
3
|
import { getType } from "../types/registry";
|
|
4
4
|
|
|
5
5
|
import * as encode from "../encoding/encode";
|
|
6
|
-
import { EncodeSchemaError, assertInstanceType, assertType } from "../encoding/assert";
|
|
6
|
+
// import { EncodeSchemaError, assertInstanceType, assertType } from "../encoding/assert";
|
|
7
7
|
|
|
8
8
|
import type { ChangeTree, Ref } from "./ChangeTree";
|
|
9
9
|
import type { Encoder } from "./Encoder";
|
|
@@ -12,6 +12,7 @@ import type { PrimitiveType } from "../annotations";
|
|
|
12
12
|
|
|
13
13
|
import type { Iterator } from "../encoding/decode";
|
|
14
14
|
import type { ArraySchema } from "../types/custom/ArraySchema";
|
|
15
|
+
import type { Metadata } from "../Metadata";
|
|
15
16
|
|
|
16
17
|
export type EncodeOperation<T extends Ref = any> = (
|
|
17
18
|
encoder: Encoder,
|
|
@@ -24,40 +25,27 @@ export type EncodeOperation<T extends Ref = any> = (
|
|
|
24
25
|
hasView: boolean,
|
|
25
26
|
) => void;
|
|
26
27
|
|
|
27
|
-
export function encodePrimitiveType(
|
|
28
|
-
type: PrimitiveType,
|
|
29
|
-
bytes: Buffer,
|
|
30
|
-
value: any,
|
|
31
|
-
klass: Schema,
|
|
32
|
-
field: string | number,
|
|
33
|
-
it: Iterator,
|
|
34
|
-
) {
|
|
35
|
-
assertType(value, type as string, klass, field);
|
|
36
|
-
|
|
37
|
-
const encodeFunc = encode[type as string];
|
|
38
|
-
|
|
39
|
-
if (encodeFunc) {
|
|
40
|
-
encodeFunc(bytes, value, it);
|
|
41
|
-
// encodeFunc(bytes, value);
|
|
42
|
-
|
|
43
|
-
} else {
|
|
44
|
-
throw new EncodeSchemaError(`a '${type}' was expected, but ${value} was provided in ${klass.constructor.name}#${field}`);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
28
|
export function encodeValue(
|
|
49
29
|
encoder: Encoder,
|
|
50
30
|
bytes: Buffer,
|
|
51
|
-
ref: Ref,
|
|
31
|
+
// ref: Ref,
|
|
52
32
|
type: any,
|
|
53
33
|
value: any,
|
|
54
|
-
field: string | number,
|
|
34
|
+
// field: string | number,
|
|
55
35
|
operation: OPERATION,
|
|
56
36
|
it: Iterator,
|
|
57
37
|
) {
|
|
58
|
-
if (type
|
|
59
|
-
//
|
|
60
|
-
|
|
38
|
+
if (typeof (type) === "string") {
|
|
39
|
+
//
|
|
40
|
+
// Primitive values
|
|
41
|
+
//
|
|
42
|
+
// assertType(value, type as string, ref as Schema, field);
|
|
43
|
+
|
|
44
|
+
encode[type]?.(bytes, value, it);
|
|
45
|
+
|
|
46
|
+
} else if (type[Symbol.metadata] !== undefined) {
|
|
47
|
+
// // TODO: move this to the `@type()` annotation
|
|
48
|
+
// assertInstanceType(value, type as typeof Schema, ref as Schema, field);
|
|
61
49
|
|
|
62
50
|
//
|
|
63
51
|
// Encode refId for this instance.
|
|
@@ -70,22 +58,16 @@ export function encodeValue(
|
|
|
70
58
|
encoder.tryEncodeTypeId(bytes, type as typeof Schema, value.constructor as typeof Schema, it);
|
|
71
59
|
}
|
|
72
60
|
|
|
73
|
-
} else if (typeof (type) === "string") {
|
|
74
|
-
//
|
|
75
|
-
// Primitive values
|
|
76
|
-
//
|
|
77
|
-
encodePrimitiveType(type as PrimitiveType, bytes, value, ref as Schema, field, it);
|
|
78
|
-
|
|
79
61
|
} else {
|
|
80
|
-
//
|
|
81
|
-
// Custom type (MapSchema, ArraySchema, etc)
|
|
82
|
-
//
|
|
83
|
-
const definition = getType(Object.keys(type)[0]);
|
|
62
|
+
// //
|
|
63
|
+
// // Custom type (MapSchema, ArraySchema, etc)
|
|
64
|
+
// //
|
|
65
|
+
// const definition = getType(Object.keys(type)[0]);
|
|
84
66
|
|
|
85
|
-
//
|
|
86
|
-
// ensure a ArraySchema has been provided
|
|
87
|
-
//
|
|
88
|
-
assertInstanceType(ref[field], definition.constructor, ref as Schema, field);
|
|
67
|
+
// //
|
|
68
|
+
// // ensure a ArraySchema has been provided
|
|
69
|
+
// //
|
|
70
|
+
// assertInstanceType(ref[field], definition.constructor, ref as Schema, field);
|
|
89
71
|
|
|
90
72
|
//
|
|
91
73
|
// Encode refId for this instance.
|
|
@@ -107,13 +89,6 @@ export const encodeSchemaOperation: EncodeOperation = function (
|
|
|
107
89
|
operation: OPERATION,
|
|
108
90
|
it: Iterator,
|
|
109
91
|
) {
|
|
110
|
-
const ref = changeTree.ref;
|
|
111
|
-
const metadata = ref['constructor'][Symbol.metadata];
|
|
112
|
-
|
|
113
|
-
const field = metadata[index];
|
|
114
|
-
const type = metadata[field].type;
|
|
115
|
-
const value = ref[field];
|
|
116
|
-
|
|
117
92
|
// "compress" field index + operation
|
|
118
93
|
bytes[it.offset++] = (index | operation) & 255;
|
|
119
94
|
|
|
@@ -122,8 +97,21 @@ export const encodeSchemaOperation: EncodeOperation = function (
|
|
|
122
97
|
return;
|
|
123
98
|
}
|
|
124
99
|
|
|
100
|
+
const ref = changeTree.ref;
|
|
101
|
+
const metadata: Metadata = ref['constructor'][Symbol.metadata];
|
|
102
|
+
const field = metadata[index];
|
|
103
|
+
|
|
125
104
|
// TODO: inline this function call small performance gain
|
|
126
|
-
encodeValue(
|
|
105
|
+
encodeValue(
|
|
106
|
+
encoder,
|
|
107
|
+
bytes,
|
|
108
|
+
// ref,
|
|
109
|
+
metadata[index].type,
|
|
110
|
+
ref[field.name],
|
|
111
|
+
// index,
|
|
112
|
+
operation,
|
|
113
|
+
it
|
|
114
|
+
);
|
|
127
115
|
}
|
|
128
116
|
|
|
129
117
|
/**
|
|
@@ -134,12 +122,10 @@ export const encodeKeyValueOperation: EncodeOperation = function (
|
|
|
134
122
|
encoder: Encoder,
|
|
135
123
|
bytes: Buffer,
|
|
136
124
|
changeTree: ChangeTree,
|
|
137
|
-
|
|
125
|
+
index: number,
|
|
138
126
|
operation: OPERATION,
|
|
139
127
|
it: Iterator,
|
|
140
128
|
) {
|
|
141
|
-
const ref = changeTree.ref;
|
|
142
|
-
|
|
143
129
|
// encode operation
|
|
144
130
|
bytes[it.offset++] = operation & 255;
|
|
145
131
|
|
|
@@ -149,13 +135,15 @@ export const encodeKeyValueOperation: EncodeOperation = function (
|
|
|
149
135
|
}
|
|
150
136
|
|
|
151
137
|
// encode index
|
|
152
|
-
encode.number(bytes,
|
|
138
|
+
encode.number(bytes, index, it);
|
|
153
139
|
|
|
154
140
|
// Do not encode value for DELETE operations
|
|
155
141
|
if (operation === OPERATION.DELETE) {
|
|
156
142
|
return;
|
|
157
143
|
}
|
|
158
144
|
|
|
145
|
+
const ref = changeTree.ref;
|
|
146
|
+
|
|
159
147
|
//
|
|
160
148
|
// encode "alias" for dynamic fields (maps)
|
|
161
149
|
//
|
|
@@ -164,16 +152,38 @@ export const encodeKeyValueOperation: EncodeOperation = function (
|
|
|
164
152
|
//
|
|
165
153
|
// MapSchema dynamic key
|
|
166
154
|
//
|
|
167
|
-
const dynamicIndex = changeTree.ref['$indexes'].get(
|
|
155
|
+
const dynamicIndex = changeTree.ref['$indexes'].get(index);
|
|
168
156
|
encode.string(bytes, dynamicIndex, it);
|
|
169
157
|
}
|
|
170
158
|
}
|
|
171
159
|
|
|
172
|
-
const type = changeTree.getType(
|
|
173
|
-
const value = changeTree.getValue(
|
|
160
|
+
const type = changeTree.getType(index);
|
|
161
|
+
const value = changeTree.getValue(index);
|
|
162
|
+
|
|
163
|
+
// try { throw new Error(); } catch (e) {
|
|
164
|
+
// // only print if not coming from Reflection.ts
|
|
165
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
166
|
+
// console.log("encodeKeyValueOperation -> ", {
|
|
167
|
+
// ref: changeTree.ref.constructor.name,
|
|
168
|
+
// field,
|
|
169
|
+
// operation: OPERATION[operation],
|
|
170
|
+
// value: value?.toJSON(),
|
|
171
|
+
// items: ref.toJSON(),
|
|
172
|
+
// });
|
|
173
|
+
// }
|
|
174
|
+
// }
|
|
174
175
|
|
|
175
176
|
// TODO: inline this function call small performance gain
|
|
176
|
-
encodeValue(
|
|
177
|
+
encodeValue(
|
|
178
|
+
encoder,
|
|
179
|
+
bytes,
|
|
180
|
+
// ref,
|
|
181
|
+
type,
|
|
182
|
+
value,
|
|
183
|
+
// index,
|
|
184
|
+
operation,
|
|
185
|
+
it
|
|
186
|
+
);
|
|
177
187
|
}
|
|
178
188
|
|
|
179
189
|
/**
|
|
@@ -191,18 +201,22 @@ export const encodeArray: EncodeOperation = function (
|
|
|
191
201
|
hasView: boolean,
|
|
192
202
|
) {
|
|
193
203
|
const ref = changeTree.ref;
|
|
204
|
+
const useOperationByRefId = hasView && changeTree.isFiltered && (typeof (changeTree.getType(field)) !== "string");
|
|
194
205
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
let refOrIndex: number;
|
|
207
|
+
|
|
208
|
+
if (useOperationByRefId) {
|
|
209
|
+
refOrIndex = ref['tmpItems'][field][$changes].refId;
|
|
210
|
+
|
|
211
|
+
if (operation === OPERATION.DELETE) {
|
|
212
|
+
operation = OPERATION.DELETE_BY_REFID;
|
|
213
|
+
|
|
214
|
+
} else if (operation === OPERATION.ADD) {
|
|
215
|
+
operation = OPERATION.ADD_BY_REFID;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
} else {
|
|
219
|
+
refOrIndex = field;
|
|
206
220
|
}
|
|
207
221
|
|
|
208
222
|
// encode operation
|
|
@@ -214,7 +228,7 @@ export const encodeArray: EncodeOperation = function (
|
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
// encode index
|
|
217
|
-
encode.number(bytes,
|
|
231
|
+
encode.number(bytes, refOrIndex, it);
|
|
218
232
|
|
|
219
233
|
// Do not encode value for DELETE operations
|
|
220
234
|
if (operation === OPERATION.DELETE) {
|
|
@@ -233,5 +247,14 @@ export const encodeArray: EncodeOperation = function (
|
|
|
233
247
|
// });
|
|
234
248
|
|
|
235
249
|
// TODO: inline this function call small performance gain
|
|
236
|
-
encodeValue(
|
|
250
|
+
encodeValue(
|
|
251
|
+
encoder,
|
|
252
|
+
bytes,
|
|
253
|
+
// ref,
|
|
254
|
+
type,
|
|
255
|
+
value,
|
|
256
|
+
// field,
|
|
257
|
+
operation,
|
|
258
|
+
it
|
|
259
|
+
);
|
|
237
260
|
}
|
package/src/encoder/Encoder.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { Schema } from "../Schema";
|
|
2
|
-
import { TypeContext } from "../
|
|
3
|
-
import { $changes, $encoder, $filter } from "../types/symbols";
|
|
2
|
+
import { TypeContext } from "../types/TypeContext";
|
|
3
|
+
import { $changes, $encoder, $filter, $isNew, $onEncodeEnd } from "../types/symbols";
|
|
4
4
|
|
|
5
5
|
import * as encode from "../encoding/encode";
|
|
6
6
|
import type { Iterator } from "../encoding/decode";
|
|
7
7
|
|
|
8
|
-
import { SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
|
|
9
|
-
import { Root } from "./
|
|
8
|
+
import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
|
|
9
|
+
import { Root } from "./Root";
|
|
10
10
|
import { getNextPowerOf2 } from "../utils";
|
|
11
|
+
|
|
11
12
|
import type { StateView } from "./StateView";
|
|
13
|
+
import type { Metadata } from "../Metadata";
|
|
14
|
+
import type { ChangeTree } from "./ChangeTree";
|
|
12
15
|
|
|
13
16
|
export class Encoder<T extends Schema = any> {
|
|
14
17
|
static BUFFER_SIZE = 8 * 1024;// 8KB
|
|
@@ -19,14 +22,16 @@ export class Encoder<T extends Schema = any> {
|
|
|
19
22
|
|
|
20
23
|
root: Root;
|
|
21
24
|
|
|
22
|
-
constructor(
|
|
23
|
-
this.setRoot(root);
|
|
25
|
+
constructor(state: T) {
|
|
24
26
|
|
|
25
27
|
//
|
|
26
28
|
// TODO: cache and restore "Context" based on root schema
|
|
27
29
|
// (to avoid creating a new context for every new room)
|
|
28
30
|
//
|
|
29
|
-
this.context = new TypeContext(
|
|
31
|
+
this.context = new TypeContext(state.constructor as typeof Schema);
|
|
32
|
+
this.root = new Root(this.context);
|
|
33
|
+
|
|
34
|
+
this.setState(state);
|
|
30
35
|
|
|
31
36
|
// console.log(">>>>>>>>>>>>>>>> Encoder types");
|
|
32
37
|
// this.context.schemas.forEach((id, schema) => {
|
|
@@ -34,33 +39,38 @@ export class Encoder<T extends Schema = any> {
|
|
|
34
39
|
// });
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
protected
|
|
38
|
-
this.root = new Root();
|
|
42
|
+
protected setState(state: T) {
|
|
39
43
|
this.state = state;
|
|
40
|
-
state[$changes].setRoot(this.root);
|
|
44
|
+
this.state[$changes].setRoot(this.root);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
encode(
|
|
44
48
|
it: Iterator = { offset: 0 },
|
|
45
49
|
view?: StateView,
|
|
46
|
-
|
|
47
|
-
changeTrees = this.root.changes
|
|
50
|
+
buffer = this.sharedBuffer,
|
|
51
|
+
changeTrees = this.root.changes,
|
|
52
|
+
isEncodeAll = this.root.allChanges === changeTrees,
|
|
53
|
+
initialOffset = it.offset // cache current offset in case we need to resize the buffer
|
|
48
54
|
): Buffer {
|
|
49
|
-
const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
|
|
50
|
-
|
|
51
|
-
const isEncodeAll = this.root.allChanges === changeTrees;
|
|
52
55
|
const hasView = (view !== undefined);
|
|
53
56
|
const rootChangeTree = this.state[$changes];
|
|
54
57
|
|
|
55
|
-
const
|
|
58
|
+
const shouldClearChanges = !isEncodeAll && !hasView;
|
|
56
59
|
|
|
57
|
-
for (const [changeTree, changes] of
|
|
60
|
+
for (const [changeTree, changes] of changeTrees.entries()) {
|
|
58
61
|
const ref = changeTree.ref;
|
|
59
62
|
|
|
60
63
|
const ctor = ref['constructor'];
|
|
61
64
|
const encoder = ctor[$encoder];
|
|
62
65
|
const filter = ctor[$filter];
|
|
63
66
|
|
|
67
|
+
// try { throw new Error(); } catch (e) {
|
|
68
|
+
// // only print if not coming from Reflection.ts
|
|
69
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
70
|
+
// console.log("ChangeTree:", { ref: ref.constructor.name, });
|
|
71
|
+
// }
|
|
72
|
+
// }
|
|
73
|
+
|
|
64
74
|
if (hasView) {
|
|
65
75
|
if (!view.items.has(changeTree)) {
|
|
66
76
|
view.invisible.add(changeTree);
|
|
@@ -72,9 +82,10 @@ export class Encoder<T extends Schema = any> {
|
|
|
72
82
|
}
|
|
73
83
|
|
|
74
84
|
// skip root `refId` if it's the first change tree
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// (unless it "hasView", which will need to revisit the root)
|
|
86
|
+
if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
|
|
87
|
+
buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;
|
|
88
|
+
encode.number(buffer, changeTree.refId, it);
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
const changesIterator = changes.entries();
|
|
@@ -88,91 +99,115 @@ export class Encoder<T extends Schema = any> {
|
|
|
88
99
|
// TODO: avoid checking if no view tags were defined
|
|
89
100
|
//
|
|
90
101
|
if (filter && !filter(ref, fieldIndex, view)) {
|
|
91
|
-
// console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
|
|
92
|
-
|
|
93
102
|
// console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
|
|
94
103
|
// view?.invisible.add(changeTree);
|
|
95
104
|
continue;
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
// try { throw new Error(); } catch (e) {
|
|
108
|
+
// // only print if not coming from Reflection.ts
|
|
109
|
+
// if (!e.stack.includes("src/Reflection.ts")) {
|
|
110
|
+
// console.log("WILL ENCODE", {
|
|
111
|
+
// ref: changeTree.ref.constructor.name,
|
|
112
|
+
// fieldIndex,
|
|
113
|
+
// operation: OPERATION[operation],
|
|
114
|
+
// });
|
|
115
|
+
// }
|
|
116
|
+
// }
|
|
117
|
+
|
|
118
|
+
encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
|
|
105
119
|
}
|
|
120
|
+
|
|
121
|
+
// if (shouldClearChanges) {
|
|
122
|
+
// changeTree.endEncode();
|
|
123
|
+
// }
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
if (it.offset >
|
|
109
|
-
const newSize = getNextPowerOf2(
|
|
110
|
-
console.warn(
|
|
126
|
+
if (it.offset > buffer.byteLength) {
|
|
127
|
+
const newSize = getNextPowerOf2(buffer.byteLength * 2);
|
|
128
|
+
console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
|
|
129
|
+
|
|
130
|
+
import { Encoder } from "@colyseus/schema";
|
|
131
|
+
Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
|
|
132
|
+
`);
|
|
111
133
|
|
|
112
134
|
//
|
|
113
135
|
// resize buffer and re-encode (TODO: can we avoid re-encoding here?)
|
|
114
136
|
//
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
buffer = Buffer.allocUnsafeSlow(newSize);
|
|
138
|
+
|
|
139
|
+
// assign resized buffer to local sharedBuffer
|
|
140
|
+
if (buffer === this.sharedBuffer) {
|
|
141
|
+
this.sharedBuffer = buffer;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return this.encode({ offset: initialOffset }, view, buffer, changeTrees, isEncodeAll);
|
|
117
145
|
|
|
118
146
|
} else {
|
|
119
147
|
//
|
|
120
148
|
// only clear changes after making sure buffer resize is not required.
|
|
121
149
|
//
|
|
122
|
-
if (
|
|
150
|
+
if (shouldClearChanges) {
|
|
123
151
|
//
|
|
124
152
|
// FIXME: avoid iterating over change trees twice.
|
|
125
153
|
//
|
|
126
154
|
this.onEndEncode(changeTrees);
|
|
127
155
|
}
|
|
128
156
|
|
|
129
|
-
|
|
130
|
-
return bytes.slice(0, it.offset);
|
|
157
|
+
return buffer.subarray(0, it.offset);
|
|
131
158
|
}
|
|
132
159
|
}
|
|
133
160
|
|
|
134
|
-
encodeAll(it: Iterator = { offset: 0 }) {
|
|
135
|
-
// console.log(
|
|
161
|
+
encodeAll(it: Iterator = { offset: 0 }, buffer: Buffer = this.sharedBuffer) {
|
|
162
|
+
// console.log(`\nencodeAll(), this.root.allChanges (${this.root.allChanges.size})`);
|
|
163
|
+
// this.debugChanges("allChanges");
|
|
136
164
|
|
|
137
|
-
|
|
138
|
-
// console.log("->", item[0].refId, item[0].ref.toJSON());
|
|
139
|
-
// });
|
|
140
|
-
|
|
141
|
-
return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
|
|
165
|
+
return this.encode(it, undefined, buffer, this.root.allChanges, true);
|
|
142
166
|
}
|
|
143
167
|
|
|
144
168
|
encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
|
|
145
169
|
const viewOffset = it.offset;
|
|
146
170
|
|
|
147
|
-
// console.log(
|
|
148
|
-
// this.
|
|
171
|
+
// console.log(`\nencodeAllView(), this.root.allFilteredChanges (${this.root.allFilteredChanges.size})`);
|
|
172
|
+
// this.debugChanges("allFilteredChanges");
|
|
149
173
|
|
|
150
174
|
// try to encode "filtered" changes
|
|
151
|
-
this.encode(it, view, bytes, this.root.allFilteredChanges);
|
|
175
|
+
this.encode(it, view, bytes, this.root.allFilteredChanges, true, viewOffset);
|
|
152
176
|
|
|
153
177
|
return Buffer.concat([
|
|
154
|
-
bytes.
|
|
155
|
-
bytes.
|
|
178
|
+
bytes.subarray(0, sharedOffset),
|
|
179
|
+
bytes.subarray(viewOffset, it.offset)
|
|
156
180
|
]);
|
|
157
181
|
}
|
|
158
182
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
debugChanges(
|
|
184
|
+
field: "changes" | "allFilteredChanges" | "allChanges" | "filteredChanges" | Map<ChangeTree, Map<number, OPERATION>>
|
|
185
|
+
) {
|
|
186
|
+
const changeSet = (typeof (field) === "string")
|
|
187
|
+
? this.root[field]
|
|
188
|
+
: field;
|
|
189
|
+
|
|
190
|
+
Array.from(changeSet.entries()).map((item) => {
|
|
191
|
+
const metadata: Metadata = item[0].ref.constructor[Symbol.metadata];
|
|
192
|
+
console.log("->", { ref: item[0].ref.constructor.name, refId: item[0].refId, changes: item[1].size });
|
|
193
|
+
item[1].forEach((op, index) => {
|
|
194
|
+
console.log(" ->", {
|
|
195
|
+
index,
|
|
196
|
+
field: metadata?.[index],
|
|
197
|
+
op: OPERATION[op],
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
170
202
|
|
|
171
203
|
encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {
|
|
172
204
|
const viewOffset = it.offset;
|
|
173
205
|
|
|
174
|
-
//
|
|
175
|
-
this.
|
|
206
|
+
// console.log(`\nencodeView(), view.changes (${view.changes.size})`);
|
|
207
|
+
// this.debugChanges(view.changes);
|
|
208
|
+
|
|
209
|
+
// console.log(`\nencodeView(), this.root.filteredChanges (${this.root.filteredChanges.size})`);
|
|
210
|
+
// this.debugChanges("filteredChanges");
|
|
176
211
|
|
|
177
212
|
// encode visibility changes (add/remove for this view)
|
|
178
213
|
const viewChangesIterator = view.changes.entries();
|
|
@@ -207,9 +242,12 @@ export class Encoder<T extends Schema = any> {
|
|
|
207
242
|
// clear "view" changes after encoding
|
|
208
243
|
view.changes.clear();
|
|
209
244
|
|
|
245
|
+
// try to encode "filtered" changes
|
|
246
|
+
this.encode(it, view, bytes, this.root.filteredChanges, false, viewOffset);
|
|
247
|
+
|
|
210
248
|
return Buffer.concat([
|
|
211
|
-
bytes.
|
|
212
|
-
bytes.
|
|
249
|
+
bytes.subarray(0, sharedOffset),
|
|
250
|
+
bytes.subarray(viewOffset, it.offset)
|
|
213
251
|
]);
|
|
214
252
|
}
|
|
215
253
|
|
|
@@ -217,6 +255,14 @@ export class Encoder<T extends Schema = any> {
|
|
|
217
255
|
const changeTreesIterator = changeTrees.entries();
|
|
218
256
|
for (const [changeTree, _] of changeTreesIterator) {
|
|
219
257
|
changeTree.endEncode();
|
|
258
|
+
// changeTree.changes.clear();
|
|
259
|
+
|
|
260
|
+
// // ArraySchema and MapSchema have a custom "encode end" method
|
|
261
|
+
// changeTree.ref[$onEncodeEnd]?.();
|
|
262
|
+
|
|
263
|
+
// // Not a new instance anymore
|
|
264
|
+
// delete changeTree[$isNew];
|
|
265
|
+
|
|
220
266
|
}
|
|
221
267
|
}
|
|
222
268
|
|
|
@@ -242,4 +288,4 @@ export class Encoder<T extends Schema = any> {
|
|
|
242
288
|
encode.number(bytes, targetTypeId, it);
|
|
243
289
|
}
|
|
244
290
|
}
|
|
245
|
-
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { OPERATION } from "../encoding/spec";
|
|
2
|
+
import { TypeContext } from "../types/TypeContext";
|
|
3
|
+
import { ChangeTree } from "./ChangeTree";
|
|
4
|
+
|
|
5
|
+
export class Root {
|
|
6
|
+
protected nextUniqueId: number = 0;
|
|
7
|
+
refCount = new WeakMap<ChangeTree, number>();
|
|
8
|
+
|
|
9
|
+
// all changes
|
|
10
|
+
allChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
11
|
+
allFilteredChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
12
|
+
|
|
13
|
+
// pending changes to be encoded
|
|
14
|
+
changes = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
15
|
+
filteredChanges = new Map<ChangeTree, Map<number, OPERATION>>();
|
|
16
|
+
|
|
17
|
+
constructor(public types: TypeContext) { }
|
|
18
|
+
|
|
19
|
+
getNextUniqueId() {
|
|
20
|
+
return this.nextUniqueId++;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
add(changeTree: ChangeTree) {
|
|
24
|
+
const refCount = this.refCount.get(changeTree) || 0;
|
|
25
|
+
this.refCount.set(changeTree, refCount + 1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
remove(changeTree: ChangeTree) {
|
|
29
|
+
const refCount = this.refCount.get(changeTree);
|
|
30
|
+
if (refCount <= 1) {
|
|
31
|
+
this.allChanges.delete(changeTree);
|
|
32
|
+
this.changes.delete(changeTree);
|
|
33
|
+
|
|
34
|
+
if (changeTree.isFiltered || changeTree.isPartiallyFiltered) {
|
|
35
|
+
this.allFilteredChanges.delete(changeTree);
|
|
36
|
+
this.filteredChanges.delete(changeTree);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.refCount.delete(changeTree);
|
|
40
|
+
|
|
41
|
+
} else {
|
|
42
|
+
this.refCount.set(changeTree, refCount - 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
changeTree.forEachChild((child, _) => this.remove(child));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clear() {
|
|
49
|
+
this.changes.clear();
|
|
50
|
+
}
|
|
51
|
+
}
|