@lagless/codegen 0.0.33
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 +403 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/dirname.js +6 -0
- package/dist/dirname.js.map +1 -0
- package/dist/generator.js +156 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/nx-generator.js +66 -0
- package/dist/nx-generator.js.map +1 -0
- package/dist/parser.js +160 -0
- package/dist/parser.js.map +1 -0
- package/dist/template-engine.js +149 -0
- package/dist/template-engine.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/files/component/__name__.ts.template +65 -0
- package/files/core/__projectName__.core.ts.template +16 -0
- package/files/filter/__name__.ts.template +13 -0
- package/files/input/__name__.ts.template +33 -0
- package/files/input-registry/__projectName__InputRegistry.ts.template +9 -0
- package/files/playerResource/__name__.ts.template +63 -0
- package/files/runner/__projectName__.runner.ts.template +16 -0
- package/files/singleton/__name__.ts.template +63 -0
- package/generators.json +9 -0
- package/package.json +67 -0
- package/schema.json +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# @lagless/codegen
|
|
2
|
+
|
|
3
|
+
## 1. Responsibility & Context
|
|
4
|
+
|
|
5
|
+
Code generator that transforms YAML schema definitions into TypeScript ECS (Entity Component System) classes for Lagless simulations. Reads declarative schema files describing components, singletons, filters, inputs, and player resources, then generates type-safe TypeScript classes with deterministic memory layouts, input registries, core modules, and ECSRunner subclasses. Eliminates boilerplate and ensures consistency across ECS projects.
|
|
6
|
+
|
|
7
|
+
## 2. Architecture Role
|
|
8
|
+
|
|
9
|
+
**Upstream dependencies:** `@lagless/binary`, `@lagless/core`
|
|
10
|
+
**Downstream consumers:** `circle-sumo-simulation` (uses generated code from `ecs.yaml`)
|
|
11
|
+
|
|
12
|
+
This tool sits outside the runtime dependency graph — it's a build-time/development-time code generator. Generated code depends on `@lagless/core` and `@lagless/binary`, but the generator itself is not imported by applications. Integrates with Nx workspace generators for monorepo workflows.
|
|
13
|
+
|
|
14
|
+
## 3. Public API
|
|
15
|
+
|
|
16
|
+
### Parser
|
|
17
|
+
|
|
18
|
+
- **`parseYamlConfig(configContent: string, configPath?: string): { schema: ECSSchema, projectName: string }`** — Parse YAML config string into `ECSSchema` object. `projectName` extracted from YAML `projectName:` field or derived from `configPath` (e.g., `circle-sumo/...` → `CircleSumo`). Throws on invalid schema.
|
|
19
|
+
|
|
20
|
+
- **`parseFieldType(typeStr: string): FieldDefinition`** — Parse field type string (e.g., `"float32"`, `"uint8[16]"`) into `FieldDefinition` with `{ type, isArray, arrayLength? }`. Used internally by schema parser.
|
|
21
|
+
|
|
22
|
+
- **`parseInputFieldType(fieldName: string, fieldType: string): InputFieldDefinition`** — Parse input field with byte length calculation. Returns `{ name, type, isArray, arrayLength?, byteLength }`.
|
|
23
|
+
|
|
24
|
+
- **`getProjectNameFromConfigPath(configPath: string): string`** — Extract project name from file path. Converts kebab-case to PascalCase (e.g., `circle-sumo/ecs.yaml` → `CircleSumo`).
|
|
25
|
+
|
|
26
|
+
### Generator
|
|
27
|
+
|
|
28
|
+
- **`generateCode(options: GenerateCodeOptions): Promise<void>`** — Main code generation entry point. Generates all TypeScript classes (components, singletons, filters, inputs, player resources, input registry, core module, runner class, barrel export file) from schema.
|
|
29
|
+
- Options: `{ schema, projectName, outputDir, templateDir, fileOperations }`
|
|
30
|
+
|
|
31
|
+
- **`generateBarrelFileContent(schema: ECSSchema, projectName: string): string`** — Generate barrel export file (`index.ts`) content that re-exports all generated classes.
|
|
32
|
+
|
|
33
|
+
### Template Engine
|
|
34
|
+
|
|
35
|
+
- **`generateFromTemplate(options: TemplateOptions): Promise<void>`** — Render EJS templates from `templateDir` to `outputDir` with provided `data`. Processes `.template` files and `__variable__` filename patterns.
|
|
36
|
+
- Options: `{ templateDir, outputDir, data, fileOperations }`
|
|
37
|
+
|
|
38
|
+
- **`TemplateEngine`** — Class-based template engine. Use `generateFromTemplate()` function for simpler usage.
|
|
39
|
+
|
|
40
|
+
### Types
|
|
41
|
+
|
|
42
|
+
- **`ECSConfig`** — YAML schema interface with optional fields: `projectName`, `components`, `singletons`, `playerResources`, `filters`, `inputs`
|
|
43
|
+
|
|
44
|
+
- **`GenerateCodeOptions`** — Generator function options: `schema`, `projectName`, `outputDir`, `templateDir`, `fileOperations`
|
|
45
|
+
|
|
46
|
+
- **`FileOperations`** — Abstraction for file I/O operations. Allows generator to work with Node.js `fs` module (CLI) or Nx `Tree` API (workspace generators).
|
|
47
|
+
- Methods: `readFile`, `writeFile`, `joinPath`, `exists`, `readDir?`, `isDirectory?`
|
|
48
|
+
|
|
49
|
+
## 4. Preconditions
|
|
50
|
+
|
|
51
|
+
- **YAML schema file exists** at the specified config path (e.g., `src/lib/schema/ecs.yaml`)
|
|
52
|
+
- **Schema must contain at least one of:** `components` or `singletons` (cannot be empty)
|
|
53
|
+
- **Template files exist** in the specified template directory (default: `tools/codegen/files/`)
|
|
54
|
+
- **Output directory is writable** (generator creates it if missing)
|
|
55
|
+
- **For Nx generator:** Nx workspace initialized with `@lagless/codegen` in dependencies
|
|
56
|
+
|
|
57
|
+
## 5. Postconditions
|
|
58
|
+
|
|
59
|
+
- **Generated TypeScript files** in the output directory (default: `<config_dir>/../code-gen/`)
|
|
60
|
+
- **Component classes** with SoA memory layout for each component in schema
|
|
61
|
+
- **Singleton classes** with typed array accessors for singleton fields
|
|
62
|
+
- **Filter classes** with bitmask matching logic for entity iteration
|
|
63
|
+
- **Input classes** with binary serialization/deserialization methods
|
|
64
|
+
- **PlayerResource classes** with per-player state management
|
|
65
|
+
- **Input registry** mapping input IDs to input classes
|
|
66
|
+
- **Core module** (`<ProjectName>.core.ts`) with `getECSSchema()` function
|
|
67
|
+
- **Runner class** (`<ProjectName>.runner.ts`) extending `ECSRunner` with schema integration
|
|
68
|
+
- **Barrel export** (`index.ts`) re-exporting all generated classes
|
|
69
|
+
- All files formatted and ready for use in ECS simulation
|
|
70
|
+
|
|
71
|
+
## 6. Invariants & Constraints
|
|
72
|
+
|
|
73
|
+
- **Component IDs are powers of 2** — `id = 2^n` where `n` is the component's index in the YAML file. Required for bitmask filtering (e.g., first component = 1, second = 2, third = 4, etc.).
|
|
74
|
+
- **Input IDs start at 1** — Sequential integers (not powers of 2). First input = 1, second = 2, etc.
|
|
75
|
+
- **Field type strings** must match `@lagless/binary` supported types: `uint8`, `uint16`, `uint32`, `int8`, `int16`, `int32`, `float32`, `float64`
|
|
76
|
+
- **Array syntax:** `type[length]` where `length > 0` (e.g., `uint8[16]` for 16-byte UUID)
|
|
77
|
+
- **Project name** must be valid TypeScript identifier (PascalCase recommended)
|
|
78
|
+
- **Filter include/exclude** component names must exist in the `components:` section
|
|
79
|
+
- **Template files** use EJS syntax (`<%= variable %>`, `<% if (condition) { %>...`)
|
|
80
|
+
- **Output is deterministic** — Same YAML input always produces identical TypeScript output (important for version control)
|
|
81
|
+
|
|
82
|
+
## 7. Safety Notes (AI Agent)
|
|
83
|
+
|
|
84
|
+
### DO NOT
|
|
85
|
+
|
|
86
|
+
- **DO NOT** modify component ID calculation logic — changing from powers of 2 breaks bitmask filtering in `@lagless/core`
|
|
87
|
+
- **DO NOT** add unsupported field types — only types in `@lagless/binary` `typeToArrayConstructor` are valid
|
|
88
|
+
- **DO NOT** change template file structure without updating `generateCode()` logic — generator assumes specific template directory layout (`component/`, `singleton/`, `filter/`, etc.)
|
|
89
|
+
- **DO NOT** generate code directly into `src/` — always use a separate output directory (e.g., `code-gen/`) to distinguish generated from hand-written code
|
|
90
|
+
- **DO NOT** edit generated files manually — changes will be overwritten on next generation. Modify the YAML schema or templates instead.
|
|
91
|
+
|
|
92
|
+
### Common Mistakes
|
|
93
|
+
|
|
94
|
+
- **Forgetting to regenerate** after YAML changes — run `nx g @lagless/codegen:ecs --configPath <path>` after editing schema
|
|
95
|
+
- **Invalid component references in filters** — filter `include`/`exclude` must reference components defined in `components:` section
|
|
96
|
+
- **Zero or negative array lengths** — `uint8[0]` and `uint8[-1]` are invalid (parser throws error)
|
|
97
|
+
- **Missing projectName** — Either set `projectName:` in YAML or ensure config path contains project directory for auto-detection
|
|
98
|
+
- **Template syntax errors** — EJS templates use `<%= %>` for output and `<% %>` for logic; JavaScript syntax must be valid
|
|
99
|
+
|
|
100
|
+
## 8. Usage Examples
|
|
101
|
+
|
|
102
|
+
### CLI Usage
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Generate code from YAML schema
|
|
106
|
+
npx lagless-codegen -c path/to/ecs.yaml
|
|
107
|
+
|
|
108
|
+
# With custom output directory
|
|
109
|
+
npx lagless-codegen -c path/to/ecs.yaml -o src/generated
|
|
110
|
+
|
|
111
|
+
# With custom templates
|
|
112
|
+
npx lagless-codegen -c path/to/ecs.yaml -t my-templates/
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Nx Generator Usage
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# In Nx monorepo
|
|
119
|
+
nx g @lagless/codegen:ecs --configPath circle-sumo/circle-sumo-simulation/src/lib/schema/ecs.yaml
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Example YAML Schema
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
# src/lib/schema/ecs.yaml
|
|
126
|
+
projectName: MyGame
|
|
127
|
+
components:
|
|
128
|
+
Transform2d:
|
|
129
|
+
positionX: float32
|
|
130
|
+
positionY: float32
|
|
131
|
+
rotation: float32
|
|
132
|
+
Health:
|
|
133
|
+
current: float32
|
|
134
|
+
max: float32
|
|
135
|
+
singletons:
|
|
136
|
+
GameState:
|
|
137
|
+
startedAtTick: uint32
|
|
138
|
+
finishedAtTick: uint32
|
|
139
|
+
playerResources:
|
|
140
|
+
PlayerResource:
|
|
141
|
+
id: uint8[16]
|
|
142
|
+
score: uint32
|
|
143
|
+
inputs:
|
|
144
|
+
Move:
|
|
145
|
+
direction: float32
|
|
146
|
+
speed: float32
|
|
147
|
+
Shoot:
|
|
148
|
+
angle: float32
|
|
149
|
+
filters:
|
|
150
|
+
AliveFilter:
|
|
151
|
+
include:
|
|
152
|
+
- Transform2d
|
|
153
|
+
- Health
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Programmatic Usage
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { parseYamlConfig, generateCode } from '@lagless/codegen';
|
|
160
|
+
import * as fs from 'fs';
|
|
161
|
+
import * as path from 'path';
|
|
162
|
+
|
|
163
|
+
const yamlContent = fs.readFileSync('ecs.yaml', 'utf-8');
|
|
164
|
+
const { schema, projectName } = parseYamlConfig(yamlContent, 'ecs.yaml');
|
|
165
|
+
|
|
166
|
+
await generateCode({
|
|
167
|
+
schema,
|
|
168
|
+
projectName,
|
|
169
|
+
outputDir: './generated',
|
|
170
|
+
templateDir: './node_modules/@lagless/codegen/files',
|
|
171
|
+
fileOperations: {
|
|
172
|
+
readFile: (p) => fs.readFileSync(p, 'utf-8'),
|
|
173
|
+
writeFile: (p, content) => {
|
|
174
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
175
|
+
fs.writeFileSync(p, content, 'utf-8');
|
|
176
|
+
},
|
|
177
|
+
joinPath: (...segments) => path.join(...segments),
|
|
178
|
+
exists: (p) => fs.existsSync(p),
|
|
179
|
+
readDir: (p) => fs.readdirSync(p),
|
|
180
|
+
isDirectory: (p) => fs.statSync(p).isDirectory(),
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log('Generated ECS code!');
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 9. Testing Guidance
|
|
188
|
+
|
|
189
|
+
No test suite currently exists for this module. When adding tests, consider:
|
|
190
|
+
|
|
191
|
+
- **Parser tests:** Validate YAML parsing with valid/invalid schemas, component ID generation, field type parsing, project name extraction
|
|
192
|
+
- **Generator tests:** Verify generated code compiles and matches expected output (snapshot testing)
|
|
193
|
+
- **Template tests:** Check EJS templates render correctly with sample data
|
|
194
|
+
- **Integration tests:** Generate code from real schema, compile with TypeScript, instantiate classes
|
|
195
|
+
- **Error handling:** Test invalid YAML syntax, missing required fields, unsupported types, circular dependencies in filters
|
|
196
|
+
- Use `@lagless/core` test utilities to verify generated classes integrate correctly with ECS runtime
|
|
197
|
+
|
|
198
|
+
## 10. Change Checklist
|
|
199
|
+
|
|
200
|
+
When modifying this module:
|
|
201
|
+
|
|
202
|
+
1. **Changing component ID generation:** Update `parseYamlConfig()` logic AND document migration path for existing projects (component ID changes break save files)
|
|
203
|
+
2. **Adding new field types:** Update `parseFieldType()` AND ensure type is supported in `@lagless/binary` `typeToArrayConstructor`
|
|
204
|
+
3. **Modifying templates:** Test generated code compiles with TypeScript AND passes `@lagless/core` runtime checks
|
|
205
|
+
4. **Changing template directory structure:** Update `generateCode()` function to match new layout
|
|
206
|
+
5. **Adding new YAML sections:** Update `ECSConfig` interface, parser logic, generator function, AND add corresponding template directory
|
|
207
|
+
6. **Modifying CLI flags:** Update `commander` options in `cli.ts` AND update documentation
|
|
208
|
+
7. **Changing output file names:** Update `generateBarrelFileContent()` to match new naming scheme
|
|
209
|
+
8. **Breaking changes:** Bump version and document migration guide for users
|
|
210
|
+
|
|
211
|
+
## 11. Integration Notes
|
|
212
|
+
|
|
213
|
+
### With Nx Workspace
|
|
214
|
+
|
|
215
|
+
1. Add `@lagless/codegen` to workspace dependencies
|
|
216
|
+
2. Create `generators.json` in project root:
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"generators": {
|
|
220
|
+
"ecs": {
|
|
221
|
+
"factory": "./src/nx-generator",
|
|
222
|
+
"schema": "./schema.json",
|
|
223
|
+
"description": "Generate ECS code from YAML"
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
3. Run generator: `nx g @lagless/codegen:ecs --configPath <path>`
|
|
229
|
+
|
|
230
|
+
### With ECS Projects
|
|
231
|
+
|
|
232
|
+
1. Create YAML schema file (e.g., `src/lib/schema/ecs.yaml`)
|
|
233
|
+
2. Run codegen to generate classes in `src/lib/schema/code-gen/`
|
|
234
|
+
3. Import generated classes:
|
|
235
|
+
```typescript
|
|
236
|
+
import { Transform2d, Health, AliveFilter, MyGameRunner } from './schema/code-gen';
|
|
237
|
+
```
|
|
238
|
+
4. Extend runner class:
|
|
239
|
+
```typescript
|
|
240
|
+
export class GameRunner extends MyGameRunner {
|
|
241
|
+
constructor() {
|
|
242
|
+
super({ /* ECSConfig */ });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
5. Regenerate after schema changes: `nx g @lagless/codegen:ecs --configPath ...`
|
|
247
|
+
|
|
248
|
+
### Generated File Structure
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
code-gen/
|
|
252
|
+
├── index.ts # Barrel export (re-exports all classes)
|
|
253
|
+
├── Transform2d.ts # Component class
|
|
254
|
+
├── Health.ts # Component class
|
|
255
|
+
├── GameState.ts # Singleton class
|
|
256
|
+
├── PlayerResource.ts # PlayerResource class
|
|
257
|
+
├── Move.ts # Input class
|
|
258
|
+
├── Shoot.ts # Input class
|
|
259
|
+
├── AliveFilter.ts # Filter class
|
|
260
|
+
├── MyGameInputRegistry.ts # Input registry (maps input IDs to classes)
|
|
261
|
+
├── MyGame.core.ts # Core module (getECSSchema() function)
|
|
262
|
+
└── MyGame.runner.ts # Runner class (extends ECSRunner)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## 12. Appendix
|
|
266
|
+
|
|
267
|
+
### YAML Schema Format Reference
|
|
268
|
+
|
|
269
|
+
```yaml
|
|
270
|
+
# Optional: Project name (PascalCase). If omitted, derived from config path.
|
|
271
|
+
projectName: MyGame
|
|
272
|
+
|
|
273
|
+
# Components: Define entity data structures with typed fields
|
|
274
|
+
components:
|
|
275
|
+
ComponentName:
|
|
276
|
+
fieldName: fieldType
|
|
277
|
+
# Supported types: uint8, uint16, uint32, int8, int16, int32, float32, float64
|
|
278
|
+
# Arrays: type[length] (e.g., uint8[16] for 16-byte array)
|
|
279
|
+
|
|
280
|
+
# Singletons: Global state (one instance per simulation)
|
|
281
|
+
singletons:
|
|
282
|
+
SingletonName:
|
|
283
|
+
fieldName: fieldType
|
|
284
|
+
|
|
285
|
+
# Player Resources: Per-player state (one instance per player)
|
|
286
|
+
playerResources:
|
|
287
|
+
ResourceName:
|
|
288
|
+
fieldName: fieldType
|
|
289
|
+
|
|
290
|
+
# Inputs: Player commands (serialized over network)
|
|
291
|
+
inputs:
|
|
292
|
+
InputName:
|
|
293
|
+
fieldName: fieldType
|
|
294
|
+
|
|
295
|
+
# Filters: Entity iterators based on component membership
|
|
296
|
+
filters:
|
|
297
|
+
FilterName:
|
|
298
|
+
include:
|
|
299
|
+
- ComponentName1
|
|
300
|
+
- ComponentName2
|
|
301
|
+
exclude:
|
|
302
|
+
- ComponentName3
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Component ID Assignment
|
|
306
|
+
|
|
307
|
+
Components receive IDs as powers of 2 based on their order in the YAML file:
|
|
308
|
+
|
|
309
|
+
| Index | Component | ID (decimal) | ID (binary) |
|
|
310
|
+
|-------|-----------|--------------|-------------|
|
|
311
|
+
| 0 | First | 1 | 0b00001 |
|
|
312
|
+
| 1 | Second | 2 | 0b00010 |
|
|
313
|
+
| 2 | Third | 4 | 0b00100 |
|
|
314
|
+
| 3 | Fourth | 8 | 0b01000 |
|
|
315
|
+
| 4 | Fifth | 16 | 0b10000 |
|
|
316
|
+
|
|
317
|
+
**Why powers of 2:** Entity bitmasks store component membership as a single integer. Filter matching uses bitwise AND operations:
|
|
318
|
+
```typescript
|
|
319
|
+
entity.bitmask & filter.includeMask === filter.includeMask // Has all required components
|
|
320
|
+
entity.bitmask & filter.excludeMask === 0 // Has no excluded components
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Supported Field Types
|
|
324
|
+
|
|
325
|
+
| Type | Bytes | Range/Precision |
|
|
326
|
+
|-----------|-------|-------------------------------------|
|
|
327
|
+
| `uint8` | 1 | 0 to 255 |
|
|
328
|
+
| `uint16` | 2 | 0 to 65,535 |
|
|
329
|
+
| `uint32` | 4 | 0 to 4,294,967,295 |
|
|
330
|
+
| `int8` | 1 | -128 to 127 |
|
|
331
|
+
| `int16` | 2 | -32,768 to 32,767 |
|
|
332
|
+
| `int32` | 4 | -2,147,483,648 to 2,147,483,647 |
|
|
333
|
+
| `float32` | 4 | IEEE 754 single precision |
|
|
334
|
+
| `float64` | 8 | IEEE 754 double precision |
|
|
335
|
+
|
|
336
|
+
**Array syntax:** Append `[length]` for fixed-size arrays (e.g., `uint8[16]` = 16-byte array).
|
|
337
|
+
|
|
338
|
+
### Template Directory Structure
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
files/
|
|
342
|
+
├── component/
|
|
343
|
+
│ └── __name__.ts.template # Component class template
|
|
344
|
+
├── singleton/
|
|
345
|
+
│ └── __name__.ts.template # Singleton class template
|
|
346
|
+
├── playerResource/
|
|
347
|
+
│ └── __name__.ts.template # PlayerResource class template
|
|
348
|
+
├── filter/
|
|
349
|
+
│ └── __name__.ts.template # Filter class template
|
|
350
|
+
├── input/
|
|
351
|
+
│ └── __name__.ts.template # Input class template
|
|
352
|
+
├── input-registry/
|
|
353
|
+
│ └── __projectName__InputRegistry.ts.template
|
|
354
|
+
├── core/
|
|
355
|
+
│ └── __projectName__.core.ts.template
|
|
356
|
+
└── runner/
|
|
357
|
+
└── __projectName__.runner.ts.template
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Templates use EJS syntax:
|
|
361
|
+
- `<%= variable %>` — Output value
|
|
362
|
+
- `<% if (condition) { %>...<% } %>` — Control flow
|
|
363
|
+
- `__name__`, `__projectName__` — Filename placeholders replaced during generation
|
|
364
|
+
|
|
365
|
+
### CLI Options
|
|
366
|
+
|
|
367
|
+
| Flag | Alias | Required | Description |
|
|
368
|
+
|------|-------|----------|-------------|
|
|
369
|
+
| `--config <path>` | `-c` | Yes | Path to YAML configuration file |
|
|
370
|
+
| `--output <path>` | `-o` | No | Output directory (default: `<config_dir>/../code-gen`) |
|
|
371
|
+
| `--templates <path>` | `-t` | No | Templates directory (default: built-in templates) |
|
|
372
|
+
|
|
373
|
+
### Example Generated Component Class
|
|
374
|
+
|
|
375
|
+
Input YAML:
|
|
376
|
+
```yaml
|
|
377
|
+
components:
|
|
378
|
+
Health:
|
|
379
|
+
current: float32
|
|
380
|
+
max: float32
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Generated TypeScript (simplified):
|
|
384
|
+
```typescript
|
|
385
|
+
import { ComponentBase } from '@lagless/core';
|
|
386
|
+
|
|
387
|
+
export class Health extends ComponentBase {
|
|
388
|
+
static readonly id = 4; // Power of 2 based on index
|
|
389
|
+
static readonly fields = {
|
|
390
|
+
current: { type: 'float32', isArray: false },
|
|
391
|
+
max: { type: 'float32', isArray: false },
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
unsafe = {
|
|
395
|
+
current: new Float32Array(this.mem.arrayBuffer, this.byteOffset, this.maxEntities),
|
|
396
|
+
max: new Float32Array(this.mem.arrayBuffer, this.byteOffset + 4 * this.maxEntities, this.maxEntities),
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// ...additional methods
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
All field accessors use SoA (Struct of Arrays) layout backed by the simulation's single ArrayBuffer for deterministic snapshots and rollback.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { parseYamlConfig } from './parser.js';
|
|
6
|
+
import { generateCode } from './generator.js';
|
|
7
|
+
import { DIRNAME } from './dirname.js';
|
|
8
|
+
program.name('lagless-codegen').description('Generate ECS code from YAML configuration').version('1.0.0').requiredOption('-c, --config <path>', 'Path to YAML configuration file').option('-o, --output <path>', 'Output directory (default: config_dir/../code-gen)').option('-t, --templates <path>', 'Templates directory').action(async (options)=>{
|
|
9
|
+
try {
|
|
10
|
+
await generateFromConfig(options);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
async function generateFromConfig(options) {
|
|
17
|
+
const { config: configPath, output: outputPath, templates: templatesPath } = options;
|
|
18
|
+
// Validate config file exists
|
|
19
|
+
if (!fs.existsSync(configPath)) {
|
|
20
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
21
|
+
}
|
|
22
|
+
// Read and parse config
|
|
23
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
24
|
+
if (!configContent.trim()) {
|
|
25
|
+
throw new Error(`Config file is empty: ${configPath}`);
|
|
26
|
+
}
|
|
27
|
+
const { schema, projectName } = parseYamlConfig(configContent, configPath);
|
|
28
|
+
// Determine output directory
|
|
29
|
+
const outputDir = outputPath || path.join(path.dirname(configPath), '..', 'code-gen');
|
|
30
|
+
// Determine templates directory
|
|
31
|
+
const templateDir = templatesPath || path.join(DIRNAME, '..', 'files');
|
|
32
|
+
console.log(`Generating ECS code...`);
|
|
33
|
+
console.log(`Config: ${configPath}`);
|
|
34
|
+
console.log(`Output: ${outputDir}`);
|
|
35
|
+
console.log(`Templates: ${templateDir}`);
|
|
36
|
+
console.log(`Project: ${projectName}`);
|
|
37
|
+
// Create file operations for Node.js filesystem
|
|
38
|
+
const fileOperations = {
|
|
39
|
+
readFile: (filePath)=>{
|
|
40
|
+
try {
|
|
41
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
writeFile: (filePath, content)=>{
|
|
47
|
+
const dir = path.dirname(filePath);
|
|
48
|
+
if (!fs.existsSync(dir)) {
|
|
49
|
+
fs.mkdirSync(dir, {
|
|
50
|
+
recursive: true
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
54
|
+
},
|
|
55
|
+
joinPath: (...segments)=>path.join(...segments),
|
|
56
|
+
exists: (filePath)=>fs.existsSync(filePath),
|
|
57
|
+
readDir: (dirPath)=>{
|
|
58
|
+
try {
|
|
59
|
+
return fs.readdirSync(dirPath);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
isDirectory: (filePath)=>{
|
|
65
|
+
try {
|
|
66
|
+
return fs.statSync(filePath).isDirectory();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
// Generate code
|
|
73
|
+
await generateCode({
|
|
74
|
+
schema,
|
|
75
|
+
projectName,
|
|
76
|
+
outputDir,
|
|
77
|
+
templateDir,
|
|
78
|
+
fileOperations
|
|
79
|
+
});
|
|
80
|
+
console.log('ECS code generation complete!');
|
|
81
|
+
console.log(`Generated ${schema.components.length} components and ${schema.singletons.length} singletons.`);
|
|
82
|
+
console.log(`Project name: ${projectName}`);
|
|
83
|
+
console.log(`Output written to: ${outputDir}`);
|
|
84
|
+
}
|
|
85
|
+
// Handle unhandled promise rejections
|
|
86
|
+
process.on('unhandledRejection', (reason, promise)=>{
|
|
87
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
90
|
+
program.parse();
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { program } from 'commander';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { parseYamlConfig } from './parser.js';\nimport { generateCode } from './generator.js';\nimport { FileOperations } from './template-engine.js';\nimport { DIRNAME } from './dirname.js';\n\ninterface CliOptions {\n config: string;\n output?: string;\n templates?: string;\n}\n\nprogram\n .name('lagless-codegen')\n .description('Generate ECS code from YAML configuration')\n .version('1.0.0')\n .requiredOption('-c, --config <path>', 'Path to YAML configuration file')\n .option('-o, --output <path>', 'Output directory (default: config_dir/../code-gen)')\n .option('-t, --templates <path>', 'Templates directory')\n .action(async (options: CliOptions) => {\n try {\n await generateFromConfig(options);\n } catch (error) {\n console.error('Error:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nasync function generateFromConfig(options: CliOptions): Promise<void> {\n const { config: configPath, output: outputPath, templates: templatesPath } = options;\n\n // Validate config file exists\n if (!fs.existsSync(configPath)) {\n throw new Error(`Config file not found: ${configPath}`);\n }\n\n // Read and parse config\n const configContent = fs.readFileSync(configPath, 'utf-8');\n if (!configContent.trim()) {\n throw new Error(`Config file is empty: ${configPath}`);\n }\n\n const { schema, projectName } = parseYamlConfig(configContent, configPath);\n\n // Determine output directory\n const outputDir = outputPath || path.join(path.dirname(configPath), '..', 'code-gen');\n\n // Determine templates directory\n const templateDir = templatesPath || path.join(DIRNAME, '..', 'files');\n\n console.log(`Generating ECS code...`);\n console.log(`Config: ${configPath}`);\n console.log(`Output: ${outputDir}`);\n console.log(`Templates: ${templateDir}`);\n console.log(`Project: ${projectName}`);\n\n // Create file operations for Node.js filesystem\n const fileOperations: FileOperations = {\n readFile: (filePath: string) => {\n try {\n return fs.readFileSync(filePath, 'utf-8');\n } catch {\n return '';\n }\n },\n writeFile: (filePath: string, content: string) => {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(filePath, content, 'utf-8');\n },\n joinPath: (...segments: string[]) => path.join(...segments),\n exists: (filePath: string) => fs.existsSync(filePath),\n readDir: (dirPath: string) => {\n try {\n return fs.readdirSync(dirPath);\n } catch {\n return [];\n }\n },\n isDirectory: (filePath: string) => {\n try {\n return fs.statSync(filePath).isDirectory();\n } catch {\n return false;\n }\n },\n };\n\n // Generate code\n await generateCode({\n schema,\n projectName,\n outputDir,\n templateDir,\n fileOperations,\n });\n\n console.log('ECS code generation complete!');\n console.log(`Generated ${schema.components.length} components and ${schema.singletons.length} singletons.`);\n console.log(`Project name: ${projectName}`);\n console.log(`Output written to: ${outputDir}`);\n}\n\n// Handle unhandled promise rejections\nprocess.on('unhandledRejection', (reason, promise) => {\n console.error('Unhandled Rejection at:', promise, 'reason:', reason);\n process.exit(1);\n});\n\nprogram.parse();\n"],"names":["program","fs","path","parseYamlConfig","generateCode","DIRNAME","name","description","version","requiredOption","option","action","options","generateFromConfig","error","console","Error","message","process","exit","config","configPath","output","outputPath","templates","templatesPath","existsSync","configContent","readFileSync","trim","schema","projectName","outputDir","join","dirname","templateDir","log","fileOperations","readFile","filePath","writeFile","content","dir","mkdirSync","recursive","writeFileSync","joinPath","segments","exists","readDir","dirPath","readdirSync","isDirectory","statSync","components","length","singletons","on","reason","promise","parse"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
|
package/dist/dirname.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
export const DIRNAME = // In CJS __dirname exists; in ESM we compute it from import.meta.url
|
|
4
|
+
typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=dirname.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dirname.ts"],"sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\n\nexport const DIRNAME =\n // In CJS __dirname exists; in ESM we compute it from import.meta.url\n typeof __dirname !== 'undefined'\n ? __dirname\n : path.dirname(fileURLToPath(import.meta.url));\n"],"names":["path","fileURLToPath","DIRNAME","__dirname","dirname","url"],"rangeMappings":";;;","mappings":"AAAA,OAAOA,UAAU,OAAO;AACxB,SAASC,aAAa,QAAQ,MAAM;AAEpC,OAAO,MAAMC,UACX,qEAAqE;AACrE,OAAOC,cAAc,cACjBA,YACAH,KAAKI,OAAO,CAACH,cAAc,YAAYI,GAAG,GAAG"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { generateFromTemplate as processTemplate } from './template-engine.js';
|
|
3
|
+
import { FieldTypeReverse, getTypeSizeBytes, typedArrayConstructors, typeToArrayConstructor } from '@lagless/binary';
|
|
4
|
+
export function generateBarrelFileContent(schema, projectName) {
|
|
5
|
+
let content = '// Generated by @lagless/codegen. Do not edit manually.\n\n';
|
|
6
|
+
// Export components
|
|
7
|
+
schema.components.forEach((component)=>{
|
|
8
|
+
content += `export * from './${component.name}.js';\n`;
|
|
9
|
+
});
|
|
10
|
+
// Export singletons
|
|
11
|
+
schema.singletons.forEach((singleton)=>{
|
|
12
|
+
content += `export * from './${singleton.name}.js';\n`;
|
|
13
|
+
});
|
|
14
|
+
// Export player resources
|
|
15
|
+
schema.playerResources.forEach((playerResource)=>{
|
|
16
|
+
content += `export * from './${playerResource.name}.js';\n`;
|
|
17
|
+
});
|
|
18
|
+
// Export filters
|
|
19
|
+
schema.filters.forEach((filter)=>{
|
|
20
|
+
content += `export * from './${filter.name}.js';\n`;
|
|
21
|
+
});
|
|
22
|
+
// Export inputs
|
|
23
|
+
schema.inputs.forEach((input)=>{
|
|
24
|
+
content += `export * from './${input.name}.js';\n`;
|
|
25
|
+
});
|
|
26
|
+
// Export input registry
|
|
27
|
+
content += `export * from './${projectName}InputRegistry.js';\n`;
|
|
28
|
+
// Export core
|
|
29
|
+
content += `export * from './${projectName}.core.js';\n`;
|
|
30
|
+
// Export runner
|
|
31
|
+
content += `export * from './${projectName}.runner.js';\n`;
|
|
32
|
+
return content;
|
|
33
|
+
}
|
|
34
|
+
export async function generateCode(options) {
|
|
35
|
+
const { schema, projectName, outputDir, templateDir, fileOperations } = options;
|
|
36
|
+
const { writeFile, joinPath, exists } = fileOperations;
|
|
37
|
+
// Ensure output directory exists
|
|
38
|
+
if (!exists(outputDir)) {
|
|
39
|
+
writeFile(joinPath(outputDir, '.gitkeep'), '');
|
|
40
|
+
}
|
|
41
|
+
// Generate component classes
|
|
42
|
+
for (const component of schema.components){
|
|
43
|
+
await processTemplate({
|
|
44
|
+
templateDir: joinPath(templateDir, 'component'),
|
|
45
|
+
outputDir,
|
|
46
|
+
data: _extends({}, component, {
|
|
47
|
+
component,
|
|
48
|
+
projectName,
|
|
49
|
+
typeToArrayConstructor,
|
|
50
|
+
getFieldByteSize: (field)=>{
|
|
51
|
+
const baseSize = getTypeSizeBytes(field.type);
|
|
52
|
+
return field.isArray ? baseSize * field.arrayLength : baseSize;
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
fileOperations
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// Generate singleton classes
|
|
59
|
+
for (const singleton of schema.singletons){
|
|
60
|
+
await processTemplate({
|
|
61
|
+
templateDir: joinPath(templateDir, 'singleton'),
|
|
62
|
+
outputDir,
|
|
63
|
+
data: _extends({}, singleton, {
|
|
64
|
+
singleton,
|
|
65
|
+
projectName,
|
|
66
|
+
typeToArrayConstructor
|
|
67
|
+
}),
|
|
68
|
+
fileOperations
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Generate playerResource classes
|
|
72
|
+
for (const playerResource of schema.playerResources){
|
|
73
|
+
await processTemplate({
|
|
74
|
+
templateDir: joinPath(templateDir, 'playerResource'),
|
|
75
|
+
outputDir,
|
|
76
|
+
data: _extends({}, playerResource, {
|
|
77
|
+
playerResource,
|
|
78
|
+
projectName,
|
|
79
|
+
typeToArrayConstructor
|
|
80
|
+
}),
|
|
81
|
+
fileOperations
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Generate Filter classes
|
|
85
|
+
for (const filter of schema.filters){
|
|
86
|
+
const componentsImports = [
|
|
87
|
+
...filter.include,
|
|
88
|
+
...filter.exclude
|
|
89
|
+
].map((c)=>c.name);
|
|
90
|
+
const includeMask = filter.include.reduce((acc, component)=>acc | component.id, 0);
|
|
91
|
+
const excludeMask = filter.exclude.reduce((acc, component)=>acc | component.id, 0);
|
|
92
|
+
await processTemplate({
|
|
93
|
+
templateDir: joinPath(templateDir, 'filter'),
|
|
94
|
+
outputDir,
|
|
95
|
+
data: {
|
|
96
|
+
filter,
|
|
97
|
+
includeMask,
|
|
98
|
+
excludeMask,
|
|
99
|
+
componentsImports,
|
|
100
|
+
name: filter.name,
|
|
101
|
+
projectName
|
|
102
|
+
},
|
|
103
|
+
fileOperations
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// Generate Input classes
|
|
107
|
+
for (const input of schema.inputs){
|
|
108
|
+
await processTemplate({
|
|
109
|
+
templateDir: joinPath(templateDir, 'input'),
|
|
110
|
+
outputDir,
|
|
111
|
+
data: _extends({}, input, {
|
|
112
|
+
input,
|
|
113
|
+
projectName,
|
|
114
|
+
FieldTypeReverse,
|
|
115
|
+
typedArrayConstructors
|
|
116
|
+
}),
|
|
117
|
+
fileOperations
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Generate input registry
|
|
121
|
+
await processTemplate({
|
|
122
|
+
templateDir: joinPath(templateDir, 'input-registry'),
|
|
123
|
+
outputDir,
|
|
124
|
+
data: {
|
|
125
|
+
projectName,
|
|
126
|
+
inputs: schema.inputs,
|
|
127
|
+
schema
|
|
128
|
+
},
|
|
129
|
+
fileOperations
|
|
130
|
+
});
|
|
131
|
+
// Generate ECSCore class
|
|
132
|
+
await processTemplate({
|
|
133
|
+
templateDir: joinPath(templateDir, 'core'),
|
|
134
|
+
outputDir,
|
|
135
|
+
data: {
|
|
136
|
+
projectName,
|
|
137
|
+
schema
|
|
138
|
+
},
|
|
139
|
+
fileOperations
|
|
140
|
+
});
|
|
141
|
+
// Generate Runner class
|
|
142
|
+
await processTemplate({
|
|
143
|
+
templateDir: joinPath(templateDir, 'runner'),
|
|
144
|
+
outputDir,
|
|
145
|
+
data: {
|
|
146
|
+
projectName,
|
|
147
|
+
schema
|
|
148
|
+
},
|
|
149
|
+
fileOperations
|
|
150
|
+
});
|
|
151
|
+
// Generate barrel file
|
|
152
|
+
const barrelContent = generateBarrelFileContent(schema, projectName);
|
|
153
|
+
writeFile(joinPath(outputDir, 'index.ts'), barrelContent);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=generator.js.map
|