From 4a05bbb24ec7f010f01208797680c2cdfd2800e0 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 6 Nov 2024 13:23:19 +0100 Subject: [PATCH 01/11] chore: set living ADRs of adopted ones (#274) * chore: set living ADRs of adopted ones * fix crlf * fix crlf --- content/ADR-133-scene-runtime.md | 3 +-- content/ADR-144-realm-resolution-from-connection-string.md | 2 +- content/ADR-200-raycast-sdk-component.md | 2 +- content/ADR-214-pointer-events-sdk-component.md | 2 +- content/ADR-215-gltfcomponent-sdk-component.md | 2 +- content/ADR-219-static-entities.md | 2 +- content/ADR-245-player-components.md | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/content/ADR-133-scene-runtime.md b/content/ADR-133-scene-runtime.md index a04e0f87..5c30b57a 100644 --- a/content/ADR-133-scene-runtime.md +++ b/content/ADR-133-scene-runtime.md @@ -4,7 +4,7 @@ date: 2022-11-17 title: Scene runtime definition authors: - menduz -status: Draft +status: Living type: Standards Track spdx-license: CC0-1.0 --- @@ -69,7 +69,6 @@ The runtime for the SDK7 is compatible with [CommonJS](https://en.wikipedia.org/ The exposed RPC modules are defined in the [protocol repository](https://github.com/decentraland/protocol/blob/main/proto/decentraland/kernel/apis/engine_api.proto). -> TODO: define and document naming conventions about code generation for modules ```ts // `require` instantiates a proxy to a RPC module. Every exposed function diff --git a/content/ADR-144-realm-resolution-from-connection-string.md b/content/ADR-144-realm-resolution-from-connection-string.md index 09032076..0061eddf 100644 --- a/content/ADR-144-realm-resolution-from-connection-string.md +++ b/content/ADR-144-realm-resolution-from-connection-string.md @@ -3,7 +3,7 @@ layout: adr adr: 144 title: Realm resolution from connection string date: 2022-12-13 -status: Review +status: Living type: Standards Track spdx-license: CC0-1.0 authors: diff --git a/content/ADR-200-raycast-sdk-component.md b/content/ADR-200-raycast-sdk-component.md index ba06f6a7..5f0b06dd 100644 --- a/content/ADR-200-raycast-sdk-component.md +++ b/content/ADR-200-raycast-sdk-component.md @@ -3,7 +3,7 @@ layout: adr adr: 200 title: Raycasting Component for SDK date: 2023-03-14 -status: Draft # pick one of these +status: Living type: Standards Track spdx-license: CC0-1.0 authors: diff --git a/content/ADR-214-pointer-events-sdk-component.md b/content/ADR-214-pointer-events-sdk-component.md index 4aa82e53..86f57c40 100644 --- a/content/ADR-214-pointer-events-sdk-component.md +++ b/content/ADR-214-pointer-events-sdk-component.md @@ -3,7 +3,7 @@ layout: adr adr: 214 # replace this number for the PR or ISSUE number title: PointerEvents SDK Component & Input system date: 2023-04-17 -status: Draft # pick one of these +status: Living type: Standards Track spdx-license: CC0-1.0 authors: diff --git a/content/ADR-215-gltfcomponent-sdk-component.md b/content/ADR-215-gltfcomponent-sdk-component.md index 5da99de5..b8a77cf9 100644 --- a/content/ADR-215-gltfcomponent-sdk-component.md +++ b/content/ADR-215-gltfcomponent-sdk-component.md @@ -3,7 +3,7 @@ layout: adr adr: 215 title: GltfContainer SDK Component date: 2020-02-20 -status: Draft # pick one of these +status: Living type: Standards Track spdx-license: CC0-1.0 authors: diff --git a/content/ADR-219-static-entities.md b/content/ADR-219-static-entities.md index bfd88168..222eb5bd 100644 --- a/content/ADR-219-static-entities.md +++ b/content/ADR-219-static-entities.md @@ -3,7 +3,7 @@ layout: adr adr: 219 title: Reserved & Static Entities date: 2023-04-19 -status: Draft +status: Living type: Standards Track spdx-license: CC0-1.0 authors: diff --git a/content/ADR-245-player-components.md b/content/ADR-245-player-components.md index 86ad1d7a..41fbf1d3 100644 --- a/content/ADR-245-player-components.md +++ b/content/ADR-245-player-components.md @@ -3,7 +3,7 @@ layout: adr adr: 245 title: Player Components date: 2023-08-22 -status: Review +status: Living type: Standards Track spdx-license: CC0-1.0 authors: From 6656de435b51409bdeecbac9678c6b041c2e1242 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 6 Nov 2024 13:28:43 +0100 Subject: [PATCH 02/11] chore: add example code to adr 239 (#270) * chore: add example code to adr 239 * clarity in getHides parameter, fix forceRender and included all the wearables for bodyShape hidings * explain with an example --- content/ADR-239-avatar-assembly-changes.md | 453 ++++++++++++--------- 1 file changed, 257 insertions(+), 196 deletions(-) diff --git a/content/ADR-239-avatar-assembly-changes.md b/content/ADR-239-avatar-assembly-changes.md index 1fc7cc39..e0c73896 100644 --- a/content/ADR-239-avatar-assembly-changes.md +++ b/content/ADR-239-avatar-assembly-changes.md @@ -1,196 +1,257 @@ ---- -adr: 239 -date: 2023-06-01 -title: Avatar Assembly Changes -status: Living -authors: - - davidejensen -type: Standards Track -spdx-license: CC0-1.0 ---- - -## Abstract - -This ADR discusses improvements to the avatar system in Decentraland to enhance user freedom and create a more reliable, flexible, and scalable system. The ADR establishes a prioritization order for wearable visualization, modifies the replacement system to prevent wearables from replacing each other, introduces body fallback to show base body parts when no wearables are equipped, and allows users to force the rendering of hidden wearables. These decisions will serve as the standard for the Decentraland Avatar Assembly System and can be implemented in external platforms. The suggested flow for avatar assembly is also outlined. - - -## Context, Reach & Prioritization - -Previously, the avatar system has been unreliable and limited over different aspects. -To increase users freedom, create a reliable, flexible and scalable system the following changes will be applied: -- **Wearable Visualization Priority:** A prioritization order will be defined for wearable visualization to resolve issues of mutual exclusion. -- **Hiding and Replacing:** The replacement system will be modified so that a wearable cannot replace another. -- **Body Fallback:** From now on, some categories will show parts of the base body when no wearable is equipped or hiding it. -- **Force Render:** Users will be given more ability to show a wearable that is hidden by another. - -The different decisions will become the standard base for Decentraland Avatar Assembly System, initially applied in the unity reference client, but then proposed to any external platform that uses the Decentraland avatars. - -## Solution Space Exploration - -### Wearable Visualization Priority - -Previously the hiding and replacing functionality was following a rule of "Last equipped", meaning that replaces and hides were applied based on the last wearable equipped, creating discrepancy and an inconsistent behaviour. Additionally it was not solving situations of mutual hiding (wearable A hiding B and B hiding A) -To standardize this an ordered list of categories will be followed to calculate which wearables are hiding which. -The proposed priority list is (from most important to least): -- Skins -- Top Body -- Bottom Body -- Shoes -- Helmets -- Masks -- Hats -- Top Head -- Hair -- Tiaras -- Eyewear -- Earrings - -All elements associated with the face, such as the mouth, eyes, and eyebrows, directly depend on the visualization of the head. The head is not a wearable itself, so it cannot create circular references, and it is hidden as soon as one or more wearables determine it. - -The following example illustrates the expected behavior: If a wearable of type "Mask" hides another wearable of type "Hat", and this in turn hides wearables of type "Mask", the wearable of type "Mask" will have visualization priority, hiding the Hat. - - -### Hiding and replacing - -The current result of equipping an item that replaces or hides other wearables is unexpected and inconsistent. When a player navigates through their inventory items, as they equip, unequip, or hide other wearables from other categories without notice, they cannot reverse this behavior when equipping a different wearable. -When a wearable unequips another wearable, the latter could be running a portable experience. By deactivating it, any progress or data in memory could be lost, ruining its expected behavior. -The decision-making capacity of creators is overlapping with the freedom of choice of players. When a player purchases a wearable, thinking it will combine well with what they already have in their inventory, they may be unpleasantly surprised that these are not compatible. Information about which other wearables are replaced or hidden is not clearly indicated in the marketplace or backpack. -There is no real weighty reason for a wearable to unequip another wearable, instead of hiding it to prevent clipping problems. - -On the base of that the replacing functionality for new wearables will be removed, pre-existing wearables will see the replacing list being merged with the hiding list. - -### Body fallback - -Previously, by unequipping the upper_body, the lower_body, the feet or the eyebrows, caused the system to fallback to default wearables (t-shirt, pants and shoes). -This prevents, for example, players from going barefoot or without t-shirt. - -The system will extent the concept of Body by removing the default fallback wearables. -These pieces, which can be called "Bare feet", "Naked Top" and "Naked Bottom", are not considered wearables, but rather parts of the base body geometry that are exclusively displayed when no wearables are equipped and there are no other wearables hiding the relevant category. - - -### Force Render - -To extend further players ability to use the wearables they have. If an equipped wearable is hidden by the effect of another wearable, players can unhide them and override this instruction. -This forced render effect is removed as soon as another wearable that re-hides that category is equipped, forcing the user to show it again. - -In order to store the force render categories, it is proposed to add a new field to the player profile holding a list of overridden hide categories. -This list of string will hold the current overridden categories so that each player can render other players (and itself) according to the new rules. -As an example: -```yaml -"forceRender": ["mask","top_head","feet"] -``` -This example means that when the avatar is going to be assembled the "mask", "top_head" and "feet" categories will be shown even if other wearables are hiding them. - -Here's the avatar definition with the additional forceRender field that will be provided: -```json -{ - "timestamp":1684839566179, - "avatars":[ - { - "hasClaimedName":true, - "description":"", - "tutorialStep":256, - "name":"Username", - "avatar":{ - "bodyShape":"urn:decentraland:off-chain:base-avatars:BaseMale", - "wearables":[ - "urn:decentraland:off-chain:base-avatars:eyebrows_00", - "urn:decentraland:off-chain:base-avatars:mouth_00", - "urn:decentraland:off-chain:base-avatars:casual_hair_01", - "urn:decentraland:off-chain:base-avatars:beard", - "urn:decentraland:matic:collections-v2:0xc11b9d892e12cfaca551551345266d60e9abff6e:1", - "urn:decentraland:matic:collections-v2:0x84a1d84f183fa0fd9b6b9cb1ed0ff1b7f5409ebb:5", - "urn:decentraland:ethereum:collections-v1:halloween_2020:hwn_2020_ghostblaster_tiara", - "urn:decentraland:ethereum:collections-v1:halloween_2020:hwn_2020_cat_eyes", - "urn:decentraland:off-chain:base-avatars:piratepatch", - "urn:decentraland:matic:collections-v2:0xca7c347ffdeee480092f3b1268550b60ea031077:4", - "urn:decentraland:matic:collections-v2:0x2a3a6d0c92b18102ed189233976c974473a59c87:0" - ], - "forceRender":["mask","top_head","feet"], - "emotes":[ - { - "slot":0, - "urn":"raiseHand" - }, - { - "slot":1, - "urn":"urn:decentraland:matic:collections-v2:0x167d6b63511a7b5062d1f7b07722fccbbffb5105:0" - }, - { - "slot":2, - "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:1" - }, - { - "slot":3, - "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:2" - }, - { - "slot":4, - "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:0" - }, - { - "slot":5, - "urn":"headexplode" - }, - { - "slot":6, - "urn":"tektonik" - }, - { - "slot":7, - "urn":"clap" - }, - { - "slot":8, - "urn":"handsair" - }, - { - "slot":9, - "urn":"dance" - } - ], - "snapshots":{ - "body":"https://interconnected.online/content/contents/bafkreihhwboio77zsl6evt6i3f5iun5ek65w7dyuvl44xwk33zts4qx2v4", - "face256":"https://interconnected.online/content/contents/bafkreiddnf2ixknlhio4hk7ao47agiwkqs43cg3nkoyibckgmhz5iqn3he" - }, - "eyes":{ - "color":{ - "r":0.529411792755127, - "g":0.501960813999176, - "b":0.47058823704719543, - "a":1 - } - }, - "hair":{ - "color":{ - "r":0.10980392247438431, - "g":0.10980392247438431, - "b":0.10980392247438431, - "a":1 - } - }, - "skin":{ - "color":{ - "r":0.800000011920929, - "g":0.6078431606292725, - "b":0.46666666865348816, - "a":1 - } - } - }, - "ethAddress":"0x1hd7fl09yivcn7z6asc0a8af9c3b1ba28hdy65sd", - "userId":"0x1hd7fl09yivcn7z6asc0a8af9c3b1ba28hdy65sd", - "version":12, - "hasConnectedWeb3":true - } - ] -} -``` - -### Avatar assembly suggested flow - -With the points previously discovered, the suggested procedure to visualize an avatar is the following: -* Request a user profile ( `POST https://content-server/content/active {"pointers": ["0xaddress"]}` ) -* Request the wearables listed in the profile wearables field, all wearables listed are the equipped ones, no matter their visibility. ( `POST https://content-server/content/active {"pointers": ["urn1", "urn2"]}` ) -* Process the hide list provided in each wearable following the visualization priority ( the field is the array `data.hides` in each wearable definition ) -* Apply any force render category listed in the forceRender array in the profile definition ( obtained in the profile response called `forceRender` ) +--- +adr: 239 +date: 2023-06-01 +title: Avatar Assembly Changes +status: Living +authors: + - davidejensen +type: Standards Track +spdx-license: CC0-1.0 +--- + +## Abstract + +This ADR discusses improvements to the avatar system in Decentraland to enhance user freedom and create a more reliable, flexible, and scalable system. The ADR establishes a prioritization order for wearable visualization, modifies the replacement system to prevent wearables from replacing each other, introduces body fallback to show base body parts when no wearables are equipped, and allows users to force the rendering of hidden wearables. These decisions will serve as the standard for the Decentraland Avatar Assembly System and can be implemented in external platforms. The suggested flow for avatar assembly is also outlined. + + +## Context, Reach & Prioritization + +Previously, the avatar system has been unreliable and limited over different aspects. +To increase users freedom, create a reliable, flexible and scalable system the following changes will be applied: +- **Wearable Visualization Priority:** A prioritization order will be defined for wearable visualization to resolve issues of mutual exclusion. +- **Hiding and Replacing:** The replacement system will be modified so that a wearable cannot replace another. +- **Body Fallback:** From now on, some categories will show parts of the base body when no wearable is equipped or hiding it. +- **Force Render:** Users will be given more ability to show a wearable that is hidden by another. + +The different decisions will become the standard base for Decentraland Avatar Assembly System, initially applied in the unity reference client, but then proposed to any external platform that uses the Decentraland avatars. + +## Solution Space Exploration + +### Wearable Visualization Priority + +Previously the hiding and replacing functionality was following a rule of "Last equipped", meaning that replaces and hides were applied based on the last wearable equipped, creating discrepancy and an inconsistent behaviour. Additionally it was not solving situations of mutual hiding (wearable A hiding B and B hiding A) +To standardize this an ordered list of categories will be followed to calculate which wearables are hiding which. +The proposed priority list is (from most important to least): +- Skins +- Top Body +- Bottom Body +- Shoes +- Helmets +- Masks +- Hats +- Top Head +- Hair +- Tiaras +- Eyewear +- Earrings + +All elements associated with the face, such as the mouth, eyes, and eyebrows, directly depend on the visualization of the head. The head is not a wearable itself, so it cannot create circular references, and it is hidden as soon as one or more wearables determine it. + +The following example illustrates the expected behavior: If a wearable of type "Mask" hides another wearable of type "Hat", and this in turn hides wearables of type "Mask", the wearable of type "Mask" will have visualization priority, hiding the Hat. + + +### Hiding and replacing + +The current result of equipping an item that replaces or hides other wearables is unexpected and inconsistent. When a player navigates through their inventory items, as they equip, unequip, or hide other wearables from other categories without notice, they cannot reverse this behavior when equipping a different wearable. +When a wearable unequips another wearable, the latter could be running a portable experience. By deactivating it, any progress or data in memory could be lost, ruining its expected behavior. +The decision-making capacity of creators is overlapping with the freedom of choice of players. When a player purchases a wearable, thinking it will combine well with what they already have in their inventory, they may be unpleasantly surprised that these are not compatible. Information about which other wearables are replaced or hidden is not clearly indicated in the marketplace or backpack. +There is no real weighty reason for a wearable to unequip another wearable, instead of hiding it to prevent clipping problems. + +On the base of that the replacing functionality for new wearables will be removed, pre-existing wearables will see the replacing list being merged with the hiding list. + +### Body fallback + +Previously, by unequipping the upper_body, the lower_body, the feet or the eyebrows, caused the system to fallback to default wearables (t-shirt, pants and shoes). +This prevents, for example, players from going barefoot or without t-shirt. + +The system will extent the concept of Body by removing the default fallback wearables. +These pieces, which can be called "Bare feet", "Naked Top" and "Naked Bottom", are not considered wearables, but rather parts of the base body geometry that are exclusively displayed when no wearables are equipped and there are no other wearables hiding the relevant category. + + +### Force Render + +To extend further players ability to use the wearables they have. If an equipped wearable is hidden by the effect of another wearable, players can unhide them and override this instruction. +This forced render effect is removed as soon as another wearable that re-hides that category is equipped, forcing the user to show it again. + +In order to store the force render categories, it is proposed to add a new field to the player profile holding a list of overridden hide categories. +This list of string will hold the current overridden categories so that each player can render other players (and itself) according to the new rules. +As an example: +```yaml +"forceRender": ["mask","top_head","feet"] +``` +This example means that when the avatar is going to be assembled the "mask", "top_head" and "feet" categories will be shown even if other wearables are hiding them. + +Here's the avatar definition with the additional forceRender field that will be provided: +```json +{ + "timestamp":1684839566179, + "avatars":[ + { + "hasClaimedName":true, + "description":"", + "tutorialStep":256, + "name":"Username", + "avatar":{ + "bodyShape":"urn:decentraland:off-chain:base-avatars:BaseMale", + "wearables":[ + "urn:decentraland:off-chain:base-avatars:eyebrows_00", + "urn:decentraland:off-chain:base-avatars:mouth_00", + "urn:decentraland:off-chain:base-avatars:casual_hair_01", + "urn:decentraland:off-chain:base-avatars:beard", + "urn:decentraland:matic:collections-v2:0xc11b9d892e12cfaca551551345266d60e9abff6e:1", + "urn:decentraland:matic:collections-v2:0x84a1d84f183fa0fd9b6b9cb1ed0ff1b7f5409ebb:5", + "urn:decentraland:ethereum:collections-v1:halloween_2020:hwn_2020_ghostblaster_tiara", + "urn:decentraland:ethereum:collections-v1:halloween_2020:hwn_2020_cat_eyes", + "urn:decentraland:off-chain:base-avatars:piratepatch", + "urn:decentraland:matic:collections-v2:0xca7c347ffdeee480092f3b1268550b60ea031077:4", + "urn:decentraland:matic:collections-v2:0x2a3a6d0c92b18102ed189233976c974473a59c87:0" + ], + "forceRender":["mask","top_head","feet"], + "emotes":[ + { + "slot":0, + "urn":"raiseHand" + }, + { + "slot":1, + "urn":"urn:decentraland:matic:collections-v2:0x167d6b63511a7b5062d1f7b07722fccbbffb5105:0" + }, + { + "slot":2, + "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:1" + }, + { + "slot":3, + "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:2" + }, + { + "slot":4, + "urn":"urn:decentraland:matic:collections-v2:0x875146d1d26e91c80f25f5966a84b098d3db1fc8:0" + }, + { + "slot":5, + "urn":"headexplode" + }, + { + "slot":6, + "urn":"tektonik" + }, + { + "slot":7, + "urn":"clap" + }, + { + "slot":8, + "urn":"handsair" + }, + { + "slot":9, + "urn":"dance" + } + ], + "snapshots":{ + "body":"https://interconnected.online/content/contents/bafkreihhwboio77zsl6evt6i3f5iun5ek65w7dyuvl44xwk33zts4qx2v4", + "face256":"https://interconnected.online/content/contents/bafkreiddnf2ixknlhio4hk7ao47agiwkqs43cg3nkoyibckgmhz5iqn3he" + }, + "eyes":{ + "color":{ + "r":0.529411792755127, + "g":0.501960813999176, + "b":0.47058823704719543, + "a":1 + } + }, + "hair":{ + "color":{ + "r":0.10980392247438431, + "g":0.10980392247438431, + "b":0.10980392247438431, + "a":1 + } + }, + "skin":{ + "color":{ + "r":0.800000011920929, + "g":0.6078431606292725, + "b":0.46666666865348816, + "a":1 + } + } + }, + "ethAddress":"0x1hd7fl09yivcn7z6asc0a8af9c3b1ba28hdy65sd", + "userId":"0x1hd7fl09yivcn7z6asc0a8af9c3b1ba28hdy65sd", + "version":12, + "hasConnectedWeb3":true + } + ] +} +``` + +### Avatar assembly suggested flow + +With the points previously discovered, the suggested procedure to visualize an avatar is the following: +* Request a user profile ( `POST https://content-server/content/active {"pointers": ["0xaddress"]}` ) +* Request the wearables listed in the profile wearables field, all wearables listed are the equipped ones, no matter their visibility. ( `POST https://content-server/content/active {"pointers": ["urn1", "urn2"]}` ) +* Process the hide list provided in each wearable following the visualization priority ( the field is the array `data.hides` in each wearable definition ) +* Apply any force render category listed in the forceRender array in the profile definition ( obtained in the profile response called `forceRender` ) + +### Examples +```typescript + +/** + * @param profile - holds the wearables equipped, bodyShape and the forceRender parameter + * @param forceAll - whether ALL the hides in wearables equipped should be considered or not + * @returns the set of categories that should be hidden + */ +function getHiddenList(profile: UserProfile, forceAll: boolean = false): HideableCategory[] { + const alreadyHidden = new Set() + const wearables: Map = profile.wearables + for (const category of categoriesPriority) { + const wearable = wearables.get(category) + if (!wearable) { + continue + } + + if (!forceAll) { + if (alreadyHidden.has(category)) { + continue + } + } + + // getHides provides the union of hides and replaces given the representation + // and also add the provided by the category (skin case) + for (const slot of getHides(wearable, profile.bodyShape)) { + alreadyHidden.add(slot) + } + } + + return Array.from(alreadyRemoved).filter(category => !profile.forceRender.includes(category)) +} + +// 1) Build the hidden list +const toHide = getHiddenList(profile) + +// 2) Remove the hidden wearables +for (const category of toHide) { + wearables.delete(category) +} + +// 3) Compute the current hidden list for hiding parts of the bodyshape +const hidden = getHiddenList(profile, true) +const bodyShape = getBodyShape(profile, hidden) +``` + +Given the next list of wearables with the `hides` as the array value: +```json +{ + "helmet": [ "top_head", "head", "hair", "earring" ], + "top_head": [ "hat" ], + "hands_wear": [ "hands" ], + "hat": [ "helmet", "hair" ] +} +``` +In step (1), the `toHide` array is as follows: `[ "top_head", "head", "hair", "earring", "helmet", "hair" ]`. Consequently, in step (2), the wearables `helmet` and `top_head` are removed. + +With the remaining wearables `hands_wear` and `hat`, the resulting hidden array becomes: `[ "hands", "hair" ]`. Therefore, the `bodyShape` should hide both the `hands` and `hair`. Note that if we had used the original `toHide` list to determine what body parts to conceal, the head would be hidden in the final outcome. + +Now, consider the scenario where `top_head` is included in the forceRender value: in this case, the `toHide` list remains the same, except for the exclusion of `top_head`. As a result, in step (2), this wearable is not removed. Interestingly, the `hidden` list now changes in (3), incorporating `hat`. However, this does not affect the `bodyShape` or alter the wearable list in any way. \ No newline at end of file From d8e13bb4a85760dd3db80676c8b1258e65726b96 Mon Sep 17 00:00:00 2001 From: pentreathm Date: Thu, 7 Nov 2024 12:16:52 -0300 Subject: [PATCH 03/11] docs: change adr status to living (#275) --- content/ADR-202-linked-wearables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/ADR-202-linked-wearables.md b/content/ADR-202-linked-wearables.md index 699b49be..56a9ff2b 100644 --- a/content/ADR-202-linked-wearables.md +++ b/content/ADR-202-linked-wearables.md @@ -3,7 +3,7 @@ layout: adr adr: 202 title: Linked Wearables Implementation date: 2024-08-21 -status: Draft +status: Living type: RFC spdx-license: CC0-1.0 authors: From eaaa0ce3224f9d17e37fe3623b4ee0ed59702004 Mon Sep 17 00:00:00 2001 From: Aga Date: Tue, 26 Nov 2024 17:38:07 +0100 Subject: [PATCH 04/11] docs: ADR draft for introducing the deprecated State for ADRs (#277) * docs: ADR draft for introducing the deprecated State for ADRs * Updated the formating Signed-off-by: Aga * Update ADR-253-introduce-depracated-state.md Signed-off-by: Aga * Updated status Signed-off-by: Aga --------- Signed-off-by: Aga --- content/ADR-277-introduce-depracated-state.md | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 content/ADR-277-introduce-depracated-state.md diff --git a/content/ADR-277-introduce-depracated-state.md b/content/ADR-277-introduce-depracated-state.md new file mode 100644 index 00000000..660f7a2b --- /dev/null +++ b/content/ADR-277-introduce-depracated-state.md @@ -0,0 +1,87 @@ +--- +layout: adr +adr: 277 # replace this number with the PR or ISSUE number +title: Introducing the "Deprecated" State for ADRs +date: 2024-11-15 +status: Review +type: Meta +spdx-license: CC0-1.0 +authors: + - aixaCode +--- + +## Abstract + +This ADR proposes introducing a new **Deprecated** status for ADRs that are no longer relevant due to changes in Decentraland’s architecture, protocol, clients, or feature set. The goal is to improve clarity by distinguishing between ADRs that were once implemented and valid but are now outdated, versus those that were never accepted or implemented. + +## Context, Reach & Prioritization + +Currently, the available status options include **Withdrawn** and **Stagnant**. However, these do not effectively communicate the state of ADRs that were once implemented but have since become obsolete due to changes in technology or product strategy. + +The **Withdrawn** status implies that an ADR was abandoned or deemed incorrect before implementation, while **Stagnant** is used for ADRs that were never finalized or have been inactive for too long. Neither status accurately reflects the state of ADRs that were previously accepted and implemented but are now outdated. + +Introducing a **Deprecated** status will: + +- Clearly identify ADRs that have been rendered obsolete due to changes in protocol or feature sets. +- Preserve the historical context of past decisions while indicating that they are no longer applicable. +- Reduce confusion among developers and stakeholders regarding the current relevance of ADRs. + +## Solution Space Exploration + +### Option 1: Continue Using the Existing Statuses +- **Pros**: No change to the current workflow. +- **Cons**: Does not address the confusion between ADRs that were valid but are now outdated versus those that were never fully accepted. + +### Option 2: Introduce a New **Deprecated** Status (Chosen Solution) +- **Pros**: Provides clear differentiation between ADRs that were previously implemented and those that were abandoned. Enhances clarity in documentation. +- **Cons**: Requires updating existing ADRs and adding a new status to the lifecycle. + +### Chosen Solution + +Introduce a new **Deprecated** status for ADRs that are no longer relevant but were once actively used and implemented. + +## Specification + +### Updated ADR States + +```mermaid +flowchart TB + Draft --> Withdrawn + Idea --> Draft + + Draft --> Review + Review <--> Living + Review <--> LastCall + LastCall --> Final + + Review --> Withdrawn + LastCall --> Withdrawn + + LastCall <--> Stagnant + Review <--> Stagnant + Draft <--> Stagnant + + Final --> Deprecated +``` + +The full list of ADR states will now include: + +- **Idea**: An idea that is pre-draft, not tracked in the ADR repository. +- **Draft**: The first formally tracked stage of an ADR in development. +- **Review**: The ADR is ready for peer review. +- **Last Call**: Final review window before the ADR is marked as Final. +- **Final**: The ADR is accepted as a standard and is in use. +- **Stagnant**: ADRs that are inactive for over 6 months in Draft, Review, or Last Call. +- **Withdrawn**: The ADR was abandoned or rejected and will not be pursued further. +- **Living**: The ADR is designed to be continually updated and does not reach finality (e.g., ADR-1). +- **Deprecated**: The ADR was once Final and implemented but is now outdated or no longer relevant due to protocol changes. + +### Deprecated State Requirements + +- An ADR marked as **Deprecated** MUST include a **Deprecation Reason** section explaining why it is no longer relevant. +- If applicable, it should provide links to newer ADRs that supersede it. + +### Example Deprecation Notice + +```markdown +**Notice**: This ADR has been marked as **Deprecated** as of 2024-11-15 due to the deprecation of the old Decentraland client. Refer to [ADR-123: Updated Client Architecture](/adr/ADR-123) for more information. From 8f411cf4316fbc3dd34bea000e8109dd2591d28b Mon Sep 17 00:00:00 2001 From: Aga Date: Tue, 3 Dec 2024 10:25:31 +0100 Subject: [PATCH 05/11] docs: updating ADR 277, making changes to introduce deprecated state (#278) * docs: updating ADR 277, making changes to introduce deprecated state * Removed check for Withdrawn reason * Fixing graph * Updated status --- .site-generator/index.js | 18 ++++-- .site-generator/scss/main.scss | 6 ++ README.md | 11 +++- content/ADR-1-adr-process.md | 63 ++++++++++--------- content/ADR-277-introduce-depracated-state.md | 5 +- 5 files changed, 63 insertions(+), 40 deletions(-) diff --git a/.site-generator/index.js b/.site-generator/index.js index c1d7e96c..895fb778 100644 --- a/.site-generator/index.js +++ b/.site-generator/index.js @@ -16,24 +16,25 @@ module.exports = function ({ context }) { page.layout = "adr" page.matterfront.layout = "adr" if (page.matterfront["spdx-license"] !== "CC0-1.0") - throw new Error(`Page ADR-${page.matterfront.adr} as invalid license: ${page.matterfront["spdx-license"]}`) + throw new Error(`Page ADR-${page.matterfront.adr} has an invalid license: ${page.matterfront["spdx-license"]}`) checkAdrStatus(page) } else if (page.matterfront.rfc) { console.error(page) - throw new Error(`RFC are deprecated, plase upgrade. More info in ADR-1`) + throw new Error(`RFC are deprecated, please upgrade. More info in ADR-1`) } } } function checkAdrStatus(page) { - const validStatuses = new Set(["Draft", "Review", "LastCall", "Final", "Stagnant", "Withdrawn", "Living"]) + const validStatuses = new Set(["Draft", "Review", "LastCall", "Final", "Stagnant", "Withdrawn", "Living", "Deprecated"]) const validTypes = new Set(["RFC", "Standards Track", "Meta"]) + if (!page.matterfront.status) throw new Error("ADR-" + page.matterfront.adr + " has no `status`") if (!page.matterfront.type) throw new Error("ADR-" + page.matterfront.adr + " has no `type`") if (!validTypes.has(page.matterfront.type)) throw new Error( - "ADR-" + + "ADR-" + page.matterfront.adr + " has invalid `type: " + page.matterfront.type + @@ -43,11 +44,18 @@ function checkAdrStatus(page) { if (!validStatuses.has(page.matterfront.status)) throw new Error( - "ADR-" + + "ADR-" + page.matterfront.adr + " has invalid `status: " + page.matterfront.status + "`, valid statuses are: " + Array.from(validStatuses).join(",") ) + + if (page.matterfront.status === "Deprecated" && !page.matterfront["deprecated-reason"]) + throw new Error( + "ADR-" + + page.matterfront.adr + + " is marked as `Deprecated` but has no `deprecated-reason`" + ) } diff --git a/.site-generator/scss/main.scss b/.site-generator/scss/main.scss index 23bb5297..408df153 100755 --- a/.site-generator/scss/main.scss +++ b/.site-generator/scss/main.scss @@ -53,6 +53,12 @@ color: magenta; } + .status-Deprecated { + color: #555; + text-decoration: line-through !important; + font-style: italic; + } + .status-Withdrawn { text-decoration: line-through !important; color: #999; diff --git a/README.md b/README.md index 495aab8b..613cb73a 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ states: - LastCall - Stagnant - Withdrawn +- Deprecated spdx-license: CC0-1.0 --- @@ -19,7 +20,7 @@ ADR stands for _Architecture Decision Record_. An ADR is a design document provi ## How do ADRs work? -Read [the ADR explaining the rationale](/content/ADR-1-adr-process.md) for a detailed explaination. +Read [the ADR explaining the rationale](/content/ADR-1-adr-process.md) for a detailed explanation. ### ADR Process @@ -38,11 +39,14 @@ flowchart TB Review --> Withdrawn LastCall --> Withdrawn - + LastCall <--> Stagnant Review <--> Stagnant Draft <--> Stagnant - + + + Final --> Deprecated + Living --> Deprecated ``` ## ADR Types @@ -54,3 +58,4 @@ There are three types of ADRs: - A **Meta** ADR describes a process surrounding Decentraland or proposes a change to (or an event in) a process. Process ADRs are like Standards Track ADRs but apply to areas other than the Decentraland protocol itself. - An **RFC** describes a Decentraland design issue, or provides general guidelines or information to the Decentraland community. RFCs do not necessarily represent Decentraland community consensus or a recommendation, so users and implementers are free to ignore RFCs or follow their advice. + Final --> Deprecated diff --git a/content/ADR-1-adr-process.md b/content/ADR-1-adr-process.md index 193c4924..de516a82 100644 --- a/content/ADR-1-adr-process.md +++ b/content/ADR-1-adr-process.md @@ -23,7 +23,7 @@ For Decentraland implementers, ADRs are a convenient way to track the progress o There are three types of ADR: -- A **Standards Track** ADR describes any change that affects most or all Decentraland implementations, such as—a change to the synchronzation protocol, a change in deployments validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Decentraland. Standards Track ADRs consist of three parts—a design document, an implementation, and (if warranted) an update to the [formal specification](https://github.com/decentraland/yellowpaper). +- A **Standards Track** ADR describes any change that affects most or all Decentraland implementations, such as—a change to the synchronization protocol, a change in deployments validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Decentraland. Standards Track ADRs consist of three parts—a design document, an implementation, and (if warranted) an update to the [formal specification](https://github.com/decentraland/yellowpaper). - A **Meta** ADR describes a process surrounding Decentraland or proposes a change to (or an event in) a process. Process ADRs are like Standards Track ADRs but apply to areas other than the Decentraland protocol itself. @@ -62,6 +62,9 @@ flowchart TB Review <--> Stagnant Draft <--> Stagnant + Final --> Deprecated + Living --> Deprecated + ``` **Idea** - An idea that is pre-draft. This is not tracked within the ADR Repository as a text file, it is recommended to start with an issue or discussion. @@ -76,21 +79,23 @@ If this period results in necessary normative changes it will revert the ADR to **Final** - This ADR represents the final standard. A Final ADR exists in a state of finality and should only be updated to correct errata and add non-normative clarifications. -**Stagnant** - Any ADR in `Draft` or `Review` or `Last Call` if inactive for a period of 6 months or greater is moved to `Stagnant`. An ADR may be resurrected from this state by Authors or ADR Editors through moving it back to `Draft` or it's earlier status. If not resurrected, a proposal may stay forever in this status. +**Stagnant** - Any ADR in `Draft` or `Review` or `Last Call` if inactive for a period of 6 months or greater is moved to `Stagnant`. An ADR may be resurrected from this state by Authors or ADR Editors through moving it back to `Draft` or its earlier status. If not resurrected, a proposal may stay forever in this status. -**Withdrawn** - The ADR Author(s) have withdrawn the proposed ADR. This state has finality and can no longer be resurrected using this ADR number. If the idea is pursued at later date it is considered a new proposal. +**Withdrawn** - The ADR Author(s) have withdrawn the proposed ADR. This state has finality and can no longer be resurrected using this ADR number. If the idea is pursued at a later date it is considered a new proposal. **Living** - A special status for ADRs that are designed to be continually updated and not reach a state of finality. This includes most notably ADR-1. +**Deprecated** - An ADR that was previously marked as `Final` and implemented but has become obsolete or no longer relevant due to protocol or feature changes. Deprecated ADRs must include a Deprecation Reason explaining their obsolescence and, where applicable, link to newer ADRs that supersede them. + ## What belongs in a successful ADR? Each ADR should have the following parts: -- Preamble - RFC 822 style headers containing metadata about the ADR, including the ADR number, a short descriptive title (limited to a maximum of 44 characters), a description (limited to a maximum of 140 characters), and the author details. Irrespective of the category, the title and description should not include ADR number. -- Abstract - Abstract is a multi-sentence (short paragraph) technical summary. This should be a very terse and human-readable version of the document section. **Someone should be able to read only the abstract to get the gist of what this document is about in its current state.** Abstracts should be always up to date with the current state of the document. -- Context, Reach & Prioritization - Go into detail about the subject in question. Why is this decision important? The urgency of the decision? Datapoints and related background information. Vocabulary and key terms. -- Solution Space Exploration - Discuss the potential alternatives and their impact. What alternatives are being considered, their benefits, their costs (team resources, money, time frames), and mitigations for any drawbacks. -- Specification - The technical specification should describe the syntax and semantics of any new feature. +- **Preamble** - RFC 822 style headers containing metadata about the ADR, including the ADR number, a short descriptive title (limited to a maximum of 44 characters), a description (limited to a maximum of 140 characters), and the author details. Irrespective of the category, the title and description should not include ADR number. +- **Abstract** - Abstract is a multi-sentence (short paragraph) technical summary. This should be a very terse and human-readable version of the document section. **Someone should be able to read only the abstract to get the gist of what this document is about in its current state.** Abstracts should be always up to date with the current state of the document. +- **Context, Reach & Prioritization** - Go into detail about the subject in question. Why is this decision important? The urgency of the decision? Datapoints and related background information. Vocabulary and key terms. +- **Solution Space Exploration** - Discuss the potential alternatives and their impact. What alternatives are being considered, their benefits, their costs (team resources, money, time frames), and mitigations for any drawbacks. +- **Specification** - The technical specification should describe the syntax and semantics of any new feature. ## Picking an ADR number @@ -104,25 +109,17 @@ ADRs should be written in [markdown](https://github.com/adam-p/markdown-here/wik Each ADR must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed ["front matter" by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. -`adr`: _ADR number_ (this is determined by the ADR editor) - -`title`: _The ADR title is a few words, not a complete sentence_ - -`description`: _Description is one full (short) sentence_ - -`authors`: _The list of the author's GitHub username(s)._ - -`discussion`: _The url pointing to the official discussion thread_ - -`status`: _Draft, Review, Last Call, Final, Stagnant, Withdrawn, Living_ - -`last-call-deadline`: _The date last call period ends on_ (Optional field, only needed when status is `Last Call`) - -`type`: _One of `Standards Track`, `Meta`, or `ADR`_ - -`date`: _Date the ADR was created on_ - -`withdrawal-reason`: _A sentence explaining why the ADR was withdrawn._ (Optional field, only needed when status is `Withdrawn`) +- `adr`: _ADR number_ (this is determined by the ADR editor) +- `title`: _The ADR title is a few words, not a complete sentence_ +- `description`: _Description is one full (short) sentence_ +- `authors`: _The list of the author's GitHub username(s)._ +- `discussion`: _The url pointing to the official discussion thread_ +- `status`: _Draft, Review, Last Call, Final, Stagnant, Withdrawn, Living, Deprecated_ +- `last-call-deadline`: _The date last call period ends on_ (Optional field, only needed when status is `Last Call`) +- `type`: _One of `Standards Track`, `Meta`, or `ADR`_ +- `date`: _Date the ADR was created on_ +- `withdrawal-reason`: _A sentence explaining why the ADR was withdrawn._ (Optional field, only needed when status is `Withdrawn`) +- `deprecated-reason`: _A sentence explaining why the ADR was deprecated._ (Optional field, only needed when status is `Deprecated`) Headers that permit lists must separate elements with commas. @@ -156,8 +153,6 @@ Links to external resources **SHOULD NOT** be included. External resources may d References to other ADRs should follow the format `ADR-N` where `N` is the ADR number you are referring to. Each ADR that is referenced in an ADR **MUST** be accompanied by an absolute markdown link to the absolute rendered path i.e. `/adr/ADR-1`. - - ## Auxiliary Files Images, diagrams and auxiliary files should be included in a subdirectory of the `public/resources` folder for that ADR as follows: `public/resources/ADR-N` (where **N** is to be replaced with the ADR number). When linking to an image in the ADR public/resources, use absolute links such as `/resources/ADR-1/image.png`. @@ -209,7 +204,15 @@ ADRs are encouraged to follow [RFC 2119](https://www.ietf.org/rfc/rfc2119.html) This document was derived heavily from [Ethereum EIPs](https://github.com/ethereum/eips) which was also derived from [Bitcoin's BIP-0001](https://github.com/bitcoin/bips) written by Amir Taaki which in turn was derived from [Python's PEP-0001](https://peps.python.org/). In many places text was simply copied and modified. Although the PEP-0001 text was written by Barry Warsaw, Jeremy Hylton, and David Goodger. -The initial document for ADRs for Decentraland can be found in the git history of this file and it was derived from [Documenting Architecture Decisions](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) by Michael Nygard +The initial document for ADRs for Decentraland can be found in the git history of this file and it was derived from [Documenting Architecture Decisions](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) by Michael Nygard. 1. [Importance of architecture decisions](http://www.computer.org/portal/web/csdl/doi/10.1109/MS.2009.52) 2. [Documenting software architectures](http://www.sei.cmu.edu/library/abstracts/books/0321552687.cfm) + +## Changelog + +### [2024-11-28] +- **Added `Deprecated` Status**: + - Introduced the `Deprecated` status to identify ADRs that were previously marked as `Final` but are now obsolete due to changes in Decentraland's architecture, protocol, or feature set. + - Deprecated ADRs must include a `deprecated-reason` explaining their obsolescence and, if applicable, link to newer ADRs that supersede them. + - More details https://adr.decentraland.org/adr/ADR-277 \ No newline at end of file diff --git a/content/ADR-277-introduce-depracated-state.md b/content/ADR-277-introduce-depracated-state.md index 660f7a2b..3043b906 100644 --- a/content/ADR-277-introduce-depracated-state.md +++ b/content/ADR-277-introduce-depracated-state.md @@ -1,9 +1,9 @@ --- layout: adr adr: 277 # replace this number with the PR or ISSUE number -title: Introducing the "Deprecated" State for ADRs +title: Introducing the Deprecated State for ADRs date: 2024-11-15 -status: Review +status: Final type: Meta spdx-license: CC0-1.0 authors: @@ -62,6 +62,7 @@ flowchart TB Draft <--> Stagnant Final --> Deprecated + Living --> Deprecated ``` The full list of ADR states will now include: From 45db49530d5980d589b103870b04c3e84b27058a Mon Sep 17 00:00:00 2001 From: Nicolas Earnshaw Date: Mon, 13 Jan 2025 14:06:52 -0300 Subject: [PATCH 06/11] Texture tweens (#279) * light soruces * texture tween * examples * Update content/ADR-255-texture-tweens.md Co-authored-by: Aga Signed-off-by: Nicolas Earnshaw * changes based on feedback * Update ADR-255-texture-tweens.md Added serialization definitions for Tween and Texture Signed-off-by: Alejandro Alvarez Melucci <163010988+AlejandroAlvarezMelucciDCL@users.noreply.github.com> --------- Signed-off-by: Nicolas Earnshaw Signed-off-by: Alejandro Alvarez Melucci <163010988+AlejandroAlvarezMelucciDCL@users.noreply.github.com> Co-authored-by: Aga Co-authored-by: Alejandro Alvarez Melucci <163010988+AlejandroAlvarezMelucciDCL@users.noreply.github.com> --- content/ADR-255-texture-tweens.md | 187 ++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 content/ADR-255-texture-tweens.md diff --git a/content/ADR-255-texture-tweens.md b/content/ADR-255-texture-tweens.md new file mode 100644 index 00000000..530840a0 --- /dev/null +++ b/content/ADR-255-texture-tweens.md @@ -0,0 +1,187 @@ +--- +layout: adr +adr: 255 +title: Texture tweens +date: 2024-12-02 +status: Draft +type: RFC +spdx-license: CC0-1.0 +authors: + - nearnshaw +--- + +# Abstract + +This document describes an approach for making it possible for creators to animate textures. This can be used to simulate running liquids, as well as many other interesting texture effects. + +This new feature combines very well with ADR-254 (GLTF Nodes), as it enables to do the same effects on the textures of any .gltf model. + +## Offset and tiling + +Today the settings available on a texture are limited. We need to be able to also change the offset and the scale of a texture. As part of this ADR we're also adding two new fields to all textures: + +- `offset`: _Vector2_, shifts the image from starting on the 0,0 mark. Default value `0,0` +- `tiling`: _Vector2_, determines how many times the texture fits on the surface. Default value `1,1`. The behavior of the remaining space on the texture will depend on the value of `wrapMode`. + +A creator could theoretically write a system that changes the `offset` value on every frame, but that would be very bad for performance. If something will continually change their offset, it's recommended to instead use a Tween for that. + +## Texture tween + +We'll define a new type of tween that affects the Material rather than the Transform. This is enabled by using the already existing Tween and TweenSequence components, with a new `TextureMove` option to be added to the existing `Move`, `Rotate`, and `Scale`. + +The new `TextureMove` option on the `Tween` component will have the following fields: + +- `TextureMovementType`: _(optional)_, defines if the movement will be on the `offset` or the `tiling` field (see section above) +- `start`: _Vector2_ the initial value of the offset or tiling +- `end`: _Vector2_ the final value of the offset or tiling +- `duration`: _number_ how long the transition takes, in milliseconds +- `easingFunction`: How the curve of movement over time changes, the default value is `EasingFunction.EF_LINEAR` + +The scene can also use a `TweenSequence` to make continuos movements possible, just like with other kinds of tweens. + +## Texture layers + +Materials have several textures besides the albedo_texture, including bump_texture, alpha_texture, emissive_texture. The `TextureMove` Tween affects the base texture, so all textures move together with it. + +This applies to changing the `offset` and `tiling` fields manually, as well as using a texture tween. + +## Serialization + +```yaml +parameters: + COMPONENT_ID: 1102 + COMPONENT_NAME: core::Tween + CRDT_TYPE: LastWriteWin-Element-Set +``` + +```protobuf +message Texture { + string src = 1; + optional TextureWrapMode wrap_mode = 2; // default = TextureWrapMode.Clamp + optional TextureFilterMode filter_mode = 3; // default = FilterMode.Bilinear + + // Final uv = offset + (input_uv * tiling) + optional Vector2 offset = 4; // default = Vector2.Zero; Offset for texture positioning, only works for the texture property in PbrMaterial or UnlitMaterial. + optional Vector2 tiling = 5; // default = Vector2.One; Tiling multiplier for texture repetition, only works for the texture property in PbrMaterial or UnlitMaterial. +} + +message PBTween { + float duration = 1; // in milliseconds + EasingFunction easing_function = 2; + + oneof mode { + Move move = 3; + Rotate rotate = 4; + Scale scale = 5; + TextureMove texture_move = 8; + } + + optional bool playing = 6; // default true (pause or running) + optional float current_time = 7; // between 0 and 1 +} + +// This tween mode allows to move the texture of a PbrMaterial or UnlitMaterial. +// You can also specify the movement type (offset or tiling) +message TextureMove { + decentraland.common.Vector2 start = 1; + decentraland.common.Vector2 end = 2; + optional TextureMovementType movement_type = 3; // default = TextureMovementType.TMT_OFFSET +} + +enum TextureMovementType { + TMT_OFFSET = 0; // default = TextureMovementType.TMT_OFFSET + TMT_TILING = 1; +} +``` + +## Semantics + +### Example + +Offset texture: + +```ts +Material.setPbrMaterial(myEntity, { + texture: Material.Texture.Common({ + src: 'assets/materials/wood.png', + wrapMode: TextureWrapMode.TWM_REPEAT, + offset: Vector2.create(0, 0.2), + tiling: Vector2.create(1, 1), + }), +}) +``` + +Simple continuos flow: + +```ts +const myEntity = engine.addEntity() + +MeshRenderer.setPlane(myEntity) + +Transform.create(myEntity, { + position: Vector3.create(4, 1, 4), +}) + +Material.setPbrMaterial(myEntity, { + texture: Material.Texture.Common({ + src: 'materials/water.png', + wrapMode: TextureWrapMode.TWM_REPEAT, + }), +}) + +Tween.create(myEntity, { + mode: Tween.Mode.TextureMove({ + start: Vector2.create(0, 0), + end: Vector2.create(0, 1), + }), + duration: 1000, + easingFunction: EasingFunction.EF_LINEAR, +}) + +TweenSequence.create(myEntity, { sequence: [], loop: TweenLoop.TL_RESTART }) +``` + +Square-moving tween sequence: + +```ts +//(...) + +Tween.create(myEntity, { + mode: Tween.Mode.TextureMove({ + start: Vector2.create(0, 0), + end: Vector2.create(0, 1), + }), + duration: 1000, + easingFunction: EasingFunction.EF_LINEAR, +}) + +TweenSequence.create(myEntity, { + sequence: [ + { + mode: Tween.Mode.TextureMove({ + start: Vector2.create(0, 1), + end: Vector2.create(1, 1), + }), + duration: 1000, + easingFunction: EasingFunction.EF_LINEAR, + }, + { + mode: Tween.Mode.TextureMove({ + start: Vector2.create(1, 1), + end: Vector2.create(1, 0), + }), + duration: 1000, + easingFunction: EasingFunction.EF_LINEAR, + }, + { + mode: Tween.Mode.TextureMove({ + start: Vector2.create(1, 0), + end: Vector2.create(0, 0), + }), + duration: 1000, + easingFunction: EasingFunction.EF_LINEAR, + }, + ], + loop: TweenLoop.TL_RESTART, +}) +``` From 1f42215fe6b9808d6c2eeee2992b73561e701cce Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 16 Jan 2025 10:01:48 -0300 Subject: [PATCH 07/11] ADR-280: binary management in creator hub (#284) --- content/ADR-280-binaries-creator-hub.md | 171 ++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 content/ADR-280-binaries-creator-hub.md diff --git a/content/ADR-280-binaries-creator-hub.md b/content/ADR-280-binaries-creator-hub.md new file mode 100644 index 00000000..b8f8fd98 --- /dev/null +++ b/content/ADR-280-binaries-creator-hub.md @@ -0,0 +1,171 @@ +--- +adr: 280 +date: 2025-01-15 +title: Binary Management in Creator Hub +authors: + - cazala +status: Final +type: Standards Track +spdx-license: CC0-1.0 +--- + +# Abstract + +This document describes the approach for managing Node.js binaries and their execution within the Creator Hub Electron application. It outlines the challenges of distributing Node.js-based tools within an ASAR archive and presents a solution for cross-platform binary execution. + +# Introduction + +The Creator Hub needs to run various Node.js-based tools and binaries (like `@dcl/sdk-commands`) to function properly. This presents several challenges in the context of an Electron application: + +1. The app needs Node.js to run these binaries +2. NPM is required to manage and install dependencies +3. The app is packaged in an ASAR archive for distribution +4. Binary execution needs to work cross-platform (Windows and macOS) + +## ASAR Archive Requirements + +ASAR (Atom Shell Archive) is crucial for our distribution process, particularly for macOS: + +- Apple's requirements for app distribution mandate proper signing and notarization +- ASAR provides a way to package the application into a single file that can be properly signed +- It's similar to a ZIP file but designed specifically for Electron apps +- Files within ASAR can be read using Node.js APIs as if they were in a normal directory +- However, binaries within ASAR cannot be executed directly, which is why some files must remain outside + +## The PATH Environment Variable + +The `$PATH` environment variable is fundamental to how operating systems locate executable programs: + +- It's a list of directories separated by colons (Unix) or semicolons (Windows) +- When a command is run, the system searches these directories in order to find the executable +- In our case, we need to modify the `$PATH` to ensure our forked processes can find: + 1. Our Node.js binary (the Electron binary symlink/cmd) + 2. The NPM binaries + 3. Any other binaries installed by NPM + 4. The system's original executables + +# Decision + +We've implemented a multi-layered approach to handle binary execution: + +## Node.js Binary Management + +Instead of bundling a separate Node.js binary, we utilize Electron's built-in Node.js runtime. Electron is built on Node.js, making its binary capable of running Node.js code. To make this work: + +- On macOS: Create a symlink named `node` pointing to the Electron binary +- On Windows: Create a `.cmd` file that redirects to the Electron binary + +## NPM and Package Management + +To handle NPM and package management: + +1. The `package.json` and NPM binaries are kept outside the ASAR archive +2. NPM binaries are accessed using the Node.js symlink/cmd created above +3. The `$PATH` environment variable is modified in forked processes to include: + - The directory containing our Node.js symlink/cmd + - The directory containing NPM binaries + - The system's original `$PATH` + +## Installation Process + +The installation process follows these steps: + +1. Create Node.js symlink/cmd pointing to Electron binary +2. Set up proper `$PATH` environment variable +3. Use the Node.js symlink to run NPM +4. Install dependencies from the unpackaged `package.json` +5. Track installation versions to handle updates + +## Running Arbitrary Binaries + +To run binaries like `sdk-commands`: + +1. Fork a new process using Electron's `utilityProcess` +2. Inject the modified `$PATH` containing Node.js and NPM locations +3. Execute the binary using the forked process +4. Provide utilities for output handling and process management + +## Process Monitoring + +The forked processes are augmented with monitoring capabilities through a wrapper that provides: + +1. Pattern-based Output Monitoring + + - `on(pattern, handler)`: Subscribe to output matching a RegExp pattern + - `once(pattern, handler)`: Listen for a single pattern match + - `off(index)`: Unsubscribe from a pattern + - Supports filtering by stream type (stdout, stderr, or both) + +2. Process Control + + - `wait()`: Promise that resolves with complete output or rejects on error + - `waitFor(resolvePattern, rejectPattern)`: Promise that resolves/rejects based on output patterns + - `kill()`: Graceful process termination with force-kill fallback + - `alive()`: Check if process is still running + +3. Logging and Debugging + - Automatic logging of process execution details + - Process lifecycle events (spawn, exit) + - Output streaming to application logs + - Error handling with detailed context + +Example monitoring usage: + +```typescript +const process = run("@dcl/sdk-commands", "sdk-commands", { + args: ["start"], +}); + +// Wait for specific output +await process.waitFor(/Server is listening/); + +// Monitor errors +process.on(/Error:/, (error) => { + console.error("SDK error:", error); +}); + +// Graceful shutdown +await process.kill(); +``` + +# Consequences + +## Positive + +- No need to bundle separate Node.js runtime +- Cross-platform compatibility +- Clean separation between packaged and executable files +- Reliable binary execution environment + +## Negative + +- Complex setup process +- Dependency on Electron's Node.js version +- Need to maintain files outside ASAR archive + +## Risks + +- Electron Node.js version updates might affect compatibility +- Cross-platform differences in binary execution +- Installation process interruption handling + +# Implementation Details + +```typescript +// Example of PATH setup +PATH = joinEnvPaths( + path.dirname(nodeCmdPath), // Node.js location + path.dirname(npmBinPath), // NPM location + process.env.PATH // System PATH +); + +// Example of binary execution run(package, bin, args) +const child = run("@dcl/sdk-commands", "sdk-commands", { + args: ["start"], +}); +``` + +# References + +- [Electron ASAR Documentation](https://www.electronjs.org/docs/latest/tutorial/asar-archives) +- [Node.js Process Documentation](https://nodejs.org/api/process.html#process_process_env) From ee8a49f87e1460694c1e58a06582d67e349b5701 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 16 Jan 2025 15:30:10 -0300 Subject: [PATCH 08/11] ADR-281: items in decentraland tooling (#285) * ADR-281: items in decentraland tooling * ADR-281: added links --- content/ADR-281-items.md | 169 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 content/ADR-281-items.md diff --git a/content/ADR-281-items.md b/content/ADR-281-items.md new file mode 100644 index 00000000..e580332f --- /dev/null +++ b/content/ADR-281-items.md @@ -0,0 +1,169 @@ +--- +adr: 281 +date: 2025-01-16 +title: Items in Decentraland tooling +authors: + - cazala +status: Final +type: Standards Track +spdx-license: CC0-1.0 +--- + +# Abstract + +This document describes the Items abstraction in Decentraland tooling, which provides a way to add pre-configured entities with components into scenes through a drag-and-drop interface, without requiring coding knowledge. + +# Context + +Decentraland provides an Entity Component System (ECS) SDK for building interactive scenes. Items are an abstraction built on top of this ECS system, allowing creators to easily add pre-configured entities with components into their scenes through a drag-and-drop interface, without requiring coding knowledge. When an item is dropped into a scene, it creates one or more entities and applies the appropriate components to them. + +# Types of Items + +## Static Items + +Basic 3D models with no interactive behavior, including but not limited to: + +- Building elements: windows, doors, beams, decks, fences +- Furniture: chairs, tables, beds, benches, drawers +- Nature: trees, plants, mountains, terrain variations +- Decorative: vases, lamps, statues, cultural items +- Infrastructure: train tracks, mine carts, street elements +- Materials: wood, stone, metal, glass variations +- Props: books, tools, weapons, musical instruments + +### Smart Items + +Interactive items with programmable behaviors, primarily defined through actions and triggers. They can include various types: + +- Interactive objects (e.g., "Wooden Door" with open/close actions) +- Media items (e.g., Custom Text, Image, Video items) +- Game elements (e.g., Health Bar with state management) + +Smart items typically include: + +- Predefined actions (e.g., toggle, play animation) +- Trigger conditions +- State management +- User interaction handling + +### Custom Items + +User-created items that can be either static or interactive, allowing creators to: + +- Create reusable components from existing entities +- Share and reuse complex setups +- Maintain consistency across scenes + +## Item Sources + +### Asset Packs Repository + +- Central storage for default items and smart items ([asset-packs repository](https://github.com/decentraland/asset-packs)) +- Organized in themed collections (cyberpunk, fantasy, etc.) +- Distributed via npm package `@dcl/asset-packs` +- Accessible through CDN at builder-items.decentraland.org + +Custom items can be added to the default asset-packs registry by copying their folder and contents from the custom items folder into the appropriate asset-pack folder in the [asset-packs repository](https://github.com/decentraland/asset-packs). + +### Custom Items Storage + +Custom items are stored locally with the following structure: + +``` +custom/ + item-name/ + data.json # metadata + composite.json # entity data + thumbnail.png # preview image + resources/ # associated assets (models, textures, etc.) +``` + +## Technical Implementation + +### Composites + +Items are defined using composites - a versioned collection of components and their values: + +```typescript +interface Composite { + version: number; + components: Array<{ + name: string; + data: { + [entityId: string]: { + json: any; + }; + }; + }>; +} +``` + +### ID Mapping System + +When instantiating items, the system handles entity references through special notation: + +- `{self}` references the self entity once instantiated in runtime +- `{self:componentName}` (e.g., `{self:Actions}`) references a component in the self entity +- `{entityId:componentName}` (e.g., `{512:Actions}`) references a component on another entity by their composite ID (which differs from runtime ID) + +### Resource Path Management + +- Resources are stored with relative paths +- Uses `{assetPath}` notation for template paths +- Automatically maps paths during instantiation +- Components can have resources in arbitrary properties at any nesting level +- Requires manual tracking of which properties contain resources + +### Custom Item Creation + +The process involves: + +1. Selecting existing entities in the scene +2. Creating a composite from the selection +3. Processing and copying all required resources +4. Generating metadata and thumbnails +5. Storing the item in the custom items directory + +## Usage + +### Library Integration + +Items are available through a drag-and-drop interface where users can: + +- Browse available items by category +- Preview items before placement +- Drag items directly into the scene +- Configure smart item properties + +### Instantiation + +When an item is added to a scene: + +1. The composite is loaded +2. New entities are created +3. Components are instantiated with proper references +4. Resources are loaded with correct paths +5. For smart items, behaviors are initialized + +## Consequences + +### Positive + +- Reusable components improve scene creation efficiency +- Smart items enable non-technical users to add complex interactions +- Custom items allow sharing of complex setups +- Standardized storage format ensures compatibility + +### Negative + +- Resource management adds significant complexity: + - Properties containing resources can be nested at any level + - No standard way to identify resource properties + - New SDK components with resource fields require manual tracking + - Resource copying and path mapping needs careful handling +- Multiple item sources require consistent handling + +# References + +- [Decentraland Asset Packs Repository](https://github.com/decentraland/asset-packs) +- [Decentraland SDK Repository](https://github.com/decentraland/js-sdk-tooling) From 7f3382ea7b52c8267ef2cd646e2fd09a9fba3ada Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 20 Jan 2025 11:04:04 -0300 Subject: [PATCH 09/11] ADR-282: Decentraland Inspector (#286) * ADR-282: Decentraland Inspector * ADR-282: Added integration types and examples * ADR-282: Added link to all component inspectors * ADR-282: Fix link to NodeJS file system interface * ADR-282: Move section below --- content/ADR-282-inspector.md | 480 +++++++++++++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 content/ADR-282-inspector.md diff --git a/content/ADR-282-inspector.md b/content/ADR-282-inspector.md new file mode 100644 index 00000000..f7188e01 --- /dev/null +++ b/content/ADR-282-inspector.md @@ -0,0 +1,480 @@ +--- +adr: 282 +date: 2025-01-17 +title: Decentraland Inspector +authors: + - cazala +status: Final +type: Standards Track +spdx-license: CC0-1.0 +--- + +# Abstract + +The `@dcl/inspector` package implements a scene editor interface for Decentraland, built on React, BabylonJS, and TypeScript. It provides a modular architecture that can be integrated into different environments through well-defined RPC interfaces and abstractions. + +# Core Components + +## Entity Hierarchy + +A React component that renders and manages the scene's entity tree. It: + +- Uses the ECS SDK to track entity relationships and components +- Manages special entities: `ROOT`, `PLAYER`, `CAMERA` +- Implements entity operations through the SDK components API +- Supports entity icons based on component composition (e.g., smart items, custom items, tiles) +- Uses a generic `Tree` component for rendering hierarchical data + +### Component Inspector + +Handles component editing through specialized inspectors: + +Component-specific inspectors include: + +- `TransformInspector`: Position, rotation, and scale +- `GltfInspector`: 3D model configuration +- `MaterialInspector`: Material properties and textures +- `ActionInspector`: Smart item action configuration +- `TriggerInspector`: Smart item trigger setup +- `StatesInspector`: Smart item state management +- `TextShapeInspector`: Text rendering properties +- `AudioSourceInspector`: Sound configuration +- `VideoPlayerInspector`: Video playback settings +- `NftShapeInspector`: NFT display configuration +- `AnimatorInspector`: Animation controls +- `PointerEventsInspector`: Interaction handling +- `CounterInspector`: Value tracking +- `VisibilityComponentInspector`: Display controls +- `MeshColliderInspector`: Collision properties +- `MeshRendererInspector`: Rendering settings + +A complete list of all available inspectors can be found in the [`EntityInspector`](https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/%40dcl/inspector/src/components/EntityInspector) folder of the SDK repository. + +**Features:** + +- Dynamically loads inspectors based on attached components +- Supports both single and multi-entity selection +- Implements basic/advanced view modes through `Config` component +- Uses React hooks for component state management + +### Level Editor + +3D scene visualization and manipulation: + +- Integrates with the ECS engine for real-time updates +- Implements transform gizmos using Babylon.js (position, rotation, scale) +- Handles drag-and-drop through the DataLayer API +- Manages camera controls and viewport state using Babylon.js scene management +- Uses Babylon.js for 3D rendering and scene manipulation + +### Asset Management + +The Assets panel provides access to three main sources of content: + +1. **Local Assets** + + - Shows project-specific resources (models, textures, sounds) + - Provides direct access to files in the project's directory + - Supports importing new assets from local filesystem + - Manages asset organization within the project structure + +2. **Custom Items** + + - Displays user-created reusable items + - Items can be created by selecting entities in the scene + - Stores items with their complete configuration (components, resources) + - Supports operations like rename, delete, and reuse + - Items can be either static or interactive (smart items) + +3. **Asset Packs** + + - Provides access to official Decentraland items + - Includes both static and smart items + - Organized in themed collections (cyberpunk, fantasy, etc.) + - Items are distributed via the `@dcl/asset-packs` package + - Available through CDN at builder-items.decentraland.org + +Additional functionality includes: + +- Asset import interface for adding new resources +- Asset renaming capabilities +- Custom item creation workflow +- Drag-and-drop support for scene placement + +# Integration Architecture + +## Data Layer RPC + +The Data Layer provides a complete interface for all editor operations, handling both scene state and asset management. It serves as the primary communication channel between the Inspector and its host environment, responsible for: + +1. **Scene State Management:** + + - Maintains scene entity hierarchy and component data + - Synchronizes state changes through CRDT streaming + - Handles undo/redo operations + - Manages scene saving and loading + +2. **Asset Management:** + - Creates and modifies custom items + - Manages the asset catalog + - Handles asset metadata and thumbnails + - Controls asset lifecycle (create, rename, delete) + +The Data Layer has two implementations: + +1. **Local (In-Memory):** + + - Used for development and testing + - Stores all state in memory + - No network communication required + - Fast and suitable for local development + +2. **Remote (Protobuf):** + - Used in production environments + - Communicates over network using protobuf messages + - Supports WebSocket transport + - Enables integration with remote services + +The remote Data Layer is implemented as a protobuf-defined RPC service to ensure type safety and versioning: + +```protobuf +service DataService { + // Scene state synchronization + rpc CrdtStream(stream CrdtStreamMessage) returns (stream CrdtStreamMessage) + + // Asset management + rpc CreateCustomAsset(CreateCustomAssetRequest) returns (CreateCustomAssetResponse) + rpc GetCustomAssets(Empty) returns (GetCustomAssetsResponse) + rpc GetAssetCatalog(Empty) returns (AssetCatalogResponse) + rpc DeleteCustomAsset(DeleteCustomAssetRequest) returns (Empty) + rpc RenameCustomAsset(RenameCustomAssetRequest) returns (Empty) + + // File operations + rpc GetFiles(GetFilesRequest) returns (GetFilesResponse) + rpc SaveFile(SaveFileRequest) returns (Empty) + rpc GetFile(GetFileRequest) returns (GetFileResponse) + rpc CopyFile(CopyFileRequest) returns (Empty) + + // Editor state + rpc Undo(Empty) returns (UndoRedoResponse) + rpc Redo(Empty) returns (UndoRedoResponse) + rpc Save(Empty) returns (Empty) +} +``` + +### File System Interface + +The File System Interface is a lower-level abstraction focused solely on file operations. It has three distinct implementations: + +1. In-Memory ([`feeded-local-fs.ts`](https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/@dcl/inspector/src/lib/data-layer/client/feeded-local-fs.ts)): + + - Uses Map data structures to store files in memory + - Simulates a complete file system + - Used for development and testing + - No persistence between sessions + +2. Node.js ([`sdk-commands/start/data-layer/fs.ts`](https://github.com/decentraland/js-sdk-toolchain/blob/main/packages/%40dcl/sdk-commands/src/commands/start/data-layer/fs.ts)): + + - Direct implementation using Node.js `fs` module + - Used by the CLI for local development + - Handles file watching and hot reloading + - Provides real filesystem access + +3. IFrame ([`iframe-storage.ts`](https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/@dcl/inspector/src/lib/logic/storage/iframe.ts)): + - Uses mini-rpc library for communication + - Delegates file operations to parent window through postMessage + - Parent window implements actual storage: + - Web Editor: Uses Builder Server API + - Creators Hub: Uses Electron's Node.js `fs` module + - Enables browser and desktop support through abstraction + +The interface definition remains intentionally simple: + +```typescript +export type FileSystemInterface = { + dirname: (path: string) => string; + basename: (filePath: string) => string; + join: (...paths: string[]) => string; + existFile: (filePath: string) => Promise; + readFile: (filePath: string) => Promise; + writeFile: (filePath: string, content: Buffer) => Promise; + readdir: ( + dirPath: string + ) => Promise<{ name: string; isDirectory: boolean }[]>; + rm: (filePath: string) => Promise; + cwd: () => string; +}; +``` + +### Additional RPCs + +All additional RPCs are implemented using the [`@dcl/mini-rpc`](https://github.com/decentraland/mini-rpc) library, which provides type-safe client/server communication. These include: + +1. Storage RPC: Implements the IFrame file system interface +2. Camera RPC: Controls viewport and screenshots +3. UI RPC: Manages Inspector UI state +4. Scene Metrics RPC: Reports scene statistics + +Each RPC uses postMessage for transport in IFrame implementations and follows the mini-rpc pattern of: + +- Type-safe method definitions +- Client/server architecture +- Event emission capabilities + +### Relationship Between Layers + +The Data Layer uses the File System Interface but adds: + +- Scene-specific operations (CRDT streaming, undo/redo) +- Asset management (custom items, catalogs) +- Editor state management +- Higher-level abstractions for scene editing + +Example: + +- When creating a custom item, the Data Layer: + 1. Handles the item metadata and composition + 2. Uses the File System Interface to store resources + 3. Manages thumbnails and previews + 4. Updates the asset catalog + +While the File System Interface would only handle the raw file operations without understanding the asset structure or scene context. + +# Integration Types + +## IFrame Integration + +The parent application embeds the Inspector in an IFrame and communicates through postMessage: + +### Storage RPC Setup + +The parent application needs to set up the RPC bridge to handle file system operations: + +```typescript +function initRpc(iframe: HTMLIFrameElement) { + const transport = new MessageTransport(window, iframe.contentWindow!); + const storage = new StorageRPC(transport); + + // Handle file operations + storage.handle("read_file", async ({ path }) => { + return fs.readFile(path); + }); + + storage.handle("write_file", async ({ path, content }) => { + await fs.writeFile(path, content); + }); + + storage.handle("exists", async ({ path }) => { + return fs.exists(path); + }); + + storage.handle("delete", async ({ path }) => { + await fs.rm(path); + }); + + storage.handle("list", async ({ path }) => { + const files = await fs.readdir(path); + return Promise.all( + files.map(async (name) => ({ + name, + isDirectory: await fs.isDirectory(path.join(path, name)), + })) + ); + }); + + return { + storage, + dispose: () => storage.dispose(), + }; +} +``` + +### React Component Integration + +Example of embedding the Inspector in a React application: + +```typescript +const CONTENT_URL = "http://localhost:3000"; // URL to your iframe content + +function InspectorComponent() { + const iframeRef = useRef(); + + const handleIframeRef = useCallback((iframe) => { + if (iframe) { + iframeRef.current = initRpc(iframe); + } + }, []); + + useEffect(() => { + return () => iframeRef.current?.dispose(); + }, []); + + const params = new URLSearchParams({ + dataLayerRpcParentUrl: window.location.origin, // this is the url of the parent application + }); + const url = `${CONTENT_URL}?${params}`; // url where the inspector is being served + + return