@dcl-regenesislabs/opendcl 0.1.4 → 0.1.5-23161709858.commit-828c176

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +4 -3
  2. package/context/sdk7-cheat-sheet.md +3 -3
  3. package/extensions/permissions/index.ts +1 -12
  4. package/package.json +2 -2
  5. package/skills/add-3d-models/SKILL.md +16 -1
  6. package/skills/add-interactivity/SKILL.md +146 -37
  7. package/skills/add-interactivity/references/input-reference.md +148 -0
  8. package/skills/advanced-input/SKILL.md +1 -1
  9. package/skills/advanced-rendering/SKILL.md +27 -11
  10. package/skills/animations-tweens/SKILL.md +54 -24
  11. package/skills/audio-video/SKILL.md +16 -1
  12. package/skills/audio-video/references/media-reference.md +184 -0
  13. package/skills/authoritative-server/SKILL.md +6 -4
  14. package/skills/authoritative-server/references/server-patterns.md +251 -0
  15. package/skills/build-ui/SKILL.md +27 -1
  16. package/skills/build-ui/references/ui-components.md +228 -0
  17. package/skills/camera-control/SKILL.md +3 -1
  18. package/skills/create-scene/SKILL.md +65 -1
  19. package/skills/deploy-scene/SKILL.md +26 -9
  20. package/skills/deploy-worlds/SKILL.md +13 -1
  21. package/skills/game-design/SKILL.md +230 -0
  22. package/skills/lighting-environment/SKILL.md +23 -15
  23. package/skills/multiplayer-sync/SKILL.md +198 -72
  24. package/skills/multiplayer-sync/references/networking-patterns.md +238 -0
  25. package/skills/nft-blockchain/SKILL.md +18 -31
  26. package/skills/optimize-scene/SKILL.md +55 -12
  27. package/skills/player-avatar/SKILL.md +18 -1
  28. package/skills/player-avatar/references/avatar-apis.md +152 -0
  29. package/skills/scene-runtime/SKILL.md +4 -2
  30. package/skills/scene-runtime/references/runtime-apis.md +206 -0
  31. package/skills/visual-feedback/SKILL.md +16 -1
package/README.md CHANGED
@@ -28,11 +28,11 @@ The result: **more creators building more scenes, faster.**
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
29
  - **Multi-provider LLM support** — works with Claude, OpenAI, Google, Ollama (free/local), OpenRouter, and more
30
30
  - **Scene-aware** — automatically detects your project's `scene.json`, SDK version, and entry points
31
- - **19 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
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
33
33
  - **TypeScript validation** — catches type errors immediately after writing code
34
34
  - **Free asset catalogs** — 2,700+ Creator Hub 3D models, 900+ CC0-licensed models, and 50 audio files the agent proactively suggests when building scenes
35
- - **Permission gate** — prompts for confirmation before destructive bash commands, writes to sensitive files, or any file access outside the working directory
35
+ - **Permission gate** — prompts for confirmation before destructive bash commands, writes to sensitive files, or writes/edits outside the working directory
36
36
  - **Compact tool output** — write shows path + size instead of file content, read shows a 5-line preview instead of 10
37
37
  - **Session persistence** — pick up where you left off across sessions
38
38
 
@@ -128,6 +128,7 @@ OpenDCL loads domain-specific skills on demand based on what you're asking:
128
128
  | `advanced-input` | Cursor state, movement restriction, WASD patterns |
129
129
  | `scene-runtime` | Async tasks, fetch, timers, realm info, restricted actions, testing |
130
130
  | `visual-feedback` | Use the screenshot tool to see your scene, verify changes, iterate visually |
131
+ | `game-design` | Plan game architecture, scene limits, state management, MVP planning |
131
132
 
132
133
  ## How It Works
133
134
 
@@ -160,7 +161,7 @@ opendcl/
160
161
  │ ├── dcl-tasks.ts # /tasks command (process manager)
161
162
  │ ├── process-registry.ts # Shared background process registry
