From 6a4362aad3f01b1942719bd42bb9d4509d57a290 Mon Sep 17 00:00:00 2001 From: Eibriel Date: Thu, 16 Jul 2026 10:18:34 -0300 Subject: [PATCH 1/5] feat: add TouchscreenInputControls and UiInputBinding components (#426) * feat: adding mobile_input_controls and ui_input_binding * rename PBMobileInputControls to PBTouchscreenInputControls * adding touch_button for control customization * simplifying specification * adding hide_crosshair * using TextureUnio, properly referencing PBTouchScreenControls * added import public, documented release semantics + multi-touch behavior, documented accepted actions + invalid-value fallback --- .../components/touch_screen_controls.proto | 38 +++++++++++++++++++ .../sdk/components/ui_input_binding.proto | 27 +++++++++++++ public/sdk-components.proto | 2 + 3 files changed, 67 insertions(+) create mode 100644 proto/decentraland/sdk/components/touch_screen_controls.proto create mode 100644 proto/decentraland/sdk/components/ui_input_binding.proto diff --git a/proto/decentraland/sdk/components/touch_screen_controls.proto b/proto/decentraland/sdk/components/touch_screen_controls.proto new file mode 100644 index 00000000..6069e280 --- /dev/null +++ b/proto/decentraland/sdk/components/touch_screen_controls.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package decentraland.sdk.components; +import "decentraland/sdk/components/common/id.proto"; +import "decentraland/sdk/components/common/input_action.proto"; +import "decentraland/common/texture.proto"; +option (common.ecs_component_id) = 1218; + +// The TouchScreenControls component lets a scene configure the native on-screen touch +// controls (the mobile joystick + gamepad). It must be set on the RootEntity. +// +// By default every on-screen button is shown; list a button in `touch_inputs` with +// `hide = true` to remove it (declutter). `main_action` picks which action the large +// central button triggers, and `hide_joystick` removes the native virtual joystick. It is +// a no-op on platforms without native on-screen controls (e.g. desktop). +// +// Accepted actions: only the on-screen gamepad actions map to a button — `IA_POINTER`, +// `IA_PRIMARY`, `IA_SECONDARY`, `IA_JUMP`, and `IA_ACTION_3`..`IA_ACTION_6`. Any other +// `InputAction` (movement actions, `IA_ANY`, `IA_MODIFIER`, or unknown/future values) is +// ignored: a `TouchInput` entry naming a non-button action has no effect, and a `main_action` +// that isn't a valid gamepad action falls back to the default central button (`IA_JUMP`). +message PBTouchScreenControls { + // Per-button configuration. A button not listed here keeps its default (shown). + message TouchInput { + common.InputAction input_action = 1; // which on-screen button this configures + bool hide = 2; // hide this button (default: shown) + // Override the button glyph with this texture. For the jump button it replaces all + // of its dynamic states (jump / double-jump / glide). + optional decentraland.common.TextureUnion icon = 3; + } + + repeated TouchInput touch_inputs = 1; + // The large central button's action. Only the gamepad actions are valid: + // jump / pointer / primary (E) / secondary (F) / action_3..action_6 (1/2/3/4). + // When unset, the default central button (jump) is kept. + optional common.InputAction main_action = 2; + bool hide_joystick = 3; // hide the native virtual joystick + bool hide_crosshair = 4; // hide the on-screen crosshair / reticle +} diff --git a/proto/decentraland/sdk/components/ui_input_binding.proto b/proto/decentraland/sdk/components/ui_input_binding.proto new file mode 100644 index 00000000..5d00bdd6 --- /dev/null +++ b/proto/decentraland/sdk/components/ui_input_binding.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package decentraland.sdk.components; +import "decentraland/sdk/components/common/id.proto"; +import "decentraland/sdk/components/common/input_action.proto"; +option (common.ecs_component_id) = 1219; + +// The UiInputBinding component binds a UI entity to one or more InputActions. While the +// element is pressed (touch or pointer) the listed actions are held down, driving both +// the local player input and scene InputAction listeners, just like the native on-screen +// buttons. It is typically combined with PBTouchScreenControls to replace the native +// controls with a custom touch UI. +// +// Release semantics: the held actions are released as soon as the press that started them +// ends. A renderer MUST release all actions held by this binding when any of the following +// happens: the press/touch is lifted or cancelled, the pointer/touch leaves the element +// (loses press ownership), the actions list changes (the previous set is released before +// the new set is applied), the component is removed or its actions list becomes empty, the +// UI element is hidden, disabled or removed from the tree, or the scene unloads. In short, +// no action may remain held once the element is no longer both present and actively pressed. +// +// Multi-touch: the binding is a single held state, not reference-counted per pointer. The +// actions are held while the element is pressed and released when that press ends; a second +// simultaneous press on the same element does not stack, and does not extend the hold past +// the first release. +message PBUiInputBinding { + repeated common.InputAction actions = 1; // the input actions fired while this element is pressed +} diff --git a/public/sdk-components.proto b/public/sdk-components.proto index a2f966cb..f4198460 100644 --- a/public/sdk-components.proto +++ b/public/sdk-components.proto @@ -29,6 +29,7 @@ import public "decentraland/sdk/components/raycast_result.proto"; import public "decentraland/sdk/components/raycast.proto"; import public "decentraland/sdk/components/realm_info.proto"; import public "decentraland/sdk/components/text_shape.proto"; +import public "decentraland/sdk/components/touch_screen_controls.proto"; import public "decentraland/sdk/components/tween.proto"; import public "decentraland/sdk/components/tween_state.proto"; import public "decentraland/sdk/components/tween_sequence.proto"; @@ -37,6 +38,7 @@ import public "decentraland/sdk/components/ui_dropdown_result.proto"; import public "decentraland/sdk/components/ui_dropdown.proto"; import public "decentraland/sdk/components/ui_input_result.proto"; import public "decentraland/sdk/components/ui_input.proto"; +import public "decentraland/sdk/components/ui_input_binding.proto"; import public "decentraland/sdk/components/ui_text.proto"; import public "decentraland/sdk/components/ui_transform.proto"; import public "decentraland/sdk/components/video_player.proto"; From dfbdec4e65130280cc2c199ec036edc296a5db37 Mon Sep 17 00:00:00 2001 From: Pravus Date: Wed, 22 Jul 2026 16:19:05 +0200 Subject: [PATCH 2/5] feat: virtual camera FOV field (#447) --- proto/decentraland/sdk/components/virtual_camera.proto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proto/decentraland/sdk/components/virtual_camera.proto b/proto/decentraland/sdk/components/virtual_camera.proto index 94302069..a8764e68 100644 --- a/proto/decentraland/sdk/components/virtual_camera.proto +++ b/proto/decentraland/sdk/components/virtual_camera.proto @@ -10,7 +10,9 @@ option (common.ecs_component_id) = 1076; // an 'instant' transition (like using speed/time = 0) // * The lookAtEntity defines to which entity the Camera has to look at constantly (independent from // the holding entity transform). +// * The fov defines the Field of View of the virtual camera message PBVirtualCamera { optional common.CameraTransition default_transition = 1; optional uint32 look_at_entity = 2; + optional float fov = 3; // default: 60 } \ No newline at end of file From acf163afd13a51b265893da6c7dd2c139be2bc4f Mon Sep 17 00:00:00 2001 From: Pravus Date: Tue, 28 Jul 2026 10:48:30 +0200 Subject: [PATCH 3/5] fix: avatar emote command misleading comment (#452) --- proto/decentraland/sdk/components/avatar_emote_command.proto | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proto/decentraland/sdk/components/avatar_emote_command.proto b/proto/decentraland/sdk/components/avatar_emote_command.proto index f0c9cb9d..550bd34a 100644 --- a/proto/decentraland/sdk/components/avatar_emote_command.proto +++ b/proto/decentraland/sdk/components/avatar_emote_command.proto @@ -5,8 +5,9 @@ import "decentraland/sdk/components/common/id.proto"; import "decentraland/sdk/components/common/avatar_mask.proto"; option (common.ecs_component_id) = 1088; -// AvatarEmoteCommand is a grow only value set, used to signal the renderer about -// avatar emotes playback. +// AvatarEmoteCommand is a grow only value set, written by the explorer to report +// avatar emote playback to the scene. It is appended to every player entity in the +// scene (the local player and remote avatars alike). message PBAvatarEmoteCommand { string emote_urn = 1; bool loop = 2; From df8a4ff26e8aa1944c07f23516aab1e2759be681 Mon Sep 17 00:00:00 2001 From: Mikhail Agapov <118179774+mikhail-dcl@users.noreply.github.com> Date: Tue, 28 Jul 2026 16:00:20 +0300 Subject: [PATCH 4/5] fix: restore ServiceStatus and ServiceDiscoveryMessage to archipelago.proto (#453) * feat: add new archipelago message * fix: timestamp --------- Co-authored-by: Pedro Tamborindeguy --- proto/decentraland/kernel/comms/v3/archipelago.proto | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proto/decentraland/kernel/comms/v3/archipelago.proto b/proto/decentraland/kernel/comms/v3/archipelago.proto index 8c5d6519..69cac9f7 100644 --- a/proto/decentraland/kernel/comms/v3/archipelago.proto +++ b/proto/decentraland/kernel/comms/v3/archipelago.proto @@ -84,3 +84,14 @@ message IslandData { message IslandStatusMessage { repeated IslandData data = 1; } + +message ServiceStatus { + uint64 current_time = 1; + optional string commit_hash = 2; + uint32 user_count = 3; +} + +message ServiceDiscoveryMessage { + string server_name = 1; + ServiceStatus status = 2; +} From 2c475cb104fa84496a1a0b388f9fcbd0a0657d08 Mon Sep 17 00:00:00 2001 From: Vitaly Popuzin <35366872+popuz@users.noreply.github.com> Date: Tue, 28 Jul 2026 17:23:45 +0200 Subject: [PATCH 5/5] feat: add OpenExplorerUi restricted action (ExplorerUi, OpenExplorerUiResult) (#451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Adds an `OpenExplorerUi` RPC to the existing `RestrictedActionsService` so SDK7 scenes can open native explorer UI panels (Map, Settings, Backpack, Camera Reel, Communities, Places, Events) through `~system/RestrictedActions`. - New enum `ExplorerUi` — 7 fullscreen sections; `EU_SETTINGS` holds the zero value so an unset `ui` field defaults to the least-intrusive panel. - New enum `OpenExplorerUiResult` — the open verdict: `UNSPECIFIED`, `OPENED`, `WAS_ALREADY_OPEN`, `REJECTED_NOT_CURRENT_SCENE`, `REJECTED_FEATURE_DISABLED`, `REJECTED_NO_USER_GESTURE`. - New messages `OpenExplorerUiRequest { ExplorerUi ui }` / `OpenExplorerUiResponse { OpenExplorerUiResult open_result }` (dedicated response, not the shared `SuccessResponse`; the request documents a `oneof` extension point for future per-panel params). - `rpc OpenExplorerUi(OpenExplorerUiRequest) returns (OpenExplorerUiResponse)` on `RestrictedActionsService`. Additive and non-breaking. Only `proto/decentraland/kernel/apis/restricted_actions.proto` changed. ## Context Iteration 1 of the Scene-triggered Explorer UI feature. This is the **review PR into `main`** per the protocol contribution flow; #446 carries the exact same change on `experimental` for Unity testing and will be closed once the main→experimental sync lands. ## Testing Consumed downstream by js-sdk-toolchain (decentraland/js-sdk-toolchain#1485, `~system/RestrictedActions` codegen) and unity-explorer (decentraland/unity-explorer#9472, C# implementation with QA test scene) on matching `feat/scene-triggered-explorer-ui` branches. --- .../kernel/apis/restricted_actions.proto | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/proto/decentraland/kernel/apis/restricted_actions.proto b/proto/decentraland/kernel/apis/restricted_actions.proto index f01dae0a..9ebfad06 100644 --- a/proto/decentraland/kernel/apis/restricted_actions.proto +++ b/proto/decentraland/kernel/apis/restricted_actions.proto @@ -65,6 +65,42 @@ message EmptyResponse { } message StopEmoteRequest { } +// Identifies which fullscreen explorer panel OpenExplorerUi targets. +// EU_SETTINGS holds 0 so an unset `ui` field defaults to the least-intrusive panel. +enum ExplorerUi { + EU_SETTINGS = 0; + EU_MAP = 1; + EU_BACKPACK = 2; + EU_CAMERA_REEL = 3; + EU_COMMUNITIES = 4; + EU_PLACES = 5; + EU_EVENTS = 6; +} + +// Verdict of an OpenExplorerUi request (enum rather than a bool so new outcomes stay expressible). +enum OpenExplorerUiResult { + UNSPECIFIED = 0; + OPENED = 1; + WAS_ALREADY_OPEN = 2; // a fullscreen panel is already open + REJECTED_NOT_CURRENT_SCENE = 3; // the standard restricted-actions current-scene gate rejected the call + REJECTED_FEATURE_DISABLED = 4; // the requested section is hidden by feature flags or client doesn't have that feature + REJECTED_NO_USER_GESTURE = 5; // rejected: the call did not originate from a user gesture +} + +message OpenExplorerUiRequest { + ExplorerUi ui = 1; + + // Extension point for future per-panel parameters, as a oneof so each panel gets its + // own optional param message without touching existing fields. + // message MapParams { int32 focus_x = 1; int32 focus_y = 2; } + // oneof params { MapParams map = 10; } +} + +message OpenExplorerUiResponse { + // Carries only the verdict of the open action. + OpenExplorerUiResult open_result = 1; +} + service RestrictedActionsService { // MovePlayerTo will move the player to a position relative to the current scene. // If 'duration' field is used in the request, the success response depends on the @@ -98,4 +134,7 @@ service RestrictedActionsService { // StopEmote will stop the current emote rpc StopEmote(StopEmoteRequest) returns (SuccessResponse) {} + + // OpenExplorerUi opens a specific fullscreen explorer panel and returns the open verdict. + rpc OpenExplorerUi(OpenExplorerUiRequest) returns (OpenExplorerUiResponse) {} }