@colyseus/schema 3.0.0-alpha.9 → 3.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 +148 -62
- package/bin/schema-debug +94 -0
- package/build/cjs/index.js +2222 -1513
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +2223 -1516
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +2225 -1516
- 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 -31
- package/lib/Reflection.js.map +1 -1
- package/lib/Schema.d.ts +12 -5
- package/lib/Schema.js +57 -56
- 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 +9 -46
- package/lib/codegen/languages/csharp.js.map +1 -1
- package/lib/codegen/languages/haxe.js +4 -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 +23 -25
- 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 +3 -4
- package/lib/decoder/DecodeOperation.js +35 -17
- package/lib/decoder/DecodeOperation.js.map +1 -1
- package/lib/decoder/Decoder.d.ts +5 -6
- package/lib/decoder/Decoder.js +10 -10
- package/lib/decoder/Decoder.js.map +1 -1
- package/lib/decoder/ReferenceTracker.js +4 -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 +74 -64
- package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
- package/lib/encoder/ChangeTree.d.ts +28 -20
- package/lib/encoder/ChangeTree.js +242 -188
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.d.ts +3 -6
- package/lib/encoder/EncodeOperation.js +51 -65
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +8 -7
- package/lib/encoder/Encoder.js +128 -79
- 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 +72 -74
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/encoding/assert.d.ts +7 -6
- package/lib/encoding/assert.js +13 -5
- package/lib/encoding/assert.js.map +1 -1
- package/lib/encoding/decode.d.ts +36 -19
- package/lib/encoding/decode.js +54 -84
- package/lib/encoding/decode.js.map +1 -1
- package/lib/encoding/encode.d.ts +36 -18
- package/lib/encoding/encode.js +61 -48
- package/lib/encoding/encode.js.map +1 -1
- package/lib/encoding/spec.d.ts +4 -5
- package/lib/encoding/spec.js +1 -2
- package/lib/encoding/spec.js.map +1 -1
- package/lib/index.d.ts +10 -9
- package/lib/index.js +24 -17
- 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 +29 -0
- package/lib/types/TypeContext.js +151 -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.d.ts +2 -2
- package/lib/types/custom/CollectionSchema.js +1 -0
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.d.ts +18 -16
- package/lib/types/custom/MapSchema.js +12 -4
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.d.ts +2 -2
- package/lib/types/custom/SetSchema.js +1 -0
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/lib/types/registry.d.ts +8 -1
- package/lib/types/registry.js +23 -6
- 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 +19 -18
- package/src/Metadata.ts +190 -42
- package/src/Reflection.ts +76 -38
- package/src/Schema.ts +72 -70
- package/src/annotations.ts +156 -202
- package/src/bench_encode.ts +108 -0
- package/src/codegen/languages/csharp.ts +8 -47
- package/src/codegen/languages/haxe.ts +4 -0
- package/src/codegen/languages/lua.ts +19 -27
- 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 +43 -15
- package/src/decoder/Decoder.ts +12 -10
- package/src/decoder/ReferenceTracker.ts +5 -3
- package/src/decoder/strategy/StateCallbacks.ts +152 -81
- package/src/encoder/ChangeTree.ts +282 -209
- package/src/encoder/EncodeOperation.ts +78 -78
- package/src/encoder/Encoder.ts +152 -87
- package/src/encoder/Root.ts +93 -0
- package/src/encoder/StateView.ts +80 -88
- package/src/encoding/assert.ts +17 -8
- package/src/encoding/decode.ts +73 -93
- package/src/encoding/encode.ts +76 -45
- package/src/encoding/spec.ts +3 -5
- package/src/index.ts +12 -20
- package/src/types/HelperTypes.ts +54 -2
- package/src/types/TypeContext.ts +175 -0
- package/src/types/custom/ArraySchema.ts +49 -19
- package/src/types/custom/CollectionSchema.ts +1 -0
- package/src/types/custom/MapSchema.ts +30 -17
- package/src/types/custom/SetSchema.ts +1 -0
- package/src/types/registry.ts +22 -3
- package/src/types/symbols.ts +10 -7
- package/src/utils.ts +7 -3
|
@@ -59,16 +59,16 @@ function generateClass(klass: Class, namespace: string) {
|
|
|
59
59
|
return `${getCommentHeader()}
|
|
60
60
|
|
|
61
61
|
using Colyseus.Schema;
|
|
62
|
-
|
|
62
|
+
#if UNITY_5_3_OR_NEWER
|
|
63
|
+
using UnityEngine.Scripting;
|
|
64
|
+
#endif
|
|
63
65
|
${namespace ? `\nnamespace ${namespace} {` : ""}
|
|
64
66
|
${indent}public partial class ${klass.name} : ${klass.extends} {
|
|
67
|
+
#if UNITY_5_3_OR_NEWER
|
|
68
|
+
[Preserve]
|
|
69
|
+
#endif
|
|
70
|
+
public ${klass.name}() { }
|
|
65
71
|
${klass.properties.map((prop) => generateProperty(prop, indent)).join("\n\n")}
|
|
66
|
-
|
|
67
|
-
${indent}\t/*
|
|
68
|
-
${indent}\t * Support for individual property change callbacks below...
|
|
69
|
-
${indent}\t */
|
|
70
|
-
|
|
71
|
-
${generateAllFieldCallbacks(klass, indent)}
|
|
72
72
|
${indent}}
|
|
73
73
|
${namespace ? "}" : ""}
|
|
74
74
|
`;
|
|
@@ -119,7 +119,7 @@ function generateProperty(prop: Property, indent: string = "") {
|
|
|
119
119
|
typeArgs += `, "${prop.childType}"`;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
initializer = `
|
|
122
|
+
initializer = `null`;
|
|
123
123
|
|
|
124
124
|
} else {
|
|
125
125
|
langType = getType(prop);
|
|
@@ -147,45 +147,6 @@ ${namespace ? "}" : ""}
|
|
|
147
147
|
`;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function generateAllFieldCallbacks(klass: Class, indent: string) {
|
|
151
|
-
//
|
|
152
|
-
// TODO: improve me. It would be great to generate less boilerplate in favor
|
|
153
|
-
// of a single implementation on C# Schema class itself.
|
|
154
|
-
//
|
|
155
|
-
const eventNames: string[] = [];
|
|
156
|
-
return `${klass.properties
|
|
157
|
-
.filter(prop => !prop.deprecated) // generate only for properties that haven't been deprecated.
|
|
158
|
-
.map(prop => {
|
|
159
|
-
const eventName = `__${prop.name}Change`;
|
|
160
|
-
eventNames.push(eventName);
|
|
161
|
-
|
|
162
|
-
const defaultNull = (prop.childType)
|
|
163
|
-
? "null"
|
|
164
|
-
: `default(${getType(prop)})`;
|
|
165
|
-
|
|
166
|
-
return `\t${indent}protected event PropertyChangeHandler<${getType(prop)}> ${eventName};
|
|
167
|
-
\t${indent}public Action On${capitalize(prop.name)}Change(PropertyChangeHandler<${getType(prop)}> __handler, bool __immediate = true) {
|
|
168
|
-
\t${indent}\tif (__callbacks == null) { __callbacks = new SchemaCallbacks(); }
|
|
169
|
-
\t${indent}\t__callbacks.AddPropertyCallback(nameof(this.${prop.name}));
|
|
170
|
-
\t${indent}\t${eventName} += __handler;
|
|
171
|
-
\t${indent}\tif (__immediate && this.${prop.name} != ${defaultNull}) { __handler(this.${prop.name}, ${defaultNull}); }
|
|
172
|
-
\t${indent}\treturn () => {
|
|
173
|
-
\t${indent}\t\t__callbacks.RemovePropertyCallback(nameof(${prop.name}));
|
|
174
|
-
\t${indent}\t\t${eventName} -= __handler;
|
|
175
|
-
\t${indent}\t};
|
|
176
|
-
\t${indent}}`;
|
|
177
|
-
}).join("\n\n")}
|
|
178
|
-
|
|
179
|
-
\t${indent}protected override void TriggerFieldChange(DataChange change) {
|
|
180
|
-
\t${indent}\tswitch (change.Field) {
|
|
181
|
-
${klass.properties.filter(prop => !prop.deprecated).map((prop, i) => {
|
|
182
|
-
return `\t${indent}\t\tcase nameof(${prop.name}): ${eventNames[i]}?.Invoke((${getType(prop)}) change.Value, (${getType(prop)}) change.PreviousValue); break;`;
|
|
183
|
-
}).join("\n")}
|
|
184
|
-
\t${indent}\t\tdefault: break;
|
|
185
|
-
\t\t${indent}}
|
|
186
|
-
\t${indent}}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
150
|
function getChildType(prop: Property) {
|
|
190
151
|
return typeMaps[prop.childType];
|
|
191
152
|
}
|
|
@@ -106,5 +106,9 @@ function generateProperty(prop: Property) {
|
|
|
106
106
|
initializer = typeInitializer[prop.type];
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// TODO: remove initializer. The callbacks at the Haxe decoder side have a
|
|
110
|
+
// "FIXME" comment about this on Decoder.hx
|
|
111
|
+
|
|
109
112
|
return `\t@:type(${typeArgs})\n\tpublic var ${prop.name}: ${langType} = ${initializer};\n`
|
|
113
|
+
// return `\t@:type(${typeArgs})\n\tpublic var ${prop.name}: ${langType};\n`
|
|
110
114
|
}
|
|
@@ -43,11 +43,14 @@ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Inheritance support
|
|
47
|
+
const inherits = (klass.extends !== "Schema")
|
|
48
|
+
? `, ${klass.extends}`
|
|
49
|
+
: "";
|
|
47
50
|
|
|
48
51
|
return `${getCommentHeader().replace(/\/\//mg, "--")}
|
|
49
52
|
|
|
50
|
-
local schema = require 'colyseus.
|
|
53
|
+
local schema = require 'colyseus.serializer.schema.schema'
|
|
51
54
|
${allRefs.
|
|
52
55
|
filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
|
|
53
56
|
map(ref => ref.childType).
|
|
@@ -56,25 +59,15 @@ ${allRefs.
|
|
|
56
59
|
map(childType => `local ${childType} = require '${(namespace ? `${namespace}.` : '')}${childType}'`).
|
|
57
60
|
join("\n")}
|
|
58
61
|
|
|
62
|
+
---@class ${klass.name}: ${klass.extends}
|
|
63
|
+
${klass.properties.map(prop => `---@field ${prop.name} ${getLUATypeAnnotation(prop)}`).join("\n")}
|
|
59
64
|
local ${klass.name} = schema.define({
|
|
60
65
|
${klass.properties.map(prop => generatePropertyDeclaration(prop)).join(",\n")},
|
|
61
66
|
["_fields_by_index"] = { ${klass.properties.map(prop => `"${prop.name}"`).join(", ")} },
|
|
62
|
-
})
|
|
67
|
+
}${inherits})
|
|
63
68
|
|
|
64
69
|
return ${klass.name}
|
|
65
70
|
`;
|
|
66
|
-
|
|
67
|
-
// ["on_change"] = function(changes)
|
|
68
|
-
// -- on change logic here
|
|
69
|
-
// end,
|
|
70
|
-
|
|
71
|
-
// ["on_add"] = function()
|
|
72
|
-
// -- on add logic here
|
|
73
|
-
// end,
|
|
74
|
-
|
|
75
|
-
// ["on_remove"] = function()
|
|
76
|
-
// -- on remove logic here
|
|
77
|
-
// end,
|
|
78
71
|
}
|
|
79
72
|
|
|
80
73
|
function generatePropertyDeclaration(prop: Property) {
|
|
@@ -108,18 +101,17 @@ function generatePropertyDeclaration(prop: Property) {
|
|
|
108
101
|
return ` ["${prop.name}"] = ${typeArgs}`;
|
|
109
102
|
}
|
|
110
103
|
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
function getLUATypeAnnotation(prop: Property) {
|
|
105
|
+
if (prop.type === "ref") {
|
|
106
|
+
return prop.childType;
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
} else if (prop.type === "array") {
|
|
109
|
+
return "ArraySchema";
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
} else if (prop.type === "map") {
|
|
112
|
+
return "MapSchema";
|
|
119
113
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// return `this.${prop.name} = ${initializer}`;
|
|
125
|
-
// }
|
|
114
|
+
} else {
|
|
115
|
+
return typeMaps[prop.type];
|
|
116
|
+
}
|
|
117
|
+
}
|
package/src/codegen/parser.ts
CHANGED
|
@@ -148,6 +148,49 @@ function inspectNode(node: ts.Node, context: Context, decoratorName: string) {
|
|
|
148
148
|
defineProperty(property, typeArgument);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
} else if (
|
|
152
|
+
node.getText() === "setFields" &&
|
|
153
|
+
(
|
|
154
|
+
node.parent.kind === ts.SyntaxKind.CallExpression ||
|
|
155
|
+
node.parent.kind === ts.SyntaxKind.PropertyAccessExpression
|
|
156
|
+
)
|
|
157
|
+
) {
|
|
158
|
+
/**
|
|
159
|
+
* Metadata.setFields(klassName, { ... })
|
|
160
|
+
*/
|
|
161
|
+
const callExpression = (node.parent.kind === ts.SyntaxKind.PropertyAccessExpression)
|
|
162
|
+
? node.parent.parent as ts.CallExpression
|
|
163
|
+
: node.parent as ts.CallExpression;
|
|
164
|
+
|
|
165
|
+
if (callExpression.kind !== ts.SyntaxKind.CallExpression) {
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const classNameNode = callExpression.arguments[0];
|
|
170
|
+
const className = ts.isClassExpression(classNameNode)
|
|
171
|
+
? classNameNode.name?.escapedText.toString()
|
|
172
|
+
: classNameNode.getText();
|
|
173
|
+
|
|
174
|
+
// skip if no className is provided
|
|
175
|
+
if (!className) { break; }
|
|
176
|
+
|
|
177
|
+
if (currentStructure.name !== className) {
|
|
178
|
+
currentStructure = new Class();
|
|
179
|
+
}
|
|
180
|
+
context.addStructure(currentStructure);
|
|
181
|
+
(currentStructure as Class).extends = "Schema"; // force extends to Schema
|
|
182
|
+
currentStructure.name = className;
|
|
183
|
+
|
|
184
|
+
const types = callExpression.arguments[1] as any;
|
|
185
|
+
for (let i = 0; i < types.properties.length; i++) {
|
|
186
|
+
const prop = types.properties[i];
|
|
187
|
+
|
|
188
|
+
const property = currentProperty || new Property();
|
|
189
|
+
property.name = prop.name.escapedText;
|
|
190
|
+
|
|
191
|
+
currentStructure.addProperty(property);
|
|
192
|
+
defineProperty(property, prop.initializer);
|
|
193
|
+
}
|
|
151
194
|
|
|
152
195
|
} else if (
|
|
153
196
|
node.getText() === "defineTypes" &&
|
|
@@ -192,6 +235,70 @@ function inspectNode(node: ts.Node, context: Context, decoratorName: string) {
|
|
|
192
235
|
|
|
193
236
|
break;
|
|
194
237
|
|
|
238
|
+
case ts.SyntaxKind.CallExpression:
|
|
239
|
+
/**
|
|
240
|
+
* Defining schema via `schema.schema({ ... })`
|
|
241
|
+
* - schema.schema({})
|
|
242
|
+
* - schema({})
|
|
243
|
+
* - ClassName.extends({})
|
|
244
|
+
*/
|
|
245
|
+
if (
|
|
246
|
+
(
|
|
247
|
+
(
|
|
248
|
+
(node as ts.CallExpression).expression?.getText() === "schema.schema" ||
|
|
249
|
+
(node as ts.CallExpression).expression?.getText() === "schema"
|
|
250
|
+
) ||
|
|
251
|
+
(
|
|
252
|
+
(node as ts.CallExpression).expression?.getText().indexOf(".extends") !== -1
|
|
253
|
+
)
|
|
254
|
+
) &&
|
|
255
|
+
(node as ts.CallExpression).arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression
|
|
256
|
+
) {
|
|
257
|
+
const callExpression = node as ts.CallExpression;
|
|
258
|
+
|
|
259
|
+
let className = callExpression.arguments[1]?.getText();
|
|
260
|
+
|
|
261
|
+
if (!className && callExpression.parent.kind === ts.SyntaxKind.VariableDeclaration) {
|
|
262
|
+
className = (callExpression.parent as ts.VariableDeclaration).name?.getText();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// skip if no className is provided
|
|
266
|
+
if (!className) { break; }
|
|
267
|
+
|
|
268
|
+
if (currentStructure.name !== className) {
|
|
269
|
+
currentStructure = new Class();
|
|
270
|
+
context.addStructure(currentStructure);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if ((node as ts.CallExpression).expression?.getText().indexOf(".extends") !== -1) {
|
|
274
|
+
// if it's using `.extends({})`
|
|
275
|
+
const extendsClass = (node as any).expression?.expression?.escapedText;
|
|
276
|
+
|
|
277
|
+
// skip if no extendsClass is provided
|
|
278
|
+
if (!extendsClass) { break; }
|
|
279
|
+
(currentStructure as Class).extends = extendsClass;
|
|
280
|
+
|
|
281
|
+
} else {
|
|
282
|
+
// if it's using `schema({})`
|
|
283
|
+
(currentStructure as Class).extends = "Schema"; // force extends to Schema
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
currentStructure.name = className;
|
|
287
|
+
|
|
288
|
+
const types = callExpression.arguments[0] as any;
|
|
289
|
+
for (let i = 0; i < types.properties.length; i++) {
|
|
290
|
+
const prop = types.properties[i];
|
|
291
|
+
|
|
292
|
+
const property = currentProperty || new Property();
|
|
293
|
+
property.name = prop.name.escapedText;
|
|
294
|
+
|
|
295
|
+
currentStructure.addProperty(property);
|
|
296
|
+
defineProperty(property, prop.initializer);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
break;
|
|
301
|
+
|
|
195
302
|
case ts.SyntaxKind.EnumMember:
|
|
196
303
|
if (currentStructure instanceof Enum) {
|
|
197
304
|
const initializer = (node as any).initializer?.text;
|
package/src/codegen/types.ts
CHANGED
package/src/debug.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { Reflection, Decoder } from "./index";
|
|
3
|
+
|
|
4
|
+
const contents = fs.readFileSync("/Users/endel/Projects/colyseus/clients/bubbits/project/@bubbits/backend/schema-debug.txt", { encoding: "utf8" }).toString();
|
|
5
|
+
|
|
6
|
+
let isCommentBlock = false;
|
|
7
|
+
let lastComment = "";
|
|
8
|
+
|
|
9
|
+
let decoder: Decoder;
|
|
10
|
+
|
|
11
|
+
function getBuffer(line: string) {
|
|
12
|
+
const start = line.lastIndexOf(":");
|
|
13
|
+
const buffer = Buffer.from(new Uint8Array(line.substring(start + 1).split(",").map(n => Number(n))));
|
|
14
|
+
console.log(`(${buffer.byteLength}) ${Array.from(buffer).join(",")}`)
|
|
15
|
+
// console.log("");
|
|
16
|
+
// console.log("");
|
|
17
|
+
// console.log("> ", line);
|
|
18
|
+
// console.log("> substring:", line.substring(start + 1))
|
|
19
|
+
return buffer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function decode(buffer: Buffer) {
|
|
23
|
+
try {
|
|
24
|
+
decoder.decode(buffer);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(e);
|
|
27
|
+
console.log("Last log:\n\n")
|
|
28
|
+
console.log(lastComment);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
contents.split("\n").forEach((line) => {
|
|
33
|
+
if (line.startsWith("#")) {
|
|
34
|
+
// reset last comment.
|
|
35
|
+
if (isCommentBlock === false) { lastComment = ""; }
|
|
36
|
+
|
|
37
|
+
isCommentBlock = true;
|
|
38
|
+
lastComment += line.substring(line.indexOf(":") + 1) + "\n";
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
isCommentBlock = false;
|
|
43
|
+
|
|
44
|
+
if (line.startsWith("handshake:") && !decoder) {
|
|
45
|
+
decoder = Reflection.decode(getBuffer(line));
|
|
46
|
+
|
|
47
|
+
} else if (line.startsWith("state:")) {
|
|
48
|
+
decode(getBuffer(line));
|
|
49
|
+
|
|
50
|
+
} else if (line.startsWith("patch:")) {
|
|
51
|
+
decode(getBuffer(line));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
console.log(decoder.state.toJSON());
|
|
@@ -3,7 +3,7 @@ import { Metadata } from "../Metadata";
|
|
|
3
3
|
import { Schema } from "../Schema";
|
|
4
4
|
import type { Ref } from "../encoder/ChangeTree";
|
|
5
5
|
import type { Decoder } from "./Decoder";
|
|
6
|
-
import
|
|
6
|
+
import { Iterator, decode } from "../encoding/decode";
|
|
7
7
|
import { $childType, $deleteByIndex, $getByIndex } from "../types/symbols";
|
|
8
8
|
|
|
9
9
|
import type { MapSchema } from "../types/custom/MapSchema";
|
|
@@ -28,7 +28,7 @@ export const DEFINITION_MISMATCH = -1;
|
|
|
28
28
|
export type DecodeOperation<T extends Schema = any> = (
|
|
29
29
|
decoder: Decoder<T>,
|
|
30
30
|
bytes: Buffer,
|
|
31
|
-
it:
|
|
31
|
+
it: Iterator,
|
|
32
32
|
ref: Ref,
|
|
33
33
|
allChanges: DataChange[],
|
|
34
34
|
) => number | void;
|
|
@@ -40,7 +40,7 @@ export function decodeValue(
|
|
|
40
40
|
index: number,
|
|
41
41
|
type: any,
|
|
42
42
|
bytes: Buffer,
|
|
43
|
-
it:
|
|
43
|
+
it: Iterator,
|
|
44
44
|
allChanges: DataChange[],
|
|
45
45
|
) {
|
|
46
46
|
const $root = decoder.root;
|
|
@@ -105,7 +105,14 @@ export function decodeValue(
|
|
|
105
105
|
value = decoder.createInstanceOfType(childType);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
$root.addRef(
|
|
108
|
+
$root.addRef(
|
|
109
|
+
refId,
|
|
110
|
+
value,
|
|
111
|
+
(
|
|
112
|
+
value !== previousValue || // increment ref count if value has changed
|
|
113
|
+
(operation === OPERATION.DELETE_AND_ADD && value === previousValue) // increment ref count if the same instance is being added again
|
|
114
|
+
)
|
|
115
|
+
);
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
|
|
@@ -169,12 +176,12 @@ export function decodeValue(
|
|
|
169
176
|
export const decodeSchemaOperation: DecodeOperation = function (
|
|
170
177
|
decoder: Decoder<any>,
|
|
171
178
|
bytes: Buffer,
|
|
172
|
-
it:
|
|
179
|
+
it: Iterator,
|
|
173
180
|
ref: Ref,
|
|
174
181
|
allChanges: DataChange[],
|
|
175
182
|
) {
|
|
176
183
|
const first_byte = bytes[it.offset++];
|
|
177
|
-
const metadata: Metadata = ref
|
|
184
|
+
const metadata: Metadata = ref.constructor[Symbol.metadata];
|
|
178
185
|
|
|
179
186
|
// "compressed" index + operation
|
|
180
187
|
const operation = (first_byte >> 6) << 6
|
|
@@ -182,21 +189,24 @@ export const decodeSchemaOperation: DecodeOperation = function (
|
|
|
182
189
|
|
|
183
190
|
// skip early if field is not defined
|
|
184
191
|
const field = metadata[index];
|
|
185
|
-
if (field === undefined) {
|
|
192
|
+
if (field === undefined) {
|
|
193
|
+
console.warn("@colyseus/schema: field not defined at", { index, ref: ref.constructor.name, metadata });
|
|
194
|
+
return DEFINITION_MISMATCH;
|
|
195
|
+
}
|
|
186
196
|
|
|
187
197
|
const { value, previousValue } = decodeValue(
|
|
188
198
|
decoder,
|
|
189
199
|
operation,
|
|
190
200
|
ref,
|
|
191
201
|
index,
|
|
192
|
-
|
|
202
|
+
field.type,
|
|
193
203
|
bytes,
|
|
194
204
|
it,
|
|
195
205
|
allChanges,
|
|
196
206
|
);
|
|
197
207
|
|
|
198
208
|
if (value !== null && value !== undefined) {
|
|
199
|
-
ref[field] = value;
|
|
209
|
+
ref[field.name] = value;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
// add change
|
|
@@ -205,7 +215,7 @@ export const decodeSchemaOperation: DecodeOperation = function (
|
|
|
205
215
|
ref,
|
|
206
216
|
refId: decoder.currentRefId,
|
|
207
217
|
op: operation,
|
|
208
|
-
field: field,
|
|
218
|
+
field: field.name,
|
|
209
219
|
value,
|
|
210
220
|
previousValue,
|
|
211
221
|
});
|
|
@@ -215,7 +225,7 @@ export const decodeSchemaOperation: DecodeOperation = function (
|
|
|
215
225
|
export const decodeKeyValueOperation: DecodeOperation = function (
|
|
216
226
|
decoder: Decoder<any>,
|
|
217
227
|
bytes: Buffer,
|
|
218
|
-
it:
|
|
228
|
+
it: Iterator,
|
|
219
229
|
ref: Ref,
|
|
220
230
|
allChanges: DataChange[]
|
|
221
231
|
) {
|
|
@@ -251,6 +261,7 @@ export const decodeKeyValueOperation: DecodeOperation = function (
|
|
|
251
261
|
dynamicIndex = ref['getIndex'](index);
|
|
252
262
|
}
|
|
253
263
|
|
|
264
|
+
|
|
254
265
|
const { value, previousValue } = decodeValue(
|
|
255
266
|
decoder,
|
|
256
267
|
operation,
|
|
@@ -298,12 +309,13 @@ export const decodeKeyValueOperation: DecodeOperation = function (
|
|
|
298
309
|
export const decodeArray: DecodeOperation = function (
|
|
299
310
|
decoder: Decoder<any>,
|
|
300
311
|
bytes: Buffer,
|
|
301
|
-
it:
|
|
312
|
+
it: Iterator,
|
|
302
313
|
ref: ArraySchema,
|
|
303
314
|
allChanges: DataChange[]
|
|
304
315
|
) {
|
|
305
316
|
// "uncompressed" index + operation (array/map items)
|
|
306
|
-
|
|
317
|
+
let operation = bytes[it.offset++];
|
|
318
|
+
let index: number;
|
|
307
319
|
|
|
308
320
|
if (operation === OPERATION.CLEAR) {
|
|
309
321
|
//
|
|
@@ -315,11 +327,15 @@ export const decodeArray: DecodeOperation = function (
|
|
|
315
327
|
(ref as ArraySchema).clear();
|
|
316
328
|
return;
|
|
317
329
|
|
|
330
|
+
} else if (operation === OPERATION.REVERSE) {
|
|
331
|
+
(ref as ArraySchema).reverse();
|
|
332
|
+
return;
|
|
333
|
+
|
|
318
334
|
} else if (operation === OPERATION.DELETE_BY_REFID) {
|
|
319
335
|
// TODO: refactor here, try to follow same flow as below
|
|
320
336
|
const refId = decode.number(bytes, it);
|
|
321
337
|
const previousValue = decoder.root.refs.get(refId);
|
|
322
|
-
|
|
338
|
+
index = ref.findIndex((value) => value === previousValue);
|
|
323
339
|
ref[$deleteByIndex](index);
|
|
324
340
|
allChanges.push({
|
|
325
341
|
ref,
|
|
@@ -330,10 +346,22 @@ export const decodeArray: DecodeOperation = function (
|
|
|
330
346
|
value: undefined,
|
|
331
347
|
previousValue,
|
|
332
348
|
});
|
|
349
|
+
|
|
333
350
|
return;
|
|
351
|
+
|
|
352
|
+
} else if (operation === OPERATION.ADD_BY_REFID) {
|
|
353
|
+
const refId = decode.number(bytes, it);
|
|
354
|
+
const itemByRefId = decoder.root.refs.get(refId);
|
|
355
|
+
|
|
356
|
+
// use existing index, or push new value
|
|
357
|
+
index = (itemByRefId)
|
|
358
|
+
? ref.findIndex((value) => value === itemByRefId)
|
|
359
|
+
: ref.length;
|
|
360
|
+
|
|
361
|
+
} else {
|
|
362
|
+
index = decode.number(bytes, it);
|
|
334
363
|
}
|
|
335
364
|
|
|
336
|
-
const index = decode.number(bytes, it);
|
|
337
365
|
const type = ref[$childType];
|
|
338
366
|
|
|
339
367
|
let dynamicIndex: number | string = index;
|
package/src/decoder/Decoder.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { TypeContext } from "../
|
|
1
|
+
import { TypeContext } from "../types/TypeContext";
|
|
2
2
|
import { $changes, $childType, $decoder, $onDecodeEnd } from "../types/symbols";
|
|
3
3
|
import { Schema } from "../Schema";
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { decode } from "../encoding/decode";
|
|
6
6
|
import { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';
|
|
7
|
-
import { Ref } from "../encoder/ChangeTree";
|
|
8
|
-
import { Iterator } from "../encoding/decode";
|
|
7
|
+
import type { Ref } from "../encoder/ChangeTree";
|
|
8
|
+
import type { Iterator } from "../encoding/decode";
|
|
9
9
|
import { ReferenceTracker } from "./ReferenceTracker";
|
|
10
|
-
import { DEFINITION_MISMATCH, DataChange, DecodeOperation } from "./DecodeOperation";
|
|
10
|
+
import { DEFINITION_MISMATCH, type DataChange, type DecodeOperation } from "./DecodeOperation";
|
|
11
11
|
import { Collection } from "../types/HelperTypes";
|
|
12
12
|
|
|
13
13
|
export class Decoder<T extends Schema = any> {
|
|
@@ -21,7 +21,8 @@ export class Decoder<T extends Schema = any> {
|
|
|
21
21
|
triggerChanges?: (allChanges: DataChange[]) => void;
|
|
22
22
|
|
|
23
23
|
constructor(root: T, context?: TypeContext) {
|
|
24
|
-
this.
|
|
24
|
+
this.setState(root);
|
|
25
|
+
|
|
25
26
|
this.context = context || new TypeContext(root.constructor as typeof Schema);
|
|
26
27
|
|
|
27
28
|
// console.log(">>>>>>>>>>>>>>>> Decoder types");
|
|
@@ -30,7 +31,7 @@ export class Decoder<T extends Schema = any> {
|
|
|
30
31
|
// });
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
protected
|
|
34
|
+
protected setState(root: T) {
|
|
34
35
|
this.state = root;
|
|
35
36
|
this.root = new ReferenceTracker();
|
|
36
37
|
this.root.addRef(0, root);
|
|
@@ -66,7 +67,8 @@ export class Decoder<T extends Schema = any> {
|
|
|
66
67
|
if (!nextRef) { throw new Error(`"refId" not found: ${this.currentRefId}`); }
|
|
67
68
|
ref[$onDecodeEnd]?.()
|
|
68
69
|
ref = nextRef;
|
|
69
|
-
|
|
70
|
+
|
|
71
|
+
decoder = ref.constructor[$decoder];
|
|
70
72
|
|
|
71
73
|
continue;
|
|
72
74
|
}
|
|
@@ -80,9 +82,9 @@ export class Decoder<T extends Schema = any> {
|
|
|
80
82
|
// keep skipping next bytes until reaches a known structure
|
|
81
83
|
// by local decoder.
|
|
82
84
|
//
|
|
83
|
-
const nextIterator:
|
|
85
|
+
const nextIterator: Iterator = { offset: it.offset };
|
|
84
86
|
while (it.offset < totalBytes) {
|
|
85
|
-
if (
|
|
87
|
+
if (bytes[it.offset] === SWITCH_TO_STRUCTURE) {
|
|
86
88
|
nextIterator.offset = it.offset + 1;
|
|
87
89
|
if ($root.refs.has(decode.number(bytes, nextIterator))) {
|
|
88
90
|
break;
|
|
@@ -81,6 +81,7 @@ export class ReferenceTracker {
|
|
|
81
81
|
clearRefs() {
|
|
82
82
|
this.refs.clear();
|
|
83
83
|
this.deletedRefs.clear();
|
|
84
|
+
this.callbacks = {};
|
|
84
85
|
this.refCounts = {};
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -98,8 +99,9 @@ export class ReferenceTracker {
|
|
|
98
99
|
// Ensure child schema instances have their references removed as well.
|
|
99
100
|
//
|
|
100
101
|
if (Metadata.isValidInstance(ref)) {
|
|
101
|
-
const metadata: Metadata = ref
|
|
102
|
-
for (const
|
|
102
|
+
const metadata: Metadata = ref.constructor[Symbol.metadata];
|
|
103
|
+
for (const index in metadata) {
|
|
104
|
+
const field = metadata[index as any as number].name;
|
|
103
105
|
const childRefId = typeof(ref[field]) === "object" && this.refIds.get(ref[field]);
|
|
104
106
|
if (childRefId) {
|
|
105
107
|
this.removeRef(childRefId);
|
|
@@ -148,4 +150,4 @@ export class ReferenceTracker {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
}
|
|
153
|
+
}
|