162
163
  │ └── permissions/ # Permission gate for dangerous operations
163
- ├── skills/ # 19 SKILL.md files (domain expertise)
164
+ ├── skills/ # 20 SKILL.md files (domain expertise)
164
165
  ├── prompts/ # System prompt + command templates
165
166
  ├── context/ # SDK7 reference docs + asset catalog
166
167
  └── tests/ # Vitest test suites
@@ -18,8 +18,8 @@ import ReactEcs, { ReactEcsRenderer, UiEntity, Label, Button, Input, Dropdown }
18
18
  import { movePlayerTo, teleportTo, triggerEmote, changeRealm,
19
19
  openExternalUrl, openNftDialog, triggerSceneEmote,
20
20
  copyToClipboard } from '~system/RestrictedActions'
21
- import { getSceneInformation, getRealm, readFile } from '~system/Runtime'
22
- import { getWorldTime, getExplorerInformation } from '~system/EnvironmentApi'
21
+ import { getSceneInformation, getRealm, readFile, getWorldTime,
22
+ getExplorerInformation } from '~system/Runtime'
23
23
  import { signedFetch, getHeaders } from '~system/SignedFetch'
24
24
  import { getPlayer } from '@dcl/sdk/src/players'
25
25
  ```
@@ -136,7 +136,7 @@ ColliderLayer.CL_CUSTOM1 … CL_CUSTOM8 // user-defined layers
136
136
  "featureToggles": { "voiceChat": "enabled" },
137
137
  "worldConfiguration": {
138
138
  "name": "my-world.dcl.eth",
139
- "skyboxConfig": { "fixedHour": 14.0 }
139
+ "skyboxConfig": { "fixedTime": 36000 }
140
140
  }
141
141
  }
142
142
  ```
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
10
- import { classifyBashCommand, classifyFilePath, isOutsideCwd, OUTSIDE_CWD_REASON } from "./utils.js";
10
+ import { classifyBashCommand, classifyFilePath, OUTSIDE_CWD_REASON } from "./utils.js";
11
11
  import { resolve } from "node:path";
12
12
 
13
13
  type BlockResult = { block: true; reason: string };
@@ -91,17 +91,6 @@ const extension: ExtensionFactory = (pi) => {
91
91
  return promptOrBlock(ctx, reason, `Path: ${filePath}`, () => sessionAllow.add(reason));
92
92
  }
93
93
 
94
- if (toolName === "read" || toolName === "grep" || toolName === "find" || toolName === "ls") {
95
- const filePath = (event.input as { path?: string }).path ?? "";
96
- if (!filePath) return;
97
-
98
- const resolved = resolve(ctx.cwd, filePath);
99
- const reason = isOutsideCwd(filePath, ctx.cwd);
100
- if (!reason) return;
101
- if (isPathAllowed(resolved)) return;
102
-
103
- return promptOrBlock(ctx, reason, `Path: ${filePath}`, () => allowedPaths.add(resolved));
104
- }
105
94
  });
106
95
  };
107
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcl-regenesislabs/opendcl",
3
- "version": "0.1.4",
3
+ "version": "0.1.5-23161709858.commit-828c176",
4
4
  "description": "AI coding assistant for Decentraland SDK7 scene development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,5 +67,5 @@
67
67
  "prompts/",
68
68
  "context/"
69
69
  ],
70
- "commit": "4a4167760132e6b2be9adbf0486f4b061222db69"
70
+ "commit": "828c176b27aa3da4e6eeebd001e2434c1df161fe"
71
71
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: add-3d-models
3
- description: Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading models, positioning, scaling, colliders, and browsing the open-source 3D assets catalog for free CC0 models. Use when user wants to add models, import GLB files, or find free 3D assets.
3
+ description: Add 3D models (.glb/.gltf) to a Decentraland scene using GltfContainer. Covers loading, positioning, scaling, colliders, parenting, and browsing 2,700+ free assets from the Creator Hub catalog and 991 CC0 models. Use when the user wants to add models, import GLB files, find free 3D assets, or set up model colliders. Do NOT use for materials/textures (see advanced-rendering) or model animations (see animations-tweens).
4
4
  ---
