Skip to content
This repository was archived by the owner on Aug 26, 2026. It is now read-only.

fix: honour caller skin, hair and eye color in marketplace mode - #76

Merged
RocioCM merged 2 commits into
mainfrom
fix/marketplace-avatar-colors
Aug 17, 2026
Merged

fix: honour caller skin, hair and eye color in marketplace mode#76
RocioCM merged 2 commits into
mainfrom
fix/marketplace-avatar-colors

Conversation

@RocioCM

@RocioCM RocioCM commented Aug 14, 2026

Copy link
Copy Markdown
Member

Problem

With mode=marketplace, the skinColor / hairColor / eyeColor URL params and the matching
SetSkinColor / SetHairColor / SetEyeColor JSBridge calls were silently dropped. A consumer
could not let a user try color combinations on an avatar the way mode=builder allows.

Repro (through the wearable-preview wrapper):
…/?unity=true&mode=marketplace&profile=default1&skin=%23ff0000 renders default1's own skin, not red.

Cause

PreviewController.LoadForMarketplace took its colors straight from the fetched profile
(avatar.GetAvatarColors()). AangConfiguration.SkinColor / HairColor / EyeColor — populated from
both the URL and the bridge — were only ever read in the PreviewMode.Builder branch.

Fix

LoadForMarketplace merges the two: a color the caller asked for wins, and every channel left unset
keeps the profile's own. Both views follow for free, since the avatar (avatarLoader.LoadAvatar) and
the item-alone view (wearableLoader.LoadWearable) already receive the same AvatarColors. That
also makes it work regardless of the profile param — a real address, a defaultN, or none at all.

The method now takes the AangConfiguration snapshot Reload() already holds (matching LoadUrns)
instead of individual values, so it never reaches for the singleton that RecreateFrom can swap
mid-load.

LoadForBuilder's skin fallback moves from Color.black to #cc9b76 — the value the
wearable-preview wrapper has always sent on every builder request. The companion wrapper PR
(decentraland/wearable-preview) stops sending colors nobody asked for, which is what keeps
profile-less marketplace previews on their default profile's colors; without this constant it would
leave builder callers with black skin.

Tests

No test project in this repo, so this is untested here beyond compiling. The wrapper side of the
change is verified in the companion PR (color params now travel only when the caller asks). Worth a
manual pass on: marketplace with an explicit skin, marketplace with only a profile, marketplace
with neither, and builder with no color params.

🤖 Generated with Claude Code

Marketplace previews ignored the skinColor / hairColor / eyeColor URL params
and the matching SetSkinColor / SetHairColor / SetEyeColor JSBridge calls, so a
consumer could not try color combinations on an avatar the way builder mode
allows.

LoadForMarketplace took its colors straight from the fetched profile
(avatar.GetAvatarColors()); AangConfiguration.SkinColor / HairColor / EyeColor
were only ever read in the Builder branch. It now merges them: a color the
caller asked for wins, and every channel left unset keeps the profile's own.
Both views follow, since the avatar and the item-alone wearable already receive
the same AvatarColors.

The method now takes the AangConfiguration snapshot Reload() holds (matching
LoadUrns) instead of individual values, so it never reads the singleton that
RecreateFrom can swap mid-load.

LoadForBuilder's skin fallback moves from black to #cc9b76, the value the
wearable-preview wrapper used to send on every builder request. The wrapper is
about to stop sending colors nobody asked for (so that profile-less marketplace
previews keep their default profile's colors), which would otherwise leave
builder callers with black skin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aang-renderer Ready Ready Preview Aug 14, 2026 4:24pm

Request Review

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — PR #76: fix: honour caller skin, hair and eye color in marketplace mode

Verdict: ✅ Approve — clean, well-scoped fix with no correctness or security issues.

What this PR does

LoadForMarketplace now merges caller-supplied colors (from URL params / JSBridge) with the fetched profile's colors via null-coalescing, exactly the way LoadForBuilder already worked. The method signature changes from individual values to the AangConfiguration snapshot Reload() already holds, which is both cleaner and consistent with LoadUrns(config).

The builder skin fallback moves from Color.black to #cc9b76, encoding the default the JS wrapper was already sending.

Checks performed

Area Result
Correctness AvatarColors(eyes, hair, skin) constructor order matches all call sites. Null-coalescing logic is correct — caller color wins, profile color fills the gap.
Race safety RecreateFrom creates a new AangConfiguration instance, so the config reference captured at the top of Reload()'s do-while loop stays stable across every await.
Input validation ✅ Color values go through ColorUtility.TryParseHtmlString in the setter methods; invalid input falls back to null, which correctly defers to the profile's own color.
Security ✅ No injection vectors — colors are parsed safely, no secrets, no auth changes.
API surface ✅ The change is internal (LoadForMarketplace is private). The public-facing URL params and JSBridge calls are unchanged — they now just take effect in marketplace mode as they already did in builder mode. No downstream consumer impact.
Git conventions ✅ Title follows fix: <summary>, branch follows fix/<summary>.

Minor observations (P2 — non-blocking)

[P2] DEFAULT_SKIN_COLOR precision:
new Color(0.8f, 0.607f, 0.462f) is close to #cc9b76 but has tiny floating-point drift (0.607 vs 155/255 ≈ 0.60784). Writing new Color(204f/255f, 155f/255f, 118f/255f) would be exact and self-documenting (the compiler evaluates the division at compile time). Purely cosmetic — the visual difference is sub-pixel.

CI status

  • Prebuild: ✅ pass
  • Build: ⏳ queued
  • Vercel: ✅ pass

Reviewed by Jarvis 🤖 · Requested by RocioCM via GitHub

@@ -37,6 +37,9 @@ public class PreviewController : MonoBehaviour
// survives across sessions: only ever a fallback, never an override of what was requested.
private const string PREF_AVATAR_SHOWN = "PreviewAvatarShown";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Nit: the float literals 0.607 and 0.462 have minor rounding drift from #cc9b76 (155/255 ≈ 0.60784, 118/255 ≈ 0.46275). Using compile-time division would be exact:

Suggested change
private static readonly Color DEFAULT_SKIN_COLOR = new(204f / 255f, 155f / 255f, 118f / 255f);

Visually imperceptible — just a readability/precision nit.

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

badge

Vercel Preview is ready!

Field Value
Preview https://aang-renderer-qplpmc9s9-decentraland1.vercel.app
Commit c55eb46de2500872ca0ffa56a704dc6d68a83ee7
Logs https://github.com/decentraland/aang-renderer/actions/runs/31819143770

Replace approximate float literals (0.8f, 0.607f, 0.462f) with exact
compile-time division (204f/255f, 155f/255f, 118f/255f) to match #cc9b76
precisely. Addresses P2 review finding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@RocioCM
RocioCM merged commit 1625a67 into main Aug 17, 2026
7 checks passed
@RocioCM
RocioCM deleted the fix/marketplace-avatar-colors branch August 17, 2026 16:29
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants