diff --git a/link/v8.md b/link/v8.md index 35c2797..9c02333 100644 --- a/link/v8.md +++ b/link/v8.md @@ -176,11 +176,10 @@ not discovering it in production. 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 - gargoyle body (§5.2). `Animations` never reads `AnimationFrame*.uop`, and UOP animation is a - different container with its own `AnimationSequence.uop`, not a mirror of the mul layout, so - this is not reachable by calling the existing code differently. Those bodies degrade to no - image, which is the same state every creature is in today. +1. **Six of the twelve stock player-character bodies have no art on this path** — both human ghosts + and every gargoyle body (§5.2). `Animations` never reads `AnimationFrame*.uop`. **This one is + not merely accepted: §4.3 adds a decoder for it**, because it is the player character and the + scope says player models. 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 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 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 @@ -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 ghosts (607, 608) | | -Six of twelve, including a whole playable race — and §4 accepts that rather than reimplementing -`Animations` to reach `AnimationFrame*.uop`. Those bodies render without an image, exactly as every -creature does today. +Six of twelve, including a whole playable race. These are UOP-only and **§4.3 adds a decoder for +them** — verified genuinely absent from the legacy files rather than merely mis-addressed, by +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 -error — it is the expected answer for half the set, and a status screen that flags six failures on -every import will teach an operator to ignore it. And `shard_spawn_creatures.art` staying NULL has -to remain a first-class state everywhere it is consumed, which it already is. +Until that phase lands, and on any client that lacks them entirely, the catalogue must **not** treat +a missing player body as an error. It is the expected answer for half the set, and a status screen +that flags six failures on every import teaches an operator to ignore it. +`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 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 -"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. **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 | | 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 | -| 4 | Item statics and land on demand, hued keys, the cache | servuo-plugins, module-uo | -| 5 | Deep animation keys (`body//a/f`) for the future project, plus the bulk-fill switch | servuo-plugins, module-uo | -| 6 | The atlas over the sidecar (§10); shared-filesystem requirement retired | module-uo | -| 7 | Admin surface, Import/Update, approve/reject, activity log | module-uo | -| 8 | Docs pass across five repos; live walk on the real rig | docs | +| 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 | Item statics and land on demand, hued keys, the cache | servuo-plugins, module-uo | +| 6 | Deep animation keys (`body//a/f`) for the future project, plus the bulk-fill switch | servuo-plugins, module-uo | +| 7 | The atlas over the sidecar (§10); shared-filesystem requirement retired | module-uo | +| 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 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 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 -1. **§4: settled 2026-09-10 — call ServUO's vendored `Ultima`.** No decoders reimplemented. The - three accepted costs are in §4.2; the crash is confined to the `hasExtra: true` branch that only +1. **§4: settled 2026-09-10 — call ServUO's vendored `Ultima`,** with one exception added the same + 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`. 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