5
5
 
6
6
  # Adding 3D Models to Decentraland Scenes
@@ -145,6 +145,21 @@ engine.addSystem(() => {
145
145
  })
146
146
  ```
147
147
 
148
+ ## Troubleshooting
149
+
150
+ | Problem | Cause | Solution |
151
+ |---------|-------|----------|
152
+ | Model not visible | Wrong file path | Verify the file exists at the exact path relative to project root (e.g., `models/myModel.glb`) |
153
+ | Model not visible | Position outside scene boundaries | Check Transform position is within 0-16 per parcel. Center of 1-parcel scene is (8, 0, 8) |
154
+ | Model not visible | Scale is 0 or very small | Check `Transform.scale` — default is (1,1,1). Try larger values if model was exported very small |
155
+ | Model not visible | Behind the camera | Move the avatar or rotate to look in the model's direction |
156
+ | Model loads but looks wrong | Y-up vs Z-up mismatch | Decentraland uses Y-up. Re-export from Blender with "Y Up" checked |
157
+ | "FINISHED_WITH_ERROR" load state | Corrupted or unsupported .glb | Re-export the model. Use `.glb` (binary GLTF) format. Ensure no unsupported extensions |
158
+ | Clicking model does nothing | Missing collider | Add `visibleMeshesCollisionMask: ColliderLayer.CL_POINTER` to `GltfContainer` or add `MeshCollider` |
159
+
160
+ > **Need to optimize models for scene limits?** See the **optimize-scene** skill for triangle budgets and LOD patterns.
161
+ > **Need animations from your model?** See the **animations-tweens** skill for playing GLTF animation clips with Animator.
162
+
148
163
  ## Model Best Practices
149
164
 
150
165
  - Keep models under 50MB per file for good loading times
@@ -1,10 +1,22 @@
1
1
  ---
2
2
  name: add-interactivity
3
- description: Add click handlers, hover effects, pointer events, triggers, and raycasting to Decentraland scene entities. Use when user wants to make objects clickable, add interactions, detect player proximity, or handle user input.
3
+ description: Add click handlers, hover effects, pointer events, trigger areas, raycasting, and global input to Decentraland scene entities. Use when the user wants to make objects clickable, add hover effects, detect player proximity, handle E/F key actions, or cast rays. Do NOT use for advanced input patterns like movement restriction, cursor lock, or WASD control (see advanced-input). Do NOT use for screen-space UI buttons (see build-ui).
4
4
  ---
5
5
 
6
6
  # Adding Interactivity to Decentraland Scenes
7
7
 
8
+ ## Decision Tree
9
+
10
+ | Need | Approach | API |
11
+ |------|----------|-----|
12
+ | Click/hover on a specific entity | Pointer events | `pointerEventsSystem.onPointerDown()` |
13
+ | Detect player entering an area | Trigger area | `TriggerArea` + `triggerAreaEventsSystem` |
14
+ | Poll key state every frame | Global input | `inputSystem.isTriggered()` / `isPressed()` |
15
+ | Detect objects in a direction | Raycasting | `raycastSystem` or `Raycast` component |
16
+ | Read cursor position / lock state | Cursor state | `PointerLock`, `PrimaryPointerInfo` |
17
+
18
+ ---
19
+
8
20
  ## Pointer Events (Click / Hover)
9
21
 
10
22
  ### Using the Helper System (Recommended)
@@ -32,15 +44,29 @@ pointerEventsSystem.onPointerDown(
32
44
  )
33
45
  ```
34
46
 
