@colyseus/schema 4.0.13 → 4.0.14

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.
@@ -0,0 +1,11 @@
1
+ import { File, Context } from "../types.js";
2
+ import { GenerateOptions } from "../api.js";
3
+ export declare const name = "C";
4
+ /**
5
+ * Generate individual files for each class
6
+ */
7
+ export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled header file containing all classes
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,3 +1,11 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "C++";
4
+ /**
5
+ * Generate individual files for each class
6
+ */
3
7
  export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled header file containing all classes
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,4 +1,12 @@
1
1
  import { File } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
3
  import { Context } from "../types.js";
4
+ export declare const name = "Unity/C#";
5
+ /**
6
+ * Generate individual files for each class/interface/enum
7
+ */
4
8
  export declare function generate(context: Context, options: GenerateOptions): File[];
9
+ /**
10
+ * Generate a single bundled file containing all classes, interfaces, and enums
11
+ */
12
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -0,0 +1,14 @@
1
+ import { File, Context } from "../types.js";
2
+ import { GenerateOptions } from "../api.js";
3
+ export declare const name = "GDScript";
4
+ /**
5
+ * GDScript Code Generator
6
+ */
7
+ /**
8
+ * Generate individual files for each class
9
+ */
10
+ export declare function generate(context: Context, options: GenerateOptions): File[];
11
+ /**
12
+ * Generate a single bundled file containing all classes and enums
13
+ */
14
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,3 +1,11 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "Haxe";
4
+ /**
5
+ * Generate individual files for each class
6
+ */
3
7
  export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled file containing all classes
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,6 +1,16 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "Java";
3
4
  /**
4
- * C# Code Generator
5
+ * Java Code Generator
6
+ */
7
+ /**
8
+ * Generate individual files for each class
5
9
  */
6
10
  export declare function generate(context: Context, options: GenerateOptions): File[];
