@dcl-regenesislabs/opendcl 0.2.1 → 0.2.2-26850672477.commit-99ffd91
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 +5 -3
- package/context/sdk7-cheat-sheet.md +4 -0
- package/dist/index.js +0 -12
- package/dist/index.js.map +1 -1
- package/extensions/dcl-init.ts +58 -6
- package/package.json +3 -3
- package/prompts/system.md +71 -41
- package/skills/add-3d-models/SKILL.md +120 -70
- package/skills/add-interactivity/SKILL.md +74 -2
- package/skills/advanced-input/SKILL.md +34 -1
- package/skills/advanced-rendering/SKILL.md +82 -9
- package/skills/animations-tweens/SKILL.md +203 -98
- package/skills/audio-analysis/SKILL.md +164 -0
- package/skills/audio-video/SKILL.md +184 -83
- package/skills/build-ui/SKILL.md +25 -2
- package/skills/camera-control/SKILL.md +78 -7
- package/skills/create-scene/SKILL.md +56 -13
- package/skills/deploy-scene/SKILL.md +12 -0
- package/skills/deploy-worlds/SKILL.md +35 -0
- package/skills/editor-gizmo/.gitignore +11 -0
- package/skills/editor-gizmo/SKILL.md +222 -0
- package/skills/editor-gizmo/src/__editor/camera.ts +277 -0
- package/skills/editor-gizmo/src/__editor/discovery.ts +210 -0
- package/skills/editor-gizmo/src/__editor/drag.ts +265 -0
- package/skills/editor-gizmo/src/__editor/gizmo.ts +496 -0
- package/skills/editor-gizmo/src/__editor/history.ts +72 -0
- package/skills/editor-gizmo/src/__editor/index.ts +138 -0
- package/skills/editor-gizmo/src/__editor/input.ts +55 -0
- package/skills/editor-gizmo/src/__editor/math-utils.ts +114 -0
- package/skills/editor-gizmo/src/__editor/persistence.ts +113 -0
- package/skills/editor-gizmo/src/__editor/selection.ts +157 -0
- package/skills/editor-gizmo/src/__editor/state.ts +117 -0
- package/skills/editor-gizmo/src/__editor/ui.tsx +699 -0
- package/skills/game-design/SKILL.md +1 -2
- package/skills/lighting-environment/SKILL.md +103 -56
- package/skills/multiplayer-sync/SKILL.md +31 -2
- package/skills/nft-blockchain/SKILL.md +45 -40
- package/skills/npcs/SKILL.md +180 -0
- package/skills/optimize-scene/SKILL.md +7 -2
- package/skills/particle-system/SKILL.md +222 -0
- package/skills/player-avatar/SKILL.md +133 -7
- package/skills/player-physics/SKILL.md +93 -0
- package/skills/scene-runtime/SKILL.md +9 -5
- package/skills/visual-feedback/SKILL.md +1 -0
- package/extensions/dcl-setup-ollama.ts +0 -312
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The result: **more creators building more scenes, faster.**
|
|
|
26
26
|
## Features
|
|
27
27
|
|
|
28
28
|
- **Branded header** — on startup, displays a block-character "Decentraland" ASCII art banner with version and working directory. Falls back to a compact text header on narrow terminals
|
|
29
|
-
- **Multi-provider LLM support** — works with Claude, OpenAI, Google,
|
|
29
|
+
- **Multi-provider LLM support** — works with Claude, OpenAI, Google, OpenRouter, and more
|
|
30
30
|
- **Scene-aware** — automatically detects your project's `scene.json`, SDK version, and entry points
|
|
31
31
|
- **20 built-in skills** — scaffolding, 3D models, interactivity, UI, animations, multiplayer, authoritative server, audio/video, deployment (Genesis City & Worlds), optimization, camera control, lighting, player/avatar, NFT/blockchain, advanced rendering, advanced input, scene runtime, visual feedback, game design
|
|
32
32
|
- **Integrated commands** — `/init` to scaffold, `/preview` to launch the dev server, `/tasks` to manage running processes, `/review` to audit code
|
|
@@ -99,6 +99,7 @@ This uses the open [skills](https://github.com/vercel-labs/skills) CLI to copy S
|
|
|
99
99
|
| `/deploy` | Deploy the scene to Genesis City or a World (auto-detects from scene.json) |
|
|
100
100
|
| `/tasks` | Interactively manage running background processes (preview server, etc.) |
|
|
101
101
|
| `/review` | Review scene code for quality, performance, and SDK7 best practices |
|
|
102
|
+
| `/save-editor` | Apply pending visual editor changes to scene source code |
|
|
102
103
|
| `/explain <concept>` | Explain a Decentraland SDK7 concept (e.g., `/explain tweens`) |
|
|
103
104
|
|
|
104
105
|
The agent also has a `screenshot` tool it can call automatically to see the running preview. On first use it asks for your permission to open a headless browser. The browser stays open for the entire session — no repeated logins.
|
|
@@ -129,6 +130,7 @@ OpenDCL loads domain-specific skills on demand based on what you're asking:
|
|
|
129
130
|
| `scene-runtime` | Async tasks, fetch, timers, realm info, restricted actions, testing |
|
|
130
131
|
| `visual-feedback` | Use the screenshot tool to see your scene, verify changes, iterate visually |
|
|
131
132
|
| `game-design` | Plan game architecture, scene limits, state management, MVP planning |
|
|
133
|
+
| `editor-gizmo` | Use a visual in-scene editor with translate/rotate gizmos |
|
|
132
134
|
|
|
133
135
|
## How It Works
|
|
134
136
|
|
|
@@ -150,7 +152,7 @@ opendcl/
|
|
|
150
152
|
│ └── scene-context.ts # Scene detection & context formatting
|
|
151
153
|
├── extensions/
|
|
152
154
|
│ ├── dcl-context.ts # Auto-detect scene, inject metadata
|
|
153
|
-
│ ├── dcl-init.ts # /init command
|
|
155
|
+
│ ├── dcl-init.ts # /init command + editor setup
|
|
154
156
|
│ ├── dcl-preview.ts # /preview command
|
|
155
157
|
│ ├── dcl-deploy.ts # /deploy command
|
|
156
158
|
│ ├── dcl-setup.ts # /setup command (cloud API provider config)
|
|
@@ -159,6 +161,7 @@ opendcl/
|
|
|
159
161
|
│ ├── dcl-validate.ts # Post-write TypeScript validation
|
|
160
162
|
│ ├── dcl-screenshot.ts # screenshot tool (headless Chrome, persistent browser)
|
|
161
163
|
│ ├── dcl-tasks.ts # /tasks command (process manager)
|
|
164
|
+
│ ├── dcl-editor-save.ts # /save-editor command (apply visual editor changes)
|
|
162
165
|
│ ├── process-registry.ts # Shared background process registry
|
|
163
166
|
│ └── permissions/ # Permission gate for dangerous operations
|
|
164
167
|
├── skills/ # 20 SKILL.md files (domain expertise)
|
|
@@ -231,7 +234,6 @@ OpenDCL supports any provider compatible with pi-coding-agent:
|
|
|
231
234
|
| Anthropic (Claude) | `ANTHROPIC_API_KEY` | Best quality |
|
|
232
235
|
| OpenAI | `OPENAI_API_KEY` | GPT-4o, o1, etc. |
|
|
233
236
|
| Google | `GOOGLE_API_KEY` | Gemini models |
|
|
234
|
-
| Ollama | — | Free, runs locally |
|
|
235
237
|
| OpenRouter | `OPENROUTER_API_KEY` | Access to many models |
|
|
236
238
|
|
|
237
239
|
Set the environment variable or enter the key on first run. Switch models anytime with `Ctrl+P`.
|
|
@@ -34,6 +34,10 @@ removeEntityWithChildren(engine, entity)
|
|
|
34
34
|
|
|
35
35
|
// Components — CRUD
|
|
36
36
|
Transform.create(entity, { position: Vector3.create(8, 1, 8) })
|
|
37
|
+
// ⚠️ NEVER pass undefined values in Transform fields — the SDK serializer crashes
|
|
38
|
+
// reading .x on undefined. If a field is optional, omit the key entirely:
|
|
39
|
+
// BAD: Transform.create(e, { position, rotation }) // rotation may be undefined
|
|
40
|
+
// GOOD: Transform.create(e, rotation ? { position, rotation } : { position })
|
|
37
41
|
const t = Transform.get(entity) // read-only, throws if missing
|
|
38
42
|
const t = Transform.getMutable(entity) // mutable reference
|
|
39
43
|
const t = Transform.getOrNull(entity) // read-only, returns null if missing
|
package/dist/index.js
CHANGED
|
@@ -67,10 +67,6 @@ const extensions = headless
|
|
|
67
67
|
"dcl-asset-path.ts",
|
|
68
68
|
"dcl-screenshot.ts",
|
|
69
69
|
];
|
|
70
|
-
// Conditionally load dcl-setup-ollama (hidden by default, enable with ENABLE_OLLAMA_SETUP=true)
|
|
71
|
-
if (!headless && process.env.ENABLE_OLLAMA_SETUP === "true") {
|
|
72
|
-
extensions.push("dcl-setup-ollama.ts");
|
|
73
|
-
}
|
|
74
70
|
for (const ext of extensions) {
|
|
75
71
|
args.push("-e", join(extDir, ext));
|
|
76
72
|
}
|
|
@@ -90,14 +86,6 @@ if (!isDev()) {
|
|
|
90
86
|
}
|
|
91
87
|
// InteractiveMode patches — only needed for terminal UI, skip in headless mode
|
|
92
88
|
if (!headless) {
|
|
93
|
-
// Suppress pi's generic "No models available" warning — our dcl-setup-ollama
|
|
94
|
-
// extension shows a more helpful message that mentions /setup-ollama.
|
|
95
|
-
const _showWarning = InteractiveMode.prototype.showWarning;
|
|
96
|
-
InteractiveMode.prototype.showWarning = function (msg) {
|
|
97
|
-
if (msg.startsWith("No models available"))
|
|
98
|
-
return;
|
|
99
|
-
_showWarning.call(this, msg);
|
|
100
|
-
};
|
|
101
89
|
// Suppress pi's "What's New" changelog notification on startup — it shows pi's
|
|
102
90
|
// own version/changes, which confuses OpenDCL users.
|
|
103
91
|
InteractiveMode.prototype.getChangelogForDisplay = function () {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAEzC,uFAAuF;AACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AACtD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,QAAQ,CAAC;AAC7C,CAAC;AAED,wEAAwE;AACxE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AACrD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;IAC9B,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3F,CAAC;AAED,yCAAyC;AACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,8EAA8E;AAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC7C,IAAI,QAAQ,EAAE,CAAC;IACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,wFAAwF;AACxF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,GAAG;SACrB,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC;SACpC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;SAC9E,IAAI,EAAE,CAAC;IACV,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;AAC7C,CAAC;AAED,kBAAkB;AAClB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/extensions/dcl-init.ts
CHANGED
|
@@ -3,24 +3,40 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Registers the `init` tool (LLM-callable) and `/init` command that scaffolds
|
|
5
5
|
* a new Decentraland scene project using `npx @dcl/sdk-commands init`.
|
|
6
|
+
* After scaffolding, triggers the editor-gizmo skill to set up the visual editor.
|
|
7
|
+
* On session start, prompts the user to enable the editor if a scene exists but lacks it.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
9
11
|
import { Type } from "@sinclair/typebox";
|
|
10
12
|
import { join } from "node:path";
|
|
11
13
|
import { readFile, writeFile } from "node:fs/promises";
|
|
12
|
-
import { fileExists } from "./scene-utils.js";
|
|
14
|
+
import { fileExists, findSceneRoot } from "./scene-utils.js";
|
|
15
|
+
|
|
16
|
+
function triggerEditorSkill(pi: { sendMessage(msg: unknown, opts?: unknown): void }) {
|
|
17
|
+
pi.sendMessage(
|
|
18
|
+
{
|
|
19
|
+
customType: "editor-setup",
|
|
20
|
+
content: "Scene initialized (dependencies not yet installed). Now set up the visual editor by following the editor-gizmo skill.",
|
|
21
|
+
display: true,
|
|
22
|
+
},
|
|
23
|
+
{ triggerTurn: true }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
13
26
|
|
|
14
27
|
async function initScene(
|
|
15
28
|
cwd: string,
|
|
16
|
-
pi: {
|
|
29
|
+
pi: {
|
|
30
|
+
exec(cmd: string, args: string[], opts?: unknown): Promise<{ code: number; stdout: string; stderr: string }>;
|
|
31
|
+
sendMessage(msg: unknown, opts?: unknown): void;
|
|
32
|
+
}
|
|
17
33
|
): Promise<{ message: string; isError?: boolean }> {
|
|
18
34
|
if (await fileExists(join(cwd, "scene.json"))) {
|
|
19
35
|
return { message: "A scene.json already exists in this directory. Aborting to prevent overwriting.", isError: true };
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
try {
|
|
23
|
-
const result = await pi.exec("npx", ["@dcl/sdk-commands", "init", "--yes"], {
|
|
39
|
+
const result = await pi.exec("npx", ["@dcl/sdk-commands", "init", "--yes", "--skip-install"], {
|
|
24
40
|
cwd,
|
|
25
41
|
timeout: 180000,
|
|
26
42
|
});
|
|
@@ -36,7 +52,23 @@ async function initScene(
|
|
|
36
52
|
} catch {
|
|
37
53
|
// Non-fatal: scene was still initialized successfully
|
|
38
54
|
}
|
|
39
|
-
|
|
55
|
+
|
|
56
|
+
// Append OpenDCL build artifacts to .gitignore so they don't get committed.
|
|
57
|
+
// main.crdt is generated from main-entities.ts at bundle time.
|
|
58
|
+
try {
|
|
59
|
+
const ignorePath = join(cwd, ".gitignore");
|
|
60
|
+
let existing = "";
|
|
61
|
+
try { existing = await readFile(ignorePath, "utf-8"); } catch {}
|
|
62
|
+
if (!existing.split(/\r?\n/).includes("main.crdt")) {
|
|
63
|
+
const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
64
|
+
await writeFile(ignorePath, existing + sep + "main.crdt\n");
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
// Non-fatal: scene still works without .gitignore updates
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
triggerEditorSkill(pi);
|
|
71
|
+
return { message: "Scene initialized! Setting up visual editor..." };
|
|
40
72
|
} else {
|
|
41
73
|
return { message: `Init failed (exit code ${result.code}): ${result.stderr || result.stdout}`, isError: true };
|
|
42
74
|
}
|
|
@@ -50,7 +82,7 @@ const extension: ExtensionFactory = (pi) => {
|
|
|
50
82
|
name: "init",
|
|
51
83
|
label: "Init Scene",
|
|
52
84
|
description:
|
|
53
|
-
"Initialize a new Decentraland SDK7 scene. Scaffolds scene.json, package.json, tsconfig.json,
|
|
85
|
+
"Initialize a new Decentraland SDK7 scene with visual editor. Scaffolds scene.json, package.json, tsconfig.json, src/index.ts, and the __editor/ directory. Use when user wants to create or start a new scene.",
|
|
54
86
|
parameters: Type.Object({}),
|
|
55
87
|
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
56
88
|
const result = await initScene(ctx.cwd, pi);
|
|
@@ -60,7 +92,7 @@ const extension: ExtensionFactory = (pi) => {
|
|
|
60
92
|
});
|
|
61
93
|
|
|
62
94
|
pi.registerCommand("init", {
|
|
63
|
-
description: "Initialize a new Decentraland scene project
|
|
95
|
+
description: "Initialize a new Decentraland scene project with visual editor",
|
|
64
96
|
handler: async (_args, ctx) => {
|
|
65
97
|
ctx.ui.notify("Initializing new Decentraland scene...", "info");
|
|
66
98
|
const result = await initScene(ctx.cwd, pi);
|
|
@@ -68,6 +100,26 @@ const extension: ExtensionFactory = (pi) => {
|
|
|
68
100
|
if (!result.isError) await ctx.reload();
|
|
69
101
|
},
|
|
70
102
|
});
|
|
103
|
+
|
|
104
|
+
// Prompt on session start if scene exists but editor is not installed
|
|
105
|
+
let editorPromptShown = false;
|
|
106
|
+
pi.on("before_agent_start", async (_event, ctx) => {
|
|
107
|
+
if (editorPromptShown) return;
|
|
108
|
+
editorPromptShown = true;
|
|
109
|
+
|
|
110
|
+
const sceneRoot = await findSceneRoot(ctx.cwd);
|
|
111
|
+
if (!sceneRoot) return;
|
|
112
|
+
|
|
113
|
+
if (await fileExists(join(sceneRoot, "src", "__editor", "state.ts"))) return;
|
|
114
|
+
|
|
115
|
+
const enable = await ctx.ui.confirm(
|
|
116
|
+
"Visual Editor",
|
|
117
|
+
"Enable the visual editor for this scene?"
|
|
118
|
+
);
|
|
119
|
+
if (!enable) return;
|
|
120
|
+
|
|
121
|
+
triggerEditorSkill(pi);
|
|
122
|
+
});
|
|
71
123
|
};
|
|
72
124
|
|
|
73
125
|
export default extension;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcl-regenesislabs/opendcl",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2-26850672477.commit-99ffd91",
|
|
4
4
|
"description": "AI coding assistant for Decentraland SDK7 scene development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">=22.0.0"
|
|
67
67
|
},
|
|
68
68
|
"files": [
|
|
69
69
|
"dist/",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"prompts/",
|
|
73
73
|
"context/"
|
|
74
74
|
],
|
|
75
|
-
"commit": "
|
|
75
|
+
"commit": "99ffd91e623721dc4921ceae1e45d342f4211044"
|
|
76
76
|
}
|
package/prompts/system.md
CHANGED
|
@@ -25,26 +25,58 @@ You are **OpenDCL**, an AI coding assistant specialized in Decentraland SDK7 sce
|
|
|
25
25
|
- 1 parcel: ~512 entities, ~10,000 triangles. Scales with parcel count.
|
|
26
26
|
- All coordinates are in meters. Y is up. Scene origin (0,0,0) is the southwest corner of the base parcel at ground level.
|
|
27
27
|
|
|
28
|
-
###
|
|
28
|
+
### Authoring Model: Data in `main-entities.ts`, Behavior in `src/`
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
OpenDCL scenes split the source of truth in two:
|
|
31
|
+
|
|
32
|
+
- **`main-entities.ts`** at the scene root — typed declarative entities keyed by Name, with their data components (Transform, GltfContainer, MeshRenderer, Material, AudioSource, etc.). Compiled to `main.crdt` at build time and preloaded by the engine before `main()` runs.
|
|
33
|
+
- **`src/index.ts`** — behavior only. References entities by Name and attaches systems, pointer events, tweens.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
**Adding a declared entity (in `main-entities.ts`):**
|
|
36
|
+
```typescript
|
|
37
|
+
import type { Scene } from '@dcl/sdk/scene-types'
|
|
38
|
+
|
|
39
|
+
export const scene = {
|
|
40
|
+
blue_cube: {
|
|
41
|
+
components: {
|
|
42
|
+
Transform: { position: { x: 8, y: 1, z: 8 }, rotation: { x: 0, y: 0, z: 0, w: 1 }, scale: { x: 1, y: 1, z: 1 } },
|
|
43
|
+
MeshRenderer: { mesh: { $case: 'box', box: { uvs: [] } } },
|
|
44
|
+
Material: { material: { $case: 'pbr', pbr: { albedoColor: { r: 1, g: 0, b: 0, a: 1 } } } },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
} satisfies Scene
|
|
39
48
|
```
|
|
40
49
|
|
|
41
|
-
**
|
|
50
|
+
**Referencing it in code:**
|
|
42
51
|
```typescript
|
|
43
|
-
import { pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
|
|
52
|
+
import { engine, pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'
|
|
53
|
+
import type { scene } from '../main-entities'
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
type EntityName = keyof typeof scene
|
|
56
|
+
|
|
57
|
+
export function main() {
|
|
58
|
+
const cube = engine.getEntityOrNullByName<EntityName>('blue_cube')
|
|
59
|
+
if (cube === null) return
|
|
60
|
+
|
|
61
|
+
pointerEventsSystem.onPointerDown(
|
|
62
|
+
{ entity: cube, opts: { button: InputAction.IA_POINTER, hoverText: 'Click me' } },
|
|
63
|
+
() => { /* handle click */ },
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Rules:**
|
|
69
|
+
- Every declarative entity goes in `main-entities.ts` with a unique Name. The `satisfies Scene` clause keeps literal keys typed for safe references.
|
|
70
|
+
- Parents are referenced by Name (`parent: 'barrel_1'`); the build resolves them.
|
|
71
|
+
- Pure-data components (Transform, GltfContainer, MeshRenderer, MeshCollider, Material, AudioSource, VideoPlayer, TextShape, Animator config, NftShape, Billboard, VisibilityComponent) all live in `main-entities.ts`.
|
|
72
|
+
- Behavior, callbacks, systems, conditional logic stay in `src/`.
|
|
73
|
+
- The `scene` literal must contain only JSON-compatible values — no function calls, no spreads, no comments inside the object.
|
|
74
|
+
|
|
75
|
+
**Dynamic entities** spawned at runtime (effects, projectiles, runtime markers) still use `engine.addEntity()` and **don't get Names** — they're invisible to the editor and not persisted:
|
|
76
|
+
```typescript
|
|
77
|
+
const explosion = engine.addEntity()
|
|
78
|
+
Transform.create(explosion, { position: Vector3.create(...) })
|
|
79
|
+
GltfContainer.create(explosion, { src: 'models/Explosion.glb' })
|
|
48
80
|
```
|
|
49
81
|
|
|
50
82
|
**Systems (per-frame logic):**
|
|
@@ -73,10 +105,11 @@ export function setupUi() {
|
|
|
73
105
|
```
|
|
74
106
|
scene-project/
|
|
75
107
|
├── scene.json # Scene metadata (parcels, title, main entry)
|
|
108
|
+
├── main-entities.ts # Declarative entities (data) — preloaded into the engine before main() runs
|
|
76
109
|
├── package.json # Dependencies (@dcl/sdk)
|
|
77
|
-
├── tsconfig.json # TypeScript config
|
|
110
|
+
├── tsconfig.json # TypeScript config (must include main-entities.ts)
|
|
78
111
|
└── src/
|
|
79
|
-
├── index.ts # Main entry point
|
|
112
|
+
├── index.ts # Main entry point — references entities by Name, attaches behavior
|
|
80
113
|
└── ui.tsx # UI components (optional)
|
|
81
114
|
```
|
|
82
115
|
|
|
@@ -94,19 +127,25 @@ scene-project/
|
|
|
94
127
|
## How to Help Users
|
|
95
128
|
|
|
96
129
|
### Empty Folder (No scene.json)
|
|
97
|
-
|
|
98
|
-
2. **Use the `init` tool first** — this uses the official SDK scaffolding to create scene.json, package.json, tsconfig.json, and src/index.ts with the correct, up-to-date configuration, and installs dependencies. Never create these files manually.
|
|
99
|
-
3. After init completes, customize `scene.json` (title, description, parcels) and add the first element to `src/index.ts`. Then offer next steps — don't build the entire scene at once.
|
|
130
|
+
**Do NOT ask the user what they want to build.** Instead, immediately run the `init` tool to scaffold the project — no questions, no menu of options, just init. This uses the official SDK scaffolding to create scene.json, package.json, tsconfig.json, and src/index.ts with the correct, up-to-date configuration, and installs dependencies. Never create these files manually. After init completes, ask the user what they'd like to do next. Offer small, concrete steps — don't propose building an entire scene at once.
|
|
100
131
|
|
|
101
132
|
### Existing Scene
|
|
102
|
-
1. Read scene.json and src/index.ts to understand the project.
|
|
133
|
+
1. Read scene.json, main-entities.ts (if it exists), and src/index.ts to understand the project.
|
|
103
134
|
2. Offer contextual help — adding features, fixing bugs, optimizing.
|
|
104
135
|
3. Always preserve existing code when making edits.
|
|
136
|
+
4. When adding a new declarative entity (cube, model, lamp, etc.), edit `main-entities.ts` and reference the new Name from `src/index.ts` if it needs behavior. Don't reach for `engine.addEntity()` in code as a default.
|
|
105
137
|
|
|
106
138
|
### Best Practices
|
|
139
|
+
|
|
140
|
+
**Always declare static / editable entities in `main-entities.ts`** — never via `engine.addEntity()` in `src/index.ts` for things the user might want to move, rotate, or see in the visual editor. Entities declared in `main-entities.ts` are preloaded by the engine before `main()` runs, are visible in the editor's hierarchy panel, and are draggable. Entities created at runtime via `engine.addEntity()` are invisible to the editor and lost when the user reloads.
|
|
141
|
+
|
|
142
|
+
Use `engine.addEntity()` only for genuinely dynamic things (effects spawned at runtime, projectiles, throwaway markers). Those entities should NOT have a `Name` component — Naming is the marker for "this entity belongs in main-entities.ts."
|
|
143
|
+
|
|
144
|
+
When the user asks to add a barrel, a tree, a prop, a model — that goes in `main-entities.ts` first, and code references it via `engine.getEntityOrNullByName<EntityName>(name)`. Don't fall back to the old `addEntity + Transform.create + GltfContainer.create` pattern in code unless the user explicitly asks for runtime spawning.
|
|
145
|
+
|
|
107
146
|
- Always position objects within the scene boundaries (based on parcels).
|
|
108
|
-
-
|
|
109
|
-
- For 3D models,
|
|
147
|
+
- For positions/rotations inside `main-entities.ts`, use plain object literals (`{ x: 8, y: 1, z: 8 }`), not `Vector3.create()` — the `scene` literal must stay JSON-compatible. In `src/index.ts` (behavior code), `Vector3.create()` and `Quaternion.fromEulerDegrees()` are fine.
|
|
148
|
+
- For 3D models, declare `GltfContainer: { src: 'models/myModel.glb' }` inside the entity's `components` block in `main-entities.ts`.
|
|
110
149
|
- `GltfContainer` only works with **local files** — never use external URLs for the `src` field. Always download models into the scene's `models/` directory first.
|
|
111
150
|
- Place `.glb` files in a `models/` directory, textures in `images/`.
|
|
112
151
|
- Don't start the preview server automatically after writing code. The user will type `/preview` when ready.
|
|
@@ -115,39 +154,30 @@ scene-project/
|
|
|
115
154
|
- Search with `grep -i "keyword" skills/add-3d-models/references/model-catalog.md`, fetch the preview thumbnail to confirm, then download with curl.
|
|
116
155
|
- Download matching models with `curl -o models/filename.glb "URL"` before referencing them in code.
|
|
117
156
|
|
|
118
|
-
### Visual
|
|
119
|
-
|
|
120
|
-
When the preview server is running, **proactively use the `screenshot` tool after making scene changes**. Don't wait for the user to check — verify your own work:
|
|
121
|
-
|
|
122
|
-
1. Write code or modify the scene.
|
|
123
|
-
2. Use `screenshot` (with a `wait` of ~2000ms for hot-reload) to see the result.
|
|
124
|
-
3. Describe what you see honestly — what's working, what's missing, what looks wrong.
|
|
125
|
-
4. If something is off, fix it and screenshot again.
|
|
157
|
+
### Visual Feedback
|
|
126
158
|
|
|
127
|
-
|
|
159
|
+
When the preview server is running, use the `screenshot` tool **after completing code changes** to verify the result. Do NOT use screenshots to explore or navigate the scene.
|
|
128
160
|
|
|
129
|
-
|
|
161
|
+
1. Make all code changes first.
|
|
162
|
+
2. Take **one** screenshot (with `wait: 2000` for hot-reload) to verify.
|
|
163
|
+
3. Describe what you see honestly — what works, what's wrong.
|
|
164
|
+
4. If something is off, fix the code and take **one more** screenshot to confirm.
|
|
130
165
|
|
|
131
|
-
|
|
166
|
+
Keep it to **1-2 screenshots per task**. Each screenshot consumes significant tokens. Do not wander around taking multiple screenshots to "explore" — that wastes the user's budget.
|
|
132
167
|
|
|
133
|
-
|
|
134
|
-
1. Make code changes.
|
|
135
|
-
2. Wait for hot reload (~2s), then take a screenshot.
|
|
136
|
-
3. Analyze whether the result matches the goal.
|
|
137
|
-
4. If not, make targeted fixes and screenshot again.
|
|
138
|
-
5. Repeat (up to 5 iterations) until done.
|
|
168
|
+
The screenshot tool supports actions before capture (move, look, click, key press), but use these sparingly and only when needed to verify a specific thing (e.g., moving to see an object you just placed behind the spawn point).
|
|
139
169
|
|
|
140
170
|
## Tools & Commands
|
|
141
171
|
|
|
142
172
|
You have these Decentraland-specific tools — **use them directly** when the user's request matches:
|
|
143
173
|
- `init` — Scaffold a new scene (**always use this first** in an empty folder)
|
|
144
174
|
- `preview` — Start the Bevy-web preview server
|
|
145
|
-
- `screenshot` — Capture a screenshot of the running preview
|
|
175
|
+
- `screenshot` — Capture a screenshot of the running preview to verify code changes. Limit to 1-2 per task.
|
|
146
176
|
- `deploy` — Deploy to Genesis City or a World (auto-detects from scene.json)
|
|
147
177
|
- `tasks` — List or stop running background processes
|
|
148
178
|
|
|
149
179
|
The user can also type these as `/init`, `/preview`, `/deploy`, `/tasks` slash commands directly.
|
|
150
|
-
Additional user-only commands: `/review`, `/explain`, `/setup
|
|
180
|
+
Additional user-only commands: `/review`, `/explain`, `/setup`
|
|
151
181
|
|
|
152
182
|
## Pacing
|
|
153
183
|
|