35
- ### Available Input Actions
47
+ ### All Input Actions
48
+ ```typescript
49
+ InputAction.IA_POINTER // Left mouse button
50
+ InputAction.IA_PRIMARY // E key
51
+ InputAction.IA_SECONDARY // F key
52
+ InputAction.IA_ACTION_3 // 1 key
53
+ InputAction.IA_ACTION_4 // 2 key
54
+ InputAction.IA_ACTION_5 // 3 key
55
+ InputAction.IA_ACTION_6 // 4 key
56
+ InputAction.IA_JUMP // Space key
57
+ InputAction.IA_FORWARD // W key
58
+ InputAction.IA_BACKWARD // S key
59
+ InputAction.IA_LEFT // A key
60
+ InputAction.IA_RIGHT // D key
61
+ InputAction.IA_WALK // Shift key
62
+ ```
63
+
64
+ ### All Event Types
36
65
  ```typescript
37
- InputAction.IA_POINTER // Left click / primary
38
- InputAction.IA_PRIMARY // E key
39
- InputAction.IA_SECONDARY // F key
40
- InputAction.IA_ACTION_3 // Key 1
41
- InputAction.IA_ACTION_4 // Key 2
42
- InputAction.IA_ACTION_5 // Key 3
43
- InputAction.IA_ACTION_6 // Key 4
66
+ PointerEventType.PET_DOWN // Button pressed
67
+ PointerEventType.PET_UP // Button released
68
+ PointerEventType.PET_HOVER_ENTER // Cursor enters entity
69
+ PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity
44
70
  ```
45
71
 
46
72
  ### Pointer Up (Release)
@@ -77,6 +103,8 @@ GltfContainer.create(entity, {
77
103
  })
78
104
  ```
79
105
 
106
+ ---
107
+
80
108
  ## Trigger Areas (Proximity Detection)
81
109
 
82
110
  Detect when the player enters, exits, or stays inside an area:
@@ -121,9 +149,66 @@ Transform.create(mover, { position: Vector3.create(8, 0, 8) })
121
149
  MeshCollider.setBox(mover, ColliderLayer.CL_CUSTOM1)
122
150
  ```
123
151
 
152
+ ---
153
+
124
154
  ## Raycasting
125
155
 
126
- Cast rays to detect objects in a direction:
156
+ ### Raycast Direction Types
157
+
158
+ Four direction modes are available:
159
+
160
+ ```typescript
161
+ // 1. Local direction — relative to entity rotation
162
+ { $case: 'localDirection', localDirection: Vector3.Forward() }
163
+
164
+ // 2. Global direction — world-space, ignores entity rotation
165
+ { $case: 'globalDirection', globalDirection: Vector3.Down() }
166
+
167
+ // 3. Global target — aim at a world position
168
+ { $case: 'globalTarget', globalTarget: Vector3.create(10, 0, 10) }
169
+
170
+ // 4. Target entity — aim at another entity
171
+ { $case: 'targetEntity', targetEntity: entityId }
172
+ ```
173
+
174
+ ### Callback-Based Raycasting (Recommended)
175
+
176
+ ```typescript
177
+ import { raycastSystem, RaycastQueryType, ColliderLayer } from '@dcl/sdk/ecs'
178
+
179
+ // Local direction raycast
180
+ raycastSystem.registerLocalDirectionRaycast(
181
+ { entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Forward(), maxDistance: 16, collisionMask: ColliderLayer.CL_POINTER } },
182
+ (result) => {
183
+ if (result.hits.length > 0) {
184
+ console.log('Hit:', result.hits[0].entityId)
185
+ }
186
+ }
187
+ )
188
+
189
+ // Global direction raycast
190
+ raycastSystem.registerGlobalDirectionRaycast(
191
+ { entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Down(), maxDistance: 20 } },
192
+ (result) => { /* handle hits */ }
193
+ )
194
+
195
+ // Target position raycast
196
+ raycastSystem.registerGlobalTargetRaycast(
197
+ { entity: myEntity, opts: { globalTarget: Vector3.create(8, 0, 8), maxDistance: 20 } },
198
+ (result) => { /* handle result */ }
199
+ )
200
+
201
+ // Target entity raycast
202
+ raycastSystem.registerTargetEntityRaycast(
203
+ { entity: sourceEntity, opts: { targetEntity: targetEntity, maxDistance: 15 } },
204
+ (result) => { /* handle result */ }
205
+ )
206
+
207
+ // Remove raycast from entity
208
+ raycastSystem.removeRaycasterEntity(myEntity)
209
+ ```
210
+
211
+ ### Component-Based Raycasting
127
212
 
