Skip to content

feat: add AvatarNametag component (id 1221) - #467

Merged
popuz merged 7 commits into
experimentalfrom
feat/avatar-nametag-experimental
Aug 31, 2026
Merged

popuz merged 7 commits into
experimentalfrom
feat/avatar-nametag-experimental

Conversation

@popuz

@popuz popuz commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

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 billboarded TextShape on the NAME_TAG anchor 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 itselfengine.PlayerEntity for the local player, getPlayer({ userId }).entity for anyone else. There is deliberately no target/userId field:

  • the scene already holds that entity at the point where it would otherwise pass an address (engine.getEntitiesWith(PlayerIdentityData)), so a target field would be a round trip through a string and back;
  • two entities targeting the same player would need a conflict rule, and the CRDT has no authority model to express one;
  • comms drops every entity id below 512, so a component on a player entity is local-only by construction. Scenes that want everyone to see the same plate distribute it through their own state — the doc comment says so explicitly.

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 Color3 and optional. The Avatar* family is Color3 throughout; Color4 is 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; under WIRE_JSON breaking 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: hidden and 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.proto intentionally untouched

That aggregate is not exhaustive — 19 components are missing from it, including all of 1209–1220 consecutively. Both consumers glob decentraland/sdk/components/**/*.proto directly (unity-explorer/scripts/src/build-protocol.ts, js-sdk-toolchain's compileEcsComponents), so adding an import there would be cosmetic.

Base

Opened against experimental rather than main because the consuming client (unity-explorer) pins experimental builds — a main-based tarball lacks common/options.proto and the Pulse protos and would drop the Pulse bindings on codegen.

🤖 Generated with Claude Code

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>
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

Test this pull request on NPM or Yarn

  • The @dcl/protocol package can be tested in scenes by running the following NPM command:
    npm install "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-33403949390.commit-50d70bb.tgz"
  • The @dcl/protocol package can be tested in scenes by running the following YARN command:
    yarn add "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-33403949390.commit-50d70bb.tgz"

popuz and others added 2 commits August 21, 2026 12:54
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>
@popuz popuz self-assigned this Aug 25, 2026
popuz and others added 4 commits August 28, 2026 17:46
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>
@popuz
popuz merged commit 50d70bb into experimental Aug 31, 2026
3 checks passed
@popuz
popuz deleted the feat/avatar-nametag-experimental branch August 31, 2026 16:17
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`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant