docs(link): the Asset Bridge (Protocol 8) — client assets without UOFiddler #234
77
link/v8.md
77
link/v8.md
@@ -176,11 +176,10 @@ not discovering it in production.
|
|||||||
|
|
||||||
Three costs come with it, all known and none of them blocking:
|
Three costs come with it, all known and none of them blocking:
|
||||||
|
|
||||||
1. **Six of the twelve stock player-character bodies have no art** — both human ghosts and every
|
1. **Six of the twelve stock player-character bodies have no art on this path** — both human ghosts
|
||||||
gargoyle body (§5.2). `Animations` never reads `AnimationFrame*.uop`, and UOP animation is a
|
and every gargoyle body (§5.2). `Animations` never reads `AnimationFrame*.uop`. **This one is
|
||||||
different container with its own `AnimationSequence.uop`, not a mirror of the mul layout, so
|
not merely accepted: §4.3 adds a decoder for it**, because it is the player character and the
|
||||||
this is not reachable by calling the existing code differently. Those bodies degrade to no
|
scope says player models.
|
||||||
image, which is the same state every creature is in today.
|
|
||||||
2. **`System.Drawing` is a hard dependency, in the decode and not just the encode.** `Frame`
|
2. **`System.Drawing` is a hard dependency, in the decode and not just the encode.** `Frame`
|
||||||
writes ARGB1555 straight through a `LockBits` pointer, so a Linux/Mono shard needs libgdiplus
|
writes ARGB1555 straight through a `LockBits` pointer, so a Linux/Mono shard needs libgdiplus
|
||||||
to read a sprite at all. See §17.
|
to read a sprite at all. See §17.
|
||||||
@@ -191,6 +190,33 @@ Three costs come with it, all known and none of them blocking:
|
|||||||
The residual risk that remains is a patched or custom client tripping an out-of-bounds read on a
|
The residual risk that remains is a patched or custom client tripping an out-of-bounds read on a
|
||||||
path we *do* call. §16's phase 0 is where that gets exercised rather than assumed.
|
path we *do* call. §16's phase 0 is where that gets exercised rather than assumed.
|
||||||
|
|
||||||
|
### 4.3 One decoder we do write: UOP animation, for the player bodies
|
||||||
|
|
||||||
|
The six missing bodies are the player character, and the scope is player models, so they get a
|
||||||
|
decoder rather than a caveat. It is deliberately the **narrowest possible** addition: a reader for
|
||||||
|
`AnimationFrame*.uop`, used *only* for bodies the legacy path cannot resolve. Everything the
|
||||||
|
vendored code can already decode keeps going through the vendored code.
|
||||||
|
|
||||||
|
This client ships `AnimationFrame1/2/3/4/6.uop` (107, 118, 253, 115 and 24 MB) plus
|
||||||
|
`AnimationSequence.uop`. ServUO's `FileIndex` already contains a UOP reader — but `Animations`
|
||||||
|
never constructs one, and the UOP animation *payload* is its own format rather than a repackaged
|
||||||
|
mul record, so wiring the existing `FileIndex` in is not sufficient. Two license-compatible
|
||||||
|
references exist to work from: ClassicUO's animation loader (GPL-3, and we are GPL-3-or-later) and
|
||||||
|
UOFiddler 4.22 (Beerware, already established in §9).
|
||||||
|
|
||||||
|
**The trap this must not fall into, and it is why the phase exists at all.** `Bodyconv.def` maps
|
||||||
|
gargoyle 666 to `anim5`, and `BodyConverter.Convert` faithfully returns fileType 5 — where the
|
||||||
|
client has nothing. Asking the *other* anim files for index 666 does not fail. It returns 175
|
||||||
|
decodable action/direction combinations of **a giant spider**, because something unrelated occupies
|
||||||
|
that index in `anim2.mul`; fileTypes 3 and 4 return misaligned colour fragments. Rendered and
|
||||||
|
confirmed.
|
||||||
|
|
||||||
|
So the extractor takes `BodyConverter.Convert`'s answer and, if that yields nothing, reports
|
||||||
|
nothing. **It must never sweep file types looking for a hit** — that does not find missing art, it
|
||||||
|
silently puts a spider on the gargoyle page, and no error is raised anywhere. A "0 rows" outcome is
|
||||||
|
correct behaviour; a plausible wrong picture is the failure this protocol most needs to avoid,
|
||||||
|
because nothing downstream can detect it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Addressing: one key for every asset
|
## 5. Addressing: one key for every asset
|
||||||
@@ -281,14 +307,15 @@ the **opposite order** to the other two races (695 male, 694 female), and a shar
|
|||||||
| Elf male/female (605, 606) | **Every gargoyle body (666, 667, 694, 695)** |
|
| Elf male/female (605, 606) | **Every gargoyle body (666, 667, 694, 695)** |
|
||||||
| Elf ghosts (607, 608) | |
|
| Elf ghosts (607, 608) | |
|
||||||
|
|
||||||
Six of twelve, including a whole playable race — and §4 accepts that rather than reimplementing
|
Six of twelve, including a whole playable race. These are UOP-only and **§4.3 adds a decoder for
|
||||||
`Animations` to reach `AnimationFrame*.uop`. Those bodies render without an image, exactly as every
|
them** — verified genuinely absent from the legacy files rather than merely mis-addressed, by
|
||||||
creature does today.
|
rendering what the other anim files hold at those indices and finding spiders.
|
||||||
|
|
||||||
Two things follow for the build. The catalogue must **not** treat a missing player body as an
|
Until that phase lands, and on any client that lacks them entirely, the catalogue must **not** treat
|
||||||
error — it is the expected answer for half the set, and a status screen that flags six failures on
|
a missing player body as an error. It is the expected answer for half the set, and a status screen
|
||||||
every import will teach an operator to ignore it. And `shard_spawn_creatures.art` staying NULL has
|
that flags six failures on every import teaches an operator to ignore it.
|
||||||
to remain a first-class state everywhere it is consumed, which it already is.
|
`shard_spawn_creatures.art` staying NULL remains a first-class state everywhere it is consumed,
|
||||||
|
which it already is.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -448,7 +475,7 @@ gets it, once, and it is cached from then on.
|
|||||||
Because §5.1 dropped four of the five directions, a **complete** one-direction animation set for
|
Because §5.1 dropped four of the five directions, a **complete** one-direction animation set for
|
||||||
every body is now ~173,000 frames rather than ~865,000 — around 170 MB. That is still not the
|
every body is now ~173,000 frames rather than ~865,000 — around 170 MB. That is still not the
|
||||||
default and still not something to import before anything asks for it, but it has moved from
|
default and still not something to import before anything asks for it, but it has moved from
|
||||||
"never" to "a thing an operator could reasonably choose", and phase 5 should leave room for a
|
"never" to "a thing an operator could reasonably choose", and phase 6 should leave room for a
|
||||||
bulk-fill-everything switch rather than assuming on-demand is the only mode.
|
bulk-fill-everything switch rather than assuming on-demand is the only mode.
|
||||||
|
|
||||||
**Hued variants are on demand, always.** `static/3922/h33` is generated when something on the wire
|
**Hued variants are on demand, always.** `static/3922/h33` is generated when something on the wire
|
||||||
@@ -544,24 +571,32 @@ disagree, so a split bump means the next bundle silently fails to compose.
|
|||||||
| 1 | The transport: `assets.sources`, flow control, the sidecar line cap, `EXTRACTOR_VERSION`, protocol bump | servuo-plugins, link |
|
| 1 | The transport: `assets.sources`, flow control, the sidecar line cap, `EXTRACTOR_VERSION`, protocol bump | servuo-plugins, link |
|
||||||
| 2 | Clilocs end to end; retire the converter and `UOFIDDLER.md` §Part 1 | all |
|
| 2 | Clilocs end to end; retire the converter and `UOFIDDLER.md` §Part 1 | all |
|
||||||
| 3 | Body resolution (§8) + the 1,144-body catalogue; `shard_spawn_creatures.art` filled | servuo-plugins, module-uo |
|
| 3 | Body resolution (§8) + the 1,144-body catalogue; `shard_spawn_creatures.art` filled | servuo-plugins, module-uo |
|
||||||
| 4 | Item statics and land on demand, hued keys, the cache | servuo-plugins, module-uo |
|
| 4 | **The UOP animation decoder (§4.3)** — the six player bodies the legacy path cannot reach, and the never-sweep-file-types rule enforced in code | servuo-plugins |
|
||||||
| 5 | Deep animation keys (`body/<id>/a<n>/f<n>`) for the future project, plus the bulk-fill switch | servuo-plugins, module-uo |
|
| 5 | Item statics and land on demand, hued keys, the cache | servuo-plugins, module-uo |
|
||||||
| 6 | The atlas over the sidecar (§10); shared-filesystem requirement retired | module-uo |
|
| 6 | Deep animation keys (`body/<id>/a<n>/f<n>`) for the future project, plus the bulk-fill switch | servuo-plugins, module-uo |
|
||||||
| 7 | Admin surface, Import/Update, approve/reject, activity log | module-uo |
|
| 7 | The atlas over the sidecar (§10); shared-filesystem requirement retired | module-uo |
|
||||||
| 8 | Docs pass across five repos; live walk on the real rig | docs |
|
| 8 | Admin surface, Import/Update, approve/reject, activity log | module-uo |
|
||||||
|
| 9 | Docs pass across five repos; live walk on the real rig | docs |
|
||||||
|
|
||||||
Phase 0 exists because §4 chose to call code that can take the shard down if it is wrong, and the
|
Phase 0 exists because §4 chose to call code that can take the shard down if it is wrong, and the
|
||||||
honest way to hold that choice is to try to break it on purpose — in the real host process, against
|
honest way to hold that choice is to try to break it on purpose — in the real host process, against
|
||||||
a client that has been patched — *before* building seven phases on top of it. The probes behind
|
a client that has been patched — *before* building eight phases on top of it. The probes behind
|
||||||
§1.1 were run from PowerShell against a stock client; neither of those is the environment this will
|
§1.1 were run from PowerShell against a stock client; neither of those is the environment this will
|
||||||
actually run in.
|
actually run in.
|
||||||
|
|
||||||
|
Phase 4 sits **after** the catalogue rather than inside it on purpose. The catalogue is useful with
|
||||||
|
1,138 of 1,144 bodies, the UOP reader is the one piece of genuinely new format work in this
|
||||||
|
protocol, and putting it on the critical path would hold up every website-side phase behind it. Its
|
||||||
|
acceptance test is not "it decodes" — it is that a gargoyle looks like a gargoyle, checked by eye,
|
||||||
|
because §4.3's whole point is that this failure mode produces confident, wrong pictures.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 17. Decisions still open
|
## 17. Decisions still open
|
||||||
|
|
||||||
1. **§4: settled 2026-09-10 — call ServUO's vendored `Ultima`.** No decoders reimplemented. The
|
1. **§4: settled 2026-09-10 — call ServUO's vendored `Ultima`,** with one exception added the same
|
||||||
three accepted costs are in §4.2; the crash is confined to the `hasExtra: true` branch that only
|
day: **§4.3's UOP animation decoder**, scoped to the player bodies the legacy path cannot reach
|
||||||
|
and to nothing else (phase 4). The crash is confined to the `hasExtra: true` branch that only
|
||||||
`Gumps` reaches, and nothing here calls `Gumps`.
|
`Gumps` reaches, and nothing here calls `Gumps`.
|
||||||
2. **§17.1's consequence: Linux/Mono shards.** §4.2 item 2 leaves `System.Drawing` in the decode
|
2. **§17.1's consequence: Linux/Mono shards.** §4.2 item 2 leaves `System.Drawing` in the decode
|
||||||
path, so a non-Windows shard needs **libgdiplus** installed or art extraction fails there. Three
|
path, so a non-Windows shard needs **libgdiplus** installed or art extraction fails there. Three
|
||||||
|
|||||||
Reference in New Issue
Block a user