128
213
  ```typescript
129
214
  import { engine, Raycast, RaycastResult, RaycastQueryType } from '@dcl/sdk/ecs'
@@ -147,6 +232,27 @@ engine.addSystem(() => {
147
232
  })
148
233
  ```
149
234
 
235
+ ### Camera Raycast
236
+
237
+ Cast a ray from the camera to detect what the player is looking at:
238
+
239
+ ```typescript
240
+ raycastSystem.registerGlobalDirectionRaycast(
241
+ {
242
+ entity: engine.CameraEntity,
243
+ opts: {
244
+ direction: Vector3.rotate(Vector3.Forward(), Transform.get(engine.CameraEntity).rotation),
245
+ maxDistance: 16
246
+ }
247
+ },
248
+ (result) => {
249
+ if (result.hits.length > 0) console.log('Looking at:', result.hits[0].entityId)
250
+ }
251
+ )
252
+ ```
253
+
254
+ ---
255
+
150
256
  ## Global Input Handling
151
257
 
152
258
  Listen for key presses anywhere (not entity-specific):
@@ -164,9 +270,35 @@ engine.addSystem(() => {
164
270
  if (inputSystem.isPressed(InputAction.IA_SECONDARY)) {
165
271
  console.log('F key is held!')
166
272
  }
273
+
274
+ // Entity-specific input via system
275
+ const clickData = inputSystem.getInputCommand(
276
+ InputAction.IA_POINTER,
277
+ PointerEventType.PET_DOWN,
278
+ myEntity
279
+ )
280
+ if (clickData) {
281
+ console.log('Entity clicked via system:', clickData.hit.entityId)
282
+ }
167
283
  })
168
284
  ```
169
285
 
286
+ ## Cursor State
287
+
288
+ ```typescript
289
+ import { PointerLock, PrimaryPointerInfo } from '@dcl/sdk/ecs'
290
+
291
+ // Check if cursor is locked
292
+ const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
293
+
294
+ // Get cursor position and world ray
295
+ const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
296
+ console.log('Cursor position:', pointerInfo.screenCoordinates)
297
+ console.log('World ray direction:', pointerInfo.worldRayDirection)
298
+ ```
299
+
300
+ ---
301
+
170
302
  ## Toggle Pattern (Click to Switch States)
171
303
 
172
304
  Common pattern for toggleable objects:
@@ -186,33 +318,6 @@ pointerEventsSystem.onPointerDown(
186
318
  )
187
319
  ```
188
320
 
189
- ### Raycast System Helpers
190
-
191
- Use `raycastSystem` for convenient raycasting without manual component management:
192
-
193
- ```typescript
194
- import { raycastSystem, RaycastQueryType, ColliderLayer } from '@dcl/sdk/ecs'
195
-
196
- // Register a continuous local-direction raycast
197
- raycastSystem.registerLocalDirectionRaycast(
198
- { entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Forward(), maxDistance: 16, collisionMask: ColliderLayer.CL_POINTER } },
199
- (result) => {
200
- if (result.hits.length > 0) {
201
- console.log('Hit:', result.hits[0].entityId)
202
- }
203
- }
204
- )
205
-
206
- // Register a global-direction raycast
207
- raycastSystem.registerGlobalDirectionRaycast(
208
- { entity: myEntity, opts: { queryType: RaycastQueryType.RQT_HIT_FIRST, direction: Vector3.Down(), maxDistance: 20 } },
209
- (result) => { /* handle hits */ }
210
- )
211
-
212
- // Remove raycast from entity
213
- raycastSystem.removeRaycasterEntity(myEntity)
214
- ```
215
-
216
321
  ## Best Practices
