@dcl-regenesislabs/opendcl 0.1.0-22239132687.commit-eccf1dd → 0.1.0-22312642473.commit-ea3ce8c

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.
@@ -11,25 +11,23 @@ Decentraland SDK7 uses a React-like JSX system for 2D UI overlays.
11
11
 
12
12
  ### File: src/ui.tsx
13
13
  ```tsx
14
- import ReactEcs, { UiEntity, Label, Button } from '@dcl/sdk/react-ecs'
15
-
16
- function MyUI() {
17
- return (
18
- <UiEntity
19
- uiTransform={{
20
- width: '100%',
21
- height: '100%',
22
- justifyContent: 'center',
23
- alignItems: 'center'
24
- }}
25
- >
26
- <Label value="Hello Decentraland!" fontSize={24} />
27
- </UiEntity>
28
- )
29
- }
14
+ import ReactEcs, { ReactEcsRenderer, UiEntity, Label, Button } from '@dcl/sdk/react-ecs'
15
+
16
+ const MyUI = () => (
17
+ <UiEntity
18
+ uiTransform={{
19
+ width: '100%',
20
+ height: '100%',
21
+ justifyContent: 'center',
22
+ alignItems: 'center'
23
+ }}
24
+ >
25
+ <Label value="Hello Decentraland!" fontSize={24} />
26
+ </UiEntity>
27
+ )
30
28
 
31
29
  export function setupUi() {
32
- ReactEcs.setUiRenderer(MyUI)
30
+ ReactEcsRenderer.setUiRenderer(MyUI)
33
31
  }
34
32
  ```
35
33
 
