decentraland / decentraland/abgen
worlds: name resolution omits the dclName / index-name fallback, so 50 live worlds can never be served a manifest
- Dominant language
- Rust
- Stars
- 0
- Forks
- 1
- Avg merge
- 12h 3m
- Merged PRs (30d)
- 48
Description
## Summary
`ActiveEntity::world_name()` resolves a world's name as `worldConfiguration.name` only. `worlds-content-server` has long resolved it as `worldConfiguration.name ?? worldConfiguration.dclName ?? `. As a result **50 worlds currently in the worlds index can never be served an asset bundle**, regardless of whether their bundles exist: `get_world_manifest` skips them before it ever looks at a manifest.
## Reproduction (live, prd)
```
$ curl -s https://asset-bundle-registry-abgen.decentraland.org/worlds/menduz.dcl.eth/manifest
{"occupied":[],"spawn_coordinate":{"x":0,"y":0},"total":0}
$ curl -s https://asset-bundle-registry-abgen.decentraland.org/worlds/brightmoments.dcl.eth/manifest
{"occupied":[],"spawn_coordinate":{"x":0,"y":0},"total":0}
# control -- a world whose metadata carries worldConfiguration.name
$ curl -s https://asset-bundle-registry-abgen.decentraland.org/worlds/tensaix2j.dcl.eth/manifest
{"occupied":["0,0","0,1","0,10",...],...}
```
All 50 are live and servable worlds: `/world//about` on `worlds-content-server` returns a `configurations.realmName`, and their entity documents fetch with HTTP 200 from `/contents/`.
## Cause
`crate/dcl-contents/src/types.rs:101-110`:
```rust
pub fn world_name(&self) -> Option<&str> {
self.metadata
.get("worldConfiguration")
.and_then(|w| w.get("name"))
.and_then(|n| n.as_str())
}
pub fn is_world(&self) -> bool { self.world_name().is_some() }
```
`worlds-content-server`, `src/logic/world-runtime-metadata-utils.ts`, handles two further shapes:
```ts
// Old deployments may not even have a worldConfiguration
if (!worldConfiguration) { return { name: worldName } }
...
// Deprecated dclName
if (cloned.dclName) { cloned.name = cloned.dclName; delete cloned.dclName }
```
Classifying the 50 by metadata shape against the live index:
| shape | count |
|---|---|
| `worldConfiguration` present, only `dclName` set (deprecated key) | 19 |
| no `worldConfiguration` object at all | 31 |
`migrateConfiguration` covers both; `world_name()` covers neither.
## Affected sites
- **`crate/dcl-contents/src/handlers/worlds.rs:28-31`** -- the load-bearing one. `ent.world_name().is_none_or(|n| !n.eq_ignore_ascii_case(&world_name))` -> `continue`. For these entities `world_name()` is `None`, so the guard is unconditionally true and the entity is skipped **before** `state.manifests.get(...)` is consulted. Converting the bundles does not help; the entity never reaches the servability check.
- **`crate/dcl-contents/src/content.rs:193`** -- `AND lower(d.entity_metadata->'worldConfiguration'->>'name') = $1` matches none of them, so a lookup by world name returns nothing.
- **`crate/dcl-contents/src/handlers/status.rs:41`** -- `entity_status_from` receives `is_world=false`, so `lods` is populated instead of `None`. These worlds are reported as pending LODs they should never be expected to have.
- **`crate/dcl-contents/src/types.rs:108`** -- `is_world()` inherits the gap wherever it is used.
The lambda itself is **not** affected: `Job` carries a caller-supplied `contentServerUrl` and performs no world classification, so conversion works today if invoked directly. The gap is entirely in resolution and serving.
## Suggested fix
Resolve the name the way `worlds-content-server` does, and thread the index/pointer-derived name through so the fallback has a third link:
```
worldConfiguration.name ?? worldConfiguration.dclName ??
```
Comparisons should be case-insensitive, or the value normalised to lowercase on the way in. 4 of the 19 carry mixed-case `dclName` against a lowercased index name: `CURSED.dcl.eth`, `PGN.dcl.eth`, `GreatCarls.dcl.eth`, `CyberDude.dcl.eth`. `worlds.rs` already uses `eq_ignore_ascii_case`; `content.rs:193` already lowercases both sides; a `dclName` fallback needs the same treatment.
Note that `content.rs:193` cannot be fixed by a fallback alone, since the deprecated key would have to be part of the SQL predicate (`COALESCE(... ->>'name', ... ->>'dclName')`).
## Affected worlds
`dclName` only (19):
```
brightmoments cursed cyberdude greatcarls idrip joaquin juna kbhomes kiko knj
landz metaryuk pgn quieteye rane raytopolov serato softtech wanaka
```
no `worldConfiguration` (31):
```
148 216 42meta accord agika agusaldasoro anarchist android17 apeinverse bodypaint
crim cryptobunny cryptotroll daylightgamer dclcurations diversivegamer doki
faaatheeerrrrrr fabeeobreen fuckingcors jmr jokerfeign luxworld mattimus menduz
metagora potradamus rezon seanny simonrose theinn
```
(all `.dcl.eth`)
Found while auditing world coverage for a corpus backfill; the backfill tool has the same gap and is tracked separately.
Contributor guide
Assessment
This issue has not been assessed yet.