11
+ /**
12
+ * Generate a single bundled file containing all classes
13
+ * Note: Java typically requires one public class per file, so bundled mode
14
+ * generates all classes in a single file with package-private visibility
15
+ */
16
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,3 +1,11 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "JavaScript";
4
+ /**
5
+ * Generate individual files for each class
6
+ */
3
7
  export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled file containing all classes
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,3 +1,11 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "LUA";
4
+ /**
5
+ * Generate individual files for each class
6
+ */
3
7
  export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled file containing all classes
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -1,3 +1,11 @@
1
1
  import { File, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
+ export declare const name = "TypeScript";
4
+ /**
5
+ * Generate individual files for each class/interface
6
+ */
3
7
  export declare function generate(context: Context, options: GenerateOptions): File[];
8
+ /**
9
+ * Generate a single bundled file containing all classes and interfaces
10
+ */
11
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -49,4 +49,18 @@ export interface File {
49
49
  name: string;
50
50
  content: string;
51
51
  }
52
+ /**
53
+ * Structured file representation for code generation.
54
+ * Separates imports, local references, and body content to enable
55
+ * clean bundling without string parsing.
56
+ */
57
+ export interface GeneratedFile {
58
+ name: string;
59
+ /** External imports (e.g., "@colyseus/schema", "Colyseus.Schema") */
60
+ imports: string[];
61
+ /** References to other generated classes (used for imports in non-bundle mode) */
62
+ localRefs: string[];
63
+ /** The class/interface/enum definition body (without imports or namespace wrapper) */
64
+ body: string;
65
+ }
52
66
  export declare function getInheritanceTree(klass: Class, allClasses: Class[], includeSelf?: boolean): Class[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/schema",
3
- "version": "4.0.13",
3
+ "version": "4.0.14",
4
4
  "description": "Binary state serializer with delta encoding for games",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,21 +5,24 @@ import { File } from "./types.js";
5
5
  import { parseFiles } from "./parser.js";
6
6
 
7
7
  // Statically import all language generators (for bundling)
8
- import { generate as csharp } from "./languages/csharp.js";
9
- import { generate as cpp } from "./languages/cpp.js";
10
- import { generate as haxe } from "./languages/haxe.js";
11
- import { generate as ts } from "./languages/ts.js";
12
- import { generate as js } from "./languages/js.js";
13
- import { generate as java } from "./languages/java.js";
14
- import { generate as lua } from "./languages/lua.js";
8
+ import * as csharp from "./languages/csharp.js";
9
+ import * as cpp from "./languages/cpp.js";
10
+ import * as haxe from "./languages/haxe.js";
11
+ import * as ts from "./languages/ts.js";
12
+ import * as js from "./languages/js.js";
13
+ import * as java from "./languages/java.js";
14
+ import * as lua from "./languages/lua.js";
15
+ import * as c from "./languages/c.js";
16
+ import * as gdscript from "./languages/gdscript.js";
15
17
 
16
- const generators: Record<string, Function> = { csharp, cpp, haxe, ts, js, java, lua, };
18
+ export const generators: Record<string, any> = { csharp, cpp, haxe, ts, js, java, lua, c, gdscript, };
17
19
 
18
20
  export interface GenerateOptions {
19
21
  files: string[],
20
22
  output: string;
21
23
  decorator?: string;
22
24
  namespace?: string;
25
+ bundle?: boolean;
23
26
  }
24
27
 
25
28
  export function generate(targetId: string, options: GenerateOptions) {
@@ -54,13 +57,21 @@ export function generate(targetId: string, options: GenerateOptions) {
54
57
  // Post-process classes before generating
55
58
  structures.classes.forEach(klass => klass.postProcessing());
56
59
 
57
- const files = generator(structures, options);
58
-
59
- files.forEach((file: File) => {
60
- const outputPath = path.resolve(options.output, file.name);
61
- fs.writeFileSync(outputPath, file.content);
62
- console.log("generated:", file.name);
63
- });
60
+ if (options.bundle && generator.renderBundle) {
61
+ // Bundle mode: generate all classes/interfaces/enums into a single file
62
+ const bundled = generator.renderBundle(structures, options);
63
+ const outputPath = path.resolve(options.output, bundled.name);
64
+ fs.writeFileSync(outputPath, bundled.content);
65
+ console.log("generated (bundled):", bundled.name);
66
+ } else {
67
+ // Standard mode: write individual files
68
+ const generatedFiles = generator.generate(structures, options);
69
+ generatedFiles.forEach((file: File) => {
70
+ const outputPath = path.resolve(options.output, file.name);
71
+ fs.writeFileSync(outputPath, file.content);
72
+ console.log("generated:", file.name);
73
+ });
74
+ }
64
75
  }
65
76
 
66
77
  function recursiveFiles(dir: string): string[] {
@@ -1,15 +1,5 @@
1
1
  import argv from "./argv.js";
2
- import { generate } from "./api.js";
3
-
4
- const supportedTargets: Record<string, string> = {
5
- csharp: 'generate for C#/Unity',
6
- cpp: 'generate for C++',
7
- haxe: 'generate for Haxe',
8
- ts: 'generate for TypeScript',
9
- js: 'generate for JavaScript',
10
- java: 'generate for Java',
11
- lua: 'generate for LUA',
12
- }
2
+ import { generate, generators } from "./api.js";
13
3
 
14
4
  function displayHelp() {
15
5
  console.log(`\nschema-codegen [path/to/Schema.ts]
@@ -19,10 +9,13 @@ Usage (C#/Unity)
19
9
 
20
10
  Valid options:
21
11
  --output: the output directory for generated client-side schema files
12
+ --bundle: bundle all generated files into a single file
13
+
14
+ Generators:
22
15
  ${Object.
23
- keys(supportedTargets).
16
+ keys(generators).
24
17
  map((targetId) => (
25
- ` --${targetId}: ${supportedTargets[targetId]}`
18
+ ` --${targetId}: generate for ${generators[targetId].name}`
26
19
  )).
27
20
  join("\n")}
28
21
 
@@ -38,7 +31,7 @@ if (args.help) {
38
31
  }
39
32
 
40
33
  let targetId: string;
41
- for (let target in supportedTargets) {
34
+ for (let target in generators) {
42
35
  if (args[target]) {
43
36
  targetId = target;
44
37
  }
@@ -55,7 +48,8 @@ try {
55
48
  files: args._,
56
49
  decorator: args.decorator,
57
50
  output: args.output,
58
- namespace: args.namespace
51
+ namespace: args.namespace,
52
+ bundle: args.bundle
59
53
  });
60
54
 
61
55
  } catch (e) {
@@ -0,0 +1,282 @@
1
+ import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } from "../types.js";
2
+ import { GenerateOptions } from "../api.js";
3
+
4
+ export const name = "C";
5
+
6
+ /**
7
+ * Type mappings for C
8
+ */
9
+ const typeMaps: { [key: string]: string } = {
10
+ "string": "char*",
11
+ "number": "double",
12
+ "boolean": "bool",
13
+ "int8": "int8_t",
14
+ "uint8": "uint8_t",
15
+ "int16": "int16_t",
16
+ "uint16": "uint16_t",
17
+ "int32": "int32_t",
18
+ "uint32": "uint32_t",
19
+ "int64": "int64_t",
20
+ "uint64": "uint64_t",
21
+ "float32": "float",
22
+ "float64": "double",
23
+ };
24
+
25
+ /**
26
+ * Colyseus field type enum mappings
27
+ */
28
+ const fieldTypeMaps: { [key: string]: string } = {
29
+ "string": "COLYSEUS_FIELD_STRING",
30
+ "number": "COLYSEUS_FIELD_NUMBER",
31
+ "boolean": "COLYSEUS_FIELD_BOOLEAN",
32
+ "int8": "COLYSEUS_FIELD_INT8",
33
+ "uint8": "COLYSEUS_FIELD_UINT8",
34
+ "int16": "COLYSEUS_FIELD_INT16",
35
+ "uint16": "COLYSEUS_FIELD_UINT16",
36
+ "int32": "COLYSEUS_FIELD_INT32",
37
+ "uint32": "COLYSEUS_FIELD_UINT32",
38
+ "int64": "COLYSEUS_FIELD_INT64",
39
+ "uint64": "COLYSEUS_FIELD_UINT64",
40
+ "float32": "COLYSEUS_FIELD_FLOAT32",
41
+ "float64": "COLYSEUS_FIELD_FLOAT64",
42
+ "ref": "COLYSEUS_FIELD_REF",
43
+ "array": "COLYSEUS_FIELD_ARRAY",
44
+ "map": "COLYSEUS_FIELD_MAP",
45
+ };
46
+
47
+ const COMMON_INCLUDES = `#include "colyseus/schema/types.h"
48
+ #include "colyseus/schema/collections.h"
49
+ #include <stdlib.h>
50
+ #include <stddef.h>
51
+ #include <stdbool.h>`;
52
+
53
+ /**
54
+ * Native C Code Generator
55
+ */
56
+
57
+ const toSnakeCase = (s: string) => {
58
+ return s.replace(/([A-Z])/g, (match, p1, offset) =>
59
+ (offset > 0 ? '_' : '') + p1.toLowerCase()
60
+ );
61
+ };
62
+
63
+ const distinct = (value: string, index: number, self: string[]) =>
64
+ self.indexOf(value) === index;
65
+
66
+ /**
67
+ * Generate individual files for each class
68
+ */
69
+ export function generate(context: Context, options: GenerateOptions): File[] {
70
+ return context.classes.map(klass => ({
71
+ name: toSnakeCase(klass.name) + ".h",
72
+ content: generateClass(klass, options.namespace, context.classes)
73
+ }));
74
+ }
75
+
76
+ /**
77
+ * Generate a single bundled header file containing all classes
78
+ */
79
+ export function renderBundle(context: Context, options: GenerateOptions): File {
80
+ const fileName = options.namespace ? `${toSnakeCase(options.namespace)}.h` : "schema.h";
81
+ const guardName = `__SCHEMA_CODEGEN_${(options.namespace || "SCHEMA").toUpperCase()}_H__`;
82
+
83
+ const classBodies = context.classes.map(klass =>
84
+ generateClassBody(klass, context.classes)
85
+ ).join("\n\n");
86
+
87
+ const content = `${getCommentHeader()}
88
+ #ifndef ${guardName}
89
+ #define ${guardName} 1
90
+
91
+ ${COMMON_INCLUDES}
92
+
93
+ ${classBodies}
94
+
95
+ #endif
96
+ `;
97
+
98
+ return { name: fileName, content };
99
+ }
100
+
101
+ /**
102
+ * Generate just the class body (without guards/includes) for bundling
103
+ */
104
+ function generateClassBody(klass: Class, allClasses: Class[]): string {
105
+ const snakeName = toSnakeCase(klass.name);
106
+ const typeName = `${snakeName}_t`;
107
+ const allProperties = getAllProperties(klass, allClasses);
108
+
109
+ return `${generateTypedef(klass, typeName, allClasses)}
110
+
111
+ ${generateFieldsArray(klass, typeName, snakeName, allProperties)}
112
+
113
+ ${generateCreateFunction(snakeName, typeName)}
114
+
115
+ ${generateDestroyFunction(klass, snakeName, typeName, allProperties)}
116
+
117
+ ${generateVtable(klass, snakeName, typeName, allProperties)}`;
118
+ }
119
+
120
+ /**
121
+ * Generate a complete class file with guards/includes (for individual file mode)
122
+ */
123
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
124
+ const snakeName = toSnakeCase(klass.name);
125
+ const typeName = `${snakeName}_t`;
126
+ const guardName = `__SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__`;
127
+
128
+ const allRefs: Property[] = [];
129
+
130
+ klass.properties.forEach(property => {
131
+ if (property.type === "ref" || property.type === "array" || property.type === "map") {
132
+ allRefs.push(property);
133
+ }
134
+ });
135
+
136
+ // Generate includes for referenced schema types
137
+ const refIncludes = allRefs
138
+ .filter(ref => ref.childType && typeMaps[ref.childType] === undefined)
139
+ .map(ref => ref.childType)
140
+ .concat(getInheritanceTree(klass, allClasses, false).map(k => k.name))
141
+ .filter(distinct)
142
+ .map(childType => `#include "${toSnakeCase(childType)}.h"`)
143
+ .join("\n");
144
+
145
+ return `${getCommentHeader()}
146
+ #ifndef ${guardName}
147
+ #define ${guardName} 1
148
+
149
+ ${COMMON_INCLUDES}
150
+ ${refIncludes ? `\n${refIncludes}\n` : ""}
151
+ ${generateClassBody(klass, allClasses)}
152
+
153
+ #endif
154
+ `;
155
+ }
156
+
157
+ function generateTypedef(klass: Class, typeName: string, allClasses: Class[]) {
158
+ const allProperties = getAllProperties(klass, allClasses);
159
+
160
+ const fields = allProperties.map(prop => {
161
+ const cType = getCType(prop);
162
+ return ` ${cType} ${prop.name};`;
163
+ }).join("\n");
164
+
165
+ return `typedef struct {
166
+ colyseus_schema_t __base;
167
+ ${fields}
168
+ } ${typeName};`;
169
+ }
170
+
171
+ function getCType(prop: Property): string {
172
+ if (prop.type === "ref") {
173
+ return `${toSnakeCase(prop.childType)}_t*`;
174
+ } else if (prop.type === "array") {
175
+ if (typeMaps[prop.childType]) {
176
+ return `colyseus_array_schema_t*`;
177
+ } else {
178
+ return `colyseus_array_schema_t*`;
179
+ }
180
+ } else if (prop.type === "map") {
181
+ if (typeMaps[prop.childType]) {
182
+ return `colyseus_map_schema_t*`;
183
+ } else {
184
+ return `colyseus_map_schema_t*`;
185
+ }
186
+ } else {
187
+ return typeMaps[prop.type] || `${toSnakeCase(prop.type)}_t*`;
188
+ }
189
+ }
190
+
191
+ function getFieldType(prop: Property): string {
192
+ return fieldTypeMaps[prop.type] || "COLYSEUS_FIELD_REF";
193
+ }
194
+
195
+ function getFieldTypeString(prop: Property): string {
196
+ // Always return the type itself (ref, array, map, string, number, etc.)
197
+ return prop.type;
198
+ }
199
+
200
+ function generateFieldsArray(klass: Class, typeName: string, snakeName: string, allProperties: Property[]) {
201
+ if (allProperties.length === 0) {
202
+ return `static const colyseus_field_t ${snakeName}_fields[] = {};`;
203
+ }
204
+
205
+ const fields = allProperties.map((prop, i) => {
206
+ const fieldType = getFieldType(prop);
207
+ const typeString = getFieldTypeString(prop);
208
+
209
+ let vtableRef = "NULL";
210
+
211
+ if (prop.type === "ref" && prop.childType && !typeMaps[prop.childType]) {
212
+ const childSnake = toSnakeCase(prop.childType);
213
+ vtableRef = `&${childSnake}_vtable`;
214
+ } else if ((prop.type === "array" || prop.type === "map") && prop.childType && !typeMaps[prop.childType]) {
215
+ const childSnake = toSnakeCase(prop.childType);
216
+ vtableRef = `&${childSnake}_vtable`;
217
+ }
218
+
219
+ return ` {${prop.index}, "${prop.name}", ${fieldType}, "${typeString}", offsetof(${typeName}, ${prop.name}), ${vtableRef}, NULL}`;
220
+ }).join(",\n");
221
+
222
+ return `static const colyseus_field_t ${snakeName}_fields[] = {
223
+ ${fields}
224
+ };`;
225
+ }
226
+
227
+ function generateCreateFunction(snakeName: string, typeName: string) {
228
+ return `static ${typeName}* ${snakeName}_create(void) {
229
+ ${typeName}* instance = calloc(1, sizeof(${typeName}));
230
+ return instance;
231
+ }`;
232
+ }
233
+
234
+ function generateDestroyFunction(klass: Class, snakeName: string, typeName: string, allProperties: Property[]) {
235
+ const freeStatements: string[] = [];
236
+
237
+ allProperties.forEach(prop => {
238
+ if (prop.type === "string") {
239
+ freeStatements.push(` if (instance->${prop.name}) free(instance->${prop.name});`);
240
+ } else if (prop.type === "ref") {
241
+ if (typeMaps[prop.childType]) {
242
+ freeStatements.push(` if (instance->${prop.name}) free(instance->${prop.name});`);
243
+ } else {
244
+ const childSnake = toSnakeCase(prop.childType);
245
+ freeStatements.push(` if (instance->${prop.name}) ${childSnake}_destroy((colyseus_schema_t*)instance->${prop.name});`);
246
+ }
247
+ } else if (prop.type === "array" || prop.type === "map") {
248
+ // arrays and maps are scheduled for destruction at the decoder level
249
+ // freeStatements.push(` if (instance->${prop.name}) colyseus_${prop.type}_destroy(instance->${prop.name});`);
250
+ }
251
+ });
252
+
253
+ const freeCode = freeStatements.length > 0 ? freeStatements.join("\n") + "\n" : "";
254
+
255
+ return `static void ${snakeName}_destroy(colyseus_schema_t* schema) {
256
+ ${typeName}* instance = (${typeName}*)schema;
257
+ ${freeCode} free(instance);
258
+ }`;
259
+ }
260
+
261
+ function generateVtable(klass: Class, snakeName: string, typeName: string, allProperties: Property[]) {
262
+ const fieldCount = allProperties.length;
263
+
264
+ return `static const colyseus_schema_vtable_t ${snakeName}_vtable = {
265
+ "${klass.name}",
266
+ sizeof(${typeName}),
267
+ (colyseus_schema_t* (*)(void))${snakeName}_create,
268
+ ${snakeName}_destroy,
269
+ ${snakeName}_fields,
270
+ ${fieldCount}
271
+ };`;
272
+ }
273
+
274
+ function getAllProperties(klass: Class, allClasses: Class[]) {
275
+ let properties: Property[] = [];
276
+
277
+ getInheritanceTree(klass, allClasses).reverse().forEach((k) => {
278
+ properties = properties.concat(k.properties);
279
+ });
280
+
281
+ return properties;
282
+ }
@@ -1,6 +1,8 @@
1
1
  import { Class, Property, File, getCommentHeader, getInheritanceTree, Context } from "../types.js";
2
2
  import { GenerateOptions } from "../api.js";
3
3
 
4
+ export const name = "C++";
5
+
4
6
  const typeMaps: { [key: string]: string } = {
5
7
  "string": "string",
6
8
  "number": "varint_t",
@@ -33,6 +35,12 @@ const typeInitializer: { [key: string]: string } = {
33
35
  "float64": "0",
34
36
  }
35
37
 
38
+ const COMMON_INCLUDES = `#include "schema.h"
39
+ #include <typeinfo>
40
+ #include <typeindex>
41
+
42
+ using namespace colyseus::schema;`;
43
+
36
44
  /**
37
45
  * C++ Code Generator
38
46
  */
@@ -44,6 +52,9 @@ const capitalize = (s: string) => {
44
52
  const distinct = (value: string, index: number, self: string[]) =>
45
53
  self.indexOf(value) === index;
46
54
 
55
+ /**
56
+ * Generate individual files for each class
57
+ */
47
58
  export function generate (context: Context, options: GenerateOptions): File[] {
48
59
  return context.classes.map(klass => ({
49
60
  name: klass.name + ".hpp",
@@ -51,7 +62,35 @@ export function generate (context: Context, options: GenerateOptions): File[] {
51
62
  }));
52
63
  }
53
64
 
54
- function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
65
+ /**
66
+ * Generate a single bundled header file containing all classes
67
+ */
68
+ export function renderBundle(context: Context, options: GenerateOptions): File {
69
+ const fileName = options.namespace ? `${options.namespace}.hpp` : "schema.hpp";
70
+ const guardName = `__SCHEMA_CODEGEN_${(options.namespace || "SCHEMA").toUpperCase()}_H__`;
71
+
72
+ const classBodies = context.classes.map(klass => generateClassBody(klass, context.classes, options.namespace));
73
+
74
+ const content = `${getCommentHeader()}
75
+ #ifndef ${guardName}
76
+ #define ${guardName} 1
77
+
78
+ ${COMMON_INCLUDES}
79
+
80
+ ${options.namespace ? `namespace ${options.namespace} {\n` : ""}
81
+ ${classBodies.join("\n\n")}
82
+ ${options.namespace ? "}" : ""}
83
+
84
+ #endif
85
+ `;
86
+
87
+ return { name: fileName, content };
88
+ }
89
+
90
+ /**
91
+ * Generate just the class body (without includes/guards) for bundling
92
+ */
93
+ function generateClassBody(klass: Class, allClasses: Class[], namespace: string): string {
55
94
  const propertiesPerType: {[type: string]: Property[]} = {};
56
95
  const allRefs: Property[] = [];
57
96
  klass.properties.forEach(property => {
@@ -79,26 +118,7 @@ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
79
118
  \t\treturn ${klass.extends}::createInstance(type);
80
119
  \t}`;
81
120
 
82
- return `${getCommentHeader()}
83
- #ifndef __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__
84
- #define __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__ 1
85
-
86
- #include "schema.h"
87
- #include <typeinfo>
88
- #include <typeindex>
89
-
90
- ${allRefs.
91
- filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
92
- map(ref => ref.childType).
93
- concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
94
- filter(distinct).
95
- map(childType => `#include "${childType}.hpp"`).
96
- join("\n")}
97
-
98
- using namespace colyseus::schema;
99
-
100
- ${namespace ? `namespace ${namespace} {` : ""}
101
- class ${klass.name} : public ${klass.extends} {
121
+ return `class ${klass.name} : public ${klass.extends} {
102
122
  public:
103
123
  ${klass.properties.map(prop => generateProperty(prop)).join("\n")}
104
124
 
@@ -119,7 +139,39 @@ ${Object.keys(propertiesPerType).map(type =>
119
139
  join("\n")}
120
140
 
121
141
  ${createInstanceMethod}
122
- };
142
+ };`;
143
+ }
144
+
145
+ /**
146
+ * Generate a complete class file with includes/guards (for individual file mode)
147
+ */
148
+ function generateClass(klass: Class, namespace: string, allClasses: Class[]) {
149
+ const allRefs: Property[] = [];
150
+ klass.properties.forEach(property => {
151
+ let type = property.type;
152
+ // keep all refs list
153
+ if ((type === "ref" || type === "array" || type === "map")) {
154
+ allRefs.push(property);
155
+ }
156
+ });
157
+
158
+ const localIncludes = allRefs.
159
+ filter(ref => ref.childType && typeMaps[ref.childType] === undefined).
160
+ map(ref => ref.childType).
161
+ concat(getInheritanceTree(klass, allClasses, false).map(klass => klass.name)).
162
+ filter(distinct).
163
+ map(childType => `#include "${childType}.hpp"`).
164
+ join("\n");
165
+
166
+ return `${getCommentHeader()}
167
+ #ifndef __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__
168
+ #define __SCHEMA_CODEGEN_${klass.name.toUpperCase()}_H__ 1
169
+
170
+ ${COMMON_INCLUDES}
171
+ ${localIncludes}
172
+
173
+ ${namespace ? `namespace ${namespace} {` : ""}
174
+ ${generateClassBody(klass, allClasses, namespace)}
123
175
  ${namespace ? "}" : ""}
124
176
 
125
177
  #endif