@colyseus/schema 2.0.4 → 2.0.6

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.
Files changed (107) hide show
  1. package/README.md +0 -4
  2. package/build/cjs/index.js +48 -48
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/index.mjs +130 -104
  5. package/build/esm/index.mjs.map +1 -1
  6. package/build/umd/index.js +50 -50
  7. package/lib/Reflection.js +87 -119
  8. package/lib/Reflection.js.map +1 -1
  9. package/lib/Schema.js +195 -257
  10. package/lib/Schema.js.map +1 -1
  11. package/lib/annotations.d.ts +6 -6
  12. package/lib/annotations.js +64 -92
  13. package/lib/annotations.js.map +1 -1
  14. package/lib/changes/ChangeTree.d.ts +1 -1
  15. package/lib/changes/ChangeTree.js +63 -70
  16. package/lib/changes/ChangeTree.js.map +1 -1
  17. package/lib/changes/ReferenceTracker.js +24 -27
  18. package/lib/changes/ReferenceTracker.js.map +1 -1
  19. package/lib/codegen/api.js +9 -9
  20. package/lib/codegen/api.js.map +1 -1
  21. package/lib/codegen/argv.d.ts +1 -1
  22. package/lib/codegen/argv.js +11 -11
  23. package/lib/codegen/argv.js.map +1 -1
  24. package/lib/codegen/cli.js +21 -10
  25. package/lib/codegen/cli.js.map +1 -1
  26. package/lib/codegen/languages/cpp.js +126 -77
  27. package/lib/codegen/languages/cpp.js.map +1 -1
  28. package/lib/codegen/languages/csharp.js +121 -62
  29. package/lib/codegen/languages/csharp.js.map +1 -1
  30. package/lib/codegen/languages/haxe.js +34 -26
  31. package/lib/codegen/languages/haxe.js.map +1 -1
  32. package/lib/codegen/languages/java.js +39 -27
  33. package/lib/codegen/languages/java.js.map +1 -1
  34. package/lib/codegen/languages/js.js +48 -32
  35. package/lib/codegen/languages/js.js.map +1 -1
  36. package/lib/codegen/languages/lua.js +35 -24
  37. package/lib/codegen/languages/lua.js.map +1 -1
  38. package/lib/codegen/languages/ts.js +63 -68
  39. package/lib/codegen/languages/ts.js.map +1 -1
  40. package/lib/codegen/parser.d.ts +9 -1
  41. package/lib/codegen/parser.js +88 -46
  42. package/lib/codegen/parser.js.map +1 -1
  43. package/lib/codegen/types.d.ts +8 -0
  44. package/lib/codegen/types.js +64 -54
  45. package/lib/codegen/types.js.map +1 -1
  46. package/lib/encoding/decode.js +15 -15
  47. package/lib/encoding/decode.js.map +1 -1
  48. package/lib/encoding/encode.js +14 -14
  49. package/lib/encoding/encode.js.map +1 -1
  50. package/lib/events/EventEmitter.d.ts +1 -1
  51. package/lib/events/EventEmitter.js +16 -47
  52. package/lib/events/EventEmitter.js.map +1 -1
  53. package/lib/filters/index.js +7 -8
  54. package/lib/filters/index.js.map +1 -1
  55. package/lib/index.js +11 -11
  56. package/lib/index.js.map +1 -1
  57. package/lib/types/ArraySchema.d.ts +1 -1
  58. package/lib/types/ArraySchema.js +161 -219
  59. package/lib/types/ArraySchema.js.map +1 -1
  60. package/lib/types/CollectionSchema.d.ts +1 -1
  61. package/lib/types/CollectionSchema.js +63 -71
  62. package/lib/types/CollectionSchema.js.map +1 -1
  63. package/lib/types/HelperTypes.d.ts +9 -9
  64. package/lib/types/MapSchema.d.ts +16 -16
  65. package/lib/types/MapSchema.js +68 -78
  66. package/lib/types/MapSchema.js.map +1 -1
  67. package/lib/types/SetSchema.js +62 -71
  68. package/lib/types/SetSchema.js.map +1 -1
  69. package/lib/types/index.js +1 -1
  70. package/lib/types/index.js.map +1 -1
  71. package/lib/types/typeRegistry.js +1 -1
  72. package/lib/types/typeRegistry.js.map +1 -1
  73. package/lib/types/utils.js +9 -10
  74. package/lib/types/utils.js.map +1 -1
  75. package/lib/utils.js +10 -13
  76. package/lib/utils.js.map +1 -1
  77. package/package.json +18 -15
  78. package/src/Reflection.ts +159 -0
  79. package/src/Schema.ts +1024 -0
  80. package/src/annotations.ts +400 -0
  81. package/src/changes/ChangeTree.ts +295 -0
  82. package/src/changes/ReferenceTracker.ts +81 -0
  83. package/src/codegen/api.ts +46 -0
  84. package/src/codegen/argv.ts +40 -0
  85. package/src/codegen/cli.ts +65 -0
  86. package/src/codegen/languages/cpp.ts +297 -0
  87. package/src/codegen/languages/csharp.ts +208 -0
  88. package/src/codegen/languages/haxe.ts +110 -0
  89. package/src/codegen/languages/java.ts +115 -0
  90. package/src/codegen/languages/js.ts +115 -0
  91. package/src/codegen/languages/lua.ts +125 -0
  92. package/src/codegen/languages/ts.ts +129 -0
  93. package/src/codegen/parser.ts +299 -0
  94. package/src/codegen/types.ts +177 -0
  95. package/src/encoding/decode.ts +278 -0
  96. package/src/encoding/encode.ts +283 -0
  97. package/src/filters/index.ts +23 -0
  98. package/src/index.ts +59 -0
  99. package/src/spec.ts +49 -0
  100. package/src/types/ArraySchema.ts +612 -0
  101. package/src/types/CollectionSchema.ts +199 -0
  102. package/src/types/HelperTypes.ts +34 -0
  103. package/src/types/MapSchema.ts +268 -0
  104. package/src/types/SetSchema.ts +208 -0
  105. package/src/types/typeRegistry.ts +19 -0
  106. package/src/types/utils.ts +62 -0
  107. package/src/utils.ts +28 -0
