@downdraft/cli 0.0.0 → 0.1.1
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 +11 -0
- package/package.json +36 -3
- package/scripts/default-icon.png +0 -0
- package/scripts/package-desktop.mjs +931 -0
- package/scripts/package-mobile.mjs +822 -0
- package/scripts/package-native.mjs +638 -0
- package/src/args.ts +333 -0
- package/src/assets.ts +523 -0
- package/src/blob-store-s3.ts +145 -0
- package/src/build-games.ts +45 -0
- package/src/build.ts +40 -0
- package/src/bun-env.d.ts +6 -0
- package/src/debug.ts +56 -0
- package/src/dev.ts +231 -0
- package/src/dist.ts +36 -0
- package/src/export.ts +38 -0
- package/src/index.ts +131 -0
- package/src/list-games.ts +37 -0
- package/src/mcp.ts +320 -0
- package/src/new.ts +63 -0
- package/src/paths.ts +101 -0
- package/src/plugin-command.ts +214 -0
- package/src/process-utils.ts +179 -0
- package/src/release.ts +432 -0
- package/src/scaffold-plugin.ts +280 -0
- package/src/scaffold.ts +207 -0
- package/src/test.ts +187 -0
- package/src/usage.ts +256 -0
- package/templates/_shared/ai-companion/devin-config.eta +11 -0
- package/templates/_shared/ai-companion/engine-prompt.eta +155 -0
- package/templates/_shared/ai-companion/logging-rule.eta +3 -0
- package/templates/_shared/ai-companion/loops-rule.eta +3 -0
- package/templates/_shared/partials/config-json-fields.eta +3 -0
- package/templates/_shared/partials/dev-deps.eta +8 -0
- package/templates/_shared/partials/package-json-fields.eta +12 -0
- package/templates/_shared/partials/tsconfig.eta +16 -0
- package/templates/_shared/partials/vscode-extensions.eta +11 -0
- package/templates/_shared/partials/vscode-settings.eta +29 -0
- package/templates/_shared/partials/vscode-tasks.eta +93 -0
- package/templates/full/.gitignore.eta +5 -0
- package/templates/full/.vscode/extensions.json.eta +1 -0
- package/templates/full/.vscode/settings.json.eta +1 -0
- package/templates/full/.vscode/tasks.json.eta +1 -0
- package/templates/full/README.md.eta +45 -0
- package/templates/full/assets/shaders/.eta +0 -0
- package/templates/full/downdraft.config.json.eta +10 -0
- package/templates/full/package.json.eta +9 -0
- package/templates/full/src/entities/.eta +0 -0
- package/templates/full/src/game-module.ts.eta +30 -0
- package/templates/full/src/native-entry.ts.eta +14 -0
- package/templates/full/src/sim-host.ts.eta +19 -0
- package/templates/full/src/sim-worker.ts.eta +99 -0
- package/templates/full/src/systems/.eta +0 -0
- package/templates/full/tsconfig.json.eta +1 -0
- package/templates/gamemodule/README.md.eta +32 -0
- package/templates/gamemodule/downdraft.config.json.eta +3 -0
- package/templates/gamemodule/package.json.eta +9 -0
- package/templates/gamemodule/src/game-module.ts.eta +63 -0
- package/templates/gamemodule/src/native-entry.ts.eta +14 -0
- package/templates/gamemodule/src/sim-host.ts.eta +25 -0
- package/templates/gamemodule/src/sim-worker.ts.eta +24 -0
- package/templates/gamemodule/tsconfig.json.eta +1 -0
- package/templates/minimal/.gitignore.eta +5 -0
- package/templates/minimal/.vscode/extensions.json.eta +1 -0
- package/templates/minimal/.vscode/settings.json.eta +1 -0
- package/templates/minimal/.vscode/tasks.json.eta +1 -0
- package/templates/minimal/README.md.eta +32 -0
- package/templates/minimal/downdraft.config.json.eta +3 -0
- package/templates/minimal/package.json.eta +9 -0
- package/templates/minimal/src/game-module.ts.eta +44 -0
- package/templates/minimal/src/native-entry.ts.eta +14 -0
- package/templates/minimal/tsconfig.json.eta +1 -0
- package/templates/physics/.gitignore.eta +5 -0
- package/templates/physics/.vscode/extensions.json.eta +1 -0
- package/templates/physics/.vscode/settings.json.eta +1 -0
- package/templates/physics/.vscode/tasks.json.eta +1 -0
- package/templates/physics/README.md.eta +36 -0
- package/templates/physics/downdraft.config.json.eta +6 -0
- package/templates/physics/package.json.eta +9 -0
- package/templates/physics/src/game-module.ts.eta +29 -0
- package/templates/physics/src/native-entry.ts.eta +14 -0
- package/templates/physics/src/sim-host.ts.eta +19 -0
- package/templates/physics/src/sim-worker.ts.eta +155 -0
- package/templates/physics/tsconfig.json.eta +1 -0
package/src/usage.ts
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// usage.ts — per-command usage strings + schema registry
|
|
3
|
+
// ============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Single source of truth for every `draft` subcommand's synopsis, flags, and
|
|
6
|
+
// positionals. `index.ts` generates the top-level help from this registry;
|
|
7
|
+
// each command module imports its schema + usage string for `--help` output.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import type { CommandSchema } from "./args";
|
|
11
|
+
|
|
12
|
+
export interface CommandEntry {
|
|
13
|
+
/** Command name as typed on the CLI (e.g. `"new"`). */
|
|
14
|
+
name: string;
|
|
15
|
+
/** One-line synopsis shown after `Usage:`. */
|
|
16
|
+
usage: string;
|
|
17
|
+
/** Short description for the top-level command list. */
|
|
18
|
+
summary: string;
|
|
19
|
+
/** Argument schema (flags + positionals). */
|
|
20
|
+
schema: CommandSchema;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Desktop target vocabulary (normalized): win | linux | mac | all
|
|
25
|
+
// Mobile target vocabulary: android | ios | all
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
const DESKTOP_TARGETS = ["win", "linux", "mac", "all"] as const;
|
|
29
|
+
const BUILD_TARGETS = ["current", "win", "linux", "mac"] as const;
|
|
30
|
+
const RELEASE_TARGETS = ["win", "linux", "mac", "android", "all"] as const;
|
|
31
|
+
const RELEASE_STAGES = ["build", "package", "release"] as const;
|
|
32
|
+
const RENDERER_TARGETS = ["gpu", "cpu"] as const;
|
|
33
|
+
const BUILD_MODES = ["dev", "debug", "prod"] as const;
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Command registry
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
export const COMMANDS: CommandEntry[] = [
|
|
40
|
+
{
|
|
41
|
+
name: "release",
|
|
42
|
+
usage: "draft release [options]",
|
|
43
|
+
summary: "Native build + package pipeline — compiles the game to a standalone binary via scripts/package-native.mjs",
|
|
44
|
+
schema: {
|
|
45
|
+
flags: [
|
|
46
|
+
{ name: "game", alias: "g", type: "string", description: "Game to release (games/<game>). For multiple games, use --games. If omitted, infers from the current directory, or defaults to the only game in games/." },
|
|
47
|
+
{ name: "games", type: "string", description: "Comma-separated game names (e.g. sandjongg,to-the-ocean)" },
|
|
48
|
+
{ name: "target", alias: "t", type: "string", default: "all", enum: [...RELEASE_TARGETS], description: "Target platform(s): win, linux, mac, android, or all (android can't mix with desktop targets)" },
|
|
49
|
+
{ name: "format", type: "string", description: "Package format(s), comma-separated: dir, deb, appimage, flatpak (linux only; default: build.linux.target in package.json, else dir)" },
|
|
50
|
+
{ name: "runtime", type: "string", enum: ["bun", "node", "deno", "all"], description: "JS runtime embedded in the package: bun (compiled binary), node, deno, or all (default: build.runtime in package.json, else bun). node/deno are linux-only today" },
|
|
51
|
+
{ name: "stage", type: "string", default: "release", enum: [...RELEASE_STAGES], description: "Stage: build, package, or release (all compile the native binary)" },
|
|
52
|
+
{ name: "mode", type: "string", default: "prod", enum: [...BUILD_MODES], description: "Build mode" },
|
|
53
|
+
{ name: "abi", type: "string", default: "arm64-v8a", description: "Android ABI(s): arm64-v8a, x86_64, or all (android target only)" },
|
|
54
|
+
{ name: "min-sdk", type: "string", description: "Android minimum SDK (default 30; android target only)" },
|
|
55
|
+
{ name: "node-flavor", type: "string", description: "Embedded Node build flavor: full (default) or lite — lite drops intl/inspector/sqlite/amaro (android target only)" },
|
|
56
|
+
{ name: "libs", type: "string", description: "Force-include optional engine cdylibs, comma-separated (android target only)" },
|
|
57
|
+
{ name: "exclude-libs", type: "string", description: "Exclude optional engine cdylibs, comma-separated (android target only)" },
|
|
58
|
+
{ name: "no-strip", type: "boolean", description: "Keep debug info/symtab in packaged .so's for native debugging (android target only)" },
|
|
59
|
+
{ name: "out", type: "string", default: "release", description: "Artifact output directory" },
|
|
60
|
+
{ name: "skip-build", type: "boolean", description: "Alias for --stage=package" },
|
|
61
|
+
{ name: "build-only", type: "boolean", description: "Alias for --stage=build" },
|
|
62
|
+
{ name: "mcp", type: "boolean", description: "Retain the MCP automation endpoint in the packaged binary" },
|
|
63
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "new",
|
|
69
|
+
usage: "draft new [path] [options]",
|
|
70
|
+
summary: "Scaffold a new game project",
|
|
71
|
+
schema: {
|
|
72
|
+
positionals: [{ name: "path", description: "Target directory (default: current dir)" }],
|
|
73
|
+
flags: [
|
|
74
|
+
{ name: "template", type: "string", default: "minimal", description: "Project template (minimal | physics | full | gamemodule)" },
|
|
75
|
+
{ name: "name", type: "string", description: "Project name (defaults to directory basename)" },
|
|
76
|
+
{ name: "description", type: "string", description: "Project description" },
|
|
77
|
+
{ name: "author", type: "string", description: "Author name (written as { name } in package.json)" },
|
|
78
|
+
{ name: "version", type: "string", default: "0.1.0", description: "Initial version" },
|
|
79
|
+
{ name: "ai-companion", type: "boolean", description: "Scaffold .devin/ config + engine-prompt.md" },
|
|
80
|
+
{ name: "force", type: "boolean", description: "Scaffold into a non-empty directory" },
|
|
81
|
+
{ name: "list-templates", type: "boolean", description: "List available templates and exit" },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "dev",
|
|
87
|
+
usage: "draft dev [options]",
|
|
88
|
+
summary: "Start the game on the native runtime (winit + wgpu; Bun, Node, or Deno host — see --runtime). Run from a game directory (cwd inference).",
|
|
89
|
+
schema: {
|
|
90
|
+
flags: [
|
|
91
|
+
{ name: "entry", type: "string", description: "Game entrypoint file (defaults to src/native-entry.ts)" },
|
|
92
|
+
{ name: "port", type: "number", description: "MCP HTTP port (default: auto-assign)" },
|
|
93
|
+
{ name: "runtime", type: "string", enum: ["bun", "node", "deno"], description: "JS runtime hosting the dev shell (default: bun → node → deno auto-detect)" },
|
|
94
|
+
{ name: "watch", type: "boolean", description: "Back-compat no-op — the HMR dev shell always watches" },
|
|
95
|
+
{ name: "no-hmr", type: "boolean", description: "Disable HMR — spawn the entry directly (bun run), no dev shell" },
|
|
96
|
+
{ name: "native", type: "boolean", description: "Back-compat alias — native is the default runtime" },
|
|
97
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "debug",
|
|
103
|
+
usage: "draft debug [path] [options]",
|
|
104
|
+
summary: "Run the engine in debug mode with profiling/visualization",
|
|
105
|
+
schema: {
|
|
106
|
+
positionals: [{ name: "path", description: "Project path (default: current dir)" }],
|
|
107
|
+
flags: [
|
|
108
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
109
|
+
{ name: "no-devtools", type: "boolean", description: "Disable the devtools overlay (sets DOWNDRAFT_DISABLE_DEVTOOLS=1)" },
|
|
110
|
+
{ name: "inspector", type: "boolean", description: "Enable the Node inspector (chrome://inspect)" },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "build",
|
|
116
|
+
usage: "draft build [path] [options]",
|
|
117
|
+
summary: "Build a game for a target platform",
|
|
118
|
+
schema: {
|
|
119
|
+
positionals: [{ name: "path", description: "Project path (default: current dir)" }],
|
|
120
|
+
flags: [
|
|
121
|
+
{ name: "game", alias: "g", type: "string", description: "Game to build (resolves games/<game>; overrides path)" },
|
|
122
|
+
{ name: "target", type: "string", default: "current", enum: [...BUILD_TARGETS], description: "Target platform" },
|
|
123
|
+
{ name: "mode", type: "string", default: "prod", enum: [...BUILD_MODES], description: "Build mode" },
|
|
124
|
+
{ name: "out", type: "string", default: "dist", description: "Output directory" },
|
|
125
|
+
{ name: "no-minify", type: "boolean", description: "Disable minification" },
|
|
126
|
+
{ name: "sourcemap", type: "boolean", description: "Generate source maps (on by default in non-prod modes)" },
|
|
127
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "build-games",
|
|
133
|
+
usage: "draft build-games --games=<csv> --platforms=<csv>",
|
|
134
|
+
summary: "Deprecated alias for `draft release --games=<csv>` (VSCode task)",
|
|
135
|
+
schema: {
|
|
136
|
+
flags: [
|
|
137
|
+
{ name: "games", type: "string", required: true, description: "Comma-separated game directory names (e.g. sandjongg)" },
|
|
138
|
+
{ name: "platforms", type: "string", required: true, description: "Comma-separated desktop targets (win | linux | mac; legacy :format suffixes are ignored)" },
|
|
139
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: "dist",
|
|
145
|
+
usage: "draft dist [options]",
|
|
146
|
+
summary: "Package a game for distribution (deprecated alias for `draft release`)",
|
|
147
|
+
schema: {
|
|
148
|
+
flags: [
|
|
149
|
+
{ name: "game", alias: "g", type: "string", required: true, description: "Game to package (games/<game>)" },
|
|
150
|
+
{ name: "target", alias: "t", type: "string", default: "all", enum: [...DESKTOP_TARGETS], description: "Target platform" },
|
|
151
|
+
{ name: "config", alias: "c", type: "string", description: "Explicit path to a build.config.ts / config file" },
|
|
152
|
+
{ name: "project-dir", type: "string", description: "Override the project directory (default: repo root)" },
|
|
153
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "export",
|
|
159
|
+
usage: "draft export [path] [options]",
|
|
160
|
+
summary: "Package a built game for distribution (per-platform launchers)",
|
|
161
|
+
schema: {
|
|
162
|
+
positionals: [{ name: "path", description: "Project path (default: current dir)" }],
|
|
163
|
+
flags: [
|
|
164
|
+
{ name: "target", type: "string", default: "all", enum: [...DESKTOP_TARGETS], description: "Target platform" },
|
|
165
|
+
{ name: "out", type: "string", default: "export", description: "Output directory" },
|
|
166
|
+
{ name: "no-compress", type: "boolean", description: "Disable compression" },
|
|
167
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "assets",
|
|
173
|
+
usage: "draft assets <command> [project] [options]",
|
|
174
|
+
summary: "Manage remote asset packs (pull, push, list, init, add, add-store)",
|
|
175
|
+
schema: {
|
|
176
|
+
positionals: [
|
|
177
|
+
{ name: "command", required: true, description: "init | add-store | add | pull | push | list" },
|
|
178
|
+
{ name: "project", description: "Project path (default: current dir)" },
|
|
179
|
+
],
|
|
180
|
+
flags: [
|
|
181
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "mcp",
|
|
187
|
+
usage: "draft mcp <action> [args] [options]",
|
|
188
|
+
summary: "Call MCP automation tools on a running game (instances | tools | call | screenshot | stdio | run)",
|
|
189
|
+
schema: {
|
|
190
|
+
positionals: [
|
|
191
|
+
{ name: "action", required: true, description: "instances | tools | call | screenshot | stdio | run" },
|
|
192
|
+
{ name: "args", variadic: true, description: "Action args: <tool> [json] | <file.png> | <script> [script-args]" },
|
|
193
|
+
],
|
|
194
|
+
flags: [
|
|
195
|
+
{ name: "app", type: "string", description: "Select instance by appId" },
|
|
196
|
+
{ name: "pid", type: "number", description: "Select instance by PID" },
|
|
197
|
+
{ name: "url", type: "string", description: "Explicit MCP HTTP URL (skips PID-file discovery)" },
|
|
198
|
+
{ name: "token", type: "string", description: "Bearer token (auto-read from <pid>.token otherwise)" },
|
|
199
|
+
{ name: "json", type: "boolean", description: "Print the raw result envelope (image data elided to {bytes} unless --out)" },
|
|
200
|
+
{ name: "out", type: "string", description: "Write image/binary result blocks to file(s)" },
|
|
201
|
+
{ name: "max-bytes", type: "number", description: "Cap printed output in bytes (default 262144)" },
|
|
202
|
+
{ name: "schema", type: "string", description: "tools action: print one tool's inputSchema JSON" },
|
|
203
|
+
{ name: "game", alias: "g", type: "string", description: "run action: game name (games/<name>)" },
|
|
204
|
+
{ name: "entry", type: "string", description: "run action: explicit native entry file" },
|
|
205
|
+
{ name: "headed", type: "boolean", description: "run action: show the game window" },
|
|
206
|
+
{ name: "gpu", type: "string", enum: ["auto", "hardware", "swiftshader"], description: "run action: WebGPU backend (default: swiftshader)" },
|
|
207
|
+
{ name: "deterministic", type: "boolean", description: "run action: DOWNDRAFT_DETERMINISTIC=1 (fixed seed, paused render loop)" },
|
|
208
|
+
{ name: "port", alias: "p", type: "number", description: "run action: pin the MCP port (default: auto)" },
|
|
209
|
+
{ name: "timeout", type: "number", description: "Per-request timeout ms; readiness budget for run" },
|
|
210
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: "test",
|
|
216
|
+
usage: "draft test [options]",
|
|
217
|
+
summary: "Run e2e tests via bun:test (SwiftShader + deterministic by default; default specs use the in-game MCP RPC harness)",
|
|
218
|
+
schema: {
|
|
219
|
+
flags: [
|
|
220
|
+
{ name: "game", alias: "g", type: "string", required: true, description: "Game to test (games/<game>)" },
|
|
221
|
+
{ name: "spec", alias: "s", type: "string", description: "Spec file to run (default: tests/e2e/<game>-smoke.spec.ts)" },
|
|
222
|
+
{ name: "port", alias: "p", type: "number", default: 0, description: "MCP port (0 = auto-assign a free port)" },
|
|
223
|
+
{ name: "renderer", alias: "r", type: "string", default: "cpu", enum: [...RENDERER_TARGETS], description: "WebGPU backend: cpu=SwiftShader, gpu=hardware" },
|
|
224
|
+
{ name: "no-deterministic", type: "boolean", description: "Disable fixed seed / render loop pause" },
|
|
225
|
+
{ name: "headed", type: "boolean", description: "Show the window instead of running headless" },
|
|
226
|
+
{ name: "verbose", alias: "v", type: "boolean", description: "Verbose logging" },
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
/** Look up a command entry by name. */
|
|
233
|
+
export function getCommand(name: string): CommandEntry | undefined {
|
|
234
|
+
return COMMANDS.find((c) => c.name === name);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Render the top-level `draft` help string from the registry. */
|
|
238
|
+
export function renderTopLevelHelp(version: string): string {
|
|
239
|
+
const lines: string[] = [];
|
|
240
|
+
lines.push(`DownDraft Engine CLI v${version}`);
|
|
241
|
+
lines.push("");
|
|
242
|
+
lines.push("Usage: draft <command> [options]");
|
|
243
|
+
lines.push("");
|
|
244
|
+
lines.push("Commands:");
|
|
245
|
+
const maxName = Math.max(...COMMANDS.map((c) => c.name.length));
|
|
246
|
+
COMMANDS.forEach((c) => {
|
|
247
|
+
lines.push(` ${c.name.padEnd(maxName + 2)} ${c.summary}`);
|
|
248
|
+
});
|
|
249
|
+
lines.push("");
|
|
250
|
+
lines.push("Global options:");
|
|
251
|
+
lines.push(" -h, --help Show help for a command (draft <cmd> --help)");
|
|
252
|
+
lines.push(" -V, --version Print the CLI version and exit");
|
|
253
|
+
lines.push("");
|
|
254
|
+
lines.push("Run `draft help <command>` for command-specific details.");
|
|
255
|
+
return lines.join("\n");
|
|
256
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# DownDraft Engine — API Reference for AI Companions
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
DownDraft is a TypeScript-first game engine built on winit + wgpu (a single native process, hosted under Bun, Node, or Deno — Bun is the default) + WebGPU, with an MCP server enabling AI agents to design, build, debug, and manage games via natural language.
|
|
6
|
+
|
|
7
|
+
**Execution contexts:**
|
|
8
|
+
1. **Native host (main thread)** — Game module, renderer, render loop, engine orchestrator, MCP server, asset manager
|
|
9
|
+
2. **Sim worker** — ECS world, game systems, physics, AI, modules (SharedArrayBuffer channels for frame sync)
|
|
10
|
+
3. **DB worker** — Persistence layer with schema versioning
|
|
11
|
+
|
|
12
|
+
## ECS (Entity Component System)
|
|
13
|
+
|
|
14
|
+
- **Entity**: `{ index: u32, generation: u32 }` — generational indices prevent stale-handle corruption
|
|
15
|
+
- **Component**: Plain data class implementing `Component` interface, registered with `ComponentId`
|
|
16
|
+
- **Archetype**: Set of component types, SoA column storage for cache-friendly iteration
|
|
17
|
+
- **Query**: Declares read/write access to component types, returns matching archetypes
|
|
18
|
+
- **System**: Function `(query, resources, commands) => void`
|
|
19
|
+
- **Schedule**: Single-threaded by default, dependency-ordered. Multi-threading via module/SAB.
|
|
20
|
+
- **Events**: Double-buffered channels (written this tick, readable next tick)
|
|
21
|
+
- **Commands**: Deferred operations (spawn/despawn, add/remove component) applied at stage boundaries
|
|
22
|
+
|
|
23
|
+
### Hierarchy
|
|
24
|
+
- Parent/child tree stored in side table (not archetype columns) — avoids archetype moves on reparenting
|
|
25
|
+
- Dirty-flag propagation: parent Transform change marks children dirty (O(depth))
|
|
26
|
+
- Lazy world matrix: computed on read when dirty
|
|
27
|
+
|
|
28
|
+
### Change Detection
|
|
29
|
+
- Per-component `lastChanged` tick counter — O(1) check for consumers
|
|
30
|
+
- Cross-thread: `Atomics` on SAB headers for frame sync
|
|
31
|
+
|
|
32
|
+
## Core API
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { World, Camera, MeshBuilder, Component, system, createLogger } from "@downdraft/engine";
|
|
36
|
+
|
|
37
|
+
// Define components
|
|
38
|
+
const Health = Component.register<{ current: number; max: number }>("Health", { current: 100, max: 100 });
|
|
39
|
+
|
|
40
|
+
// Define a system
|
|
41
|
+
const damageSystem = system(
|
|
42
|
+
(w: World, dt: number) => {
|
|
43
|
+
const view = w.view([Health]);
|
|
44
|
+
for (const ent of view) {
|
|
45
|
+
const health = w.getComponent(ent, Health);
|
|
46
|
+
if (health && health.data.current <= 0) {
|
|
47
|
+
w.despawn(ent);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Setup
|
|
54
|
+
const world = new World();
|
|
55
|
+
const camera = new Camera();
|
|
56
|
+
camera.setAspect(16, 9);
|
|
57
|
+
world.setResource("camera", camera);
|
|
58
|
+
|
|
59
|
+
// Spawn entities
|
|
60
|
+
const entity = world.spawn();
|
|
61
|
+
world.addComponent(entity, Health.create({ current: 50, max: 100 }));
|
|
62
|
+
|
|
63
|
+
// Procedural meshes
|
|
64
|
+
const cube = MeshBuilder.cube(1);
|
|
65
|
+
const sphere = MeshBuilder.sphere(0.5, 16, 16);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Module System
|
|
69
|
+
|
|
70
|
+
Modules are loadable packages that register systems, components, resources, and assets.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Module } from "@downdraft/engine";
|
|
74
|
+
|
|
75
|
+
const myModule: Module = {
|
|
76
|
+
name: "my-module",
|
|
77
|
+
register(ctx: ModuleContext) {
|
|
78
|
+
// Register components, systems, resources
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
world.registerModule(myModule);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Engine Libraries (`@downdraft/engine/libraries/*`)
|
|
86
|
+
- `@downdraft/engine/libraries/water` — Gerstner waves, refraction, reflection
|
|
87
|
+
- `@downdraft/engine/libraries/physics-rapier` — Rapier physics backend
|
|
88
|
+
- `@downdraft/engine/libraries/physics-native` — Pure-TS physics backend
|
|
89
|
+
- `@downdraft/engine/libraries/marching-cubes` — Volumetric deformable terrain
|
|
90
|
+
- `@downdraft/engine/libraries/surface-nets` — Surface nets mesh extraction
|
|
91
|
+
- `@downdraft/engine/libraries/models` — GLTF/GLB/PLY/3DS model loading
|
|
92
|
+
- `@downdraft/engine/libraries/audio-kira` — Kira audio backend
|
|
93
|
+
- `@downdraft/engine/libraries/entities` — Generic model renderer
|
|
94
|
+
- `@downdraft/engine/libraries/lighting` — Lighting system
|
|
95
|
+
- `@downdraft/engine/libraries/weather` — Weather simulation
|
|
96
|
+
- `@downdraft/engine/libraries/weatherfx` — Cloud particle effects
|
|
97
|
+
- `@downdraft/engine/libraries/navmesh` — Navigation meshes
|
|
98
|
+
- `@downdraft/engine/libraries/persistence` — Save system (OPFS, File, IndexedDB)
|
|
99
|
+
- `@downdraft/engine/libraries/networking` — Multiplayer networking
|
|
100
|
+
- `@downdraft/engine/libraries/gaussian-splats` — Gaussian splat rendering
|
|
101
|
+
- `@downdraft/engine/libraries/sand` — Falling-sand simulation
|
|
102
|
+
- `@downdraft/engine/libraries/stickman` — Stickman skeleton rendering
|
|
103
|
+
|
|
104
|
+
### Engine Modules (`@downdraft/engine/modules/*`)
|
|
105
|
+
- `@downdraft/engine/modules/devtools` — DevTools panel, scene inspector, GPU debugging
|
|
106
|
+
- `@downdraft/engine/modules/camera-controls` — Camera input handling
|
|
107
|
+
- `@downdraft/engine/modules/terrain` — Terrain composition (marching-cubes + LOD)
|
|
108
|
+
- `@downdraft/engine/modules/movement-3d` — 3D player movement
|
|
109
|
+
- `@downdraft/engine/modules/movement-2d` — 2D grid-based movement
|
|
110
|
+
- `@downdraft/engine/modules/sailing` — Sailing mechanics (wind, buoyancy, steering)
|
|
111
|
+
- `@downdraft/engine/modules/mcp` — MCP automation server
|
|
112
|
+
- `@downdraft/engine/modules/xr` — WebXR support
|
|
113
|
+
|
|
114
|
+
### Game Modules (to-the-ocean example: `@to-the-ocean/module-*`)
|
|
115
|
+
- `@to-the-ocean/module-crafting` — Crafting recipes and system
|
|
116
|
+
- `@to-the-ocean/module-inventory` — Inventory system
|
|
117
|
+
- `@to-the-ocean/module-wildlife` — Wildlife AI (fish, sharks, eels, etc.)
|
|
118
|
+
- `@to-the-ocean/module-buoyancy` — Water buoyancy physics
|
|
119
|
+
- `@to-the-ocean/module-collision` — Entity collision detection
|
|
120
|
+
|
|
121
|
+
### Game Libraries (to-the-ocean example: `@to-the-ocean/library-*`)
|
|
122
|
+
- `@to-the-ocean/library-items` — Item definitions and management
|
|
123
|
+
- `@to-the-ocean/library-fishing` — Fishing mechanics
|
|
124
|
+
- `@to-the-ocean/library-economy` — Market/trading system
|
|
125
|
+
- `@to-the-ocean/library-survival` — Survival stats (hunger, thirst, oxygen)
|
|
126
|
+
- `@to-the-ocean/library-boats` — Boat building and piloting
|
|
127
|
+
|
|
128
|
+
## Builder Modes
|
|
129
|
+
|
|
130
|
+
| Mode | Use case |
|
|
131
|
+
|---|---|
|
|
132
|
+
| **dev** | Full DevTools, hot-reload, telemetry. Fastest iteration. |
|
|
133
|
+
| **debug** | Local verification on the production native runtime. |
|
|
134
|
+
| **prod** | Optimized production build. No devtools/debug overhead. |
|
|
135
|
+
|
|
136
|
+
## CLI Commands
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
draft new [path] [options] Scaffold a new game project
|
|
140
|
+
draft dev [options] Start dev server with HMR
|
|
141
|
+
draft debug [options] Run engine in debug mode
|
|
142
|
+
draft release [options] Unified build + package + sign pipeline
|
|
143
|
+
(--stage=build|package|release, --target=win|linux|mac|all)
|
|
144
|
+
draft assets <cmd> Manage remote asset packs
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## MCP Tools (for AI agents)
|
|
148
|
+
|
|
149
|
+
The MCP server exposes tools for scene management, entity operations, component manipulation, material/shader editing, mesh import, animation, lighting, camera, physics, audio, scripting, asset management, debugging, checkpoints, and build/export.
|
|
150
|
+
|
|
151
|
+
## Conventions
|
|
152
|
+
|
|
153
|
+
- **Logging**: No manual `[module]` prefixes — automatic source attribution
|
|
154
|
+
- **Loops**: Prefer indexed `for` loops over `for..of`
|
|
155
|
+
- **Package manager**: Bun (workspace protocol for internal deps)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"version": "<%= it.version %>",<% if (it.description) { %>
|
|
2
|
+
"description": "<%= it.description %>",<% } %><% if (it.author) { %>
|
|
3
|
+
"author": { "name": "<%= it.author %>" },<% } %>
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "draft dev",
|
|
7
|
+
"build": "draft release --stage=build",
|
|
8
|
+
"release": "draft release",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"lint": "oxlint",
|
|
11
|
+
"test": "npm test"
|
|
12
|
+
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
7
|
+
"types": ["@webgpu/types", "node", "vite/client"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"explorer.fileNesting.enabled": true,
|
|
3
|
+
"explorer.fileNesting.expand": false,
|
|
4
|
+
"explorer.fileNesting.patterns": {
|
|
5
|
+
"*.ts": "${capture}.js, ${capture}.spec.ts, ${capture}.*.spec.ts, ${capture}.test.ts",
|
|
6
|
+
"*.tsx": "${capture}.spec.ts, ${capture}.*.spec.ts, ${capture}.test.ts",
|
|
7
|
+
"*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts, ${capture}.wasm, ${capture}.wasm.map",
|
|
8
|
+
"tsconfig.json": "tsconfig.*.json",
|
|
9
|
+
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock"
|
|
10
|
+
},
|
|
11
|
+
"editor.codeActionsOnSave": {
|
|
12
|
+
"source.organizeImports": "explicit"
|
|
13
|
+
},
|
|
14
|
+
"files.associations": {
|
|
15
|
+
"*.wgsl": "wgsl"
|
|
16
|
+
},
|
|
17
|
+
"files.watcherExclude": {
|
|
18
|
+
"**/node_modules/**": true,
|
|
19
|
+
"**/dist/**": true,
|
|
20
|
+
"**/export/**": true,
|
|
21
|
+
"**/tmp/**": true
|
|
22
|
+
},
|
|
23
|
+
"search.exclude": {
|
|
24
|
+
"**/node_modules/**": true,
|
|
25
|
+
"**/dist/**": true,
|
|
26
|
+
"**/export/**": true,
|
|
27
|
+
"**/tmp/**": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"tasks": [
|
|
4
|
+
{
|
|
5
|
+
"label": "Dev",
|
|
6
|
+
"detail": "Start the game in dev mode with hot reload",
|
|
7
|
+
"type": "shell",
|
|
8
|
+
"command": "draft dev",
|
|
9
|
+
"options": {
|
|
10
|
+
"cwd": "${workspaceFolder}"
|
|
11
|
+
},
|
|
12
|
+
"isBackground": true,
|
|
13
|
+
"problemMatcher": [],
|
|
14
|
+
"group": {
|
|
15
|
+
"kind": "build",
|
|
16
|
+
"isDefault": true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"label": "Release",
|
|
21
|
+
"detail": "Build + package for a desktop target",
|
|
22
|
+
"type": "shell",
|
|
23
|
+
"command": "draft release --target=${input:releaseTarget} --mode=${input:buildMode}",
|
|
24
|
+
"options": {
|
|
25
|
+
"cwd": "${workspaceFolder}"
|
|
26
|
+
},
|
|
27
|
+
"presentation": {
|
|
28
|
+
"echo": true,
|
|
29
|
+
"reveal": "always",
|
|
30
|
+
"panel": "dedicated",
|
|
31
|
+
"clear": true,
|
|
32
|
+
"showReuseMessage": false
|
|
33
|
+
},
|
|
34
|
+
"problemMatcher": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"label": "Build Only",
|
|
38
|
+
"detail": "Bundle the game with Vite (no packaging)",
|
|
39
|
+
"type": "shell",
|
|
40
|
+
"command": "draft release --stage=build --mode=${input:buildMode}",
|
|
41
|
+
"options": {
|
|
42
|
+
"cwd": "${workspaceFolder}"
|
|
43
|
+
},
|
|
44
|
+
"problemMatcher": []
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"label": "Typecheck",
|
|
48
|
+
"detail": "Run tsc --noEmit",
|
|
49
|
+
"type": "shell",
|
|
50
|
+
"command": "npm run typecheck",
|
|
51
|
+
"options": {
|
|
52
|
+
"cwd": "${workspaceFolder}"
|
|
53
|
+
},
|
|
54
|
+
"problemMatcher": ["$tsc"]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"label": "Lint",
|
|
58
|
+
"detail": "Run oxlint",
|
|
59
|
+
"type": "shell",
|
|
60
|
+
"command": "npm run lint",
|
|
61
|
+
"options": {
|
|
62
|
+
"cwd": "${workspaceFolder}"
|
|
63
|
+
},
|
|
64
|
+
"problemMatcher": []
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"label": "Test",
|
|
68
|
+
"detail": "Run tests",
|
|
69
|
+
"type": "shell",
|
|
70
|
+
"command": "npm test",
|
|
71
|
+
"options": {
|
|
72
|
+
"cwd": "${workspaceFolder}"
|
|
73
|
+
},
|
|
74
|
+
"problemMatcher": []
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"inputs": [
|
|
78
|
+
{
|
|
79
|
+
"id": "releaseTarget",
|
|
80
|
+
"type": "pickString",
|
|
81
|
+
"options": ["all", "win", "linux", "mac"],
|
|
82
|
+
"default": "all",
|
|
83
|
+
"description": "Target platform(s)"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "buildMode",
|
|
87
|
+
"type": "pickString",
|
|
88
|
+
"options": ["prod", "dev", "debug"],
|
|
89
|
+
"default": "prod",
|
|
90
|
+
"description": "Build mode"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%~ include("_shared/partials/vscode-extensions", it) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%~ include("_shared/partials/vscode-settings", it) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%~ include("_shared/partials/vscode-tasks", it) %>
|