@@ -56,6 +54,8 @@ export function main() {
56
54
 
57
55
  ### UiEntity (Container)
58
56
  ```tsx
57
+ import { Color4 } from '@dcl/sdk/math'
58
+
59
59
  <UiEntity
60
60
  uiTransform={{
61
61
  width: 300, // Pixels or '50%'
@@ -70,17 +70,19 @@ export function main() {
70
70
  display: 'flex' // 'flex' | 'none' (hide)
71
71
  }}
72
72
  uiBackground={{
73
- color: { r: 0, g: 0, b: 0, a: 0.8 } // Semi-transparent black
73
+ color: Color4.create(0, 0, 0, 0.8) // Semi-transparent black
74
74
  }}
75
75
  />
76
76
  ```
77
77
 
78
78
  ### Label (Text)
79
79
  ```tsx
80
+ import { Color4 } from '@dcl/sdk/math'
81
+
80
82
  <Label
81
83
  value="Score: 100"
82
84
  fontSize={18}
83
- color={{ r: 1, g: 1, b: 1, a: 1 }}
85
+ color={Color4.White()}
84
86
  textAlign="middle-center"
85
87
  font="sans-serif"
86
88
  uiTransform={{ width: 200, height: 30 }}
@@ -103,12 +105,16 @@ export function main() {
103
105
  ### Input
104
106
  ```tsx
105
107
  import { Input } from '@dcl/sdk/react-ecs'
108
+ import { Color4 } from '@dcl/sdk/math'
106
109
 
107
110
  <Input
108
111
  placeholder="Type here..."
109
112
  fontSize={14}
110
- color={{ r: 1, g: 1, b: 1, a: 1 }}
113
+ color={Color4.White()}
111
114
  uiTransform={{ width: 250, height: 35 }}
115
+ onChange={(value) => {
116
+ console.log('Value changing:', value)
117
+ }}
112
118
  onSubmit={(value) => {
113
119
  console.log('Submitted:', value)
114
120
  }}
@@ -135,45 +141,45 @@ import { Dropdown } from '@dcl/sdk/react-ecs'
135
141
  Use module-level variables for UI state (React hooks are NOT available):
136
142
 
137
143
  ```tsx
144
+ import { Color4 } from '@dcl/sdk/math'
145
+
138
146
  let score = 0
139
147
  let showMenu = false
140
148
 
141
- function GameUI() {
142
- return (
143
- <UiEntity uiTransform={{ width: '100%', height: '100%' }}>
144
- {/* HUD - always visible */}
145
- <Label
146
- value={`Score: ${score}`}
147
- fontSize={20}
149
+ const GameUI = () => (
150
+ <UiEntity uiTransform={{ width: '100%', height: '100%' }}>
151
+ {/* HUD - always visible */}
152
+ <Label
153
+ value={`Score: ${score}`}
154
+ fontSize={20}
155
+ uiTransform={{
156
+ positionType: 'absolute',
157
+ position: { top: 10, left: 10 }
158
+ }}
159
+ />
160
+
161
+ {/* Menu - conditionally shown */}
162
+ {showMenu && (
163
+ <UiEntity
148
164
  uiTransform={{
165
+ width: 300,
166
+ height: 400,
149
167
  positionType: 'absolute',
150
- position: { top: 10, left: 10 }
168
+ position: { top: '50%', left: '50%' }
151
169
  }}
152
- />
153
-
154
- {/* Menu - conditionally shown */}
155
- {showMenu && (
156
- <UiEntity
157
- uiTransform={{
158
- width: 300,
159
- height: 400,
160
- positionType: 'absolute',
161
- position: { top: '50%', left: '50%' }
162
- }}
163
- uiBackground={{ color: { r: 0.1, g: 0.1, b: 0.1, a: 0.9 } }}
164
- >
165
- <Label value="Game Menu" fontSize={24} />
166
- <Button
167
- value="Resume"
168
- variant="primary"
169
- onMouseDown={() => { showMenu = false }}
170
- uiTransform={{ width: 200, height: 40 }}
171
- />
172
- </UiEntity>
173
- )}
174
- </UiEntity>
175
- )
176
- }
170
+ uiBackground={{ color: Color4.create(0.1, 0.1, 0.1, 0.9) }}
171
+ >
172
+ <Label value="Game Menu" fontSize={24} />
173
+ <Button
174
+ value="Resume"
175
+ variant="primary"
176
+ onMouseDown={() => { showMenu = false }}
177
+ uiTransform={{ width: 200, height: 40 }}
178
+ />
179
+ </UiEntity>
180
+ )}
181
+ </UiEntity>
182
+ )
177
183
 
178
184
  // Update state from game logic
179
185
  export function addScore(points: number) {
@@ -189,25 +195,25 @@ export function toggleMenu() {
189
195
 
190
196
  ### Health Bar
191
197
  ```tsx
198
+ import { Color4 } from '@dcl/sdk/math'
199
+
192
200
  let health = 100
193
201
 
194
- function HealthBar() {
195
- return (
202
+ const HealthBar = () => (
203
+ <UiEntity
204
+ uiTransform={{
205
+ width: 200, height: 20,
206
+ positionType: 'absolute',
207
+ position: { bottom: 20, left: '50%' }
208
+ }}
209
+ uiBackground={{ color: Color4.create(0.3, 0.3, 0.3, 0.8) }}
210
+ >
196
211
  <UiEntity
197
- uiTransform={{
198
- width: 200, height: 20,
199
- positionType: 'absolute',
200
- position: { bottom: 20, left: '50%' }
201
- }}
202
- uiBackground={{ color: { r: 0.3, g: 0.3, b: 0.3, a: 0.8 } }}
203
- >
204
- <UiEntity
205
- uiTransform={{ width: `${health}%`, height: '100%' }}
206
- uiBackground={{ color: { r: 0.2, g: 0.8, b: 0.2, a: 1 } }}
207
- />
208
- </UiEntity>
209
- )
210
- }
212
+ uiTransform={{ width: `${health}%`, height: '100%' }}
213
+ uiBackground={{ color: Color4.create(0.2, 0.8, 0.2, 1) }}
214
+ />
215
+ </UiEntity>
216
+ )
211
217
  ```
212
218
 
213
219
  ### Image Background
@@ -228,4 +234,4 @@ function HealthBar() {
228
234
  - UI is rendered as a 2D overlay on top of the 3D scene
229
235
  - Use `display: 'none'` in `uiTransform` to hide elements without removing them
230
236
  - File extension must be `.tsx` for JSX support
231
- - Only one `ReactEcs.setUiRenderer()` call per scene — combine all UI into one root component
237
+ - Only one `ReactEcsRenderer.setUiRenderer()` call per scene — combine all UI into one root component
@@ -10,7 +10,7 @@ description: Control camera behavior in Decentraland scenes. Switch between firs
10
10
  Access the camera's current position and rotation via the reserved `engine.CameraEntity`:
11
11
 
12
12
  ```typescript
13
- import { engine, Transform, CameraMode, CameraType } from '@dcl/sdk/ecs'
13
+ import { engine, Transform } from '@dcl/sdk/ecs'
14
14
 
15
15
  function trackCamera() {
16
16
  if (!Transform.has(engine.CameraEntity)) return
@@ -28,6 +28,8 @@ engine.addSystem(trackCamera)
28
28
  Check whether the player is in first-person or third-person:
29
29
 
30
30
  ```typescript
31
+ import { engine, CameraMode, CameraType } from '@dcl/sdk/ecs'
32
+
31
33
  function checkCameraMode() {
32
34
  if (!CameraMode.has(engine.CameraEntity)) return
33
35
 
@@ -73,7 +75,7 @@ When the player leaves the area, the camera reverts to their preferred mode.
73
75
  Create scripted camera positions for cutscenes or special views:
74
76
 
75
77
  ```typescript
76
- import { engine, Transform, VirtualCamera, CameraTransition } from '@dcl/sdk/ecs'
78
+ import { engine, Transform, VirtualCamera, MainCamera } from '@dcl/sdk/ecs'
77
79
  import { Vector3, Quaternion } from '@dcl/sdk/math'
78
80
 
79
81
  const cinematicCam = engine.addEntity()
@@ -84,16 +86,23 @@ Transform.create(cinematicCam, {
84
86
 
85
87
  VirtualCamera.create(cinematicCam, {
86
88
  defaultTransition: {
87
- transitionMode: CameraTransition.CT_SPEED,
88
- speed: 1.0
89
+ transitionMode: VirtualCamera.Transition.Speed(1.0)
89
90
  }
90
91
  })
92
+
93
+ // Activate the virtual camera
94
+ MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = cinematicCam
95
+
96
+ // Return to normal camera
97
+ MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = undefined
91
98
  ```
92
99
 
93
100
  ### Transition Modes
94
101
 
95
- - `CameraTransition.CT_SPEED` — smooth transition at a fixed speed
96
- - Higher `speed` values = faster camera movement to the virtual camera position
102
+ ```typescript
103
+ VirtualCamera.Transition.Speed(1.0) // Speed-based smooth transition
104
+ VirtualCamera.Transition.Time(2) // Time-based transition (2 seconds)
105
+ ```
97
106
 
98
107
  ### Look-At Target
99
108
 
@@ -106,10 +115,12 @@ Transform.create(target, { position: Vector3.create(8, 1, 8) })
106
115
  VirtualCamera.create(cinematicCam, {
107
116
  lookAtEntity: target,
108
117
  defaultTransition: {
109
- transitionMode: CameraTransition.CT_SPEED,
110
- speed: 2.0
118
+ transitionMode: VirtualCamera.Transition.Speed(2.0)
111
119
  }
112
120
  })
121
+
122
+ // Activate
123
+ MainCamera.getMutable(engine.CameraEntity).virtualCameraEntity = cinematicCam
113
124
  ```
114
125
 
115
126
  ## Tracking Camera Position
@@ -117,7 +128,7 @@ VirtualCamera.create(cinematicCam, {
117
128
  Poll camera position each frame for camera-triggered events:
118
129
 
119
130
  ```typescript
120
- import { engine, Transform, CameraMode, CameraType } from '@dcl/sdk/ecs'
131
+ import { engine, Transform } from '@dcl/sdk/ecs'
121
132
  import { Vector3 } from '@dcl/sdk/math'
122
133
 
123
134
  let lastNotifiedZone = ''
@@ -16,11 +16,26 @@ If the user hasn't described their scene, ask them:
16
16
 
17
17
  ## 2. Scaffold the Project with `/init`
18
18
 
19
- **Always run `/init` first.** This uses the official `@dcl/sdk-commands init` to create scene.json, package.json, tsconfig.json, and src/index.ts with the correct, up-to-date configuration.
19
+ **Always run `/init` first.** This uses the official `@dcl/sdk-commands init` to create scene.json, package.json, tsconfig.json, and src/index.ts with the correct, up-to-date configuration, and installs dependencies automatically.
20
20
 
21
21
  Never manually create scene.json, package.json, or tsconfig.json — the SDK templates may change between versions and hand-written copies will diverge.
22
22
 
23
- ## 3. Customize the Generated Files
23
+ ## 3. Find Matching 3D Assets
24
+
25
+ Before writing scene code, check both asset catalogs for free models that match the user's theme:
26
+
27
+ 1. Read `context/asset-packs-catalog.md` (2,700+ Creator Hub models — furniture, structures, decorations, nature, etc.)
28
+ 2. Read `context/open-source-3d-assets.md` (991 CC0 models — cyberpunk, medieval, nature, sci-fi, etc.)
29
+ 3. Suggest matching models to the user
30
+ 4. Download selected models into the scene's `models/` directory:
31
+ ```bash
32
+ mkdir -p models
33
+ curl -o models/arcade_machine.glb "https://builder-items.decentraland.org/contents/bafybei..."
34
+ ```
35
+
36
+ > **Important**: `GltfContainer` only works with local files. Never use external URLs for the model `src` field.
37
+
38
+ ## 4. Customize the Generated Files
24
39
 
25
40
  After `/init` completes, customize the generated files based on what the user wants:
26
41
 
@@ -51,12 +66,11 @@ export function main() {
51
66
  }
52
67
  ```
53
68
 
54
- ## 4. Post-Creation Steps
69
+ ## 5. Post-Creation Steps
55
70
 
56
- After creating the files, tell the user:
57
- 1. Run `npm install` to install dependencies
58
- 2. Use the `preview` tool to start the preview server (or run `npx @dcl/sdk-commands start --bevy-web` manually)
59
- 3. The scene will open in a browser at http://localhost:8000
71
+ After customizing the files:
72
+ 1. Use the `preview` tool to start the preview server (or run `npx @dcl/sdk-commands start --bevy-web` manually)
73
+ 2. The scene will open in a browser at http://localhost:8000
60
74
 
61
75
  ## Important Notes
62
76
 
@@ -39,6 +39,8 @@ LightSource.create(light, {
39
39
  Emit a cone of light in a direction:
40
40
 
41
41
  ```typescript
42
+ import { Quaternion } from '@dcl/sdk/math'
43
+
42
44
  const spotlight = engine.addEntity()
43
45
  Transform.create(spotlight, {
44
46
  position: Vector3.create(8, 4, 8),
@@ -175,10 +177,10 @@ For a visual glow without casting light on surroundings:
175
177
  import { engine, Material } from '@dcl/sdk/ecs'
176
178
  import { Color4, Color3 } from '@dcl/sdk/math'
177
179
 
178
- // Self-illuminated material
180
+ // Self-illuminated material (emissiveColor uses Color3, not Color4)
179
181
  Material.setPbrMaterial(entity, {
180
182
  albedoColor: Color4.create(0, 0, 0, 1),
181
- emissiveColor: Color4.create(0, 1, 0, 1), // Green glow
183
+ emissiveColor: Color3.create(0, 1, 0), // Green glow
182
184
  emissiveIntensity: 2.0
183
185
  })
184
186
  ```
@@ -190,7 +192,7 @@ For an object that both glows visually and casts light:
190
192
  ```typescript
191
193
  // Visual glow on the mesh
192
194
  Material.setPbrMaterial(bulb, {
193
- emissiveColor: Color4.create(1, 0.9, 0.7, 1),
195
+ emissiveColor: Color3.create(1, 0.9, 0.7),
194
196
  emissiveIntensity: 1.5
195
197
  })
196
198
 
@@ -9,25 +9,29 @@ Decentraland scenes are inherently multiplayer. All players in the same scene sh
9
9
 
10
10
  ## How Sync Works
11
11
 
12
- - Components on entities created via `engine.addEntity()` are **automatically synced** between all players in the scene.
12
+ - Entities must be explicitly synced using `syncEntity()` from `@dcl/sdk/network`.
13
13
  - The Decentraland runtime uses CRDTs (Conflict-free Replicated Data Types) to resolve conflicts.
14
14
  - Last-write-wins semantics for most components (Transform, Material, etc.).
15
15
  - No server code needed — sync is built into the runtime.
16
16
 
17
17
  ## Basic Synced Entity
18
18
 
19
- Any entity with standard components syncs automatically:
19
+ Use `syncEntity()` to mark an entity and its components for multiplayer sync:
20
20
 
21
21
  ```typescript
22
22
  import { engine, Transform, MeshRenderer, Material } from '@dcl/sdk/ecs'
23
+ import { syncEntity } from '@dcl/sdk/network'
23
24
  import { Vector3, Color4 } from '@dcl/sdk/math'
24
25
 
25
- // This entity and all its components sync to all players
26
+ // Create entity
26
27
  const sharedCube = engine.addEntity()
27
28
  Transform.create(sharedCube, { position: Vector3.create(8, 1, 8) })
28
29
  MeshRenderer.setBox(sharedCube)
29
30
  Material.setPbrMaterial(sharedCube, { albedoColor: Color4.Red() })
30
31
 
32
+ // Sync this entity's Transform to all players
33
+ syncEntity(sharedCube, [Transform.componentId])
34
+
31
35
  // When any player changes the transform, all players see it
32
36
  function moveCube() {
33
37
  const transform = Transform.getMutable(sharedCube)
@@ -37,23 +41,25 @@ function moveCube() {
37
41
 
38
42
  ## Custom Synced Components
39
43
 
40
- Define custom components that sync between players:
44
+ Define custom components and sync them between players:
41
45
 
42
46
  ```typescript
43
47
  import { engine, Schemas } from '@dcl/sdk/ecs'
48
+ import { syncEntity } from '@dcl/sdk/network'
44
49
 
45
- // Define a custom synced component
50
+ // Define a custom component
46
51
  const ScoreBoard = engine.defineComponent('scoreBoard', {
47
52
  score: Schemas.Int,
48
53
  playerName: Schemas.String,
49
54
  lastUpdated: Schemas.Int64
50
55
  })
51
56
 
52
- // Use it on an entity — automatically syncs
57
+ // Create and sync the entity
53
58
  const board = engine.addEntity()
54
59
  ScoreBoard.create(board, { score: 0, playerName: '', lastUpdated: 0 })
60
+ syncEntity(board, [ScoreBoard.componentId])
55
61
 
56
- // Update from any player
62
+ // Update from any player — synced via CRDT
57
63
  function addScore(points: number) {
58
64
  const data = ScoreBoard.getMutable(board)
59
65
  data.score += points
@@ -124,9 +130,10 @@ engine.addSystem(() => {
124
130
 
125
131
  ## Important Notes
126
132
 
127
- - **All component changes sync automatically** — no explicit "send" calls needed
133
+ - **Entities must be explicitly synced** via `syncEntity(entity, [componentIds])` pass the `componentId` of each component to sync
128
134
  - **CRDT resolution**: If two players change the same component simultaneously, last-write-wins
129
135
  - **No server-side code**: Decentraland scenes run entirely client-side with CRDT sync
130
136
  - **Entity limits apply**: Each synced entity counts toward the scene's entity budget
131
137
  - **Custom schemas must be deterministic**: Same component name = same schema across all clients
138
+ - **Use `Schemas.Int64` for timestamps**: `Schemas.Number` corrupts large numbers (13+ digits). Always use `Schemas.Int64` for values like `Date.now()`
132
139
  - For server-authoritative multiplayer with validation and anti-cheat, see the `authoritative-server` skill
@@ -138,13 +138,34 @@ Transform.create(npc, { position: Vector3.create(8, 0, 8) })
138
138
  AvatarShape.create(npc, {
139
139
  id: 'npc-1',
140
140
  name: 'Guard',
141
+ bodyShape: 'urn:decentraland:off-chain:base-avatars:BaseMale', // or BaseFemale
141
142
  wearables: [
142
- 'urn:decentraland:off-chain:base-avatars:f_eyes_01',
143
- 'urn:decentraland:off-chain:base-avatars:f_eyebrows_01',
144
- 'urn:decentraland:off-chain:base-avatars:f_mouth_01',
145
- 'urn:decentraland:off-chain:base-avatars:sport_jacket',
146
- 'urn:decentraland:off-chain:base-avatars:oxford_pants'
147
- ]
143
+ 'urn:decentraland:off-chain:base-avatars:eyebrows_00',
144
+ 'urn:decentraland:off-chain:base-avatars:mouth_00',
145
+ 'urn:decentraland:off-chain:base-avatars:eyes_00',
146
+ 'urn:decentraland:off-chain:base-avatars:blue_tshirt',
147
+ 'urn:decentraland:off-chain:base-avatars:brown_pants',
148
+ 'urn:decentraland:off-chain:base-avatars:classic_shoes',
149
+ 'urn:decentraland:off-chain:base-avatars:short_hair'
150
+ ],
151
+ hairColor: { r: 0.92, g: 0.76, b: 0.62 }, // RGB values 0-1
152
+ skinColor: { r: 0.94, g: 0.85, b: 0.6 }, // RGB values 0-1
153
+ emotes: []
154
+ })
155
+ ```
156
+
157
+ ### Mannequin (Show Only Wearables)
158
+
159
+ Display just the wearables without a full avatar body:
160
+
161
+ ```typescript
162
+ AvatarShape.create(mannequin, {
163
+ id: 'mannequin-1',
164
+ name: 'Display',
165
+ wearables: [
166
+ 'urn:decentraland:matic:collections-v2:0x90e5cb2d673699be8f28d339c818a0b60144c494:0'
167
+ ],
168
+ show_only_wearables: true
148
169
  })
149
170
  ```
150
171