217
322
 
218
323
  - Always set `maxDistance` on pointer events (8-16m is typical)
@@ -221,3 +326,7 @@ raycastSystem.removeRaycasterEntity(myEntity)
221
326
  - Use `MeshCollider` for invisible trigger surfaces
222
327
  - For complex interactions, use a system with state tracking
223
328
  - Test interactions in preview — hover text should be visible and clear
329
+ - Set `continuous: false` on raycasts unless you need per-frame results
330
+ - Design for both desktop and mobile — mobile has no keyboard, rely on pointer and on-screen buttons
331
+
332
+ For the full input action list and advanced patterns, see `{baseDir}/references/input-reference.md`.
@@ -0,0 +1,148 @@
1
+ # Input System Reference
2
+
3
+ ## All Input Actions
4
+
5
+ | Action | Key Binding | Constant |
6
+ |--------|-------------|----------|
7
+ | Left mouse button | Mouse click / tap | `InputAction.IA_POINTER` |
8
+ | Primary action | E key | `InputAction.IA_PRIMARY` |
9
+ | Secondary action | F key | `InputAction.IA_SECONDARY` |
10
+ | Action 3 | 1 key | `InputAction.IA_ACTION_3` |
11
+ | Action 4 | 2 key | `InputAction.IA_ACTION_4` |
12
+ | Action 5 | 3 key | `InputAction.IA_ACTION_5` |
13
+ | Action 6 | 4 key | `InputAction.IA_ACTION_6` |
14
+ | Jump | Space key | `InputAction.IA_JUMP` |
15
+ | Forward | W key | `InputAction.IA_FORWARD` |
16
+ | Backward | S key | `InputAction.IA_BACKWARD` |
17
+ | Left | A key | `InputAction.IA_LEFT` |
18
+ | Right | D key | `InputAction.IA_RIGHT` |
19
+ | Walk | Shift key | `InputAction.IA_WALK` |
20
+
21
+ **Notes:**
22
+ - Mouse wheel is **not available** as an input
23
+ - Always design for both desktop and mobile — mobile has no keyboard, rely on pointer and on-screen buttons
24
+ - Set `maxDistance` on pointer events (8-10 meters typical) to prevent interactions from across the scene
25
+ - Use `hoverText` to communicate what an interaction does before the player commits
26
+
27
+ ## All Pointer Event Types
28
+
29
+ ```typescript
30
+ PointerEventType.PET_DOWN // Button/key pressed
31
+ PointerEventType.PET_UP // Button/key released
32
+ PointerEventType.PET_HOVER_ENTER // Cursor enters entity bounds
33
+ PointerEventType.PET_HOVER_LEAVE // Cursor leaves entity bounds
34
+ ```
35
+
36
+ ## Declarative Pointer Events Component
37
+
38
+ Instead of the callback system, you can use the `PointerEvents` component directly:
39
+
40
+ ```typescript
41
+ import { PointerEvents, PointerEventType, InputAction } from '@dcl/sdk/ecs'
42
+
43
+ PointerEvents.create(entity, {
44
+ pointerEvents: [
45
+ {
46
+ eventType: PointerEventType.PET_DOWN,
47
+ eventInfo: {
48
+ button: InputAction.IA_POINTER,
49
+ hoverText: 'Click me',
50
+ showFeedback: true,
51
+ maxDistance: 10
52
+ }
53
+ }
54
+ ]
55
+ })
56
+ ```
57
+
58
+ Then read results in a system using `inputSystem.getInputCommand()`.
59
+
60
+ ## Raycast Direction Types
61
+
62
+ ```typescript
63
+ // 1. Local direction — relative to entity rotation
64
+ { $case: 'localDirection', localDirection: Vector3.Forward() }
65
+
66
+ // 2. Global direction — world-space direction, ignores entity rotation
67
+ { $case: 'globalDirection', globalDirection: Vector3.Down() }
68
+
69
+ // 3. Global target — aim at a specific world position
70
+ { $case: 'globalTarget', globalTarget: Vector3.create(10, 0, 10) }
71
+
72
+ // 4. Target entity — aim at another entity dynamically
73
+ { $case: 'targetEntity', targetEntity: entityId }
74
+ ```
75
+
76
+ ### Raycast Options
77
+
78
+ ```typescript
79
+ {
80
+ direction: Vector3.Forward(),
81
+ maxDistance: 16,
82
+ queryType: RaycastQueryType.RQT_HIT_FIRST, // or RQT_QUERY_ALL
83
+ originOffset: Vector3.create(0, 0.5, 0), // offset from entity origin
84
+ collisionMask: ColliderLayer.CL_PHYSICS | ColliderLayer.CL_CUSTOM1,
85
+ continuous: false // true = every frame, false = one-shot
86
+ }
87
+ ```
88
+
89
+ ### Camera Raycast
90
+
91
+ Cast a ray from the camera to detect what the player is looking at:
92
+
93
+ ```typescript
94
+ raycastSystem.registerGlobalDirectionRaycast(
95
+ {
96
+ entity: engine.CameraEntity,
97
+ opts: {
98
+ direction: Vector3.rotate(Vector3.Forward(), Transform.get(engine.CameraEntity).rotation),
99
+ maxDistance: 16
100
+ }
101
+ },
102
+ (result) => {
103
+ if (result.hits.length > 0) console.log('Looking at:', result.hits[0].entityId)
104
+ }
105
+ )
106
+ ```
107
+
108
+ ## Avatar Modifier Areas
109
+
110
+ Modify how avatars appear or behave in a region:
111
+
112
+ ```typescript
113
+ import { AvatarModifierArea, AvatarModifierType } from '@dcl/sdk/ecs'
114
+
115
+ AvatarModifierArea.create(entity, {
116
+ area: { box: Vector3.create(4, 3, 4) },
117
+ modifiers: [AvatarModifierType.AMT_HIDE_AVATARS],
118
+ excludeIds: ['0x123...abc'] // Optional
119
+ })
120
+
121
+ // Available modifiers:
122
+ // AMT_HIDE_AVATARS — Hide all avatars in the area
123
+ // AMT_DISABLE_PASSPORTS — Disable clicking on avatars to see profiles
124
+ // AMT_DISABLE_JUMPING — Prevent jumping in the area
125
+ ```
126
+
127
+ ## Cursor State
128
+
129
+ ```typescript
130
+ // Check if cursor is locked (pointer lock mode)
131
+ const isLocked = PointerLock.get(engine.CameraEntity).isPointerLocked
132
+
133
+ // Get cursor position and world ray
134
+ const pointerInfo = PrimaryPointerInfo.get(engine.RootEntity)
135
+ console.log('Cursor screen position:', pointerInfo.screenCoordinates)
136
+ console.log('World ray direction:', pointerInfo.worldRayDirection)
137
+ ```
138
+
139
+ ## Trigger Area Callback Fields
140
+
141
+ The trigger area event callback provides:
142
+ - `triggeredEntity` — the entity that activated the area
143
+ - `eventType` — ENTER, EXIT, or STAY
144
+ - `trigger.entity` — the trigger area entity
145
+ - `trigger.layer` — the collider layer
146
+ - `trigger.position` — position of the triggered entity
147
+ - `trigger.rotation` — rotation of the triggered entity
148
+ - `trigger.scale` — scale of the triggered entity
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: advanced-input
3
- description: Advanced input handling in Decentraland scenes. Use PointerLock to detect cursor capture state, InputModifier to freeze or restrict player movement, PrimaryPointerInfo for cursor position and world ray, inputSystem.getInputCommand for per-entity input polling, and keyboard patterns for WASD movement controls. Use when user wants custom input, cursor control, movement restriction, keyboard handling, FPS controls, or input polling.
3
+ description: Advanced input handling in Decentraland. PointerLock (cursor capture state), InputModifier (freeze/restrict player movement), PrimaryPointerInfo (cursor position and world ray), WASD keyboard patterns, and action bar slots. Use when the user wants movement restriction, cursor control, FPS controls, input polling, or cutscene freezing. Do NOT use for basic click/hover events on entities (see add-interactivity).
4
4
  ---
