docs(link): the Asset Bridge (Protocol 8) — client assets without UOFiddler #234
81
link/v8.md
81
link/v8.md
@@ -169,10 +169,12 @@ application, and a minimal PNG writer over `System.IO.Compression.DeflateStream`
|
||||
into a `Bitmap` via `LockBits`, so System.Drawing is in the *decode*, not just the encode — a
|
||||
Linux/Mono shard needs libgdiplus even to read a sprite. Writing our own pixels and our own PNG
|
||||
makes the feature portable by construction.
|
||||
3. **It removes the UOP gap.** ServUO's vendored `Animations` reads legacy `anim*.mul` only. This
|
||||
client has a full 195 MB `anim.mul` so most bodies resolve — but **gargoyle bodies 666/667
|
||||
returned nothing**, because gargoyles live in `AnimationFrame*.uop`. A player race missing from
|
||||
an asset store built for "player models and everything" is not a caveat, it is a defect.
|
||||
3. **It removes the UOP gap, which lands squarely on the player bodies.** ServUO's vendored
|
||||
`Animations` reads legacy `anim*.mul` only, never `AnimationFrame*.uop`. This client has a full
|
||||
195 MB `anim.mul` so most creatures resolve — but **six of the twelve stock player-character
|
||||
bodies return nothing**, including every gargoyle and both human ghosts (§5.2). A missing
|
||||
playable race in an asset store built for "player models and everything" is not a caveat, it is
|
||||
a defect, and it is in the highest-attention part of the scope.
|
||||
4. **We are already in this business.** §9 writes a cliloc decompressor from scratch regardless.
|
||||
|
||||
A2 also ends the dependency on whatever version of `Ultima` a given ServUO happens to vendor,
|
||||
@@ -210,13 +212,20 @@ Three properties this shape buys:
|
||||
### 5.1 There is no direction segment, because only one direction is wanted
|
||||
|
||||
Bodies are stored in **five** directions and the client mirrors three of them to reach eight. Only
|
||||
one is needed here — the one facing the viewer — so **direction is fixed by the extractor and is
|
||||
not part of the key**. Leaving it in would advertise a choice nobody is going to make and would
|
||||
five-fold every count in §11 for nothing.
|
||||
one is needed here, so **direction is fixed by the extractor and is not part of the key**. Leaving
|
||||
it in would advertise a choice nobody is going to vary and would five-fold every count in §11 for
|
||||
nothing.
|
||||
|
||||
**The front-facing index is 0.** This was determined by rendering all five for a human, a wolf and
|
||||
a dragon rather than from a table, because the answer is not obvious and the small-thumbnail version
|
||||
of the same test suggested the opposite:
|
||||
**Which one depends on whether the body is a player character:**
|
||||
|
||||
| Body | Direction | Why |
|
||||
|---|---|---|
|
||||
| A player character body | **0** — head-on, facing the viewer | A character is a portrait; it should look at you |
|
||||
| Everything else | **1** — front three-quarter | The view that actually reads as a creature (see the caveat below) |
|
||||
|
||||
Which index is which was determined by **rendering all five** for a human, a wolf and a dragon
|
||||
rather than from a table, because the answer is not obvious and the small-thumbnail version of the
|
||||
same test suggested the exact opposite:
|
||||
|
||||
| Index | View |
|
||||
|---|---|
|
||||
@@ -226,12 +235,45 @@ of the same test suggested the opposite:
|
||||
| 3 | Rear three-quarter |
|
||||
| 4 | Directly away — back of the head, and a quadruped's tail toward the camera |
|
||||
|
||||
One caveat the render made obvious, and it is the reason this is written down rather than assumed:
|
||||
**index 0 is the least legible view for four-legged and long-bodied creatures.** A wolf seen head-on
|
||||
is a dark blob; the same wolf at index 2 is unmistakably a wolf, and at index 1 is the view
|
||||
UOFiddler's own thumbnail list picks. For a bestiary tile that matters. The extractor therefore
|
||||
takes the index as a **configuration value defaulting to 0**, so changing the catalogue's mind later
|
||||
is a setting and a re-import, not a protocol change.
|
||||
The caveat the render made obvious is what produced the split: **index 0 is the least legible view
|
||||
for four-legged and long-bodied creatures.** A wolf seen head-on is a dark blob; the same wolf at
|
||||
index 1 is unmistakably a wolf, which is also why UOFiddler's own thumbnail list picks that view. A
|
||||
humanoid has no such problem — it reads fine head-on, and head-on is what a character portrait
|
||||
wants.
|
||||
|
||||
Both indices stay **configuration values** (defaulting to 0 and 1), so changing the catalogue's mind
|
||||
later is a setting and a re-import, not a protocol change.
|
||||
|
||||
### 5.2 "Player character body" is asked of the shard, never hardcoded
|
||||
|
||||
`Server.Race.AllRaces` gives every registered race, and each carries `MaleBody`, `FemaleBody`,
|
||||
`MaleGhostBody` and `FemaleGhostBody`. The plugin enumerates those four ids per race and that set —
|
||||
nothing else — takes index 0. On stock ServUO 57.4 that is twelve ids:
|
||||
|
||||
| Race | Male | Female | Male ghost | Female ghost |
|
||||
|---|---|---|---|---|
|
||||
| Human | 400 | 401 | 402 | 403 |
|
||||
| Elf | 605 | 606 | 607 | 608 |
|
||||
| Gargoyle | 666 | 667 | **695** | **694** |
|
||||
|
||||
This is the §8 argument again in miniature: only code inside ServUO can answer it, and asking is
|
||||
the only thing that works on a shard with a custom race. Two details make the case that a
|
||||
hardcoded list would have been wrong — `RaceDefinitions.cs` passes the gargoyle's ghost bodies in
|
||||
the **opposite order** to the other two races (695 male, 694 female), and a shard that calls
|
||||
`RegisterRace` adds ids no table of ours would contain.
|
||||
|
||||
**Half of that set does not decode with ServUO's vendored library.** Measured:
|
||||
|
||||
| Decodes | Does not |
|
||||
|---|---|
|
||||
| Human male/female (400, 401) | **Human ghosts (402, 403)** |
|
||||
| Elf male/female (605, 606) | **Every gargoyle body (666, 667, 694, 695)** |
|
||||
| Elf ghosts (607, 608) | |
|
||||
|
||||
Six of twelve, including a whole playable race. So the one part of the asset scope with the most
|
||||
attention on it — the player character, head-on — is precisely the part the vendored `Animations`
|
||||
serves worst, and §4's recommendation to own the decoders is what fixes it. This is the single
|
||||
strongest piece of evidence for that choice.
|
||||
|
||||
---
|
||||
|
||||
@@ -505,8 +547,9 @@ prove byte-identical output *before* building six phases on top of it.
|
||||
libgdiplus dependency, the UOP gap and the vendor-drift risk all going away at once.
|
||||
2. **§11: gump art deferred.** Confirm that paperdoll and equipment gump art is genuinely
|
||||
out of scope for 8, given it is the one kind whose existing decoder crashes.
|
||||
3. **§5.1: direction is fixed at index 0** (head-on) and is not in the key — settled 2026-09-10.
|
||||
What is *not* settled is whether the catalogue should default to index 1 or 2 instead, since a
|
||||
head-on wolf is a dark blob and a side-on wolf is a wolf. It is a setting either way.
|
||||
3. **§5.1/§5.2: direction — settled 2026-09-10.** Player character bodies use index 0, everything
|
||||
else index 1, direction is not in the key, and the player-body set is enumerated from
|
||||
`Race.AllRaces` rather than hardcoded. Nothing outstanding; recorded here because it changes
|
||||
every count in §11.
|
||||
4. **§13: the default audience** for asset serving — inheriting the using page's audience is
|
||||
proposed; the operator sets the policy either way.
|
||||
|
||||
Reference in New Issue
Block a user