@@ -0,0 +1,208 @@
1
+ import {
2
+ Class,
3
+ Property,
4
+ File,
5
+ getCommentHeader,
6
+ Interface,
7
+ Enum,
8
+ } from "../types";
9
+ import { GenerateOptions } from "../api";
10
+ import { Context } from "../types";
11
+
12
+ const typeMaps = {
13
+ "string": "string",
14
+ "number": "float",
15
+ "boolean": "bool",
16
+ "int8": "sbyte",
17
+ "uint8": "byte",
18
+ "int16": "short",
19
+ "uint16": "ushort",
20
+ "int32": "int",
21
+ "uint32": "uint",
22
+ "int64": "long",
23
+ "uint64": "ulong",
24
+ "float32": "float",
25
+ "float64": "double",
26
+ }
27
+
28
+ /**
29
+ * C# Code Generator
30
+ */
31
+ const capitalize = (s) => {
32
+ if (typeof s !== 'string') return ''
33
+ return s.charAt(0).toUpperCase() + s.slice(1);
34
+ }
35
+
36
+ export function generate(context: Context, options: GenerateOptions): File[] {
37
+ // enrich typeMaps with enums
38
+ context.enums.forEach((structure) => {
39
+ typeMaps[structure.name] = structure.name;
40
+ });
41
+ return [
42
+ ...context.classes.map(structure => ({
43
+ name: `${structure.name}.cs`,
44
+ content: generateClass(structure, options.namespace)
45
+ })),
46
+ ...context.interfaces.map(structure => ({
47
+ name: `${structure.name}.cs`,
48
+ content: generateInterface(structure, options.namespace),
49
+ })),
50
+ ...context.enums.filter(structure => structure.name !== 'OPERATION').map((structure) => ({
51
+ name: `${structure.name}.cs`,
52
+ content: generateEnum(structure, options.namespace),
53
+ })),
54
+ ];
55
+ }
56
+
57
+ function generateClass(klass: Class, namespace: string) {
58
+ const indent = (namespace) ? "\t" : "";
59
+ return `${getCommentHeader()}
60
+
61
+ using Colyseus.Schema;
62
+ using Action = System.Action;
63
+ ${namespace ? `\nnamespace ${namespace} {` : ""}
64
+ ${indent}public partial class ${klass.name} : ${klass.extends} {
65
+ ${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
+ ${indent}}
73
+ ${namespace ? "}" : ""}
74
+ `;
75
+ }
76
+
77
+ function generateEnum(_enum: Enum, namespace: string) {
78
+ const indent = namespace ? "\t" : "";
79
+ return `${getCommentHeader()}
80
+ ${namespace ? `\nnamespace ${namespace} {` : ""}
81
+ ${indent}public struct ${_enum.name} {
82
+
83
+ ${_enum.properties
84
+ .map((prop) => {
85
+ let dataType: string = "int";
86
+ let value: any;
87
+
88
+ if(prop.type) {
89
+ if(isNaN(Number(prop.type))) {
90
+ value = prop.type;
91
+ dataType = "string";
92
+ } else {
93
+ value = Number(prop.type);
94
+ dataType = Number.isInteger(value)? 'int': 'float';
95
+ }
96
+ } else {
97
+ value = _enum.properties.indexOf(prop);
98
+ }
99
+ return `${indent}\tpublic const ${dataType} ${prop.name} = ${value};`;
100
+ })
101
+ .join("\n")}
102
+ ${indent}}`
103
+ }
104
+
105
+ function generateProperty(prop: Property, indent: string = "") {
106
+ let typeArgs = `"${prop.type}"`;
107
+ let property = "public";
108
+ let langType: string;
109
+ let initializer = "";
110
+
111
+ if (prop.childType) {
112
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
113
+
114
+ langType = getType(prop);
115
+ typeArgs += `, typeof(${langType})`;
116
+
117
+ if (!isUpcaseFirst) {
118
+ typeArgs += `, "${prop.childType}"`;
119
+ }
120
+
121
+ initializer = `new ${langType}()`;
122
+
123
+ } else {
124
+ langType = getType(prop);
125
+ initializer = `default(${langType})`;
126
+ }
127
+
128
+ property += ` ${langType} ${prop.name}`;
129
+
130
+ let ret = (prop.deprecated) ? `\t\t[System.Obsolete("field '${prop.name}' is deprecated.", true)]\n` : '';
131
+
132
+ return ret + `\t${indent}[Type(${prop.index}, ${typeArgs})]
133
+ \t${indent}${property} = ${initializer};`;
134
+ }
135
+
136
+ function generateInterface(struct: Interface, namespace: string) {
137
+ const indent = (namespace) ? "\t" : "";
138
+ return `${getCommentHeader()}
139
+
140
+ using Colyseus.Schema;
141
+ ${namespace ? `\nnamespace ${namespace} {` : ""}
142
+ ${indent}public class ${struct.name} {
143
+ ${struct.properties.map(prop => `\t${indent}public ${getType(prop)} ${prop.name};`).join("\n")}
144
+ ${indent}}
145
+ ${namespace ? "}" : ""}
146
+ `;
147
+ }
148
+
149
+ function generateAllFieldCallbacks(klass: Class, indent: string) {
150
+ //
151
+ // TODO: improve me. It would be great to generate less boilerplate in favor
152
+ // of a single implementation on C# Schema class itself.
153
+ //
154
+ const eventNames: string[] = [];
155
+ return `${klass.properties
156
+ .filter(prop => !prop.deprecated) // generate only for properties that haven't been deprecated.
157
+ .map(prop => {
158
+ const eventName = `_${prop.name}Change`;
159
+ eventNames.push(eventName);
160
+ return `\t${indent}protected event PropertyChangeHandler<${getType(prop)}> ${eventName};
161
+ \t${indent}public Action On${capitalize(prop.name)}Change(PropertyChangeHandler<${getType(prop)}> handler) {
162
+ \t${indent}\tif (__callbacks == null) { __callbacks = new SchemaCallbacks(); }
163
+ \t${indent}\t__callbacks.AddPropertyCallback(nameof(${prop.name}));
164
+ \t${indent}\t${eventName} += handler;
165
+ \t${indent}\treturn () => {
166
+ \t${indent}\t\t__callbacks.RemovePropertyCallback(nameof(${prop.name}));
167
+ \t${indent}\t\t${eventName} -= handler;
168
+ \t${indent}\t};
169
+ \t${indent}}`;
170
+ }).join("\n\n")}
171
+
172
+ \t${indent}protected override void TriggerFieldChange(DataChange change) {
173
+ \t${indent}\tswitch (change.Field) {
174
+ ${klass.properties.filter(prop => !prop.deprecated).map((prop, i) => {
175
+ return `\t${indent}\t\tcase nameof(${prop.name}): ${eventNames[i]}?.Invoke((${getType(prop)}) change.Value, (${getType(prop)}) change.PreviousValue); break;`;
176
+ }).join("\n")}
177
+ \t${indent}\t\tdefault: break;
178
+ \t\t${indent}}
179
+ \t${indent}}`;
180
+ }
181
+
182
+ function getChildType(prop: Property) {
183
+ return typeMaps[prop.childType];
184
+ }
185
+
186
+ function getType(prop: Property) {
187
+ if (prop.childType) {
188
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
189
+ let type: string;
190
+
191
+ if(prop.type === "ref") {
192
+ type = (isUpcaseFirst)
193
+ ? prop.childType
194
+ : getChildType(prop);
195
+ } else {
196
+ const containerClass = capitalize(prop.type);
197
+ type = (isUpcaseFirst)
198
+ ? `${containerClass}Schema<${prop.childType}>`
199
+ : `${containerClass}Schema<${getChildType(prop)}>`;
200
+ }
201
+ return type;
202
+
203
+ } else {
204
+ return (prop.type === "array")
205
+ ? `${typeMaps[prop.childType] || prop.childType}[]`
206
+ : typeMaps[prop.type];
207
+ }
208
+ }
@@ -0,0 +1,110 @@
1
+ import { Class, Property, File, getCommentHeader, Context } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ const typeMaps = {
5
+ "string": "String",
6
+ "number": "Dynamic",
7
+ "boolean": "Bool",
8
+ "int8": "Int",
9
+ "uint8": "UInt",
10
+ "int16": "Int",
11
+ "uint16": "UInt",
12
+ "int32": "Int",
13
+ "uint32": "UInt",
14
+ "int64": "Int",
15
+ "uint64": "UInt",
16
+ "float32": "Float",
17
+ "float64": "Float",
18
+ }
19
+
20
+ const typeInitializer = {
21
+ "string": '""',
22
+ "number": "0",
23
+ "boolean": "false",
24
+ "int8": "0",
25
+ "uint8": "0",
26
+ "int16": "0",
27
+ "uint16": "0",
28
+ "int32": "0",
29
+ "uint32": "0",
30
+ "int64": "0",
31
+ "uint64": "0",
32
+ "float32": "0",
33
+ "float64": "0",
34
+ }
35
+
36
+ export function generate (context: Context, options: GenerateOptions): File[] {
37
+ return context.classes.map(klass => ({
38
+ name: klass.name + ".hx",
39
+ content: generateClass(klass, options.namespace, context.classes)
40
+ }));
41
+ }
42
+
43
+ function getInheritanceTree(klass: Class, allClasses: Class[], includeSelf: boolean = true) {
44
+ let currentClass = klass;
45
+ let inheritanceTree: Class[] = [];
46
+
47
+ if (includeSelf) {
48
+ inheritanceTree.push(currentClass);
49
+ }
50
+
51
+ while (currentClass.extends !== "Schema") {
52
+ currentClass = allClasses.find(klass => klass.name == currentClass.extends);
53
+ inheritanceTree.push(currentClass);
54
+ }
55
+
56
+ return inheritanceTree;
57
+ }
58
+
59
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
60
+ return `${getCommentHeader()}
61
+
62
+ ${namespace ? `package ${namespace};` : ""}
63
+ import io.colyseus.serializer.schema.Schema;
64
+ import io.colyseus.serializer.schema.types.*;
65
+
66
+ class ${klass.name} extends ${klass.extends} {
67
+ ${klass.properties.map(prop => generateProperty(prop)).join("\n")}
68
+ }
69
+ `;
70
+ }
71
+
72
+ function generateProperty(prop: Property) {
73
+ let langType: string;
74
+ let initializer = "";
75
+ let typeArgs = `"${prop.type}"`;
76
+
77
+ if (prop.childType) {
78
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
79
+
80
+ if (isUpcaseFirst) {
81
+ typeArgs += `, ${prop.childType}`;
82
+
83
+ } else {
84
+ typeArgs += `, "${prop.childType}"`;
85
+ }
86
+
87
+ if(prop.type === "ref") {
88
+ langType = `${prop.childType}`;
89
+ initializer = `new ${prop.childType}()`;
90
+
91
+ } else if(prop.type === "array") {
92
+ langType = (isUpcaseFirst)
93
+ ? `ArraySchema<${prop.childType}>`
94
+ : `ArraySchema<${typeMaps[prop.childType]}>`;
95
+ initializer = `new ${langType}()`;
96
+
97
+ } else if(prop.type === "map") {
98
+ langType = (isUpcaseFirst)
99
+ ? `MapSchema<${prop.childType}>`
100
+ : `MapSchema<${typeMaps[prop.childType]}>`;
101
+ initializer = `new ${langType}()`;
102
+ }
103
+
104
+ } else {
105
+ langType = typeMaps[prop.type];
106
+ initializer = typeInitializer[prop.type];
107
+ }
108
+
109
+ return `\t@:type(${typeArgs})\n\tpublic var ${prop.name}: ${langType} = ${initializer};\n`
110
+ }
@@ -0,0 +1,115 @@
1
+ import { Class, Property, File, getCommentHeader, Context } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ const typeMaps = {
5
+ "string": "String",
6
+ "number": "float",
7
+ "boolean": "boolean",
8
+ "int8": "byte",
9
+ "uint8": "short",
10
+ "int16": "short",
11
+ "uint16": "int",
12
+ "int32": "int",
13
+ "uint32": "long",
14
+ "int64": "long",
15
+ "uint64": "long",
16
+ "float32": "float",
17
+ "float64": "double",
18
+ }
19
+
20
+ const typeInitializer = {
21
+ "string": '""',
22
+ "number": "0",
23
+ "boolean": "false",
24
+ "int8": "0",
25
+ "uint8": "0",
26
+ "int16": "0",
27
+ "uint16": "0",
28
+ "int32": "0",
29
+ "uint32": "0",
30
+ "int64": "0",
31
+ "uint64": "0",
32
+ "float32": "0",
33
+ "float64": "0",
34
+ }
35
+
36
+ /**
37
+ * C# Code Generator
38
+ */
39
+
40
+ export function generate (context: Context, options: GenerateOptions): File[] {
41
+ return context.classes.map(klass => ({
42
+ name: klass.name + ".java",
43
+ content: generateClass(klass, options.namespace)
44
+ }));
45
+ }
46
+
47
+ function generateClass(klass: Class, namespace: string) {
48
+ const indent = (namespace) ? "\t" : "";
49
+ return `${getCommentHeader()}
50
+ ${namespace ? `\npackage ${namespace};` : ""}
51
+
52
+ import io.colyseus.serializer.schema.Schema;
53
+ import io.colyseus.serializer.schema.annotations.SchemaClass;
54
+ import io.colyseus.serializer.schema.annotations.SchemaField;
55
+
56
+ @SchemaClass
57
+ ${indent}public class ${klass.name} extends ${klass.extends} {
58
+ ${klass.properties.map(prop => generateProperty(prop, indent)).join("\n\n")}
59
+ ${indent}}
60
+ ${namespace ? "}" : ""}
61
+ `;
62
+ }
63
+
64
+ function generateProperty(prop: Property, indent: string = "") {
65
+ let typeArgs = `${prop.index}/${prop.type}`;
66
+ let property = "public";
67
+ let langType: string;
68
+ let ctorArgs: string = "";
69
+ let initializer = "";
70
+
71
+ if (prop.childType) {
72
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
73
+
74
+ if (prop.type !== "ref" && isUpcaseFirst) {
75
+ ctorArgs = `${prop.childType}.class`;
76
+ }
77
+
78
+ if(prop.type === "ref") {
79
+ langType = (isUpcaseFirst)
80
+ ? prop.childType
81
+ : typeMaps[prop.childType];
82
+
83
+ initializer = `new ${langType}${(prop.type !== "ref" && isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
84
+
85
+ } else if(prop.type === "array") {
86
+ langType = (isUpcaseFirst)
87
+ ? `ArraySchema<${prop.childType}>`
88
+ : `ArraySchema`;
89
+
90
+ initializer = `new ArraySchema${(isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
91
+
92
+ } else if(prop.type === "map") {
93
+ langType = (isUpcaseFirst)
94
+ ? `MapSchema<${prop.childType}>`
95
+ : `MapSchema`;
96
+
97
+ initializer = `new MapSchema${(isUpcaseFirst) ? "<>" : ""}(${ctorArgs})`;
98
+ }
99
+
100
+ if (prop.type !== "ref") {
101
+ typeArgs += (isUpcaseFirst)
102
+ ? `/ref`
103
+ : `/${prop.childType}`;
104
+ }
105
+
106
+ } else {
107
+ langType = typeMaps[prop.type];
108
+ initializer = typeInitializer[prop.type];
109
+ }
110
+
111
+ property += ` ${langType} ${prop.name}`;
112
+
113
+ return `\t@SchemaField("${typeArgs}")\t${indent}
114
+ \t${indent}${property} = ${initializer};`
115
+ }
@@ -0,0 +1,115 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ const typeMaps = {
5
+ "string": "string",
6
+ "number": "number",
7
+ "boolean": "boolean",
8
+ "int8": "number",
9
+ "uint8": "number",
10
+ "int16": "number",
11
+ "uint16": "number",
12
+ "int32": "number",
13
+ "uint32": "number",
14
+ "int64": "number",
15
+ "uint64": "number",
16
+ "float32": "number",
17
+ "float64": "number",
18
+ }
19
+
20
+ const distinct = (value, index, self) => self.indexOf(value) === index;
21
+
22
+ export function generate (context: Context, options: GenerateOptions): File[] {
23
+ return context.classes.map(klass => ({
24
+ name: klass.name + ".js",
25
+ content: generateClass(klass, options.namespace, context.classes)
26
+ }));
27
+ }
28
+
29
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
30
+ const allRefs: Property[] = [];
31
+ klass.properties.forEach(property => {
32
+ let type = property.type;
33
+
34
+ // keep all refs list
35
+ if ((type === "ref" || type === "array" || type === "map")) {
36
+ allRefs.push(property);
37
+ }
38
+ });
39
+
40
+ return `${getCommentHeader()}
41
+
42
+ const schema = require("@colyseus/schema");
43
+ const Schema = schema.Schema;
44
+ const type = schema.type;
45
+ ${allRefs.
46
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
47
+ map(ref => ref.childType).
48
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
49
+ filter(distinct).
50
+ map(childType => `const ${childType} = require("./${childType}");`).
51
+ join("\n")}
52
+
53
+ class ${klass.name} extends ${klass.extends} {
54
+ constructor () {
55
+ super();
56
+ ${klass.properties.
57
+ filter(prop => prop.childType !== undefined).
58
+ map(prop => " " + generatePropertyInitializer(prop)).join("\n")}
59
+ }
60
+ }
61
+ ${klass.properties.map(prop => generatePropertyDeclaration(klass.name, prop)).join("\n")}
62
+
63
+ export default ${klass.name};
64
+ `;
65
+ }
66
+
67
+ function generatePropertyDeclaration(className: string, prop: Property) {
68
+ let typeArgs: string;
69
+
70
+ if (prop.childType) {
71
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
72
+
73
+ if (isUpcaseFirst) {
74
+ typeArgs += `, ${prop.childType}`;
75
+
76
+ } else {
77
+ typeArgs += `, "${prop.childType}"`;
78
+ }
79
+
80
+ if(prop.type === "ref") {
81
+ typeArgs = `${prop.childType}`;
82
+
83
+ } else if(prop.type === "array") {
84
+ typeArgs = (isUpcaseFirst)
85
+ ? `[ ${prop.childType} ]`
86
+ : `[ "${prop.childType}" ]`;
87
+
88
+ } else if(prop.type === "map") {
89
+ typeArgs = (isUpcaseFirst)
90
+ ? `{ map: ${prop.childType} }`
91
+ : `{ map: "${prop.childType}" }`;
92
+ }
93
+
94
+ } else {
95
+ typeArgs = `"${prop.type}"`;
96
+ }
97
+
98
+ return `type(${typeArgs})(${className}.prototype, "${prop.name}");`;
99
+ }
100
+
101
+ function generatePropertyInitializer(prop: Property) {
102
+ let initializer = "";
103
+
104
+ if(prop.type === "ref") {
105
+ initializer = `new ${prop.childType}()`;
106
+
107
+ } else if(prop.type === "array") {
108
+ initializer = `new schema.ArraySchema()`;
109
+
110
+ } else if(prop.type === "map") {
111
+ initializer = `new schema.MapSchema()`;
112
+ }
113
+
114
+ return `this.${prop.name} = ${initializer}`;
115
+ }
@@ -0,0 +1,125 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } from "../types";
2
+ import { GenerateOptions } from "../api";
3
+
4
+ /**
5
+ TODO:
6
+ - Support inheritance
7
+ - Support importing Schema dependencies
8
+ */
9
+
10
+ const typeMaps = {
11
+ "string": "string",
12
+ "number": "number",
13
+ "boolean": "boolean",
14
+ "int8": "number",
15
+ "uint8": "number",
16
+ "int16": "number",
17
+ "uint16": "number",
18
+ "int32": "number",
19
+ "uint32": "number",
20
+ "int64": "number",
21
+ "uint64": "number",
22
+ "float32": "number",
23
+ "float64": "number",
24
+ }
25
+
26
+ const distinct = (value, index, self) => self.indexOf(value) === index;
27
+
28
+ export function generate (context: Context, options: GenerateOptions): File[] {
29
+ return context.classes.map(klass => ({
30
+ name: klass.name + ".lua",
31
+ content: generateClass(klass, options.namespace, context.classes)
32
+ }));
33
+ }
34
+
35
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
36
+ const allRefs: Property[] = [];
37
+ klass.properties.forEach(property => {
38
+ let type = property.type;
39
+
40
+ // keep all refs list
41
+ if ((type === "ref" || type === "array" || type === "map")) {
42
+ allRefs.push(property);
43
+ }
44
+ });
45
+
46
+ // TOOD: inheritance
47
+
48
+ return `${getCommentHeader().replace(/\/\//mg, "--")}
49
+
50
+ local schema = require 'colyseus.serialization.schema.schema'
51
+ ${allRefs.
52
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
53
+ map(ref => ref.childType).
54
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
55
+ filter(distinct).
56
+ map(childType => `local ${childType} = require '${(namespace ? `${namespace}.` : '')}${childType}'`).
57
+ join("\n")}
58
+
59
+ local ${klass.name} = schema.define({
60
+ ${klass.properties.map(prop => generatePropertyDeclaration(prop)).join(",\n")},
61
+ ["_fields_by_index"] = { ${klass.properties.map(prop => `"${prop.name}"`).join(", ")} },
62
+ })
63
+
64
+ return ${klass.name}
65
+ `;
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
+ }
79
+
80
+ function generatePropertyDeclaration(prop: Property) {
81
+ let typeArgs: string;
82
+
83
+ if (prop.childType) {
84
+ const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
85
+
86
+ if (isUpcaseFirst) {
87
+ typeArgs += `${prop.childType}`;
88
+
89
+ } else {
90
+ typeArgs += `"${prop.childType}"`;
91
+ }
92
+
93
+ if(prop.type === "ref") {
94
+ typeArgs = (isUpcaseFirst)
95
+ ? `${prop.childType}`
96
+ : `"${prop.childType}"`;
97
+
98
+ } else {
99
+ typeArgs = (isUpcaseFirst)
100
+ ? `{ ${prop.type} = ${prop.childType} }`
101
+ : `{ ${prop.type} = "${prop.childType}" }`;
102
+ }
103
+
104
+ } else {
105
+ typeArgs = `"${prop.type}"`;
106
+ }
107
+
108
+ return ` ["${prop.name}"] = ${typeArgs}`;
109
+ }
110
+
111
+ // function generatePropertyInitializer(prop: Property) {
112
+ // let initializer = "";
113
+
114
+ // if(prop.type === "ref") {
115
+ // initializer = `new ${prop.childType}()`;
116
+
117
+ // } else if(prop.type === "array") {
118
+ // initializer = `new schema.ArraySchema()`;
119
+
120
+ // } else if(prop.type === "map") {
121
+ // initializer = `new schema.MapSchema()`;
122
+ // }
123
+
124
+ // return `this.${prop.name} = ${initializer}`;
125
+ // }