5
5
 
6
6
  # Advanced Input Handling in Decentraland
@@ -1,10 +1,26 @@
1
1
  ---
2
2
  name: advanced-rendering
3
- description: Advanced rendering features in Decentraland scenes. Use Billboard to make entities face the camera, TextShape for 3D text, advanced PBR material properties like metallic/roughness/transparency, GltfNodeModifiers for per-node visibility and material overrides in GLTF models, and VisibilityComponent to show/hide entities. Use when user wants billboards, 3D text, text labels, material effects, transparency, glow, or model node control.
3
+ description: Advanced rendering in Decentraland scenes. Billboard (face camera), TextShape (3D world text), PBR materials (metallic, roughness, transparency, emissive glow), GltfNodeModifiers (per-node shadow/material overrides), VisibilityComponent (show/hide entities), and texture modes. Use when the user wants billboards, floating labels, 3D text, material effects, glow, transparency, or model node control. Do NOT use for screen-space UI (see build-ui) or loading 3D models (see add-3d-models).
4
4
  ---
5
5
 
6
6
  # Advanced Rendering in Decentraland
7
7
 
8
+ ## When to Use Which Rendering Feature
9
+
10
+ | Need | Component | When |
11
+ |------|-----------|------|
12
+ | Entity faces the camera | `Billboard` | Name tags, signs, sprite-like objects |
13
+ | Text in the 3D world | `TextShape` | Labels, signs, floating text above entities |
14
+ | Custom material appearance | `Material.setPbrMaterial` | Metallic, rough, transparent, emissive surfaces |
15
+ | Show/hide without removing | `VisibilityComponent` | LOD systems, toggling objects, conditional display |
16
+ | Modify GLTF model nodes | `GltfNodeModifiers` | Override materials or shadow casting on specific mesh nodes |
17
+
18
+ **Decision flow:**
19
+ 1. Need text on screen? → Use **build-ui** (React-ECS Label) instead
20
+ 2. Need text in 3D space? → `TextShape` (+ `Billboard` to face camera)
21
+ 3. Need glowing/transparent materials? → `Material.setPbrMaterial` with emissive/transparency
22
+ 4. Need to override material on a model node? → `GltfNodeModifiers` with `modifiers` array
23
+
8
24
  ## Billboard (Face the Camera)
