feat: add AvatarNametag component (id 1221) - #467
Merged
Merged
Conversation
Adds PBAvatarNametag component for scene-provided text labels above player nametags. Component is attached to player entities; labels are local-only and never relayed to other players. Co-Authored-By: Claude <noreply@anthropic.com>
Test this pull request on NPM or Yarn
|
out-ts/out-js bindings of the published package are generated from public/*.proto only, so without this import the package ships the proto without its TS binding. Co-Authored-By: Claude <noreply@anthropic.com>
Comment-only change: the component now documents resolving on any avatar-bearing entity - the local player, a player in the scene, or a scene-spawned avatar with an AvatarShape. The entity-id reuse warning is scoped to player entities, where it belongs. Co-Authored-By: Claude <noreply@anthropic.com>
AMT_HIDE_NAMETAGS keeps the plate (it hides the name only), while a player who turns nametags off in the client sees no plates either - matching the implemented explorer behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plate draws a border natively (a translucent lightening of the scene's background color, per the design system); border_color lets the scene pick an explicit opaque color instead. Color3 like the other two color fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The default was a translucent lightening of the background, which put a rim on every plate whether the scene asked for one or not. It is now the background color itself, so the border is opt-in and a plate without one reads as a flat capsule. Implemented in unity-explorer 03a3303. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 tasks
popuz
added a commit
to decentraland/unity-explorer
that referenced
this pull request
Sep 4, 2026
# Pull Request Description ## What does this PR change? First Explorer-side implementation of the new `AvatarNametag` SDK component (protocol: `PBAvatarNametag`, component id `1221`, decentraland/protocol#467): a scene-authored plate rendered **above** the avatar's native nametag (design-system "Additional Tag"), so scenes can rank/role-mark players and NPCs. **What a scene can do** - Put `AvatarNametag` on the local player (`engine.PlayerEntity`), a remote player entity, or a scene-spawned `AvatarShape` (NPC). On any other entity the component is a no-op. - `label` — rendered verbatim: rich text disabled, emoji supported, long labels truncate with ellipsis. An **empty label still draws the plate** (color-only marking is a supported use case). - `label_color` / `background_color` — optional, fall back to the native nametag colors. - `border_color` — optional, falls back to `background_color`. **Behaviour** - Only the **current scene**'s component is honored: plates drop when the player leaves the scene and reapply on re-entry. - Removing the component (or its entity) removes the plate. - `N` (hide nametags) hides plates together with names; `AvatarModifierArea` `HIDE_NAMETAGS` hides only the native name — the plate stays and slides down into the name's slot. - A write that lands before the avatar exists (player mid-connect, NPC mid-load) is applied as soon as the avatar shows up; writes to entities that can never back an avatar are dropped, ending the retry. **How** - Scene world: new `PropagateSceneAvatarTagSystem` (+ `AvatarNametagWorldPlugin`) resolves the component's scene entity to a global-world avatar — the local player by CRDT id, a remote player via `SDKProfile` wallet → participant table, an NPC via its `SDKAvatarShapeComponent` global-world twin — and writes a `SceneAvatarTagComponent` there. Covered by 20 EditMode tests (`PropagateSceneAvatarTagSystemShould`). - Global world: `NametagPlacementSystem` renders the plate as a new sub-tree of `NametagElement` (UI Toolkit + USS), `NameTagCleanUpSystem` keeps holders alive while a plate exists. - Protocol is pinned to an experimental build of decentraland/protocol#467 (`scripts/package.json`); to be repinned once the protocol PR is released. ## Test Instructions Test scene: deployed to the world **`flutterecho.dcl.eth`** — a control panel driving every field of the component against three targets (local player, NPC with a name, nameless NPC), plus multiplayer auto-tagging. Uploading Recording 2026-09-03 124311.mp4… **Steps (standard run)**: ```bash metaforge explorer run 9829 ``` Then in-client: `/goto flutterecho.dcl.eth` **Expected on load**: 1. Your own avatar gets a roster plate within ~1s (with a fresh session the first roster entry is the "Blue" periwinkle pill — 1:1 the design-system reference). 2. NPC **John** at (6, 0, 6): plate `Boss` above his native "John" nametag. 3. Nameless NPC at (9, 0, 6): only the `Sign Only` plate — **no empty name box** under it. ### Test Steps (control panel, four corner panels) 1. **1 · TARGET** — switch between Player / NPC John / NPC Sign; the readout shows the state the panel last sent. 2. **2 · LABEL TEXT** — apply the presets: - the deliberately long label → truncates with ellipsis; - `⭐ CLUB OWNER ⭐` → emoji render inside the plate; - `10 spaces (width only)` → text-less plate widened by whitespace alone; - **Clear (empty label)** → a bare plate remains visible (intended, not a bug). 3. **3 · COLORS** — cycle label / background / border swatches; `native` resets to renderer defaults; border `native` = background color; label `Royal` on background `Royal` → text invisible, plate keeps its size. 4. **4 · SCENE & MULTIPLAYER**: - `Hide Nametags Area: ON` (or walk into the orange-marked zone at (13, ·, 13)) → native names hide, plates stay and slide down into the name's slot; - press `N` → names **and** plates hide together; - `deleteFrom (remove component)` → the selected target's plate disappears. 5. **Scene transitions** — walk out of the scene bounds → all plates drop; walk back in → they reapply. 6. **Chat** — send a chat message: the plate stays visible while the chat bubble is shown (intended). **Multiplayer (second client, fresh account)**: ```bash metaforge account create --clear metaforge explorer run 9829 ``` **Expected result**: - Every connected player is auto-tagged from a fixed roster, deterministically by wallet sort — both clients must show **identical** label/color assignments. - A late joiner gets tagged within ~1s on the already-running client. - After using the panel on the local player, `Clear (empty label)` hands control back to the auto-tag roster. **Logs while testing**: ```bash metaforge explorer logs tail --filter "AvatarNametagTest" ``` ### Additional Testing Notes - The plate on the nameless NPC may sit slightly high — known cosmetic offset, out of scope here. - The test scene source will be ported to `sdk7-test-scenes` once the protocol component is released. ## Quality Checklist - [x] Changes have been tested locally - [ ] Documentation has been updated (if required) - [x] Performance impact has been considered - [x] For SDK features: Test scene is included (deployed to `flutterecho.dcl.eth`)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
PBAvatarNametag— a plate with scene-provided text drawn above a player's native nametag. Immediate use case: player ranks in Clean The Club, which today are faked with a billboardedTextShapeon theNAME_TAGanchor by every scene that wants them.Single new file:
proto/decentraland/sdk/components/avatar_nametag.proto. Component id 1221 (verified free).Addressing
The component is attached to the player entity itself —
engine.PlayerEntityfor the local player,getPlayer({ userId }).entityfor anyone else. There is deliberately no target/userId field:engine.getEntitiesWith(PlayerIdentityData)), so a target field would be a round trip through a string and back;Player entity ids (32..255) are reused after a disconnect, so the comment also tells creators to resolve the entity on every write and to remove the component when the player leaves.
Colors
Both color fields are
Color3andoptional. TheAvatar*family isColor3throughout;Color4is the UI convention. Plate opacity is already driven by the client's distance fade, so a per-tag alpha would multiply with it. Omitting a color inherits the client's native nametag styling, which is why the defaults are worded semantically rather than as RGB literals — the nametag restyle is still in design and a literal here would propagate into generated docstrings.Field numbers 4–6 are left free for a later
show_background/opacity/ icon slot; underWIRE_JSONbreaking rules those are pure additions.No length limit
The protocol has no validation mechanism (no protovalidate, no PGV) and there is no precedent for stating a numeric cap. The visual limit already exists and is adaptive: the plate is single-line with
overflow: hiddenand an ellipsis. The comment describes the single-line behaviour rather than prescribing a transformation, so the client stays free to change how it handles a stray newline.public/sdk-components.protointentionally untouchedThat aggregate is not exhaustive — 19 components are missing from it, including all of 1209–1220 consecutively. Both consumers glob
decentraland/sdk/components/**/*.protodirectly (unity-explorer/scripts/src/build-protocol.ts,js-sdk-toolchain'scompileEcsComponents), so adding an import there would be cosmetic.Base
Opened against
experimentalrather thanmainbecause the consuming client (unity-explorer) pins experimental builds — a main-based tarball lackscommon/options.protoand the Pulse protos and would drop the Pulse bindings on codegen.🤖 Generated with Claude Code