From c13aef29bb1b519384e6667b91dc8a7b40822663 Mon Sep 17 00:00:00 2001 From: Nicolas Echezarreta Date: Fri, 15 Nov 2024 10:34:55 -0800 Subject: [PATCH 1/8] fix gizmo icon (#1038) --- .../src/components/Toolbar/Gizmos/Gizmos.css | 21 ++++++++++--------- .../src/components/Toolbar/Gizmos/Gizmos.tsx | 12 +++++------ .../components/Toolbar/Gizmos/icons/free.svg | 7 ++----- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.css b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.css index e820761ad..a0d8920eb 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.css +++ b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.css @@ -9,10 +9,17 @@ height: 100%; } -.Gizmos .gizmo.position { - margin-right: 1px; +.Gizmos .gizmo.free { + margin: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; + background-image: url(./icons/free.svg); +} + +.Gizmos .gizmo.position { + margin-left: 1px; + margin-right: 1px; + border-radius: 0px; background-image: url(./icons/position.svg); } @@ -24,16 +31,10 @@ .Gizmos .gizmo.scale { margin-left: 1px; - margin-right: 0; - border-radius: 0; - background-image: url(./icons/scale.svg); -} - -.Gizmos .gizmo.free { - margin-left: 1px; + margin-right: 3px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; - background-image: url(./icons/free.svg); + background-image: url(./icons/scale.svg); } .Gizmos .open-panel { diff --git a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.tsx b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.tsx index 5ade57a44..024c854ff 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.tsx +++ b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/Gizmos.tsx @@ -70,6 +70,12 @@ export const Gizmos = withSdk(({ sdk }) => { return (
+ { onClick={handleScaleGizmo} title="Scaling tool" /> -
diff --git a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/icons/free.svg b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/icons/free.svg index dcbe9d1ca..28cd150bb 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/Gizmos/icons/free.svg +++ b/packages/@dcl/inspector/src/components/Toolbar/Gizmos/icons/free.svg @@ -1,6 +1,3 @@ - - - - - + + From 2c002ca9e6feb98a771337190db2945e013d7b93 Mon Sep 17 00:00:00 2001 From: Alejandro Alvarez Melucci <163010988+AlejandroAlvarezMelucciDCL@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:45:14 -0300 Subject: [PATCH 2/8] chore: helper functions for InputModifier (#1035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial implementation of helper function for InputModifier * Updated protocol, regenerated API and run a lint pass * Added addStandardModifier helper function * Fixed properties naming * Helper function iteration. Added documentation. * Helper function iteration. Added documentation. * Added helper function to @dcl/ecs/src/components/Index.ts * Lint fix * Updated index.ts and types.ts * Updated API * Updated types.ts and API * fix tests * Updated InputModifier helper and tests * update playground assetsmd * Removed unused import --------- Co-authored-by: Gonzalo DCL --- package-lock.json | 14 +++--- package.json | 2 +- .../src/components/extended/InputModifier.ts | 44 +++++++++++++++++++ packages/@dcl/ecs/src/components/index.ts | 5 +++ packages/@dcl/ecs/src/components/types.ts | 1 + packages/@dcl/ecs/src/index.ts | 5 ++- .../etc/playground-assets.api.md | 17 ++++++- packages/@dcl/sdk-commands/package-lock.json | 14 +++--- packages/@dcl/sdk-commands/package.json | 2 +- test/ecs/components/InputModifier.spec.ts | 15 +++++++ test/ecs/components/Material.spec.ts | 8 +++- test/ecs/components/UiBackground.spec.ts | 12 ++--- .../static-scene.test.ts.crdt | 6 +-- .../testing-fw.test.ts.crdt | 6 +-- .../two-way-crdt.test.ts.crdt | 6 +-- test/snapshots/package-lock.json | 2 +- .../append-value-crdt.ts.crdt | 6 +-- .../production-bundles/billboard.ts.crdt | 6 +-- .../production-bundles/cube-deleted.ts.crdt | 6 +-- .../snapshots/production-bundles/cube.ts.crdt | 6 +-- .../production-bundles/cubes.ts.crdt | 6 +-- .../production-bundles/pointer-events.ts.crdt | 6 +-- .../schema-components.ts.crdt | 6 +-- test/snapshots/production-bundles/ui.ts.crdt | 6 +-- .../with-main-function.ts.crdt | 6 +-- 25 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 packages/@dcl/ecs/src/components/extended/InputModifier.ts diff --git a/package-lock.json b/package-lock.json index 75c4d21cf..4732002bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "Apache-2.0", "dependencies": { "@actions/core": "^1.10.0", - "@dcl/protocol": "1.0.0-11599848164.commit-ef74edc", + "@dcl/protocol": "1.0.0-11780504028.commit-c886080", "@dcl/quickjs-emscripten": "^0.21.0-3680274614.commit-1808aa1", "@dcl/ts-proto": "1.153.0", "@types/fs-extra": "^9.0.12", @@ -577,9 +577,9 @@ } }, "node_modules/@dcl/protocol": { - "version": "1.0.0-11599848164.commit-ef74edc", - "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11599848164.commit-ef74edc.tgz", - "integrity": "sha512-XSUOA0LbchlBUk5/BMJFBl0+qOm4UOIxwMGp38A1n4LQAatGO/RXjzReJRveqiFma+X7eq3e6+KCDqTLwepN0w==", + "version": "1.0.0-11780504028.commit-c886080", + "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11780504028.commit-c886080.tgz", + "integrity": "sha512-R2OnlV2yZ6S4+Kcf796v8v+HI6TwCGBXitbDBUwUczukrSbw8u9mpGXBi08SPoV025DQs5AA6XXWkfb2uDNUZg==", "dependencies": { "@dcl/ts-proto": "1.154.0" } @@ -8232,9 +8232,9 @@ } }, "@dcl/protocol": { - "version": "1.0.0-11599848164.commit-ef74edc", - "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11599848164.commit-ef74edc.tgz", - "integrity": "sha512-XSUOA0LbchlBUk5/BMJFBl0+qOm4UOIxwMGp38A1n4LQAatGO/RXjzReJRveqiFma+X7eq3e6+KCDqTLwepN0w==", + "version": "1.0.0-11780504028.commit-c886080", + "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11780504028.commit-c886080.tgz", + "integrity": "sha512-R2OnlV2yZ6S4+Kcf796v8v+HI6TwCGBXitbDBUwUczukrSbw8u9mpGXBi08SPoV025DQs5AA6XXWkfb2uDNUZg==", "requires": { "@dcl/ts-proto": "1.154.0" }, diff --git a/package.json b/package.json index 28538db1a..c8821a791 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "bugs": "https://github.com/decentraland/js-sdk-toolchain/issues", "dependencies": { "@actions/core": "^1.10.0", - "@dcl/protocol": "1.0.0-11599848164.commit-ef74edc", + "@dcl/protocol": "1.0.0-11780504028.commit-c886080", "@dcl/quickjs-emscripten": "^0.21.0-3680274614.commit-1808aa1", "@dcl/ts-proto": "1.153.0", "@types/fs-extra": "^9.0.12", diff --git a/packages/@dcl/ecs/src/components/extended/InputModifier.ts b/packages/@dcl/ecs/src/components/extended/InputModifier.ts new file mode 100644 index 000000000..677e09dea --- /dev/null +++ b/packages/@dcl/ecs/src/components/extended/InputModifier.ts @@ -0,0 +1,44 @@ +import { IEngine, LastWriteWinElementSetComponentDefinition } from '../../engine' +import { InputModifier, PBInputModifier_StandardInput, PBInputModifier } from '../generated/index.gen' +import {} from '../generated/InputModifier.gen' + +/** + * @public + */ +export interface InputModifierHelper { + /** + * @returns a input modifier mode + */ + Standard: (standard: PBInputModifier_StandardInput) => PBInputModifier['mode'] +} + +/** + * @public + */ +export interface InputModifierComponentDefinitionExtended + extends LastWriteWinElementSetComponentDefinition { + /** + * InputModifier helper with constructor + */ + Mode: InputModifierHelper +} + +const InputModifierHelper: InputModifierHelper = { + Standard(standard: PBInputModifier_StandardInput) { + return { + $case: 'standard', + standard + } + } +} + +export function defineInputModifierComponent( + engine: Pick +): InputModifierComponentDefinitionExtended { + const theComponent = InputModifier(engine) + + return { + ...theComponent, + Mode: InputModifierHelper + } +} diff --git a/packages/@dcl/ecs/src/components/index.ts b/packages/@dcl/ecs/src/components/index.ts index c79a06abb..12649e375 100644 --- a/packages/@dcl/ecs/src/components/index.ts +++ b/packages/@dcl/ecs/src/components/index.ts @@ -15,6 +15,7 @@ import { defineTransformComponent, TransformComponentExtended } from './manual/T import { AudioStreamComponentDefinitionExtended, defineAudioStreamComponent } from './extended/AudioStream' import { MediaState } from './generated/pb/decentraland/sdk/components/common/media_state.gen' import { defineVirtualCameraComponent, VirtualCameraComponentDefinitionExtended } from './extended/VirtualCamera' +import { defineInputModifierComponent, InputModifierComponentDefinitionExtended } from './extended/InputModifier' export * from './generated/index.gen' @@ -60,6 +61,10 @@ export const Tween: LwwComponentGetter = (engi export const VirtualCamera: LwwComponentGetter = (engine) => defineVirtualCameraComponent(engine) +/* @__PURE__*/ +export const InputModifier: LwwComponentGetter = (engine) => + defineInputModifierComponent(engine) + /** * @alpha */ diff --git a/packages/@dcl/ecs/src/components/types.ts b/packages/@dcl/ecs/src/components/types.ts index a4aba8886..42359f73e 100644 --- a/packages/@dcl/ecs/src/components/types.ts +++ b/packages/@dcl/ecs/src/components/types.ts @@ -11,3 +11,4 @@ export type { NameComponent, NameType } from './manual/Name' export type { ISyncComponents, ISyncComponentsType } from './manual/SyncComponents' export type { INetowrkEntity, INetowrkEntityType } from './manual/NetworkEntity' export type { INetowrkParent, INetowrkParentType } from './manual/NetworkParent' +export type { InputModifierHelper, InputModifierComponentDefinitionExtended } from './extended/InputModifier' diff --git a/packages/@dcl/ecs/src/index.ts b/packages/@dcl/ecs/src/index.ts index 4fe185663..e1b10d6fa 100644 --- a/packages/@dcl/ecs/src/index.ts +++ b/packages/@dcl/ecs/src/index.ts @@ -32,7 +32,8 @@ import { TweenComponentDefinitionExtended, INetowrkEntity, INetowrkParent, - VirtualCameraComponentDefinitionExtended + VirtualCameraComponentDefinitionExtended, + InputModifierComponentDefinitionExtended } from './components/types' import { NameComponent } from './components/manual/Name' @@ -47,6 +48,8 @@ export const MeshCollider: MeshColliderComponentDefinitionExtended = /* @__PURE_ export const Name: NameComponent = components.Name(engine) export const Tween: TweenComponentDefinitionExtended = /* @__PURE__*/ components.Tween(engine) export const VirtualCamera: VirtualCameraComponentDefinitionExtended = /* @__PURE__*/ components.VirtualCamera(engine) +export const InputModifier: InputModifierComponentDefinitionExtended = /* @__PURE__*/ components.InputModifier(engine) + /** * @alpha * This is going to be used for sync components through a server. diff --git a/packages/@dcl/playground-assets/etc/playground-assets.api.md b/packages/@dcl/playground-assets/etc/playground-assets.api.md index ab07bea40..ac86f6d71 100644 --- a/packages/@dcl/playground-assets/etc/playground-assets.api.md +++ b/packages/@dcl/playground-assets/etc/playground-assets.api.md @@ -1421,8 +1421,21 @@ export const enum InputAction { IA_WALK = 9 } +// Warning: (ae-missing-release-tag) "InputModifier" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const InputModifier: InputModifierComponentDefinitionExtended; + +// @public (undocumented) +export interface InputModifierComponentDefinitionExtended extends LastWriteWinElementSetComponentDefinition { + Mode: InputModifierHelper; +} + // @public (undocumented) -export const InputModifier: LastWriteWinElementSetComponentDefinition; +export interface InputModifierHelper { + // (undocumented) + Standard: (standard: PBInputModifier_StandardInput) => PBInputModifier['mode']; +} // @public export const inputSystem: IInputSystem; @@ -3891,8 +3904,10 @@ export const TextShape: LastWriteWinElementSetComponentDefinition; // @public (undocumented) export interface Texture { filterMode?: TextureFilterMode | undefined; + offset?: PBVector2 | undefined; // (undocumented) src: string; + tiling?: PBVector2 | undefined; wrapMode?: TextureWrapMode | undefined; } diff --git a/packages/@dcl/sdk-commands/package-lock.json b/packages/@dcl/sdk-commands/package-lock.json index d4487cfa7..d8a53355a 100644 --- a/packages/@dcl/sdk-commands/package-lock.json +++ b/packages/@dcl/sdk-commands/package-lock.json @@ -15,7 +15,7 @@ "@dcl/inspector": "file:../inspector", "@dcl/linker-dapp": "^0.14.2", "@dcl/mini-comms": "1.0.1-20230216163137.commit-a4c75be", - "@dcl/protocol": "1.0.0-11599848164.commit-ef74edc", + "@dcl/protocol": "1.0.0-11780504028.commit-c886080", "@dcl/quests-client": "^1.0.3", "@dcl/quests-manager": "^0.1.4", "@dcl/rpc": "^1.1.1", @@ -239,9 +239,9 @@ } }, "node_modules/@dcl/protocol": { - "version": "1.0.0-11599848164.commit-ef74edc", - "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11599848164.commit-ef74edc.tgz", - "integrity": "sha512-XSUOA0LbchlBUk5/BMJFBl0+qOm4UOIxwMGp38A1n4LQAatGO/RXjzReJRveqiFma+X7eq3e6+KCDqTLwepN0w==", + "version": "1.0.0-11780504028.commit-c886080", + "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11780504028.commit-c886080.tgz", + "integrity": "sha512-R2OnlV2yZ6S4+Kcf796v8v+HI6TwCGBXitbDBUwUczukrSbw8u9mpGXBi08SPoV025DQs5AA6XXWkfb2uDNUZg==", "dependencies": { "@dcl/ts-proto": "1.154.0" } @@ -3228,9 +3228,9 @@ } }, "@dcl/protocol": { - "version": "1.0.0-11599848164.commit-ef74edc", - "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11599848164.commit-ef74edc.tgz", - "integrity": "sha512-XSUOA0LbchlBUk5/BMJFBl0+qOm4UOIxwMGp38A1n4LQAatGO/RXjzReJRveqiFma+X7eq3e6+KCDqTLwepN0w==", + "version": "1.0.0-11780504028.commit-c886080", + "resolved": "https://registry.npmjs.org/@dcl/protocol/-/protocol-1.0.0-11780504028.commit-c886080.tgz", + "integrity": "sha512-R2OnlV2yZ6S4+Kcf796v8v+HI6TwCGBXitbDBUwUczukrSbw8u9mpGXBi08SPoV025DQs5AA6XXWkfb2uDNUZg==", "requires": { "@dcl/ts-proto": "1.154.0" } diff --git a/packages/@dcl/sdk-commands/package.json b/packages/@dcl/sdk-commands/package.json index aebff11ff..87a02d627 100644 --- a/packages/@dcl/sdk-commands/package.json +++ b/packages/@dcl/sdk-commands/package.json @@ -13,7 +13,7 @@ "@dcl/inspector": "file:../inspector", "@dcl/linker-dapp": "^0.14.2", "@dcl/mini-comms": "1.0.1-20230216163137.commit-a4c75be", - "@dcl/protocol": "1.0.0-11599848164.commit-ef74edc", + "@dcl/protocol": "1.0.0-11780504028.commit-c886080", "@dcl/quests-client": "^1.0.3", "@dcl/quests-manager": "^0.1.4", "@dcl/rpc": "^1.1.1", diff --git a/test/ecs/components/InputModifier.spec.ts b/test/ecs/components/InputModifier.spec.ts index 9cf6234c1..153a08e4d 100644 --- a/test/ecs/components/InputModifier.spec.ts +++ b/test/ecs/components/InputModifier.spec.ts @@ -20,4 +20,19 @@ describe('Generated InputModifier ProtoBuf', () => { } }) }) + it('should test Standar modifier', () => { + const newEngine = Engine() + const InputModifier = components.InputModifier(newEngine) + + testComponentSerialization(InputModifier, { + mode: InputModifier.Mode.Standard({ + disableAll: true, + disableWalk: true, + disableJog: true, + disableRun: true, + disableJump: true, + disableEmote: true + }) + }) + }) }) diff --git a/test/ecs/components/Material.spec.ts b/test/ecs/components/Material.spec.ts index a5f9c8e68..f10d1e517 100644 --- a/test/ecs/components/Material.spec.ts +++ b/test/ecs/components/Material.spec.ts @@ -77,7 +77,9 @@ describe('Generated Material ProtoBuf', () => { texture: { wrapMode: TextureWrapMode.TWM_MIRROR, filterMode: TextureFilterMode.TFM_POINT, - src: 'not-casla' + src: 'not-casla', + tiling: undefined, + offset: undefined } } }, @@ -87,7 +89,9 @@ describe('Generated Material ProtoBuf', () => { texture: { wrapMode: TextureWrapMode.TWM_MIRROR, filterMode: TextureFilterMode.TFM_TRILINEAR, - src: 'not-casla' + src: 'not-casla', + tiling: undefined, + offset: undefined } } }, diff --git a/test/ecs/components/UiBackground.spec.ts b/test/ecs/components/UiBackground.spec.ts index d260a7eea..066dbc744 100644 --- a/test/ecs/components/UiBackground.spec.ts +++ b/test/ecs/components/UiBackground.spec.ts @@ -21,15 +21,17 @@ describe('Generated UiBackground ProtoBuf', () => { texture: { src: 'some-src', wrapMode: TextureWrapMode.TWM_CLAMP, - filterMode: TextureFilterMode.TFM_BILINEAR + filterMode: TextureFilterMode.TFM_BILINEAR, + tiling: undefined, + offset: undefined } } }, textureSlices: { - top: 1 / 3, - left: 1 / 3, - right: 1 / 3, - bottom: 1 / 3 + top: 2, + left: 2, + right: 2, + bottom: 2 }, uvs: [] }) diff --git a/test/snapshots/development-bundles/static-scene.test.ts.crdt b/test/snapshots/development-bundles/static-scene.test.ts.crdt index e1a15a89a..6c76e3741 100644 --- a/test/snapshots/development-bundles/static-scene.test.ts.crdt +++ b/test/snapshots/development-bundles/static-scene.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=452.9k bytes +SCENE_COMPILED_JS_SIZE_PROD=453.4k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -11,7 +11,7 @@ EVAL test/snapshots/development-bundles/static-scene.test.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 54k - MALLOC_COUNT = 13672 + MALLOC_COUNT = 13673 ALIVE_OBJS_DELTA ~= 2.70k CALL onStart() main.crdt: PUT_COMPONENT e=0x200 c=1 t=0 data={"position":{"x":5.880000114440918,"y":2.7916901111602783,"z":7.380000114440918},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0} @@ -56,4 +56,4 @@ CALL onUpdate(0.1) OPCODES ~= 3k MALLOC_COUNT = -5 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1193.93k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1194.79k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/testing-fw.test.ts.crdt b/test/snapshots/development-bundles/testing-fw.test.ts.crdt index 9402cfc5f..95791a98d 100644 --- a/test/snapshots/development-bundles/testing-fw.test.ts.crdt +++ b/test/snapshots/development-bundles/testing-fw.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=453.4k bytes +SCENE_COMPILED_JS_SIZE_PROD=454k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -11,7 +11,7 @@ EVAL test/snapshots/development-bundles/testing-fw.test.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 63k - MALLOC_COUNT = 14193 + MALLOC_COUNT = 14194 ALIVE_OBJS_DELTA ~= 2.85k CALL onStart() LOG: ["Adding one to position.y=0"] @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 5k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 1202.54k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1203.40k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt index e9f2e6996..c8d7770eb 100644 --- a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt +++ b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=453.4k bytes +SCENE_COMPILED_JS_SIZE_PROD=454k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -11,7 +11,7 @@ EVAL test/snapshots/development-bundles/two-way-crdt.test.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 63k - MALLOC_COUNT = 14193 + MALLOC_COUNT = 14194 ALIVE_OBJS_DELTA ~= 2.85k CALL onStart() LOG: ["Adding one to position.y=0"] @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 5k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 1202.55k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1203.41k bytes \ No newline at end of file diff --git a/test/snapshots/package-lock.json b/test/snapshots/package-lock.json index 401ef4a4b..22767d9a8 100644 --- a/test/snapshots/package-lock.json +++ b/test/snapshots/package-lock.json @@ -168,7 +168,7 @@ "@dcl/inspector": "file:../inspector", "@dcl/linker-dapp": "^0.14.2", "@dcl/mini-comms": "1.0.1-20230216163137.commit-a4c75be", - "@dcl/protocol": "1.0.0-11599848164.commit-ef74edc", + "@dcl/protocol": "1.0.0-11780504028.commit-c886080", "@dcl/quests-client": "^1.0.3", "@dcl/quests-manager": "^0.1.4", "@dcl/rpc": "^1.1.1", diff --git a/test/snapshots/production-bundles/append-value-crdt.ts.crdt b/test/snapshots/production-bundles/append-value-crdt.ts.crdt index bf88349ba..9a082d9b5 100644 --- a/test/snapshots/production-bundles/append-value-crdt.ts.crdt +++ b/test/snapshots/production-bundles/append-value-crdt.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=197.8k bytes +SCENE_COMPILED_JS_SIZE_PROD=198.1k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/append-value-crdt.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 65k - MALLOC_COUNT = 12721 + MALLOC_COUNT = 12723 ALIVE_OBJS_DELTA ~= 2.84k CALL onStart() Renderer: APPEND_VALUE e=0x200 c=1063 t=0 data={"button":0,"hit":{"position":{"x":1,"y":2,"z":3},"globalOrigin":{"x":1,"y":2,"z":3},"direction":{"x":1,"y":2,"z":3},"normalHit":{"x":1,"y":2,"z":3},"length":10,"meshName":"mesh","entityId":512},"state":1,"timestamp":1,"analog":5,"tickNumber":0} @@ -55,4 +55,4 @@ CALL onUpdate(0.1) OPCODES ~= 14k MALLOC_COUNT = 31 ALIVE_OBJS_DELTA ~= 0.01k - MEMORY_USAGE_COUNT ~= 899.15k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 899.73k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/billboard.ts.crdt b/test/snapshots/production-bundles/billboard.ts.crdt index d70fca77a..545259257 100644 --- a/test/snapshots/production-bundles/billboard.ts.crdt +++ b/test/snapshots/production-bundles/billboard.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=230.9k bytes +SCENE_COMPILED_JS_SIZE_PROD=231.2k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/billboard.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 66k - MALLOC_COUNT = 14834 + MALLOC_COUNT = 14836 ALIVE_OBJS_DELTA ~= 3.24k CALL onStart() OPCODES ~= 0k @@ -77,4 +77,4 @@ CALL onUpdate(0.1) OPCODES ~= 9k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1041.51k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1042.09k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cube-deleted.ts.crdt b/test/snapshots/production-bundles/cube-deleted.ts.crdt index b4ffc63a1..21ab80d21 100644 --- a/test/snapshots/production-bundles/cube-deleted.ts.crdt +++ b/test/snapshots/production-bundles/cube-deleted.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=194.1k bytes +SCENE_COMPILED_JS_SIZE_PROD=194.3k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/cube-deleted.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 55k - MALLOC_COUNT = 11876 + MALLOC_COUNT = 11878 ALIVE_OBJS_DELTA ~= 2.62k CALL onStart() OPCODES ~= 0k @@ -42,4 +42,4 @@ CALL onUpdate(0.1) OPCODES ~= 5k MALLOC_COUNT = 1 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 861.88k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 862.46k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cube.ts.crdt b/test/snapshots/production-bundles/cube.ts.crdt index c9344e0fc..e4f4dadc8 100644 --- a/test/snapshots/production-bundles/cube.ts.crdt +++ b/test/snapshots/production-bundles/cube.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=193.9k bytes +SCENE_COMPILED_JS_SIZE_PROD=194.2k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/cube.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 55k - MALLOC_COUNT = 11849 + MALLOC_COUNT = 11851 ALIVE_OBJS_DELTA ~= 2.61k CALL onStart() OPCODES ~= 0k @@ -32,4 +32,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 851.79k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 852.37k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cubes.ts.crdt b/test/snapshots/production-bundles/cubes.ts.crdt index 59c41b18b..1f61d0820 100644 --- a/test/snapshots/production-bundles/cubes.ts.crdt +++ b/test/snapshots/production-bundles/cubes.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=231.2k bytes +SCENE_COMPILED_JS_SIZE_PROD=231.5k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/cubes.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 105k - MALLOC_COUNT = 18176 + MALLOC_COUNT = 18178 ALIVE_OBJS_DELTA ~= 4.53k CALL onStart() OPCODES ~= 0k @@ -1652,4 +1652,4 @@ CALL onUpdate(0.1) OPCODES ~= 691k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1177.44k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1178.02k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/pointer-events.ts.crdt b/test/snapshots/production-bundles/pointer-events.ts.crdt index 5c7da6d40..271ed5cce 100644 --- a/test/snapshots/production-bundles/pointer-events.ts.crdt +++ b/test/snapshots/production-bundles/pointer-events.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=194.9k bytes +SCENE_COMPILED_JS_SIZE_PROD=195.2k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/pointer-events.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 56k - MALLOC_COUNT = 12135 + MALLOC_COUNT = 12137 ALIVE_OBJS_DELTA ~= 2.70k CALL onStart() OPCODES ~= 0k @@ -43,4 +43,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 869.52k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 870.10k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/schema-components.ts.crdt b/test/snapshots/production-bundles/schema-components.ts.crdt index 621522880..ff93c6a98 100644 --- a/test/snapshots/production-bundles/schema-components.ts.crdt +++ b/test/snapshots/production-bundles/schema-components.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=194k bytes +SCENE_COMPILED_JS_SIZE_PROD=194.3k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/schema-components.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 59k - MALLOC_COUNT = 11976 + MALLOC_COUNT = 11978 ALIVE_OBJS_DELTA ~= 2.65k CALL onStart() OPCODES ~= 0k @@ -31,4 +31,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 854.21k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 854.79k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/ui.ts.crdt b/test/snapshots/production-bundles/ui.ts.crdt index eb282069d..fe8c7f005 100644 --- a/test/snapshots/production-bundles/ui.ts.crdt +++ b/test/snapshots/production-bundles/ui.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=351k bytes +SCENE_COMPILED_JS_SIZE_PROD=351.3k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/ui.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 65k - MALLOC_COUNT = 19447 + MALLOC_COUNT = 19449 ALIVE_OBJS_DELTA ~= 3.87k CALL onStart() OPCODES ~= 0k @@ -65,4 +65,4 @@ CALL onUpdate(0.1) OPCODES ~= 63k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1686.56k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1687.14k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/with-main-function.ts.crdt b/test/snapshots/production-bundles/with-main-function.ts.crdt index 3d6eef012..e35c0d92a 100644 --- a/test/snapshots/production-bundles/with-main-function.ts.crdt +++ b/test/snapshots/production-bundles/with-main-function.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=194.6k bytes +SCENE_COMPILED_JS_SIZE_PROD=194.9k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/with-main-function.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 55k - MALLOC_COUNT = 12005 + MALLOC_COUNT = 12007 ALIVE_OBJS_DELTA ~= 2.65k CALL onStart() OPCODES ~= 0k @@ -37,4 +37,4 @@ CALL onUpdate(0.1) OPCODES ~= 3k MALLOC_COUNT = 5 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 867.33k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 867.92k bytes \ No newline at end of file From cf41aed618c96b2b4bfc60c7a7a6e42dfb89c701 Mon Sep 17 00:00:00 2001 From: Nicolas Echezarreta Date: Mon, 25 Nov 2024 03:32:55 -0800 Subject: [PATCH 3/8] Add multi entity transform + gizmos refactor (#1037) * set gizmo free movement as default * split to SingleEntityInspector & MultipleEntityInspector * magic stuff for multiple entity Transform * magic stuff for multiple entity Transform #2 * add '--' for inputs * refactor gizmo manager --- .../EntityHeader/EntityHeader.tsx | 2 +- .../EntityInspector/EntityInspector.tsx | 48 +++- .../TransformInspector/TransformInspector.tsx | 9 +- .../TransformInspector/types.ts | 2 +- .../src/components/ui/TextField/TextField.tsx | 2 +- .../src/hooks/sdk/useComponentInput.ts | 183 +++++++++++++- .../src/hooks/sdk/useComponentValue.ts | 9 +- .../editorComponents/selection.ts | 18 +- .../decentraland/gizmo-manager.spec.ts | 11 +- .../lib/babylon/decentraland/gizmo-manager.ts | 236 +++++++----------- .../decentraland/sdkComponents/transform.ts | 8 +- .../sdk/operations/update-selected-entity.ts | 2 +- 12 files changed, 338 insertions(+), 192 deletions(-) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/EntityHeader/EntityHeader.tsx b/packages/@dcl/inspector/src/components/EntityInspector/EntityHeader/EntityHeader.tsx index 90abcc9dc..58ef574d1 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/EntityHeader/EntityHeader.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/EntityHeader/EntityHeader.tsx @@ -32,7 +32,7 @@ interface ModalState { cb?: () => void } -const getLabel = (sdk: SdkContextValue, entity: Entity) => { +export const getLabel = (sdk: SdkContextValue, entity: Entity) => { const nameComponent = sdk.components.Name.getOrNull(entity) switch (entity) { case ROOT: diff --git a/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx index 79fb78f24..528cfbfa8 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx @@ -1,10 +1,12 @@ +import { Entity } from '@dcl/ecs' import { useEffect, useMemo, useState } from 'react' import { withSdk } from '../../hoc/withSdk' import { useChange } from '../../hooks/sdk/useChange' -import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity' +import { useEntitiesWith } from '../../hooks/sdk/useEntitiesWith' import { useAppSelector } from '../../redux/hooks' import { getHiddenComponents } from '../../redux/ui' +import { EDITOR_ENTITIES } from '../../lib/sdk/tree' import { GltfInspector } from './GltfInspector' import { ActionInspector } from './ActionInspector' @@ -32,8 +34,42 @@ import { SmartItemBasicView } from './SmartItemBasicView' import './EntityInspector.css' -export const EntityInspector = withSdk(({ sdk }) => { - const entity = useSelectedEntity() +export function EntityInspector() { + const selectedEntities = useEntitiesWith((components) => components.Selection) + const ownedEntities = useMemo( + () => selectedEntities.filter((entity) => !EDITOR_ENTITIES.includes(entity)), + [selectedEntities] + ) + const entity = useMemo(() => (selectedEntities.length > 0 ? selectedEntities[0] : null), [selectedEntities]) + + if (ownedEntities.length > 1) { + return + } + + return +} + +const MultiEntityInspector = withSdk<{ entities: Entity[] }>(({ sdk, entities }) => { + const hiddenComponents = useAppSelector(getHiddenComponents) + const inspectors = useMemo( + () => [{ name: sdk.components.Transform.componentName, component: TransformInspector }], + [sdk] + ) + + return ( +
+
+
{entities.length} entities selected
+
+ {inspectors.map( + ({ name, component: Inspector }, index) => + !hiddenComponents[name] && + )} +
+ ) +}) + +const SingleEntityInspector = withSdk<{ entity: Entity | null }>(({ sdk, entity }) => { const hiddenComponents = useAppSelector(getHiddenComponents) const [isBasicViewEnabled, setIsBasicViewEnabled] = useState(false) @@ -123,20 +159,20 @@ export const EntityInspector = withSdk(({ sdk }) => { ) return ( -
+
{entity !== null ? ( <> {inspectors.map( ({ name, component: Inspector }, index) => - !hiddenComponents[name] && + !hiddenComponents[name] && )} {isBasicViewEnabled ? ( ) : ( advancedInspectorComponents.map( ({ name, component: Inspector }, index) => - !hiddenComponents[name] && + !hiddenComponents[name] && ) )} diff --git a/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/TransformInspector.tsx b/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/TransformInspector.tsx index c5b073f9d..3217ca8bb 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/TransformInspector.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/TransformInspector.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react' -import { isValidNumericInput, useComponentInput } from '../../../hooks/sdk/useComponentInput' +import { isValidNumericInput, useComponentInput, useMultiComponentInput } from '../../../hooks/sdk/useComponentInput' import { useHasComponent } from '../../../hooks/sdk/useHasComponent' import { withSdk } from '../../../hoc/withSdk' @@ -13,14 +13,15 @@ import { Link, Props as LinkProps } from './Link' import './TransformInspector.css' -export default withSdk(({ sdk, entity }) => { +export default withSdk(({ sdk, entities }) => { const { Transform, TransformConfig } = sdk.components + const entity = entities.find((entity) => Transform.has(entity)) || entities[0] const hasTransform = useHasComponent(entity, Transform) const transform = Transform.getOrNull(entity) ?? undefined const config = TransformConfig.getOrNull(entity) ?? undefined - const { getInputProps } = useComponentInput( - entity, + const { getInputProps } = useMultiComponentInput( + entities, Transform, fromTransform, toTransform(transform, config), diff --git a/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/types.ts b/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/types.ts index 037115990..d0aead046 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/types.ts +++ b/packages/@dcl/inspector/src/components/EntityInspector/TransformInspector/types.ts @@ -1,7 +1,7 @@ import { Entity } from '@dcl/ecs' export interface Props { - entity: Entity + entities: Entity[] } export type TransformInput = { diff --git a/packages/@dcl/inspector/src/components/ui/TextField/TextField.tsx b/packages/@dcl/inspector/src/components/ui/TextField/TextField.tsx index 27df8483d..09c98dfff 100644 --- a/packages/@dcl/inspector/src/components/ui/TextField/TextField.tsx +++ b/packages/@dcl/inspector/src/components/ui/TextField/TextField.tsx @@ -123,7 +123,7 @@ const TextField = React.forwardRef((props, ref) => { @@ -15,6 +18,9 @@ export function isValidNumericInput(input: Input[keyof Input]): boolean { if (typeof input === 'boolean') { return !!input } + if (typeof input === 'number') { + return !isNaN(input) + } return input.length > 0 && !isNaN(Number(input)) } @@ -70,10 +76,8 @@ export const useComponentInput = { + // Base case - if any value is not an object, compare directly + if (!values.every((val) => val && typeof val === 'object')) { + return values.every((val) => val === values[0]) ? values[0] : '--' + } + + // Get all keys from all objects + const allKeys = [...new Set(values.flatMap(Object.keys))] + + // Create result object + const result: any = {} + + // For each key, recursively merge values + for (const key of allKeys) { + const valuesForKey = values.map((obj) => obj[key]) + result[key] = mergeValues(valuesForKey) + } + + return result +} + +const mergeComponentValues = ( + values: ComponentValueType[], + fromComponentValueToInput: (componentValue: ComponentValueType) => InputType +): InputType => { + // Transform all component values to input format + const inputs = values.map(fromComponentValueToInput) + + // Get first input as reference + const firstInput = inputs[0] + + // Create result object with same shape as first input + const result = {} as InputType + + // For each key in first input + for (const key in firstInput) { + const valuesForKey = inputs.map((input) => input[key]) + result[key] = mergeValues(valuesForKey) + } + + return result +} + +const getEntityAndComponentValue = ( + entities: Entity[], + component: Component +): [Entity, ComponentValueType][] => { + return entities.map((entity) => [entity, getComponentValue(entity, component) as ComponentValueType]) +} + +export const useMultiComponentInput = ( + entities: Entity[], + component: Component, + fromComponentValueToInput: (componentValue: ComponentValueType) => InputType, + fromInputToComponentValue: (input: InputType) => ComponentValueType, + validateInput: (input: InputType) => boolean = () => true +) => { + // If there's only one entity, use the single entity version just to be safe for now + if (entities.length === 1) { + return useComponentInput( + entities[0], + component, + fromComponentValueToInput, + fromInputToComponentValue, + validateInput + ) + } + const sdk = useSdk() + + // Get initial merged value from all entities + const initialEntityValues = getEntityAndComponentValue(entities, component) + const initialMergedValue = useMemo( + () => + mergeComponentValues( + initialEntityValues.map(([_, component]) => component), + fromComponentValueToInput + ), + [] // only compute on mount + ) + + const [value, setMergeValue] = useState(initialMergedValue) + const [isValid, setIsValid] = useState(true) + const [isFocused, setIsFocused] = useState(false) + + // Handle input updates + const handleUpdate = useCallback( + (path: NestedKey, getter: (event: React.ChangeEvent) => any = (e) => e.target.value) => + (event: React.ChangeEvent) => { + if (!value) return + + const newValue = setValue(value, path, getter(event)) + if (!hasDiff(value, newValue, 2)) return + + // Only update if component is last-write-win and SDK exists + if (!isLastWriteWinComponent(component) || !sdk) { + setMergeValue(newValue) + return + } + + // Validate and update all entities + const entityUpdates = getEntityAndComponentValue(entities, component).map(([entity, componentValue]) => { + const updatedInput = setValue(fromComponentValueToInput(componentValue as any), path, getter(event)) + const newComponentValue = fromInputToComponentValue(updatedInput) + return { + entity, + value: newComponentValue, + isValid: validateInput(updatedInput) + } + }) + + const allUpdatesValid = entityUpdates.every(({ isValid }) => isValid) + + if (allUpdatesValid) { + entityUpdates.forEach(({ entity, value }) => { + sdk.operations.updateValue(component, entity, value) + }) + void sdk.operations.dispatch() + } + + setMergeValue(newValue) + setIsValid(allUpdatesValid) + }, + [value, sdk, component, entities, fromInputToComponentValue, fromComponentValueToInput, validateInput] + ) + + // Sync with engine changes + useChange( + (event) => { + const isRelevantUpdate = + entities.includes(event.entity) && + component.componentId === event.component?.componentId && + event.value && + event.operation === CrdtMessageType.PUT_COMPONENT + + if (!isRelevantUpdate) return + + const updatedEntityValues = getEntityAndComponentValue(entities, component) + const newMergedValue = mergeComponentValues( + updatedEntityValues.map(([_, component]) => component), + fromComponentValueToInput + ) + + if (!hasDiff(value, newMergedValue, 2) || isFocused) return + + setMergeValue(newMergedValue) + }, + [entities, component, fromComponentValueToInput, value, isFocused] + ) + + // Input props getter + const getInputProps = useCallback( + ( + path: NestedKey, + getter?: (event: React.ChangeEvent) => any + ): Pick, 'value' | 'onChange' | 'onFocus' | 'onBlur'> => ({ + value: (getValue(value, path) || '').toString(), + onChange: handleUpdate(path, getter), + onFocus: () => setIsFocused(true), + onBlur: () => setIsFocused(false) + }), + [value, handleUpdate] + ) + + return { getInputProps, isValid } +} diff --git a/packages/@dcl/inspector/src/hooks/sdk/useComponentValue.ts b/packages/@dcl/inspector/src/hooks/sdk/useComponentValue.ts index 83727769f..60cdcdb0a 100644 --- a/packages/@dcl/inspector/src/hooks/sdk/useComponentValue.ts +++ b/packages/@dcl/inspector/src/hooks/sdk/useComponentValue.ts @@ -28,12 +28,7 @@ export const useComponentValue = (entity: Entity, component: // sync state -> engine useEffect(() => { - if (value === null) return - const isEqualValue = !recursiveCheck(getComponentValue(entity, component), value, 2) - - if (isEqualValue) { - return - } + if (value === null || isComponentEqual(value)) return if (isLastWriteWinComponent(component) && sdk) { sdk.operations.updateValue(component, entity, value!) void sdk.operations.dispatch() @@ -48,7 +43,7 @@ export const useComponentValue = (entity: Entity, component: (event) => { if (entity === event.entity && component.componentId === event.component?.componentId && !!event.value) { if (event.operation === CrdtMessageType.PUT_COMPONENT) { - // TODO: This setValue is generating a isEqual comparission. + // TODO: This setValue is generating an isEqual comparission in previous effect. // Maybe we have to use two pure functions instead of an effect. // Same happens with the input & componentValue. setValue(event.value) diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/editorComponents/selection.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/editorComponents/selection.ts index 5ef0293db..3e98be78d 100644 --- a/packages/@dcl/inspector/src/lib/babylon/decentraland/editorComponents/selection.ts +++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/editorComponents/selection.ts @@ -53,28 +53,14 @@ export const setGizmoManager = (entity: EcsEntity, value: { gizmo: number }) => toggleSelection(entity, true) - const selectedEntities = Array.from(context.engine.getEntitiesWith(context.editorComponents.Selection)) const types = context.gizmos.getGizmoTypes() const type = types[value?.gizmo || 0] context.gizmos.setGizmoType(type) - - if (selectedEntities.length === 1) { - context.gizmos.setEntity(entity) - } else if (selectedEntities.length > 1) { - context.gizmos.repositionGizmoOnCentroid() - } + context.gizmos.addEntity(entity) } export const unsetGizmoManager = (entity: EcsEntity) => { const context = entity.context.deref()! - const selectedEntities = Array.from(context.engine.getEntitiesWith(context.editorComponents.Selection)) - const currentEntity = context.gizmos.getEntity() - toggleSelection(entity, false) - - if (currentEntity?.entityId === entity.entityId || selectedEntities.length === 0) { - context.gizmos.unsetEntity() - } else { - context.gizmos.repositionGizmoOnCentroid() - } + context.gizmos.removeEntity(entity) } diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.spec.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.spec.ts index 3fe518c54..ffa0e56bf 100644 --- a/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.spec.ts +++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.spec.ts @@ -65,14 +65,14 @@ describe('GizmoManager', () => { babylonEntity.rotationQuaternion = new Quaternion(0, 0, 0, 1) handler = jest.fn() gizmos.onChange(handler) - gizmos.setEntity(babylonEntity) + gizmos.addEntity(babylonEntity) entities = [dclEntity] nodes.push({ entity: dclEntity, children: [] }) }) afterEach(() => { babylonEntity.dispose() context.engine.removeEntity(dclEntity) - gizmos.unsetEntity() + gizmos.removeEntity(context.getOrCreateEntity(dclEntity)) entities = [] nodes = nodes.filter(($) => $.entity !== dclEntity) }) @@ -86,16 +86,11 @@ describe('GizmoManager', () => { it('should skip setting the entity', () => { const handler = jest.fn() gizmos.onChange(handler) - gizmos.setEntity(babylonEntity) + gizmos.addEntity(babylonEntity) expect(handler).not.toHaveBeenCalled() }) }) describe('and dragging a gizmo', () => { - it('should not execute SDK operations if transform was not changed', () => { - gizmos.gizmoManager.gizmos.positionGizmo?.onDragEndObservable.notifyObservers({} as any) - expect(context.operations.updateValue).toBeCalledTimes(0) - expect(context.operations.dispatch).toBeCalledTimes(0) - }) it('should execute SDK operations if transform was changed', () => { babylonEntity.position = new Vector3(10, 10, 10) gizmos.gizmoManager.gizmos.positionGizmo?.onDragEndObservable.notifyObservers({} as any) diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.ts index 8da5fd707..5501a8225 100644 --- a/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.ts +++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/gizmo-manager.ts @@ -3,7 +3,6 @@ import { IAxisDragGizmo, PickingInfo, Quaternion, - Node, Vector3, PointerDragBehavior, AbstractMesh, @@ -37,13 +36,8 @@ function areProportional(a: number, b: number) { return Math.abs(a - b) < 1e-5 } -// should be moved to ecs-math -function areQuaternionsEqual(a: DclQuaternion, b: DclQuaternion) { - return a.x === b.x && a.y === b.y && a.z === b.z && a.w === b.w -} - function calculateCenter(positions: Vector3[]): Vector3 { - if (positions.length === 0) throw new Error('No positions provided to calculate center') + if (positions.length === 0) new Vector3(0, 0, 0) const sum = positions.reduce((acc, pos) => { acc.x += pos.x @@ -71,21 +65,12 @@ export function createGizmoManager(context: SceneContext) { gizmoManager.gizmos.positionGizmo!.updateGizmoRotationToMatchAttachedMesh = false gizmoManager.gizmos.rotationGizmo!.updateGizmoRotationToMatchAttachedMesh = true - let lastEntity: EcsEntity | null = null + let selectedEntities: EcsEntity[] = [] let rotationGizmoAlignmentDisabled = false let positionGizmoAlignmentDisabled = false let shouldRestorRotationGizmoAlignment = false let shouldRestorPositionGizmoAlignment = false let isEnabled = true - const parentMapper: Map = new Map() - - function getSelectedEntities() { - return context.operations.getSelectedEntities() - } - - function areMultipleEntitiesSelected() { - return getSelectedEntities().length > 1 - } function fixRotationGizmoAlignment(value: TransformType) { const isProportional = @@ -117,23 +102,36 @@ export function createGizmoManager(context: SceneContext) { } } - function getTransform(entity?: EcsEntity): TransformType { - const _entity = entity ?? lastEntity - if (_entity) { - const parent = context.Transform.getOrNull(_entity.entityId)?.parent || (0 as Entity) - const value = { - position: gizmoManager.positionGizmoEnabled ? snapPosition(_entity.position) : _entity.position, - scale: gizmoManager.scaleGizmoEnabled ? snapScale(_entity.scaling) : _entity.scaling, - rotation: gizmoManager.rotationGizmoEnabled - ? _entity.rotationQuaternion - ? snapRotation(_entity.rotationQuaternion) - : Quaternion.Zero() - : _entity.rotationQuaternion ?? Quaternion.Zero(), - parent - } - return value - } else { - throw new Error('No entity selected') + function getFirstEntity() { + return selectedEntities[0] + } + + function getParent(entity: EcsEntity) { + return context.Transform.getOrNull(entity.entityId)?.parent || (0 as Entity) + } + + function computeWorldTransform(entity: EcsEntity): TransformType { + const { positionGizmoEnabled, scaleGizmoEnabled, rotationGizmoEnabled } = gizmoManager + // Compute the updated transform based on the current node position + const worldMatrix = entity.computeWorldMatrix(true) + const position = new Vector3() + const scale = new Vector3() + const rotation = new Quaternion() + worldMatrix.decompose(scale, rotation, position) + + return { + position: positionGizmoEnabled ? snapPosition(position) : position, + scale: scaleGizmoEnabled ? snapScale(scale) : scale, + rotation: rotationGizmoEnabled ? snapRotation(rotation) : rotation + } + } + + function getTransform(entity: EcsEntity): TransformType { + return { + position: entity.position, + scale: entity.scaling, + rotation: entity.rotationQuaternion ?? Quaternion.Zero(), + parent: getParent(entity) } } @@ -143,61 +141,39 @@ export function createGizmoManager(context: SceneContext) { position: DclVector3.create(position.x, position.y, position.z), rotation: DclQuaternion.create(rotation.x, rotation.y, rotation.z, rotation.w), scale: DclVector3.create(scale.x, scale.y, scale.z), - parent: parent + parent }) - void context.operations.dispatch() } + /** + * Updates the transform of all selected entities after a gizmo operation + * + * 1. Fixes rotation gizmo alignment based on the first selected entity's transform + * 2. For each selected entity: + * - Gets the original parent and resolves it to a valid entity or root node + * - Temporarily sets the entity's parent to handle transform calculations + * - Updates the entity's transform: + * - If parent is root node: Uses world space transform with snapping + * - Otherwise: Uses local space transform + * - Preserves the original parent relationship + * 3. Dispatches the transform updates to persist changes + */ function updateTransform() { - if (lastEntity === null) return - const oldTransform = context.Transform.get(lastEntity.entityId) - const newTransform = getTransform() - fixRotationGizmoAlignment(newTransform) - - // Remap all selected entities to the original parent - parentMapper.forEach((value, key, map) => { - if (key === lastEntity!.entityId) return - const entity = context.getEntityOrNull(key) - if (entity) { - entity.setParent(value) - map.delete(key) - } - }) + fixRotationGizmoAlignment(getTransform(getFirstEntity())) + for (const entity of selectedEntities) { + const originalParent = getParent(entity) + const parent = context.getEntityOrNull(originalParent ?? context.rootNode.entityId) - if ( - DclVector3.equals(newTransform.position, oldTransform.position) && - DclVector3.equals(newTransform.scale, oldTransform.scale) && - areQuaternionsEqual(newTransform.rotation, oldTransform.rotation) - ) - return - // Update last selected entity transform - updateEntityTransform(lastEntity.entityId, newTransform) - - // Update entity transform for all the selected entities - if (areMultipleEntitiesSelected()) { - for (const entityId of getSelectedEntities()) { - if (entityId === lastEntity.entityId) continue - const entity = context.getEntityOrNull(entityId)! - const transform = getTransform(entity) - updateEntityTransform(entityId, transform) - } - } - } + entity.setParent(parent) - function initTransform() { - if (lastEntity === null) return - if (areMultipleEntitiesSelected()) { - for (const entityId of getSelectedEntities()) { - if (entityId === lastEntity.entityId) continue - const entity = context.getEntityOrNull(entityId)! - parentMapper.set(entityId, entity.parent!) - entity.setParent(lastEntity) - } + updateEntityTransform(entity.entityId, { + ...(parent === context.rootNode ? computeWorldTransform(entity) : getTransform(entity)), + parent: originalParent + }) } - } - // Map to store the original parent of each entity - const originalParents = new Map() + void context.operations.dispatch() + } // Check if a transform node for the gizmo already exists, or create one function getDummyNode(): TransformNode { @@ -206,10 +182,17 @@ export function createGizmoManager(context: SceneContext) { return dummyNode } + function restoreParents() { + for (const entity of selectedEntities) { + const originalParent = getParent(entity) + const parent = context.getEntityOrNull(originalParent ?? context.rootNode.entityId) + entity.setParent(parent) + } + } + function repositionGizmoOnCentroid() { - const selectedEntities = getSelectedEntities().map((entityId) => context.getEntityOrNull(entityId)!) const positions = selectedEntities.map((entity) => { - const { x, y, z } = getTransform(entity).position + const { x, y, z } = computeWorldTransform(entity).position return new Vector3(x, y, z) }) const centroidPosition = calculateCenter(positions) @@ -219,34 +202,13 @@ export function createGizmoManager(context: SceneContext) { // so everything aligns to the right position afterwards. dummyNode.position = centroidPosition - // Store the original parents and set the dummy node as parent for each selected entity - selectedEntities.forEach((entity) => { - const parent = entity.parent as TransformNode | null - originalParents.set(entity.entityId, parent) + for (const entity of selectedEntities) { entity.setParent(dummyNode) - }) + } - // Attach the gizmo to the dummy node gizmoManager.attachToNode(dummyNode) } - function restoreOriginalParents() { - originalParents.forEach((parent, entity) => { - const ecsEntity = context.getEntityOrNull(entity)! - ecsEntity.setParent(parent) - }) - - // Clear the stored parents as they're now restored - originalParents.clear() - - // Detach the gizmo from the dummy node if needed - gizmoManager.attachToNode(null) - } - - gizmoManager.gizmos.scaleGizmo?.onDragStartObservable.add(initTransform) - gizmoManager.gizmos.positionGizmo?.onDragStartObservable.add(initTransform) - gizmoManager.gizmos.rotationGizmo?.onDragStartObservable.add(initTransform) - gizmoManager.gizmos.scaleGizmo?.onDragEndObservable.add(updateTransform) gizmoManager.gizmos.positionGizmo?.onDragEndObservable.add(updateTransform) gizmoManager.gizmos.rotationGizmo?.onDragEndObservable.add(updateTransform) @@ -300,9 +262,8 @@ export function createGizmoManager(context: SceneContext) { return () => events.off('change', cb) } - function unsetEntity() { - lastEntity = null - gizmoManager.attachToNode(lastEntity) + function removeGizmos() { + gizmoManager.attachToNode(null) gizmoManager.positionGizmoEnabled = false gizmoManager.rotationGizmoEnabled = false gizmoManager.scaleGizmoEnabled = false @@ -312,8 +273,9 @@ export function createGizmoManager(context: SceneContext) { function setEnabled(value: boolean) { if (value !== isEnabled) { isEnabled = value - if (!isEnabled && lastEntity) { - unsetEntity() + if (!isEnabled && selectedEntities.length > 0) { + restoreParents() + removeGizmos() } } } @@ -335,24 +297,23 @@ export function createGizmoManager(context: SceneContext) { }) context.scene.onPointerDown = function (_e, pickResult) { + const firstEntity = getFirstEntity() if ( - lastEntity === null || + !firstEntity || pickResult.pickedMesh === null || !gizmoManager.freeGizmoEnabled || - !context.Transform.getOrNull(lastEntity.entityId) + !context.Transform.getOrNull(firstEntity.entityId) ) return - const selectedEntities = getSelectedEntities().map((entityId) => context.getEntityOrNull(entityId)!) if (selectedEntities.some((entity) => pickResult.pickedMesh!.isDescendantOf(entity))) { - initTransform() - meshPointerDragBehavior.attach(lastEntity as unknown as AbstractMesh) + meshPointerDragBehavior.attach(firstEntity as unknown as AbstractMesh) } } context.scene.onPointerUp = function () { - if (lastEntity === null || !gizmoManager.freeGizmoEnabled || !context.Transform.getOrNull(lastEntity.entityId)) - return - updateTransform() + const firstEntity = getFirstEntity() + if (!firstEntity || !gizmoManager.freeGizmoEnabled || !context.Transform.getOrNull(firstEntity.entityId)) return + void updateTransform() meshPointerDragBehavior.detach() } @@ -372,9 +333,11 @@ export function createGizmoManager(context: SceneContext) { return isEnabled }, setEnabled, - setEntity(entity: EcsEntity | null): void { + restoreParents, + repositionGizmoOnCentroid, + addEntity(entity: EcsEntity): void { if ( - entity === lastEntity || + selectedEntities.includes(entity) || !isEnabled || entity?.isHidden() || entity?.isLocked() || @@ -382,28 +345,23 @@ export function createGizmoManager(context: SceneContext) { ) { return } - restoreOriginalParents() - if (areMultipleEntitiesSelected()) { - return repositionGizmoOnCentroid() - } else { - gizmoManager.attachToNode(entity) - lastEntity = entity - // fix gizmo rotation/position if necessary - const transform = getTransform() - fixRotationGizmoAlignment(transform) - fixPositionGizmoAlignment(transform) - } + restoreParents() + selectedEntities.push(entity) + repositionGizmoOnCentroid() + const transform = getTransform(entity) + // fix gizmo rotation/position if necessary + fixRotationGizmoAlignment(transform) + fixPositionGizmoAlignment(transform) events.emit('change') }, - repositionGizmoOnCentroid() { - restoreOriginalParents() - return repositionGizmoOnCentroid() - }, getEntity() { - return lastEntity + return getFirstEntity() }, - unsetEntity() { - unsetEntity() + removeEntity(entity: EcsEntity) { + restoreParents() + selectedEntities = selectedEntities.filter((e) => e.entityId !== entity.entityId) + if (selectedEntities.length === 0) removeGizmos() + else repositionGizmoOnCentroid() }, getGizmoTypes() { return [GizmoType.POSITION, GizmoType.ROTATION, GizmoType.SCALE, GizmoType.FREE] as const diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/sdkComponents/transform.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/sdkComponents/transform.ts index 672f54c8c..26c3c0149 100644 --- a/packages/@dcl/inspector/src/lib/babylon/decentraland/sdkComponents/transform.ts +++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/sdkComponents/transform.ts @@ -7,6 +7,8 @@ import { getRoot } from '../../../sdk/nodes' export const putTransformComponent: ComponentOperation = (entity, component) => { if (component.componentType === ComponentType.LastWriteWinElementSet) { + const gizmos = entity.context.deref()!.gizmos + gizmos.restoreParents() const newValue = component.getOrNull(entity.entityId) as TransformType | null const currentValue = entity.ecsComponentValues.transform entity.ecsComponentValues.transform = newValue || undefined @@ -46,6 +48,8 @@ export const putTransformComponent: ComponentOperation = (entity, component) => } if (needsReparenting) reparentEntity(entity) + + gizmos.repositionGizmoOnCentroid() } } @@ -87,10 +91,10 @@ function reparentEntity(entity: EcsEntity) { const isSceneRoot = newRoot === ROOT if (!isSceneRoot) { entity.setVisibility(false) - entity.context.deref()?.gizmos.unsetEntity() + entity.context.deref()?.gizmos.removeEntity(entity) } else { entity.setVisibility(true) - entity.context.deref()?.gizmos.setEntity(entity) + entity.context.deref()?.gizmos.addEntity(entity) } } } diff --git a/packages/@dcl/inspector/src/lib/sdk/operations/update-selected-entity.ts b/packages/@dcl/inspector/src/lib/sdk/operations/update-selected-entity.ts index 460cde85c..40fee3476 100644 --- a/packages/@dcl/inspector/src/lib/sdk/operations/update-selected-entity.ts +++ b/packages/@dcl/inspector/src/lib/sdk/operations/update-selected-entity.ts @@ -21,7 +21,7 @@ function isAncestorOf(ancestorId: Entity, targetId: Entity, nodes: Node[]): bool export function updateSelectedEntity(engine: IEngine) { return function updateSelectedEntity(entity: Entity, multiple: boolean = false) { - let gizmo = GizmoType.POSITION + let gizmo = GizmoType.FREE let deletedSelection = false // clear selection From c62a668b3f2a21c20aaf275c3a04cba49cfccb27 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Fri, 29 Nov 2024 10:27:07 -0300 Subject: [PATCH 4/8] fix: config issues (#1041) --- .../SmartItemBasicView/SmartItemBasicView.tsx | 8 +++----- packages/@dcl/inspector/src/hooks/sdk/useHasComponent.ts | 6 +++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/SmartItemBasicView.tsx b/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/SmartItemBasicView.tsx index ce1a6f233..088f49b96 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/SmartItemBasicView.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/SmartItemBasicView.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useMemo } from 'react' import { BsFillLightningChargeFill as SmartItemIcon } from 'react-icons/bs' import { withSdk } from '../../../hoc/withSdk' -import { useHasComponent } from '../../../hooks/sdk/useHasComponent' import { ConfigComponent } from '../../../lib/sdk/components' import { Container } from '../../Container' import { NftView } from './NftView' @@ -17,7 +16,6 @@ import './SmartItemBasicView.css' const SmartItemBasicView = withSdk(({ sdk, entity }) => { const { Config } = sdk.components - const hasConfig = useHasComponent(entity, Config) const renderField = useCallback( (field: ConfigComponent['fields'][0], idx: number) => { @@ -52,12 +50,12 @@ const SmartItemBasicView = withSdk(({ sdk, entity }) => { ) }, []) - if (!hasConfig) return null - const config = useMemo(() => { - return Config.get(entity) + return Config.getOrNull(entity) }, [entity]) + if (!config) return null + return ( { } }) + useEffect(() => { + setHasComponent(component.has(entity)) + }, [entity, component]) + return hasComponent } From 10d594bf36ea2d3e3af90288c099137e0791919e Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Tue, 3 Dec 2024 10:34:12 -0300 Subject: [PATCH 5/8] refactor: composite (#1042) * feat: refactor to use composite * chore: remove debuggers * chore: upgrade asset-packs * chore: rename enum --- packages/@dcl/inspector/package-lock.json | 382 ++++++++++++++++-- packages/@dcl/inspector/package.json | 2 +- .../components/AssetsCatalog/Asset/Asset.tsx | 2 +- .../VideoView/VideoView.tsx | 8 +- .../VideoPlayerInspector.tsx | 8 +- .../ProjectAssetExplorer/ProjectView.tsx | 2 +- .../components/ProjectAssetExplorer/types.ts | 5 +- .../src/components/Renderer/Renderer.tsx | 18 +- .../ui/FileUploadField/FileUploadField.tsx | 8 +- .../@dcl/inspector/src/lib/logic/catalog.ts | 32 +- .../inspector/src/lib/sdk/drag-drop.spec.ts | 6 +- .../@dcl/inspector/src/lib/sdk/drag-drop.ts | 10 +- .../src/lib/sdk/operations/add-asset/index.ts | 277 +++++++------ 13 files changed, 557 insertions(+), 203 deletions(-) diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json index bce6ee3da..3d9e8461d 100644 --- a/packages/@dcl/inspector/package-lock.json +++ b/packages/@dcl/inspector/package-lock.json @@ -8,7 +8,7 @@ "name": "@dcl/inspector", "version": "0.1.0", "dependencies": { - "@dcl/asset-packs": "1.20.2", + "@dcl/asset-packs": "^2.0.0", "ts-deepmerge": "^7.0.0" }, "devDependencies": { @@ -294,12 +294,15 @@ "integrity": "sha512-IOur6rSK5vN/oUpfawW6ax6vXPeADPCB44WNudeIYEYER7kwT2akNKUCLLjR19cLo006i/dkdt6UsTQ677uMxA==" }, "node_modules/@dcl/asset-packs": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@dcl/asset-packs/-/asset-packs-1.20.2.tgz", - "integrity": "sha512-mYnRMzrFDzQZb+72uZYnvTVM6WOa3gS9OnAsnemRECM8x2NuM+0XIuAQ8fYT02lQPmi6Jq+zCqHCo60tr/wL0A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@dcl/asset-packs/-/asset-packs-2.0.0.tgz", + "integrity": "sha512-RyeSOE8FoZDIjsYddgvWmez7mkv8HSlCSYlfP+QhDRvUs7j50mMoSzHMf6i8xm9IDHm8bCH94AuxHlykdahyeA==", + "license": "ISC", "dependencies": { "@dcl-sdk/utils": "^1.2.8", "@dcl/js-runtime": "7.5.2", + "@types/glob": "^8.1.0", + "glob": "^11.0.0", "mitt": "^3.0.1" } }, @@ -467,6 +470,102 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", @@ -1180,6 +1279,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "license": "MIT", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", @@ -1236,11 +1345,16 @@ "parse5": "^7.0.0" } }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "18.19.42", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.42.tgz", "integrity": "sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1476,7 +1590,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1485,7 +1598,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1586,6 +1698,12 @@ "dev": true, "peer": true }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, "node_modules/balloon-css": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/balloon-css/-/balloon-css-0.5.2.tgz", @@ -1674,6 +1792,15 @@ "node": ">=10.0.0" } }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1840,7 +1967,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1851,8 +1977,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1907,6 +2032,35 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/css-mediaquery": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", @@ -2478,12 +2632,16 @@ "node": ">=4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "peer": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/end-of-stream": { "version": "1.4.4", @@ -2884,6 +3042,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -3030,6 +3204,29 @@ "dev": true, "license": "zlib" }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/global-modules": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", @@ -3448,8 +3645,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "peer": true, "engines": { "node": ">=8" } @@ -3615,8 +3810,22 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/jest-dev-server": { "version": "9.0.2", @@ -4222,6 +4431,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -4281,6 +4499,21 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -4290,6 +4523,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -4536,6 +4778,12 @@ "node": ">= 14" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parallax-js": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/parallax-js/-/parallax-js-3.1.0.tgz", @@ -4605,6 +4853,31 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5546,6 +5819,27 @@ "dev": true, "license": "MIT" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -5568,7 +5862,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -5725,8 +6018,21 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5740,8 +6046,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6117,8 +6434,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/universalify": { "version": "2.0.1", @@ -6364,6 +6680,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json index 9184d5a29..d6f6f756a 100644 --- a/packages/@dcl/inspector/package.json +++ b/packages/@dcl/inspector/package.json @@ -2,7 +2,7 @@ "name": "@dcl/inspector", "version": "0.1.0", "dependencies": { - "@dcl/asset-packs": "1.20.2", + "@dcl/asset-packs": "^2.0.0", "ts-deepmerge": "^7.0.0" }, "devDependencies": { diff --git a/packages/@dcl/inspector/src/components/AssetsCatalog/Asset/Asset.tsx b/packages/@dcl/inspector/src/components/AssetsCatalog/Asset/Asset.tsx index a2e2336ee..0f81a2bde 100644 --- a/packages/@dcl/inspector/src/components/AssetsCatalog/Asset/Asset.tsx +++ b/packages/@dcl/inspector/src/components/AssetsCatalog/Asset/Asset.tsx @@ -11,7 +11,7 @@ import { fetchImage, resizeImage } from '../../../lib/utils/img' import { useIsMounted } from '../../../hooks/useIsMounted' const Asset: React.FC<{ value: Asset }> = ({ value }) => { - const [, drag, preview] = useDrag(() => ({ type: 'builder-asset', item: { value } }), [value]) + const [, drag, preview] = useDrag(() => ({ type: 'catalog-asset', item: { value } }), [value]) const isSmartItem = isSmart(value) const isGroundItem = isGround(value) const imgSrc = getContentsUrl(value.contents['thumbnail.png']) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/VideoView/VideoView.tsx b/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/VideoView/VideoView.tsx index 5deb240da..f47f03720 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/VideoView/VideoView.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/SmartItemBasicView/VideoView/VideoView.tsx @@ -5,7 +5,7 @@ import { Entity } from '@dcl/ecs' import { withSdk, WithSdkProps } from '../../../../hoc/withSdk' import { useHasComponent } from '../../../../hooks/sdk/useHasComponent' import { useComponentInput } from '../../../../hooks/sdk/useComponentInput' -import { ProjectAssetDrop, getNode } from '../../../../lib/sdk/drag-drop' +import { LocalAssetDrop, getNode } from '../../../../lib/sdk/drag-drop' import { withAssetDir } from '../../../../lib/data-layer/host/fs-utils' import { useAppSelector } from '../../../../redux/hooks' import { selectAssetCatalog } from '../../../../redux/app' @@ -13,7 +13,7 @@ import { Block } from '../../../Block' import { TextField, CheckboxField, RangeField, InfoTooltip } from '../../../ui' import { fromVideoPlayer, toVideoPlayer, isValidInput, isVideo, isValidVolume } from '../../VideoPlayerInspector/utils' -const DROP_TYPES = ['project-asset'] +const DROP_TYPES = ['local-asset'] export default React.memo( withSdk(({ sdk, entity }) => { @@ -44,13 +44,13 @@ export default React.memo( const [{ isHover }, drop] = useDrop( () => ({ accept: DROP_TYPES, - drop: ({ value, context }: ProjectAssetDrop, monitor) => { + drop: ({ value, context }: LocalAssetDrop, monitor) => { if (monitor.didDrop()) return const node = context.tree.get(value)! const model = getNode(node, context.tree, isVideo) if (model) void handleDrop(withAssetDir(model.asset.src)) }, - canDrop: ({ value, context }: ProjectAssetDrop) => { + canDrop: ({ value, context }: LocalAssetDrop) => { const node = context.tree.get(value)! return !!getNode(node, context.tree, isVideo) }, diff --git a/packages/@dcl/inspector/src/components/EntityInspector/VideoPlayerInspector/VideoPlayerInspector.tsx b/packages/@dcl/inspector/src/components/EntityInspector/VideoPlayerInspector/VideoPlayerInspector.tsx index 3ceac2a54..575d7dfbf 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/VideoPlayerInspector/VideoPlayerInspector.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/VideoPlayerInspector/VideoPlayerInspector.tsx @@ -9,7 +9,7 @@ import { getComponentValue } from '../../../hooks/sdk/useComponentValue' import { analytics, Event } from '../../../lib/logic/analytics' import { getAssetByModel } from '../../../lib/logic/catalog' import { CoreComponents } from '../../../lib/sdk/components' -import { ProjectAssetDrop, getNode } from '../../../lib/sdk/drag-drop' +import { LocalAssetDrop, getNode } from '../../../lib/sdk/drag-drop' import { withAssetDir } from '../../../lib/data-layer/host/fs-utils' import { useAppSelector } from '../../../redux/hooks' import { selectAssetCatalog } from '../../../redux/app' @@ -19,7 +19,7 @@ import { TextField, CheckboxField, RangeField, InfoTooltip } from '../../ui' import { fromVideoPlayer, toVideoPlayer, isValidInput, isVideo, isValidVolume } from './utils' import type { Props } from './types' -const DROP_TYPES = ['project-asset'] +const DROP_TYPES = ['local-asset'] export default withSdk(({ sdk, entity }) => { const files = useAppSelector(selectAssetCatalog) @@ -56,13 +56,13 @@ export default withSdk(({ sdk, entity }) => { const [{ isHover }, drop] = useDrop( () => ({ accept: DROP_TYPES, - drop: ({ value, context }: ProjectAssetDrop, monitor) => { + drop: ({ value, context }: LocalAssetDrop, monitor) => { if (monitor.didDrop()) return const node = context.tree.get(value)! const model = getNode(node, context.tree, isVideo) if (model) void handleDrop(withAssetDir(model.asset.src)) }, - canDrop: ({ value, context }: ProjectAssetDrop) => { + canDrop: ({ value, context }: LocalAssetDrop) => { const node = context.tree.get(value)! return !!getNode(node, context.tree, isVideo) }, diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx index 13d836ef2..f2f02decf 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx @@ -30,7 +30,7 @@ interface ModalState { export const ROOT = 'File System' -export const DRAG_N_DROP_ASSET_KEY = 'project-asset' +export const DRAG_N_DROP_ASSET_KEY = 'local-asset' export type TreeNode = Omit & { children?: string[]; matches?: string[] } diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/types.ts b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/types.ts index 3d708b9be..412d85b0f 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/types.ts +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/types.ts @@ -1,5 +1,4 @@ -import { ComponentName } from '@dcl/asset-packs' -import { CoreComponents } from '../../lib/sdk/components' +import { AssetData } from '../../lib/logic/catalog' export interface IAsset { src: string @@ -20,7 +19,7 @@ export interface AssetCellProp { export type AssetNodeBase = { name: string parent: AssetNodeFolder | null - components?: Partial> + composite?: AssetData['composite'] } export type AssetNodeItem = AssetNodeBase & { diff --git a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx index 20ee5ed69..f9054a715 100644 --- a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx +++ b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx @@ -8,7 +8,7 @@ import { Entity } from '@dcl/ecs' import { DIRECTORY, withAssetDir } from '../../lib/data-layer/host/fs-utils' import { useAppDispatch, useAppSelector } from '../../redux/hooks' import { getReloadAssets, importAsset, saveThumbnail } from '../../redux/data-layer' -import { getNode, BuilderAsset, DROP_TYPES, IDrop, ProjectAssetDrop, isDropType } from '../../lib/sdk/drag-drop' +import { getNode, CatalogAssetDrop, DROP_TYPES, IDrop, LocalAssetDrop, isDropType } from '../../lib/sdk/drag-drop' import { useRenderer } from '../../hooks/sdk/useRenderer' import { useSdk } from '../../hooks/sdk/useSdk' import { getPointerCoords } from '../../lib/babylon/decentraland/mouse-utils' @@ -212,7 +212,7 @@ const Renderer: React.FC = () => { position, basePath, sdk.enumEntity, - asset.components, + asset.composite, asset.asset.id ) await operations.dispatch() @@ -239,10 +239,10 @@ const Renderer: React.FC = () => { canvasRef.current?.focus() } - const importBuilderAsset = async (asset: Asset) => { + const importCatalogAsset = async (asset: Asset) => { const position = await getDropPosition() const fileContent: Record = {} - const destFolder = 'builder' + const destFolder = 'asset-packs' const assetPackageName = asset.name.trim().replaceAll(' ', '_').toLowerCase() const path = Object.keys(asset.contents).find(($) => isAsset($)) let thumbnail: Uint8Array | undefined @@ -299,7 +299,7 @@ const Renderer: React.FC = () => { name: asset.name, parent: null, asset: { type: path ? 'gltf' : 'unknown', src: path ?? '', id: asset.id }, - components: asset.components + composite: asset.composite } const basePath = withAssetDir(`${destFolder}/${assetPackageName}`) if (isGround(asset) && !placeSingleTile) { @@ -320,12 +320,12 @@ const Renderer: React.FC = () => { if (monitor.didDrop()) return const itemType = monitor.getItemType() - if (isDropType(item, itemType, 'builder-asset')) { - void importBuilderAsset(item.value) + if (isDropType(item, itemType, 'catalog-asset')) { + void importCatalogAsset(item.value) return } - if (isDropType(item, itemType, 'project-asset')) { + if (isDropType(item, itemType, 'local-asset')) { const node = item.context.tree.get(item.value)! const model = getNode(node, item.context.tree, isModel) if (model) { @@ -335,7 +335,7 @@ const Renderer: React.FC = () => { } }, hover(item, monitor) { - if (isDropType(item, monitor.getItemType(), 'builder-asset')) { + if (isDropType(item, monitor.getItemType(), 'catalog-asset')) { const asset = item.value if (isGround(asset)) { if (!showSingleTileHint) { diff --git a/packages/@dcl/inspector/src/components/ui/FileUploadField/FileUploadField.tsx b/packages/@dcl/inspector/src/components/ui/FileUploadField/FileUploadField.tsx index 942e4d4d9..55dc5877f 100644 --- a/packages/@dcl/inspector/src/components/ui/FileUploadField/FileUploadField.tsx +++ b/packages/@dcl/inspector/src/components/ui/FileUploadField/FileUploadField.tsx @@ -8,7 +8,7 @@ import { selectAssetCatalog, selectUploadFile, updateUploadFile } from '../../.. import { selectAssetsTab } from '../../../redux/ui' import { AssetsTab } from '../../../redux/ui/types' import { useAppDispatch, useAppSelector } from '../../../redux/hooks' -import { DropTypesEnum, ProjectAssetDrop, getNode } from '../../../lib/sdk/drag-drop' +import { DropTypesEnum, LocalAssetDrop, getNode } from '../../../lib/sdk/drag-drop' import { EXTENSIONS, withAssetDir } from '../../../lib/data-layer/host/fs-utils' import { isModel } from '../../EntityInspector/GltfInspector/utils' @@ -97,8 +97,8 @@ const FileUploadField: React.FC = ({ const [{ isHover, canDrop }, drop] = useDrop( () => ({ - accept: [DropTypesEnum.ProjectAsset], - drop: ({ value, context }: ProjectAssetDrop, monitor) => { + accept: [DropTypesEnum.LocalAsset], + drop: ({ value, context }: LocalAssetDrop, monitor) => { if (monitor.didDrop()) return const node = context.tree.get(value)! const element = getNode(node, context.tree, isValid) @@ -109,7 +109,7 @@ const FileUploadField: React.FC = ({ setDropError(true) } }, - canDrop: ({ value, context }: ProjectAssetDrop) => { + canDrop: ({ value, context }: LocalAssetDrop) => { const node = context.tree.get(value)! return !!getNode(node, context.tree, isValid) }, diff --git a/packages/@dcl/inspector/src/lib/logic/catalog.ts b/packages/@dcl/inspector/src/lib/logic/catalog.ts index a57d5700f..0873d9b00 100644 --- a/packages/@dcl/inspector/src/lib/logic/catalog.ts +++ b/packages/@dcl/inspector/src/lib/logic/catalog.ts @@ -1,11 +1,13 @@ -import { ComponentName } from '@dcl/asset-packs' -import * as _catalog from '@dcl/asset-packs/catalog.json' +import { Catalog, AssetPack, Asset, AssetData } from '@dcl/asset-packs' import { CoreComponents } from '../sdk/components' import { getConfig } from './config' +import * as _catalog from '@dcl/asset-packs/catalog.json' -export const catalog = _catalog.assetPacks as unknown as AssetPack[] +export const catalog = (_catalog as unknown as Catalog).assetPacks -// categories obtained from "builder.decentraland.org" catalog +export { Catalog, AssetPack, Asset, AssetData } + +// categories obtained from "builder-items.decentraland.org" catalog export const CATEGORIES = [ 'ground', 'utils', @@ -26,22 +28,6 @@ export const CATEGORIES = [ 'health' ] -export type AssetPack = { - id: string - name: string - thumbnail: string - assets: Asset[] -} - -export type Asset = { - id: string - name: string - category: string - tags: string[] - contents: Record - components: Partial> -} - export function getContentsUrl(hash: string) { const config = getConfig() return `${config.contentUrl}/contents/${hash}` @@ -57,12 +43,12 @@ export function getAssetsByCategory(assets: Asset[]) { } export function isSmart(asset: Partial) { - const components = Object.keys(asset?.components ?? {}) + const components = asset?.composite?.components ?? [] // when the item has more than one component, it is smart if (components.length > 1) { return true - // when the item has a single component but it's not a GltfContainer, then it's also smart (NFTShape, TextShape, MeshRenderers, etc...) - } else if (components.length === 1 && components[0] !== CoreComponents.GLTF_CONTAINER) { + // when the item has a single component but it's not a GltfContainer, then it's also smart + } else if (components.length === 1 && components[0].name !== CoreComponents.GLTF_CONTAINER) { return true } // when the item only has a GltfContainer then it's not smart diff --git a/packages/@dcl/inspector/src/lib/sdk/drag-drop.spec.ts b/packages/@dcl/inspector/src/lib/sdk/drag-drop.spec.ts index 36d1457ca..0460defe7 100644 --- a/packages/@dcl/inspector/src/lib/sdk/drag-drop.spec.ts +++ b/packages/@dcl/inspector/src/lib/sdk/drag-drop.spec.ts @@ -2,9 +2,9 @@ import * as dnd from './drag-drop' describe('sdk drag and drop', () => { it('should return true when identifier equals type', () => { - const drop = { value: 'project-asset', context: { tree: new Map() } } - expect(dnd.isDropType(drop, 'project-asset', 'project-asset')).toBe(true) - expect(dnd.isDropType(drop, 'invalid', 'project-asset')).toBe(false) + const drop = { value: 'local-asset', context: { tree: new Map() } } + expect(dnd.isDropType(drop, 'local-asset', 'local-asset')).toBe(true) + expect(dnd.isDropType(drop, 'invalid', 'local-asset')).toBe(false) }) it('should return all drop types list', () => { expect(dnd.DROP_TYPES).toStrictEqual(expect.arrayContaining(Object.values(dnd.DropTypesEnum))) diff --git a/packages/@dcl/inspector/src/lib/sdk/drag-drop.ts b/packages/@dcl/inspector/src/lib/sdk/drag-drop.ts index 659f41e38..353ab9b16 100644 --- a/packages/@dcl/inspector/src/lib/sdk/drag-drop.ts +++ b/packages/@dcl/inspector/src/lib/sdk/drag-drop.ts @@ -8,14 +8,14 @@ interface Drop { context: K } -export type ProjectAssetDrop = Drop }> -export type BuilderAsset = Drop +export type LocalAssetDrop = Drop }> +export type CatalogAssetDrop = Drop -export type IDrop = ProjectAssetDrop | BuilderAsset +export type IDrop = LocalAssetDrop | CatalogAssetDrop export enum DropTypesEnum { - ProjectAsset = 'project-asset', - BuilderAsset = 'builder-asset' + LocalAsset = 'local-asset', + CatalogAsset = 'catalog-asset' } export type DropTypes = `${DropTypesEnum}` diff --git a/packages/@dcl/inspector/src/lib/sdk/operations/add-asset/index.ts b/packages/@dcl/inspector/src/lib/sdk/operations/add-asset/index.ts index 4860732a9..acaa8cbea 100644 --- a/packages/@dcl/inspector/src/lib/sdk/operations/add-asset/index.ts +++ b/packages/@dcl/inspector/src/lib/sdk/operations/add-asset/index.ts @@ -3,12 +3,8 @@ import { IEngine, Transform as TransformEngine, GltfContainer as GltfEngine, - PBGltfContainer, Vector3Type, - PBAudioSource, LastWriteWinElementSetComponentDefinition, - PBVideoPlayer, - PBMaterial, NetworkEntity as NetworkEntityEngine } from '@dcl/ecs' import { @@ -16,7 +12,6 @@ import { Actions, COMPONENTS_WITH_ID, ComponentName, - Triggers, getJson, getNextId, getPayload @@ -27,6 +22,7 @@ import updateSelectedEntity from '../update-selected-entity' import { addChild } from '../add-child' import { isSelf, parseMaterial, parseSyncComponents } from './utils' import { EnumEntity } from '../../enum-entity' +import { AssetData } from '../../../logic/catalog' export function addAsset(engine: IEngine) { return function addAsset( @@ -36,156 +32,195 @@ export function addAsset(engine: IEngine) { position: Vector3Type, base: string, enumEntityId: EnumEntity, - components?: Partial>, + composite?: AssetData['composite'], assetId?: string ): Entity { - const child = addChild(engine)(parent, name) const Transform = engine.getComponent(TransformEngine.componentId) as typeof TransformEngine const GltfContainer = engine.getComponent(GltfEngine.componentId) as typeof GltfEngine const NetworkEntity = engine.getComponent(NetworkEntityEngine.componentId) as typeof NetworkEntityEngine - Transform.createOrReplace(child, { parent, position }) + if (composite) { + // Get all unique entity IDs from components + const entityIds = new Set() + for (const component of composite.components) { + Object.keys(component.data).forEach((id) => entityIds.add(id)) + } + + // Track all created entities + const entities = new Map() + + // If there's only one entity, it becomes the main entity + // If there are multiple entities, create a new main entity as parent + const mainEntity = + entityIds.size === 1 ? addChild(engine)(parent, name) : addChild(engine)(parent, `${name}_root`) + + Transform.createOrReplace(mainEntity, { parent, position }) + + // Set up entity hierarchy based on number of entities + const parentForChildren = entityIds.size === 1 ? parent : mainEntity + + // Create all entities + for (const entityId of entityIds) { + if (entityIds.size === 1) { + // Single entity case: use the main entity + entities.set(entityId, mainEntity) + } else { + // Multiple entities case: create child entities + const entity = entityId === '0' ? mainEntity : addChild(engine)(parentForChildren, `${name}_${entityId}`) + + if (entityId !== '0') { + Transform.createOrReplace(entity, { + parent: parentForChildren, + position: { x: 0, y: 0, z: 0 } + }) + } + entities.set(entityId, entity) + } + } - if (components) { - // values const values = new Map() - // generate ids + // Generate ids for components that need them BEFORE processing components const ids = new Map() - for (const componentName in components) { - const key = componentName as keyof typeof components - const componentValue = components[key] ? { ...components[key] } : {} - if (COMPONENTS_WITH_ID.includes(componentName) && isSelf(componentValue.id)) { - componentValue.id = getNextId(engine as any) - ids.set(componentName, componentValue.id) + for (const component of composite.components) { + const componentName = component.name + for (const [_entityId, data] of Object.entries(component.data)) { + const componentValue = { ...data.json } + if (COMPONENTS_WITH_ID.includes(componentName) && isSelf(componentValue.id)) { + ids.set(componentName, getNextId(engine as any)) + componentValue.id = ids.get(componentName) + } + values.set(componentName, componentValue) } - values.set(componentName, componentValue) } - // map paths, ids, and modify values if needed - const mapId = (id: string | number | undefined) => { + const mapId = (id: string | number) => { if (typeof id === 'string') { - if (/{self:(.+)}/.test(id)) { - const result = id.match(/{self:(.+)}/) - if (result) { - const componentName = result[1] as ComponentName - const mappedId = ids.get(componentName) - if (mappedId) { - return mappedId - } - } + const match = id.match(/{self:(.+)}/) + if (match) { + const componentName = match[1] + return ids.get(componentName) } - return parseInt(id) } return id } - for (const componentName in components) { - switch (componentName) { - case CoreComponents.GLTF_CONTAINER: { - const gltfContainer = values.get(componentName) as PBGltfContainer - gltfContainer.visibleMeshesCollisionMask ??= 1 - gltfContainer.invisibleMeshesCollisionMask ??= 2 - values.set(componentName, { ...gltfContainer, src: gltfContainer.src.replace('{assetPath}', base) }) - break - } - case CoreComponents.AUDIO_SOURCE: { - const audioSource = values.get(componentName) as PBAudioSource - values.set(componentName, { ...audioSource, src: audioSource.audioClipUrl.replace('{assetPath}', base) }) - break - } - case CoreComponents.VIDEO_PLAYER: { - const videoPlayer = values.get(componentName) as PBVideoPlayer - values.set(componentName, { ...videoPlayer, src: videoPlayer.src.replace('{assetPath}', base) }) - break - } - case CoreComponents.MATERIAL: { - const material = values.get(componentName) as PBMaterial - values.set(componentName, parseMaterial(base, material)) - break - } - case ComponentName.ACTIONS: { - const actions = values.get(componentName) as Actions - const newValue: Actions['value'] = [] - for (const action of actions.value) { - switch (action.type) { - case ActionType.PLAY_SOUND: { - const payload = getPayload(action) - newValue.push({ - ...action, - jsonPayload: getJson({ - ...payload, - src: payload.src.replace('{assetPath}', base) + + // Process and create components for each entity + for (const component of composite.components) { + const componentName = component.name + for (const [entityId] of Object.entries(component.data)) { + const targetEntity = entities.get(entityId)! + let componentValue = values.get(componentName) + + switch (componentName) { + case CoreComponents.GLTF_CONTAINER: { + componentValue.visibleMeshesCollisionMask ??= 1 + componentValue.invisibleMeshesCollisionMask ??= 2 + componentValue.src = componentValue.src.replace('{assetPath}', base) + break + } + case EditorComponentNames.Config: { + if (assetId) { + componentValue = { ...componentValue, assetId } + } + break + } + case CoreComponents.AUDIO_SOURCE: { + componentValue.src = componentValue.audioClipUrl.replace('{assetPath}', base) + break + } + case CoreComponents.VIDEO_PLAYER: { + componentValue.src = componentValue.src.replace('{assetPath}', base) + break + } + case CoreComponents.MATERIAL: { + componentValue = parseMaterial(base, componentValue) + break + } + case ComponentName.ACTIONS: { + const newValue: Actions['value'] = [] + for (const action of componentValue.value) { + switch (action.type) { + case ActionType.PLAY_SOUND: { + const payload = getPayload(action) + newValue.push({ + ...action, + jsonPayload: getJson({ + ...payload, + src: payload.src.replace('{assetPath}', base) + }) }) - }) - break - } - case ActionType.PLAY_CUSTOM_EMOTE: { - const payload = getPayload(action) - newValue.push({ - ...action, - jsonPayload: getJson({ - ...payload, - src: payload.src.replace('{assetPath}', base) + break + } + case ActionType.PLAY_CUSTOM_EMOTE: { + const payload = getPayload(action) + newValue.push({ + ...action, + jsonPayload: getJson({ + ...payload, + src: payload.src.replace('{assetPath}', base) + }) }) - }) - break + break + } + default: + newValue.push(action) + break } - default: - newValue.push(action) - break } + componentValue = { ...componentValue, value: newValue } + break } - values.set(componentName, { ...actions, value: newValue }) - break - } - case ComponentName.TRIGGERS: { - const triggers = values.get(componentName) as Triggers - const newValue = triggers.value.map((trigger) => ({ - ...trigger, - conditions: (trigger.conditions || []).map((condition) => ({ - ...condition, - id: mapId(condition.id) - })), - actions: trigger.actions.map((action) => ({ - ...action, - id: mapId(action.id) + case ComponentName.TRIGGERS: { + const newValue = componentValue.value.map((trigger: any) => ({ + ...trigger, + conditions: (trigger.conditions || []).map((condition: any) => ({ + ...condition, + id: mapId(condition.id) + })), + actions: trigger.actions.map((action: any) => ({ + ...action, + id: mapId(action.id) + })) })) - })) - values.set(componentName, { ...triggers, value: newValue }) - break - } - case CoreComponents.SYNC_COMPONENTS: { - const componentNames = values.get(componentName) as { value: string[] } - const componentIds = parseSyncComponents(engine, componentNames.value) - values.set(componentName, { componentIds }) - values.set(NetworkEntity.componentName, { entityId: enumEntityId.getNextEnumEntityId(), networkId: 0 }) - break - } - case EditorComponentNames.Config: { - values.set(componentName, { ...components[componentName], assetId }) - break + componentValue = { ...componentValue, value: newValue } + break + } + case CoreComponents.SYNC_COMPONENTS: { + const componentIds = parseSyncComponents(engine, componentValue.value) + componentValue = { componentIds } + const NetworkEntityComponent = engine.getComponent(NetworkEntity.componentId) as typeof NetworkEntity + NetworkEntityComponent.create(targetEntity, { + entityId: enumEntityId.getNextEnumEntityId(), + networkId: 0 + }) + break + } } + + const Component = engine.getComponent(componentName) as LastWriteWinElementSetComponentDefinition + Component.create(targetEntity, componentValue) } } - // create components - for (const [componentName, componentValue] of values) { - const Component = engine.getComponent(componentName) as LastWriteWinElementSetComponentDefinition - Component.create(child, componentValue) - } + // update selection + updateSelectedEntity(engine)(mainEntity) + return mainEntity } else { - // if the asset is just a path to a model, create a gltf container for it (this is the case for assets dropped from the local files tab) - GltfContainer.create(child, { + // Handle non-composite case + const mainEntity = addChild(engine)(parent, name) + Transform.createOrReplace(mainEntity, { parent, position }) + + GltfContainer.create(mainEntity, { src: `${base}/${src}`, visibleMeshesCollisionMask: 1, invisibleMeshesCollisionMask: 2 }) - } - // update selection - updateSelectedEntity(engine)(child) - - return child + // update selection + updateSelectedEntity(engine)(mainEntity) + return mainEntity + } } } From 085da37eac19a1ee4794076a6acf221cf8b796fe Mon Sep 17 00:00:00 2001 From: Gon Pombo Date: Tue, 3 Dec 2024 18:05:34 -0300 Subject: [PATCH 6/8] fix multipalyer clone issue at runtime (#1039) * fix multipalyer clone issue at runtime * update asset-packs --- packages/@dcl/inspector/package-lock.json | 8 ++++---- packages/@dcl/inspector/package.json | 2 +- packages/@dcl/sdk-commands/package-lock.json | 4 ++-- .../@dcl/sdk-commands/src/logic/bundle.ts | 19 ++++--------------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json index 3d9e8461d..48414d615 100644 --- a/packages/@dcl/inspector/package-lock.json +++ b/packages/@dcl/inspector/package-lock.json @@ -8,7 +8,7 @@ "name": "@dcl/inspector", "version": "0.1.0", "dependencies": { - "@dcl/asset-packs": "^2.0.0", + "@dcl/asset-packs": "^2.1.1", "ts-deepmerge": "^7.0.0" }, "devDependencies": { @@ -294,9 +294,9 @@ "integrity": "sha512-IOur6rSK5vN/oUpfawW6ax6vXPeADPCB44WNudeIYEYER7kwT2akNKUCLLjR19cLo006i/dkdt6UsTQ677uMxA==" }, "node_modules/@dcl/asset-packs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@dcl/asset-packs/-/asset-packs-2.0.0.tgz", - "integrity": "sha512-RyeSOE8FoZDIjsYddgvWmez7mkv8HSlCSYlfP+QhDRvUs7j50mMoSzHMf6i8xm9IDHm8bCH94AuxHlykdahyeA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@dcl/asset-packs/-/asset-packs-2.1.1.tgz", + "integrity": "sha512-DgcRbGODLPxBTw2O6BN4vNBVEwhiDBvuCR6tSIjladb7bqQ5PWZbL/OQX4Ok2V1++gTnNPuaIrXId/ryGaTaKg==", "license": "ISC", "dependencies": { "@dcl-sdk/utils": "^1.2.8", diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json index d6f6f756a..173866572 100644 --- a/packages/@dcl/inspector/package.json +++ b/packages/@dcl/inspector/package.json @@ -2,7 +2,7 @@ "name": "@dcl/inspector", "version": "0.1.0", "dependencies": { - "@dcl/asset-packs": "^2.0.0", + "@dcl/asset-packs": "^2.1.1", "ts-deepmerge": "^7.0.0" }, "devDependencies": { diff --git a/packages/@dcl/sdk-commands/package-lock.json b/packages/@dcl/sdk-commands/package-lock.json index d8a53355a..0548d6212 100644 --- a/packages/@dcl/sdk-commands/package-lock.json +++ b/packages/@dcl/sdk-commands/package-lock.json @@ -65,7 +65,7 @@ "../inspector": { "version": "0.1.0", "dependencies": { - "@dcl/asset-packs": "1.20.2", + "@dcl/asset-packs": "^2.1.1", "ts-deepmerge": "^7.0.0" }, "devDependencies": { @@ -3139,7 +3139,7 @@ "@babylonjs/inspector": "~6.18.0", "@babylonjs/loaders": "~6.18.0", "@babylonjs/materials": "~6.18.0", - "@dcl/asset-packs": "1.20.2", + "@dcl/asset-packs": "^2.1.1", "@dcl/ecs": "file:../ecs", "@dcl/ecs-math": "2.1.0", "@dcl/mini-rpc": "^1.0.7", diff --git a/packages/@dcl/sdk-commands/src/logic/bundle.ts b/packages/@dcl/sdk-commands/src/logic/bundle.ts index f5e6a023b..704aae787 100644 --- a/packages/@dcl/sdk-commands/src/logic/bundle.ts +++ b/packages/@dcl/sdk-commands/src/logic/bundle.ts @@ -63,24 +63,14 @@ import { compositeFromLoader } from '~sdk/all-composites' ${ isEditorScene && ` -import { initAssetPacks } from '@dcl/asset-packs/dist/scene-entrypoint' -initAssetPacks(engine) +import { syncEntity } from '@dcl/sdk/network' +import { initAssetPacks, setSyncEntity } from '@dcl/asset-packs/dist/scene-entrypoint' +initAssetPacks(engine, { syncEntity }) // TODO: do we need to do this on runtime ? // I think we have that information at build-time and we avoid to do evaluate this on the worker. // Read composite.json or main.crdt => If that file has a NetowrkEntity import '@dcl/@sdk/network' - -// conditionally load networking code if the NetworkEntity component is being used... -for (const path in compositeFromLoader) { - const composite = compositeProvider.getCompositeOrNull(path) - if (composite) { - const { components } = composite.composite - const hasNetworkEntity = components.find(($) => $.name === NetworkEntity.componentName) - if (hasNetworkEntity) { - import('@dcl/sdk/network') - } - } -}` +` } if ((entrypoint as any).main !== undefined) { @@ -106,7 +96,6 @@ export * from '${unixEntrypointPath}' export async function bundleProject(components: BundleComponents, options: CompileOptions, sceneJson: Scene) { const tsconfig = path.join(options.workingDirectory, 'tsconfig.json') - /* istanbul ignore if */ if (!options.single && !sceneJson.main) { throw new CliError('scene.json .main must be present') From 4123b99ea50b207270b479c572210a344efa74ea Mon Sep 17 00:00:00 2001 From: Gon Pombo Date: Tue, 10 Dec 2024 13:05:22 -0300 Subject: [PATCH 7/8] fix onChange for custom componetns (#1043) * fix onChange for custom componetns forward params to explorer alpha * udpate snapshots * add new params for explorer alpha * update dclenv description --- packages/@dcl/ecs/src/systems/crdt/index.ts | 15 ++++----- .../src/commands/start/explorer-alpha.ts | 33 +++++++++++++++---- .../sdk-commands/src/commands/start/index.ts | 17 ++++++++-- .../static-scene.test.ts.crdt | 2 +- .../testing-fw.test.ts.crdt | 4 +-- .../two-way-crdt.test.ts.crdt | 2 +- .../production-bundles/billboard.ts.crdt | 6 ++-- .../production-bundles/cubes.ts.crdt | 8 ++--- .../schema-components.ts.crdt | 2 +- 9 files changed, 60 insertions(+), 29 deletions(-) diff --git a/packages/@dcl/ecs/src/systems/crdt/index.ts b/packages/@dcl/ecs/src/systems/crdt/index.ts index cb8afb32e..9640b573f 100644 --- a/packages/@dcl/ecs/src/systems/crdt/index.ts +++ b/packages/@dcl/ecs/src/systems/crdt/index.ts @@ -216,15 +216,14 @@ export function crdtSceneSystem(engine: PreEngine, onProcessEntityComponentChang ...message, messageBuffer: buffer.buffer().subarray(offset, buffer.currentWriteOffset()) }) + } + if (onProcessEntityComponentChange) { + const rawValue = + message.type === CrdtMessageType.PUT_COMPONENT || message.type === CrdtMessageType.APPEND_VALUE + ? component.get(message.entityId) + : undefined - if (onProcessEntityComponentChange) { - const rawValue = - message.type === CrdtMessageType.PUT_COMPONENT || message.type === CrdtMessageType.APPEND_VALUE - ? component.get(message.entityId) - : undefined - - onProcessEntityComponentChange(message.entityId, message.type, component, rawValue) - } + onProcessEntityComponentChange(message.entityId, message.type, component, rawValue) } } } diff --git a/packages/@dcl/sdk-commands/src/commands/start/explorer-alpha.ts b/packages/@dcl/sdk-commands/src/commands/start/explorer-alpha.ts index 10e19a39c..ba899df8d 100644 --- a/packages/@dcl/sdk-commands/src/commands/start/explorer-alpha.ts +++ b/packages/@dcl/sdk-commands/src/commands/start/explorer-alpha.ts @@ -1,14 +1,22 @@ +import { Result } from 'arg' +import { args as startArgs } from '.' import { CliComponents } from '../../components' const isWindows = /^win/.test(process.platform) export async function runExplorerAlpha( components: CliComponents, - opts: { cwd: string; realm: string; baseCoords: { x: number; y: number }; isHub: boolean } + opts: { + cwd: string + realm: string + baseCoords: { x: number; y: number } + isHub: boolean + args: Result + } ) { const { cwd, realm, baseCoords, isHub } = opts - if (await runApp(components, { cwd, realm, baseCoords, isHub })) { + if (await runApp(components, { cwd, realm, baseCoords, isHub, args: opts.args })) { return } @@ -19,14 +27,27 @@ async function runApp( components: CliComponents, { cwd, - realm, + realm: realmValue, baseCoords, - isHub - }: { cwd: string; realm: string; baseCoords: { x: number; y: number }; isHub: boolean } + isHub, + args + }: { + cwd: string + realm: string + baseCoords: { x: number; y: number } + isHub: boolean + args: Result + } ) { const cmd = isWindows ? 'start' : 'open' + const position = args['--position'] ?? `${baseCoords.x},${baseCoords.y}` + const realm = args['--realm'] ?? realmValue + const localScene = args['--local-scene'] ?? true + const debug = args['--debug'] ?? true + const dclenv = args['--dclenv'] ?? 'org' + try { - const params = `realm=${realm}&position=${baseCoords.x},${baseCoords.y}&local-scene=true&debug=true&hub=${isHub}` + const params = `realm=${realm}&position=${position}&local-scene=${localScene}&debug=${debug}&hub=${isHub}&dclenv=${dclenv}` const app = `decentraland://"${params}"` await components.spawner.exec(cwd, cmd, [app], { silent: true }) components.logger.info(`Desktop client: decentraland://${params}\n`) diff --git a/packages/@dcl/sdk-commands/src/commands/start/index.ts b/packages/@dcl/sdk-commands/src/commands/start/index.ts index ab5b3dbd8..b2286b0bb 100644 --- a/packages/@dcl/sdk-commands/src/commands/start/index.ts +++ b/packages/@dcl/sdk-commands/src/commands/start/index.ts @@ -52,7 +52,13 @@ export const args = declareArgs({ '--desktop-client': Boolean, '--data-layer': Boolean, '--explorer-alpha': Boolean, - '--hub': Boolean + '--hub': Boolean, + // Params related to the explorer-alpha + '--debug': Boolean, + '--dclenv': String, + '--realm': String, + '--local-scene': String, + '--position': String }) export async function help(options: Options) { @@ -70,6 +76,12 @@ export async function help(options: Options) { --web3 Connects preview to browser wallet to use the associated avatar and account --skip-build Skip build and only serve the files in preview mode --desktop-client Show URL to launch preview in the desktop client (BETA) + --debug Enables Debug panel mode inside DCL Explorer (default=true) + --dclenv Decentraland Environment. Which environment to use for the content. This determines the catalyst server used, asset-bundles, etc. Possible values: org, zone, today. (default=org) + --realm Realm used to serve the content. (default=Localhost) + --local-scene Enable local scene development. + --position Initial Position to start the explorer. (default=position defined at scene.json) + Examples: @@ -97,7 +109,6 @@ export async function main(options: Options) { const isHub = !!options.args['--hub'] let hasSmartWearable = false - const workspace = await getValidWorkspace(options.components, workingDirectory) /* istanbul ignore if */ @@ -232,7 +243,7 @@ export async function main(options: Options) { if (explorerAlpha) { const realm = new URL(sortedURLs[0]).origin - await runExplorerAlpha(components, { cwd: workingDirectory, realm, baseCoords, isHub }) + await runExplorerAlpha(components, { cwd: workingDirectory, realm, baseCoords, isHub, args: options.args }) } // Open preferably localhost/127.0.0.1 diff --git a/test/snapshots/development-bundles/static-scene.test.ts.crdt b/test/snapshots/development-bundles/static-scene.test.ts.crdt index 6c76e3741..381bd87ac 100644 --- a/test/snapshots/development-bundles/static-scene.test.ts.crdt +++ b/test/snapshots/development-bundles/static-scene.test.ts.crdt @@ -56,4 +56,4 @@ CALL onUpdate(0.1) OPCODES ~= 3k MALLOC_COUNT = -5 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1194.79k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1194.77k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/testing-fw.test.ts.crdt b/test/snapshots/development-bundles/testing-fw.test.ts.crdt index 95791a98d..42f208b91 100644 --- a/test/snapshots/development-bundles/testing-fw.test.ts.crdt +++ b/test/snapshots/development-bundles/testing-fw.test.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=454k bytes +SCENE_COMPILED_JS_SIZE_PROD=453.9k bytes THE BUNDLE HAS SOURCEMAPS (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 5k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 1203.40k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1203.39k bytes \ No newline at end of file diff --git a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt index c8d7770eb..c41b78d2d 100644 --- a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt +++ b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt @@ -64,4 +64,4 @@ CALL onUpdate(0.1) OPCODES ~= 5k MALLOC_COUNT = -40 ALIVE_OBJS_DELTA ~= -0.01k - MEMORY_USAGE_COUNT ~= 1203.41k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1203.39k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/billboard.ts.crdt b/test/snapshots/production-bundles/billboard.ts.crdt index 545259257..aea59f90d 100644 --- a/test/snapshots/production-bundles/billboard.ts.crdt +++ b/test/snapshots/production-bundles/billboard.ts.crdt @@ -58,7 +58,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x201 c=1 t=2 data={"position":{"x":12,"y":3.0420734882354736,"z":1},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x204 c=1 t=2 data={"position":{"x":12,"y":3.0420734882354736,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x203 c=1 t=2 data={"position":{"x":8,"y":3.0420734882354736,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} - OPCODES ~= 9k + OPCODES ~= 10k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k CALL onUpdate(0.1) @@ -66,7 +66,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x201 c=1 t=3 data={"position":{"x":12,"y":3.087538480758667,"z":1},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x204 c=1 t=3 data={"position":{"x":12,"y":3.087538480758667,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x203 c=1 t=3 data={"position":{"x":8,"y":3.087538480758667,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} - OPCODES ~= 9k + OPCODES ~= 10k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k CALL onUpdate(0.1) @@ -74,7 +74,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x201 c=1 t=4 data={"position":{"x":12,"y":3.0945944786071777,"z":1},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x204 c=1 t=4 data={"position":{"x":12,"y":3.0945944786071777,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} Scene: PUT_COMPONENT e=0x203 c=1 t=4 data={"position":{"x":8,"y":3.0945944786071777,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":2,"y":2,"z":2},"parent":0} - OPCODES ~= 9k + OPCODES ~= 10k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k MEMORY_USAGE_COUNT ~= 1042.09k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/cubes.ts.crdt b/test/snapshots/production-bundles/cubes.ts.crdt index 1f61d0820..5f6ca10fa 100644 --- a/test/snapshots/production-bundles/cubes.ts.crdt +++ b/test/snapshots/production-bundles/cubes.ts.crdt @@ -563,7 +563,7 @@ CALL onUpdate(0) Scene: PUT_COMPONENT e=0x2b4 c=1018 t=1 data={"mesh":{"$case":"box","box":{"uvs":[]}}} Scene: PUT_COMPONENT e=0x2b5 c=1018 t=1 data={"mesh":{"$case":"box","box":{"uvs":[]}}} Scene: PUT_COMPONENT e=0x2b6 c=1018 t=1 data={"mesh":{"$case":"box","box":{"uvs":[]}}} - OPCODES ~= 998k + OPCODES ~= 1000k MALLOC_COUNT = 1840 ALIVE_OBJS_DELTA ~= 0.74k CALL onUpdate(0.1) @@ -925,7 +925,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x2b4 c=1017 t=2 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b5 c=1017 t=2 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b6 c=1017 t=2 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} - OPCODES ~= 691k + OPCODES ~= 692k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k CALL onUpdate(0.1) @@ -1287,7 +1287,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x2b4 c=1017 t=3 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b5 c=1017 t=3 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b6 c=1017 t=3 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} - OPCODES ~= 691k + OPCODES ~= 692k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k CALL onUpdate(0.1) @@ -1649,7 +1649,7 @@ CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x2b4 c=1017 t=4 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b5 c=1017 t=4 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} Scene: PUT_COMPONENT e=0x2b6 c=1017 t=4 data={"material":{"$case":"pbr","pbr":{"albedoColor":{"r":0,"g":0,"b":1,"a":1}}}} - OPCODES ~= 691k + OPCODES ~= 692k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k MEMORY_USAGE_COUNT ~= 1178.02k bytes \ No newline at end of file diff --git a/test/snapshots/production-bundles/schema-components.ts.crdt b/test/snapshots/production-bundles/schema-components.ts.crdt index ff93c6a98..d2dfe615e 100644 --- a/test/snapshots/production-bundles/schema-components.ts.crdt +++ b/test/snapshots/production-bundles/schema-components.ts.crdt @@ -31,4 +31,4 @@ CALL onUpdate(0.1) OPCODES ~= 1k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 854.79k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 854.80k bytes \ No newline at end of file From 0c727b82d18c849bc14e34108ef9ce7e3531ca64 Mon Sep 17 00:00:00 2001 From: Lean Mendoza <8042536+leanmendoza@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:36:31 -0300 Subject: [PATCH 8/8] feat: add enter/leave helpers and UI map (#1040) * wip * wip * fix tests * add missing tests * fix coverage * update snapshosts --------- Co-authored-by: Gonzalo DCL --- .vscode/launch.json | 2 +- packages/@dcl/ecs/src/systems/events.ts | 81 ++++++++++++- .../etc/playground-assets.api.md | 14 +++ .../react-ecs/src/components/Button/index.tsx | 4 +- .../src/components/Dropdown/index.tsx | 6 +- .../react-ecs/src/components/Input/index.tsx | 6 +- .../react-ecs/src/components/Label/index.tsx | 6 +- .../src/components/listeners/types.ts | 8 +- packages/@dcl/react-ecs/src/react-ecs.ts | 2 + .../@dcl/react-ecs/src/reconciler/index.ts | 42 +++++-- .../@dcl/react-ecs/src/reconciler/utils.ts | 2 + test/ecs/events/system.spec.ts | 80 +++++++++++++ test/react-ecs/listeners.spec.tsx | 112 ++++++++++++++++++ test/snapshots/production-bundles/ui.ts.crdt | 14 +-- 14 files changed, 351 insertions(+), 28 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9bd8c2409..4a1ff481a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,7 +13,7 @@ "--async-stack-traces" ], "args": [ - "${fileBasename}", + "${file}", "--verbose", "--no-cache", "-i" diff --git a/packages/@dcl/ecs/src/systems/events.ts b/packages/@dcl/ecs/src/systems/events.ts index 91d92dc2e..9789d2772 100644 --- a/packages/@dcl/ecs/src/systems/events.ts +++ b/packages/@dcl/ecs/src/systems/events.ts @@ -52,6 +52,20 @@ export interface PointerEventsSystem { */ removeOnPointerUp(entity: Entity): void + /** + * @public + * Remove the callback for onPointerHoverEnter event + * @param entity - Entity where the callback was attached + */ + removeOnPointerHoverEnter(entity: Entity): void + + /** + * @public + * Remove the callback for onPointerHoverLeave event + * @param entity - Entity where the callback was attached + */ + removeOnPointerHoverLeave(entity: Entity): void + /** * @internal * Execute callback when the user clicks the entity. @@ -88,6 +102,28 @@ export interface PointerEventsSystem { * @param opts - Opts to trigger Feedback and Button */ onPointerUp(entity: Entity, cb: EventSystemCallback, opts?: Partial): void + + /** + * @public + * Execute callback when the user place the pointer over the entity + * @param pointerData - Entity to attach the callback - Opts to trigger Feedback and Button + * @param cb - Function to execute when click fires + */ + onPointerHoverEnter( + pointerData: { entity: Entity; opts?: Partial }, + cb: EventSystemCallback + ): void + + /** + * @public + * Execute callback when the user take the pointer out of the entity + * @param pointerData - Entity to attach the callback - Opts to trigger Feedback and Button + * @param cb - Function to execute when click fires + */ + onPointerHoverLeave( + pointerData: { entity: Entity; opts?: Partial }, + cb: EventSystemCallback + ): void } /** @@ -100,7 +136,9 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy enum EventType { Click, Down, - Up + Up, + HoverEnter, + HoverLeave } type EventMapType = Map @@ -135,6 +173,10 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy function getPointerEvent(eventType: EventType) { if (eventType === EventType.Up) { return PointerEventType.PET_UP + } else if (eventType === EventType.HoverLeave) { + return PointerEventType.PET_HOVER_LEAVE + } else if (eventType === EventType.HoverEnter) { + return PointerEventType.PET_HOVER_ENTER } return PointerEventType.PET_DOWN } @@ -164,7 +206,12 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy checkNotThenable(cb(command.up), 'Click event returned a thenable. Only synchronous functions are allowed') } - if (eventType === EventType.Down || eventType === EventType.Up) { + if ( + eventType === EventType.Down || + eventType === EventType.Up || + eventType === EventType.HoverEnter || + eventType === EventType.HoverLeave + ) { const command = inputSystem.getInputCommand(opts.button, getPointerEvent(eventType), entity) if (command) { checkNotThenable(cb(command), 'Event handler returned a thenable. Only synchronous functions are allowed') @@ -198,6 +245,24 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy setPointerEvent(entity, PointerEventType.PET_UP, options) } + const onPointerHoverEnter: PointerEventsSystem['onPointerHoverEnter'] = (...args) => { + const [data, cb] = args + const { entity, opts } = data + const options = getDefaultOpts(opts) + removeEvent(entity, EventType.HoverEnter) + getEvent(entity).set(EventType.HoverEnter, { cb, opts: options }) + setPointerEvent(entity, PointerEventType.PET_HOVER_ENTER, options) + } + + const onPointerHoverLeave: PointerEventsSystem['onPointerHoverLeave'] = (...args) => { + const [data, cb] = args + const { entity, opts } = data + const options = getDefaultOpts(opts) + removeEvent(entity, EventType.HoverLeave) + getEvent(entity).set(EventType.HoverLeave, { cb, opts: options }) + setPointerEvent(entity, PointerEventType.PET_HOVER_LEAVE, options) + } + return { removeOnClick(entity: Entity) { removeEvent(entity, EventType.Click) @@ -211,6 +276,14 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy removeEvent(entity, EventType.Up) }, + removeOnPointerHoverEnter(entity: Entity) { + removeEvent(entity, EventType.HoverEnter) + }, + + removeOnPointerHoverLeave(entity: Entity) { + removeEvent(entity, EventType.HoverLeave) + }, + onClick(value, cb) { const { entity } = value const options = getDefaultOpts(value.opts) @@ -223,6 +296,8 @@ export function createPointerEventsSystem(engine: IEngine, inputSystem: IInputSy }, onPointerDown, - onPointerUp + onPointerUp, + onPointerHoverEnter, + onPointerHoverLeave } } diff --git a/packages/@dcl/playground-assets/etc/playground-assets.api.md b/packages/@dcl/playground-assets/etc/playground-assets.api.md index ac86f6d71..23bca36c0 100644 --- a/packages/@dcl/playground-assets/etc/playground-assets.api.md +++ b/packages/@dcl/playground-assets/etc/playground-assets.api.md @@ -1082,6 +1082,8 @@ export type EntityComponents = { uiDropdown: PBUiDropdown; onMouseDown: Callback; onMouseUp: Callback; + onMouseEnter: Callback; + onMouseLeave: Callback; }; // @public (undocumented) @@ -1558,6 +1560,8 @@ export interface LastWriteWinElementSetComponentDefinition extends BaseCompon export type Listeners = { onMouseDown?: Callback; onMouseUp?: Callback; + onMouseEnter?: Callback; + onMouseLeave?: Callback; }; // @public (undocumented) @@ -3313,6 +3317,14 @@ export interface PointerEventsSystem { }, cb: EventSystemCallback): void; // @deprecated (undocumented) onPointerDown(entity: Entity, cb: EventSystemCallback, opts?: Partial): void; + onPointerHoverEnter(pointerData: { + entity: Entity; + opts?: Partial; + }, cb: EventSystemCallback): void; + onPointerHoverLeave(pointerData: { + entity: Entity; + opts?: Partial; + }, cb: EventSystemCallback): void; onPointerUp(pointerData: { entity: Entity; opts?: Partial; @@ -3320,6 +3332,8 @@ export interface PointerEventsSystem { // @deprecated (undocumented) onPointerUp(entity: Entity, cb: EventSystemCallback, opts?: Partial): void; removeOnPointerDown(entity: Entity): void; + removeOnPointerHoverEnter(entity: Entity): void; + removeOnPointerHoverLeave(entity: Entity): void; removeOnPointerUp(entity: Entity): void; } diff --git a/packages/@dcl/react-ecs/src/components/Button/index.tsx b/packages/@dcl/react-ecs/src/components/Button/index.tsx index f74a36781..56ef4a23c 100644 --- a/packages/@dcl/react-ecs/src/components/Button/index.tsx +++ b/packages/@dcl/react-ecs/src/components/Button/index.tsx @@ -35,7 +35,7 @@ function getButtonProps(props: UiButtonProps) { */ /* @__PURE__ */ export function Button(props: UiButtonProps) { - const { uiTransform, uiBackground, onMouseDown, onMouseUp, ...otherProps } = props + const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...otherProps } = props const buttonProps = getButtonProps(props) const uiBackgroundProps = parseUiBackground({ ...buttonProps.uiBackground, @@ -64,6 +64,8 @@ export function Button(props: UiButtonProps) { } diff --git a/packages/@dcl/react-ecs/src/components/Input/index.tsx b/packages/@dcl/react-ecs/src/components/Input/index.tsx index a6dde2d75..a88f95047 100644 --- a/packages/@dcl/react-ecs/src/components/Input/index.tsx +++ b/packages/@dcl/react-ecs/src/components/Input/index.tsx @@ -41,13 +41,15 @@ function parseUiInput(props: Partial): PBUiInput { * @category Component */ /* @__PURE__ */ export function Input(props: EntityPropTypes & Partial) { - const { uiTransform, uiBackground, onMouseDown, onMouseUp, ...otherProps } = props + const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...otherProps } = props const inputProps = parseUiInput(otherProps) const commonProps = parseProps({ uiTransform, uiBackground, onMouseDown, - onMouseUp + onMouseUp, + onMouseEnter, + onMouseLeave }) return } diff --git a/packages/@dcl/react-ecs/src/components/Label/index.tsx b/packages/@dcl/react-ecs/src/components/Label/index.tsx index f7a06dbc8..777caa77a 100644 --- a/packages/@dcl/react-ecs/src/components/Label/index.tsx +++ b/packages/@dcl/react-ecs/src/components/Label/index.tsx @@ -23,13 +23,15 @@ export { scaleFontSize } from './utils' /* @__PURE__ */ export function Label(props: EntityPropTypes & UiLabelProps) { - const { uiTransform, uiBackground, onMouseDown, onMouseUp, ...uiTextProps } = props + const { uiTransform, uiBackground, onMouseDown, onMouseUp, onMouseEnter, onMouseLeave, ...uiTextProps } = props const commonProps = parseProps({ uiTransform, uiBackground, onMouseDown, - onMouseUp + onMouseUp, + onMouseEnter, + onMouseLeave }) const { font, textAlign, fontSize, textWrap, ...textProps } = uiTextProps const uiText: PBUiText = { diff --git a/packages/@dcl/react-ecs/src/components/listeners/types.ts b/packages/@dcl/react-ecs/src/components/listeners/types.ts index 447cd60ef..04fd9a8e9 100644 --- a/packages/@dcl/react-ecs/src/components/listeners/types.ts +++ b/packages/@dcl/react-ecs/src/components/listeners/types.ts @@ -13,11 +13,17 @@ export type Listeners = { onMouseDown?: Callback /** triggered on mouse up event */ onMouseUp?: Callback + /** triggered on mouse hover event */ + onMouseEnter?: Callback + /** triggered on mouse leave event */ + onMouseLeave?: Callback } const listeners: Listeners = { onMouseDown: undefined, - onMouseUp: undefined + onMouseUp: undefined, + onMouseEnter: undefined, + onMouseLeave: undefined } const listenersKey = Object.keys(listeners) diff --git a/packages/@dcl/react-ecs/src/react-ecs.ts b/packages/@dcl/react-ecs/src/react-ecs.ts index c385491ff..f29fcf574 100644 --- a/packages/@dcl/react-ecs/src/react-ecs.ts +++ b/packages/@dcl/react-ecs/src/react-ecs.ts @@ -21,6 +21,8 @@ export type EntityComponents = { uiDropdown: PBUiDropdown onMouseDown: Callback onMouseUp: Callback + onMouseEnter: Callback + onMouseLeave: Callback } /** diff --git a/packages/@dcl/react-ecs/src/reconciler/index.ts b/packages/@dcl/react-ecs/src/reconciler/index.ts index 20fcefad7..bcc3cfaa0 100644 --- a/packages/@dcl/react-ecs/src/reconciler/index.ts +++ b/packages/@dcl/react-ecs/src/reconciler/index.ts @@ -27,7 +27,9 @@ import { componentKeys, isNotUndefined, noopConfig, propsChanged } from './utils function getPointerEnum(pointerKey: keyof Listeners): PointerEventType { const pointers: { [key in keyof Required]: PointerEventType } = { onMouseDown: PointerEventType.PET_DOWN, - onMouseUp: PointerEventType.PET_UP + onMouseUp: PointerEventType.PET_UP, + onMouseEnter: PointerEventType.PET_HOVER_ENTER, + onMouseLeave: PointerEventType.PET_HOVER_LEAVE } return pointers[pointerKey] } @@ -80,13 +82,20 @@ export function createReconciler( upsertComponent(instance, props as { rightOf: number; parent: number }, 'uiTransform') } - function upsertListener(instance: Instance, update: Changes>) { + function upsertListener( + instance: Instance, + update: Changes> + ) { if (update.type === 'delete' || !update.props) { clickEvents.get(instance.entity)?.delete(getPointerEnum(update.component)) if (update.component === 'onMouseDown') { pointerEvents.removeOnPointerDown(instance.entity) } else if (update.component === 'onMouseUp') { pointerEvents.removeOnPointerUp(instance.entity) + } else if (update.component === 'onMouseEnter') { + pointerEvents.removeOnPointerHoverEnter(instance.entity) + } else if (update.component === 'onMouseLeave') { + pointerEvents.removeOnPointerHoverLeave(instance.entity) } return } @@ -101,13 +110,28 @@ export function createReconciler( if (alreadyHasPointerEvent) return const pointerEventSystem = - update.component === 'onMouseDown' ? pointerEvents.onPointerDown : pointerEvents.onPointerUp - pointerEventSystem(instance.entity, () => pointerEventCallback(instance.entity, pointerEvent), { - button: InputAction.IA_POINTER, - // We add this showFeedBack so the pointerEventSystem creates a PointerEvent component with our entity - // This is needed for the renderer to know which entities are clickeables - showFeedback: true - }) + update.component === 'onMouseDown' + ? pointerEvents.onPointerDown + : update.component === 'onMouseUp' + ? pointerEvents.onPointerUp + : update.component === 'onMouseEnter' + ? pointerEvents.onPointerHoverEnter + : update.component === 'onMouseLeave' && pointerEvents.onPointerHoverLeave + + if (pointerEventSystem) { + pointerEventSystem( + { + entity: instance.entity, + opts: { + button: InputAction.IA_POINTER, + // We add this showFeedBack so the pointerEventSystem creates a PointerEvent component with our entity + // This is needed for the renderer to know which entities are clickeables + showFeedback: true + } + }, + () => pointerEventCallback(instance.entity, pointerEvent) + ) + } } } diff --git a/packages/@dcl/react-ecs/src/reconciler/utils.ts b/packages/@dcl/react-ecs/src/reconciler/utils.ts index 49862fe98..5f9349b06 100644 --- a/packages/@dcl/react-ecs/src/reconciler/utils.ts +++ b/packages/@dcl/react-ecs/src/reconciler/utils.ts @@ -58,6 +58,8 @@ const entityComponent: EntityComponents = { uiTransform: undefined as any, onMouseDown: undefined as any, onMouseUp: undefined as any, + onMouseEnter: undefined as any, + onMouseLeave: undefined as any, uiInput: undefined as any, uiDropdown: undefined as any } diff --git a/test/ecs/events/system.spec.ts b/test/ecs/events/system.spec.ts index 807900a39..c65f86096 100644 --- a/test/ecs/events/system.spec.ts +++ b/test/ecs/events/system.spec.ts @@ -259,4 +259,84 @@ describe('Events System', () => { EventsSystem.removeOnPointerUp(entity) }) + + it('should run default onHoverEnter', async () => { + const entity = engine.addEntity() + let counter = 0 + EventsSystem.onPointerHoverEnter( + { + entity + }, + () => { + counter += 1 + } + ) + fakePointer(entity, PointerEventType.PET_HOVER_ENTER) + await engine.update(1) + expect(counter).toBe(1) + }) + + it('should remove pointer hover enter', async () => { + const entity = engine.addEntity() + const PointerEvents = components.PointerEvents(engine) + let counter = 0 + EventsSystem.onPointerHoverEnter( + { + entity, + opts: { hoverText: 'test' } + }, + () => { + counter += 1 + EventsSystem.removeOnPointerHoverEnter(entity) + } + ) + fakePointer(entity, PointerEventType.PET_HOVER_ENTER) + await engine.update(1) + expect(counter).toBe(1) + + await engine.update(1) + expect(counter).toBe(1) + const feedback = PointerEvents.getOrNull(entity)?.pointerEvents + expect(feedback?.length).toBe(0) + }) + + it('should run default onHoverLeave', async () => { + const entity = engine.addEntity() + let counter = 0 + EventsSystem.onPointerHoverLeave( + { + entity + }, + () => { + counter += 1 + } + ) + fakePointer(entity, PointerEventType.PET_HOVER_LEAVE) + await engine.update(1) + expect(counter).toBe(1) + }) + + it('should remove pointer hover leave', async () => { + const entity = engine.addEntity() + const PointerEvents = components.PointerEvents(engine) + let counter = 0 + EventsSystem.onPointerHoverLeave( + { + entity, + opts: { hoverText: 'test' } + }, + () => { + counter += 1 + EventsSystem.removeOnPointerHoverLeave(entity) + } + ) + fakePointer(entity, PointerEventType.PET_HOVER_LEAVE) + await engine.update(1) + expect(counter).toBe(1) + + await engine.update(1) + expect(counter).toBe(1) + const feedback = PointerEvents.getOrNull(entity)?.pointerEvents + expect(feedback?.length).toBe(0) + }) }) diff --git a/test/react-ecs/listeners.spec.tsx b/test/react-ecs/listeners.spec.tsx index b74950277..8fd113b79 100644 --- a/test/react-ecs/listeners.spec.tsx +++ b/test/react-ecs/listeners.spec.tsx @@ -125,3 +125,115 @@ describe('Ui MouseUp React Ecs', () => { expect(counter).toBe(8888) }) }) + +describe('Ui MouseEnter React Ecs', () => { + const { engine, uiRenderer } = setupEngine() + const PointerEventsResult = components.PointerEventsResult(engine) + const uiEntity = ((engine.addEntity() as number) + 1) as Entity + let fakeCounter = 0 + const mouseEnterEvent = () => { + PointerEventsResult.addValue( + uiEntity, + createTestPointerDownCommand(uiEntity, fakeCounter + 1, PointerEventType.PET_HOVER_ENTER) + ) + fakeCounter += 1 + } + let counter = 0 + let onMouseEnter: (() => void) | undefined = () => { + counter++ + } + + const ui = () => + uiRenderer.setUiRenderer(ui) + + it('the counter should be 0 at the begginning', async () => { + expect(counter).toBe(0) + await engine.update(1) + }) + it('if we create a mouseDown event, then the onMouseEnter fn must be called and increment the counter by 1', async () => { + // Click with the current onMouseEnter + mouseEnterEvent() + await engine.update(1) + expect(counter).toBe(1) + }) + + it('remove onMouseEnter handler and verify that the counter is still 1', async () => { + // Remove onMouseEnter + onMouseEnter = undefined + await engine.update(1) + expect(counter).toBe(1) + }) + + it('create a mouseDown event and verify that the counter is not being incremented', async () => { + mouseEnterEvent() + await engine.update(1) + expect(counter).toBe(1) + }) + + it('replace onMouseEnter callback with a custom counter setter, and create an event to see if its being called', async () => { + // Add a new onMouseEnter + onMouseEnter = () => { + counter = 8888 + } + await engine.update(1) + mouseEnterEvent() + await engine.update(1) + expect(counter).toBe(8888) + }) +}) + +describe('Ui MouseLeave React Ecs', () => { + const { engine, uiRenderer } = setupEngine() + const PointerEventsResult = components.PointerEventsResult(engine) + const uiEntity = ((engine.addEntity() as number) + 1) as Entity + let fakeCounter = 0 + const mouseLeaveEvent = () => { + PointerEventsResult.addValue( + uiEntity, + createTestPointerDownCommand(uiEntity, fakeCounter + 1, PointerEventType.PET_HOVER_LEAVE) + ) + fakeCounter += 1 + } + let counter = 0 + let onMouseLeave: (() => void) | undefined = () => { + counter++ + } + + const ui = () => + uiRenderer.setUiRenderer(ui) + + it('the counter should be 0 at the begginning', async () => { + expect(counter).toBe(0) + await engine.update(1) + }) + it('if we create a mouseDown event, then the onMouseLeave fn must be called and increment the counter by 1', async () => { + // Click with the current onMouseLeave + mouseLeaveEvent() + await engine.update(1) + expect(counter).toBe(1) + }) + + it('remove onMouseLeave handler and verify that the counter is still 1', async () => { + // Remove onMouseLeave + onMouseLeave = undefined + await engine.update(1) + expect(counter).toBe(1) + }) + + it('create a mouseDown event and verify that the counter is not being incremented', async () => { + mouseLeaveEvent() + await engine.update(1) + expect(counter).toBe(1) + }) + + it('replace onMouseLeave callback with a custom counter setter, and create an event to see if its being called', async () => { + // Add a new onMouseLeave + onMouseLeave = () => { + counter = 8888 + } + await engine.update(1) + mouseLeaveEvent() + await engine.update(1) + expect(counter).toBe(8888) + }) +}) diff --git a/test/snapshots/production-bundles/ui.ts.crdt b/test/snapshots/production-bundles/ui.ts.crdt index fe8c7f005..85450328a 100644 --- a/test/snapshots/production-bundles/ui.ts.crdt +++ b/test/snapshots/production-bundles/ui.ts.crdt @@ -1,4 +1,4 @@ -SCENE_COMPILED_JS_SIZE_PROD=351.3k bytes +SCENE_COMPILED_JS_SIZE_PROD=352.5k bytes (start empty vm 0.21.0-3680274614.commit-1808aa1) OPCODES ~= 0k MALLOC_COUNT = 1005 @@ -9,7 +9,7 @@ EVAL test/snapshots/production-bundles/ui.js REQUIRE: ~system/EngineApi REQUIRE: ~system/EngineApi OPCODES ~= 65k - MALLOC_COUNT = 19449 + MALLOC_COUNT = 19477 ALIVE_OBJS_DELTA ~= 3.87k CALL onStart() OPCODES ~= 0k @@ -47,22 +47,22 @@ CALL onUpdate(0) Scene: PUT_COMPONENT e=0x202 c=1093 t=1 data={"placeholder":"SARASA","disabled":false} Scene: PUT_COMPONENT e=0x209 c=1093 t=1 data={"placeholder":"","disabled":false} Scene: PUT_COMPONENT e=0x201 c=1094 t=1 data={"acceptEmpty":false,"options":["BOEDO","CASLA"],"selectedIndex":0,"disabled":false,"color":{"r":1,"g":0,"b":0,"a":1}} - OPCODES ~= 131k + OPCODES ~= 133k MALLOC_COUNT = 678 ALIVE_OBJS_DELTA ~= 0.24k CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x200 c=1 t=2 data={"position":{"x":8,"y":1,"z":8},"rotation":{"x":0,"y":0.008726535364985466,"z":0,"w":0.9999619126319885},"scale":{"x":1,"y":1,"z":1},"parent":0} - OPCODES ~= 65k + OPCODES ~= 66k MALLOC_COUNT = 250 ALIVE_OBJS_DELTA ~= 0.12k CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x200 c=1 t=3 data={"position":{"x":8,"y":1,"z":8},"rotation":{"x":0,"y":0.017452405765652657,"z":0,"w":0.9998477101325989},"scale":{"x":1,"y":1,"z":1},"parent":0} - OPCODES ~= 63k + OPCODES ~= 64k MALLOC_COUNT = 34 ALIVE_OBJS_DELTA ~= 0.02k CALL onUpdate(0.1) Scene: PUT_COMPONENT e=0x200 c=1 t=4 data={"position":{"x":8,"y":1,"z":8},"rotation":{"x":0,"y":0.026176948100328445,"z":0,"w":0.9996573328971863},"scale":{"x":1,"y":1,"z":1},"parent":0} - OPCODES ~= 63k + OPCODES ~= 64k MALLOC_COUNT = 0 ALIVE_OBJS_DELTA ~= 0.00k - MEMORY_USAGE_COUNT ~= 1687.14k bytes \ No newline at end of file + MEMORY_USAGE_COUNT ~= 1692.09k bytes \ No newline at end of file