9
25
 
10
26
  Make entities always rotate to face the player's camera:
@@ -212,20 +228,20 @@ function lodSystem() {
212
228
  engine.addSystem(lodSystem)
213
229
  ```
214
230
 
215
- ### Per-Node Material Overrides (GltfNodeModifiers)
231
+ ### Per-Node Modifiers (GltfNodeModifiers)
216
232
 
217
- Override materials on specific nodes within a GLTF model without modifying the model file:
233
+ Override material or shadow casting on specific nodes within a GLTF model:
218
234
 
219
235
  ```typescript
220
- import { GltfNode, GltfNodeState } from '@dcl/sdk/ecs'
236
+ import { GltfNodeModifiers } from '@dcl/sdk/ecs'
221
237
 
222
- // Hide a specific node in a model
223
- GltfNode.create(entity, { path: 'RootNode/Armor', visible: false })
224
-
225
- // Override a node's material
226
- GltfNode.create(entity, {
227
- path: 'RootNode/Helmet',
228
- materialOverride: Material.Texture.Common({ src: 'images/custom-skin.png' })
238
+ GltfNodeModifiers.create(entity, {
239
+ modifiers: [
240
+ {
241
+ path: 'RootNode/Armor', // GLTF hierarchy path
242
+ castShadows: false // Disable shadow casting for this node
243
+ }
244
+ ]
229
245
  })
230
246
  ```
231
247