Skip to content

Commit

Permalink
fix smart werabale hot reload (#822)
Browse files Browse the repository at this point in the history
  • Loading branch information
gonpombo8 authored Nov 1, 2023
1 parent 46ced7f commit 6b8ca94
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 27 deletions.
2 changes: 1 addition & 1 deletion packages/@dcl/sdk-commands/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export async function main(options: Options) {

for (const project of workspace.projects) {
printCurrentProjectStarting(options.components.logger, project, workspace)
if (project.kind === 'scene' || project.kind === 'wearable') {
if (project.kind === 'scene' || project.kind === 'smart-wearable') {
await buildScene(options, project)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function main(options: Options) {

for (const project of workspace.projects) {
printCurrentProjectStarting(options.components.logger, project, workspace)
if (project.kind === 'wearable') {
if (project.kind === 'smart-wearable') {
await packSmartWearable(options, project)
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/@dcl/sdk-commands/src/commands/start/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export async function main(options: Options) {
printWarning(options.components.logger, 'Support for multiple projects is still experimental.')

for (const project of workspace.projects) {
if (project.kind === 'wearable') hasSmartWearable = true
if (project.kind === 'scene' || project.kind === 'wearable') {
if (project.kind === 'smart-wearable') hasSmartWearable = true
if (project.kind === 'scene' || project.kind === 'smart-wearable') {
printCurrentProjectStarting(options.components.logger, project, workspace)

// first run `npm run build`, this can be disabled with --skip-build
Expand Down Expand Up @@ -170,7 +170,7 @@ export async function main(options: Options) {
await wireRouter(components, workspace, dataLayer)
if (watch) {
for (const project of workspace.projects) {
await wireFileWatcherToWebSockets(components, project.workingDirectory)
await wireFileWatcherToWebSockets(components, project.workingDirectory, project.kind)
}
}
await startComponents()
Expand Down
26 changes: 11 additions & 15 deletions packages/@dcl/sdk-commands/src/commands/start/server/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ import { getCatalystBaseUrl } from '../../../logic/config'
import { Workspace } from '../../../logic/workspace-validations'
import { ProjectUnion, WearableProject } from '../../../logic/project-validations'

function smartWearableNameToId(name: string) {
return name.toLocaleLowerCase().replace(/ /g, '-')
}

type LambdasWearable = Wearable & {
baseUrl: string
}
Expand Down Expand Up @@ -169,7 +165,7 @@ async function serveFolders(

if (path.resolve(fullPath) === path.resolve(baseProject.workingDirectory)) {
// if we are talking about the root directory, then we must return the json of the entity
const entity = await fakeEntityV3FromProject(components, baseProject, b64HashingFunction)
const entity = await fakeEntityV3FromProject(components, baseProject, async ($) => b64HashingFunction($))

if (!entity) return { status: 404 }

Expand Down Expand Up @@ -245,7 +241,7 @@ async function serveFolders(
return {
body: {
ok: true,
data: wearables.filter((wearable) => smartWearableNameToId(wearable?.name) === wearableId)
data: wearables.filter((wearable) => wearable.id === wearableCache.get(wearableId))
}
}
})
Expand All @@ -270,7 +266,7 @@ async function getAllPreviewWearables(
const wearablePathArray: string[] = []

for (const project of workspace.projects) {
if (project.kind === 'wearable') {
if (project.kind === 'smart-wearable') {
const wearableJsonPath = path.resolve(project.workingDirectory, 'wearable.json')
if (await components.fs.fileExists(wearableJsonPath)) {
wearablePathArray.push(wearableJsonPath)
Expand All @@ -281,7 +277,7 @@ async function getAllPreviewWearables(
const ret: LambdasWearable[] = []
for (const project of workspace.projects) {
try {
if (project.kind === 'wearable') ret.push(await serveWearable(components, project, baseUrl))
if (project.kind === 'smart-wearable') ret.push(await serveWearable(components, project, baseUrl))
} catch (err) {
components.logger.error(
`Couldn't mock the wearable ${project.workingDirectory}. Please verify the correct format and scheme.` + err
Expand Down Expand Up @@ -309,10 +305,8 @@ async function serveWearable(
throw new Error(`Invalid wearable.json (${wearableJsonPath})`)
}

const projectFiles = await getProjectPublishableFilesWithHashes(
components,
project.workingDirectory,
b64HashingFunction
const projectFiles = await getProjectPublishableFilesWithHashes(components, project.workingDirectory, async ($) =>
b64HashingFunction($)
)
const contentFiles = projectFilesToContentMappings(project.workingDirectory, projectFiles)

Expand All @@ -321,7 +315,7 @@ async function serveWearable(
thumbnailFiltered.length > 0 && thumbnailFiltered[0]!.hash && `${baseUrl}/${thumbnailFiltered[0].hash}`

// Set wearable ID.
const sceneHash = await b64HashingFunction(JSON.stringify(project.scene))
const sceneHash = b64HashingFunction(project.workingDirectory)
const wearableId = wearableCache.get(sceneHash) ?? `urn:${uuidv4()}`
wearableCache.set(sceneHash, wearableId)

Expand Down Expand Up @@ -365,7 +359,9 @@ async function getSceneJson(
const resultEntities: Entity[] = []

const allDeployments = await Promise.all(
workspace.projects.map((project) => fakeEntityV3FromProject(components, project, b64HashingFunction))
workspace.projects.map((project) =>
fakeEntityV3FromProject(components, project, async ($) => b64HashingFunction($))
)
)

for (const pointer of Array.from(requestedPointers)) {
Expand Down Expand Up @@ -504,7 +500,7 @@ async function fakeEntityV3FromProject(
metadata: sceneJson,
content: contentFiles
}
} else if (project.kind === 'wearable') {
} else if (project.kind === 'smart-wearable') {
const wearableJsonPath = path.resolve(project.workingDirectory, 'wearable.json')
try {
const wearableJson = JSON.parse(await components.fs.readFile(wearableJsonPath, 'utf-8'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import chokidar from 'chokidar'
import { getDCLIgnorePatterns } from '../../../logic/dcl-ignore'
import { PreviewComponents } from '../types'
import { sceneUpdateClients } from './routes'
import { ProjectUnion } from '../../../logic/project-validations'
import { b64HashingFunction } from '../../../logic/project-files'

function debounce<T extends (...args: any[]) => void>(callback: T, delay: number) {
let debounceTimer: NodeJS.Timeout
Expand All @@ -21,7 +23,8 @@ function debounce<T extends (...args: any[]) => void>(callback: T, delay: number
*/
export async function wireFileWatcherToWebSockets(
components: Pick<PreviewComponents, 'fs' | 'ws'>,
projectRoot: string
projectRoot: string,
projectKind: ProjectUnion['kind']
) {
const ignored = await getDCLIgnorePatterns(components, projectRoot)

Expand All @@ -35,20 +38,20 @@ export async function wireFileWatcherToWebSockets(
'all',
debounce(async (_, _file) => {
// TODO: accumulate changes in an array and debounce
return updateScene(projectRoot, sceneUpdateClients)
return updateScene(projectRoot, sceneUpdateClients, projectKind)
}, 500)
)
}

/*
* IMPORTANT: this is a legacy protocol and needs to be revisited for SDK7
*/
function updateScene(dir: string, clients: Set<WebSocket>): void {
function updateScene(dir: string, clients: Set<WebSocket>, projectKind: ProjectUnion['kind']): void {
for (const client of clients) {
if (client.readyState === WebSocket.OPEN) {
const message: sdk.SceneUpdate = {
type: sdk.SCENE_UPDATE,
payload: { sceneId: 'b64-' + Buffer.from(dir).toString('base64'), sceneType: sdk.ProjectType.SCENE }
payload: { sceneId: b64HashingFunction(dir), sceneType: projectKind }
}

client.send(sdk.UPDATE)
Expand Down
2 changes: 1 addition & 1 deletion packages/@dcl/sdk-commands/src/logic/project-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export async function getProjectPublishableFilesWithHashes(
return ret
}

export const b64HashingFunction = async (str: string) => 'b64-' + Buffer.from(str).toString('base64')
export const b64HashingFunction = (str: string) => 'b64-' + Buffer.from(str).toString('base64')
// export const ipfsHashingFunction = async (str: string) => hashV1(Buffer.from(str, 'utf8'))

interface PackageJson {
Expand Down
4 changes: 2 additions & 2 deletions packages/@dcl/sdk-commands/src/logic/project-validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getPackageJson } from './project-files'

export type BaseProject = { workingDirectory: string }
export type SceneProject = { kind: 'scene'; scene: Scene } & BaseProject
export type WearableProject = { kind: 'wearable'; scene: Scene } & BaseProject
export type WearableProject = { kind: 'smart-wearable'; scene: Scene } & BaseProject
export type ProjectUnion = SceneProject | WearableProject

/**
Expand All @@ -29,7 +29,7 @@ export async function assertValidProjectFolder(
switch (true) {
case await components.fs.fileExists(getSmartWearableFile(workingDirectory)): {
await getValidWearableJson(components, workingDirectory)
return { kind: 'wearable', scene: await getValidSceneJson(components, workingDirectory), workingDirectory }
return { kind: 'smart-wearable', scene: await getValidSceneJson(components, workingDirectory), workingDirectory }
}

case await components.fs.fileExists(getSceneFilePath(workingDirectory)): {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
25 changes: 25 additions & 0 deletions test/build-ecs/fixtures/sdk7-smart-wearable/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "simple-scene",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/decentraland/js-sdk-toolchain.git"
},
"license": "Apache-2.0",
"description": "My new Decentraland project",
"scripts": {
"start": "sdk-commands start",
"build": "sdk-commands build",
"build-prod": "sdk-commands build --production",
"watch": "sdk-commands build --watch"
},
"devDependencies": {
"@dcl/ecs": "file:../../../../packages/@dcl/ecs",
"@dcl/js-runtime": "7.0.0",
"@dcl/sdk": "file:../../../../packages/@dcl/sdk",
"@dcl/sdk-commands": "file:../../../../packages/@dcl/sdk-commands"
},
"dependencies": {
"@dcl/ecs-math": "^2.0.1-20221108141807.commit-a1344cb"
}
}
25 changes: 25 additions & 0 deletions test/build-ecs/fixtures/sdk7-smart-wearable/scene.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"ecs7": true,
"runtimeVersion": "7",
"isPortableExperience": true,
"display": {
"title": "SDK7 Smart Wearable",
"description": "SDK7 Smart Wearable description here",
"navmapThumbnail": "images/thumbnail.png",
"favicon": "favicon_asset"
},
"owner": "",
"contact": {
"name": "SDK",
"email": ""
},
"main": "bin/game.js",
"scene": {
"parcels": [
"0,0"
],
"base": "0,0"
},
"requiredPermissions": [],
"featureToggles": {}
}
85 changes: 85 additions & 0 deletions test/build-ecs/fixtures/sdk7-smart-wearable/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
engine,
Entity,
InputAction,
MeshCollider,
MeshRenderer,
Transform,
pointerEventsSystem,
SyncComponents,
Schemas,
Material,
PointerEvents,
PointerEventType,
inputSystem
} from '@dcl/ecs'
import { Vector3, Quaternion, Color4 } from '@dcl/sdk/math'

const Door = engine.defineComponent('door', { open: Schemas.Boolean })

export function getRandomHexColor(): string {
const letters = '012345789ABC sDEF'
let color = '#'
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)]
}
return color
}

// Cube factory
function createCube(x: number, y: number, z: number): Entity {
const meshEntity = engine.addEntity()
Transform.create(meshEntity, { position: { x, y, z } })
MeshRenderer.create(meshEntity, { mesh: { $case: 'box', box: { uvs: [] } } })
MeshCollider.create(meshEntity, { mesh: { $case: 'box', box: {} } })
Material.setPbrMaterial(meshEntity, { albedoColor: Color4.fromHexString(getRandomHexColor()) })

Door.create(meshEntity, { open: false })

SyncComponents.create(meshEntity, { componentIds: [Door.componentId, Material.componentId] })

PointerEvents.create(meshEntity, {
pointerEvents: [
{ eventType: PointerEventType.PET_DOWN, eventInfo: { button: InputAction.IA_POINTER, hoverText: 'Change Color' } }
]
})

return meshEntity
}

function changeColorSystem() {
for (const [entity] of engine.getEntitiesWith(Door, PointerEvents)) {
if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN, entity)) {
Material.setPbrMaterial(entity, { albedoColor: Color4.fromHexString(getRandomHexColor()) })
const door = Door.getMutable(entity)
door.open = !door.open
console.log(`The door was ${door.open ? 'opened' : 'closed'}`)
}
}
}

// Systems
function circularSystem(dt: number) {
const entitiesWithMeshRenderer = engine.getEntitiesWith(MeshRenderer, Transform)
for (const [entity, _meshRenderer, _transform] of entitiesWithMeshRenderer) {
const mutableTransform = Transform.getMutable(entity)
mutableTransform.rotation = Quaternion.multiply(
mutableTransform.rotation,
Quaternion.fromAngleAxis(dt * 1, Vector3.Up())
)
}
}

export function main() {
const initEntity = createCube(8, 1, 8)

pointerEventsSystem.onPointerDown(
{ entity: initEntity, opts: { button: InputAction.IA_POINTER, hoverText: 'CASLA' } },
function () {
createCube(1 + Math.random() * 8, Math.random() * 8, 1 + Math.random() * 8)
}
)

engine.addSystem(circularSystem)
engine.addSystem(changeColorSystem)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions test/build-ecs/fixtures/sdk7-smart-wearable/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "."
},
"extends": "@dcl/sdk/types/tsconfig.ecs7.json",
"compileOnSave": false
}
29 changes: 29 additions & 0 deletions test/build-ecs/fixtures/sdk7-smart-wearable/wearable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"data": {
"replaces": [],
"hides": [],
"tags": [
"special",
"new",
"eyebrows"
],
"representations": [
{
"bodyShapes": [
"urn:decentraland:off-chain:base-avatars:BaseMale",
"urn:decentraland:off-chain:base-avatars:BaseFemale"
],
"mainFile": "glasses.glb",
"contents": [
"glasses.glb"
],
"overrideHides": [],
"overrideReplaces": []
}
],
"category": "eyewear"
},
"name": "Smart Wearable Example",
"description": "Put the glasses to see a new world",
"rarity": "mythic"
}

0 comments on commit 6b8ca94

Please sign in to comment.