docs(link): the phase the measurement cancelled, and the 452 wrong pictures it found on the way (Phase 6)
§16 planned deep animation keys and a bulk-fill switch. Measuring first changed the phase: a complete one-direction animation set is 174,453 frames / 281.5 MB (not the ~119,000 / ~117 MB §11 estimated), every frame carries its own centre and 23,818 of 26,274 actions have frames of differing size — and the site displays still pictures. So none of it was built (§11.2, org lead 2026-09-11); `body/<id>/a<n>/f<k>` stays named in §5 and refused in practice. §4.10 is what the walk found instead. A body's actions are a contiguous band and the next body's begin immediately after, so one action past the ceiling is a real record of another creature: 643 of 795 legacy bodies pass every check there and 452 are byte-identical to body+1's action 0. `Animations.GetAnimLength` is not the ceiling — it disagrees with the index arithmetic on one body of this client, by nine actions. What shipped: the 73 bodies with no art at action 0 (a horse at `body/820/a23`), the catalogue key carrying its action, the atlas join that reads it instead of hardcoding `a0`, and the ceiling that makes the fallback walk safe. Catalogue 1,022 -> 1,095; EXTRACTOR_VERSION 2 -> 3; protocol stays 8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
214
link/v8.md
214
link/v8.md
@@ -635,6 +635,45 @@ needed its own lookup, because `Ultima.Files`' table of known client files preda
|
||||
and answers null for every `AnimationFrame*.uop` however present they are. The replacement matches
|
||||
**case-insensitively by enumeration**, which is a Linux-host concern rather than a tidiness one.
|
||||
|
||||
### 4.10 What phase 6 measured: the ceiling, and 452 validated pictures of the wrong body
|
||||
|
||||
§4.8 found the library returning the previously-decoded body's bitmap for an index entry that
|
||||
reads `length 0`. Phase 6 went looking one axis over — along **actions** rather than bodies — and
|
||||
found the same class of failure with none of the tells.
|
||||
|
||||
An index entry for an animation is `bodyBase + action * 5 + direction`, and `bodyBase` comes from a
|
||||
**band**: 110 slots for a high-detail body (22 actions), 65 for a low-detail one (13), 175 for a
|
||||
people body (35). The bands are contiguous, so the slots immediately after a body's band are the
|
||||
**next body's**. Ask for one action past the ceiling and the arithmetic lands on a real entry, at a
|
||||
real offset, holding a real animation record — of a different creature.
|
||||
|
||||
Measured on this machine's stock client, over the 795 bodies the legacy path serves:
|
||||
|
||||
| Asking one action past the band | Bodies |
|
||||
|---|---|
|
||||
| Refused by §4.5's validator (the entry is absent or unreadable) | 152 |
|
||||
| **Passes `CheckEntry` and `AnimationSane` and decodes** | **643** |
|
||||
| ...and the picture is **byte-identical to body+1's action 0** | **452** |
|
||||
|
||||
Body 1 action 22 is an ettin. Body 3 action 22 is an imp. Both confirmed by rendering them beside
|
||||
the body they belong to, because a count would have said the walk was fine — phase 0's validator
|
||||
cannot catch this and is not wrong to miss it: there is nothing defective about the record. The
|
||||
only defence is to **refuse the address**, so the ceiling lives inside
|
||||
`BridgeAssetValidator.ResolveAnimation`, where every caller already goes and no caller can skip it.
|
||||
|
||||
**The ceiling is the banding, not the library's own `GetAnimLength`.** That function exists, looks
|
||||
authoritative and disagrees with the index arithmetic on exactly one body of this client: a body
|
||||
reaching file type 5 as id 34 is excluded from the first band by `Animations.GetFileIndex`'s own
|
||||
"looks strange, though it works" special case, so it owns **13** actions while `GetAnimLength`
|
||||
answers **22**. Taking the larger number is nine actions of somebody else's art, reached on this
|
||||
client by translation from body 276. So the count is derived from the same switch that produces the
|
||||
offset, in the same file, where the two cannot drift apart.
|
||||
|
||||
This is the §4.3 never-sweep rule again in a third disguise. Sweeping file types puts a spider on
|
||||
the gargoyle page; trusting a `length 0` entry puts the last creature decoded on this one; walking
|
||||
past a band puts the *next* creature on it. All three decode cleanly, all three report success, and
|
||||
all three are caught by refusing to ask rather than by checking the answer.
|
||||
|
||||
---
|
||||
|
||||
## 5. Addressing: one key for every asset
|
||||
@@ -647,11 +686,22 @@ static/3922 one item graphic
|
||||
static/3922/h33 the same graphic, hue 33 applied
|
||||
land/3 one land tile
|
||||
body/34/a0 creature body 34, action 0, first frame
|
||||
body/400/a0/f0..f9 human male, action 0, all ten frames
|
||||
body/820/a23 body 820, whose action 0 is empty — a horse (§11.2)
|
||||
body/400/a0/f0..f9 human male, action 0, all ten frames — NOT SERVED (§11.2)
|
||||
cliloc/enu the whole converted string table (not an image)
|
||||
tree/Spawns/Trammel.xml a ServUO tree file (§10)
|
||||
```
|
||||
|
||||
**The catalogue's key names whichever action its picture came from.** One row per body either way,
|
||||
and for all but 73 of this client's bodies that action is 0 — but a body with no art there is
|
||||
catalogued at the first action that has any, and the key says which (§11.2). Calling it `a0`
|
||||
regardless would have been fewer changes downstream and a key that lies about its content, which
|
||||
is the failure §4.5, §4.8 and §4.10 each describe from a different direction.
|
||||
|
||||
**The frame depth is defined and not served.** `body/400/a0/f0..f9` is well-formed under this
|
||||
scheme and every request for it is refused `unsupported` — see §11.2 for the decision and what it
|
||||
would cost to change.
|
||||
|
||||
Three properties this shape buys:
|
||||
|
||||
- **Hue is part of the key, not a transform.** `itemId` and `hue` are already on the wire together
|
||||
@@ -659,8 +709,10 @@ Three properties this shape buys:
|
||||
key for its own picture. Applying hues website-side would mean shipping `Hues.mul` semantics into
|
||||
Node for no gain.
|
||||
- **Depth is expressible without being mandatory.** `body/400/a0` and `body/400/a0/f0..f9` are the
|
||||
same addressing scheme at two depths, which is what lets §11 bulk-import thumbnails and fetch full
|
||||
animations on demand without a second protocol.
|
||||
same addressing scheme at two depths, which is what lets §11 bulk-import thumbnails and, if
|
||||
anything ever wants them, fetch full animations on demand without a second protocol. Phase 6
|
||||
measured what that second depth costs and deferred it (§11.2); the point of the scheme is that
|
||||
deferring it is a decision about *what to serve*, not about what can be named.
|
||||
- **Nothing in the key is client-version-specific**, so a client patch changes an asset's *bytes*,
|
||||
not its name — which is what makes §7's delta work.
|
||||
|
||||
@@ -1019,9 +1071,9 @@ project. §1.1's measurements make the sizing question concrete:
|
||||
|---|---|---|
|
||||
| Item statics | **49,152** addressable, **39,189** with art (§11.1, phase 5) | No — on demand, cached, keyed by `itemId` (+ hue) |
|
||||
| Land tiles | **16,384** addressable, **4,244** with art | No — on demand |
|
||||
| Creature/player bodies, first frame | **1,022** — 787 legacy (§4.8, *not* the 1,144 the library reports) + 235 UOP (§4.9) | **Yes** — this is the catalogue |
|
||||
| One body, every action, one direction | **210 frames** (body 400); 96–210 measured across six bodies | No — on demand, per body |
|
||||
| All bodies, every action, one direction | **~119,000 frames**, ~117 MB | No — but no longer unthinkable |
|
||||
| Creature/player bodies, first frame | **1,095** — 787 legacy (§4.8, *not* the 1,144 the library reports) + 235 UOP (§4.9) + 73 at a later action (§11.2) | **Yes** — this is the catalogue |
|
||||
| One body, every action, one direction | **210 frames** (body 400); median 118 KB per body, max 9.6 MB (§11.2) | **Not served** (§11.2) |
|
||||
| All bodies, every action, one direction | **174,453 frames, 281.5 MB** — measured, not the ~119,000 estimated here | **Not served** (§11.2) |
|
||||
| The same at five directions | ~865,000 frames | Not built (§5.1) |
|
||||
| Cliloc table | 123,490 entries → 67,496 rows | **Yes** — whole-table replace |
|
||||
| ServUO tree files (§10) | ~21 files, ~10.6 MB | **Yes** |
|
||||
@@ -1040,23 +1092,26 @@ its opposite: the 235 it added are bodies with real art in a file the vendored d
|
||||
open (§4.9), each one validated at every bound and identified by a name that carries its body id.
|
||||
Both numbers moved because something was measured rather than reported.
|
||||
|
||||
Everything deeper is the *same protocol at a deeper key* (§5), fetched on demand and cached. That
|
||||
is what serves the future project without exporting 3.5 GB of someone else's copyrighted client
|
||||
into a database: a viewer that wants body 400's full walk cycle asks for `body/400/a2/f0..f9` and
|
||||
gets it, once, and it is cached from then on.
|
||||
Everything deeper is the *same protocol at a deeper key* (§5) — a viewer that wants body 400's full
|
||||
walk cycle would ask for `body/400/a2/f0..f9`. **That depth is not implemented** (§11.2): the site
|
||||
displays still pictures, so the frames have no consumer, and the design's value here is that a
|
||||
future one costs a reader and a store rather than a protocol.
|
||||
|
||||
Because §5.1 dropped four of the five directions, a **complete** one-direction animation set for
|
||||
every body is now ~119,000 frames rather than ~865,000 — around 117 MB (~173,000 before §4.8 cut
|
||||
the body count to the ones that have art; both figures carry the same measured ~151-frame average
|
||||
per body). 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 6 should leave room for a
|
||||
bulk-fill-everything switch rather than assuming on-demand is the only mode.
|
||||
every body looked like ~119,000 frames rather than ~865,000. Phase 6 measured it at **174,453
|
||||
frames and 281.5 MB** (§11.2) and, more to the point, established that nothing on this site would
|
||||
ever read them: the site shows still pictures. So neither the deep keys nor the
|
||||
bulk-fill-everything switch this paragraph anticipated was built, and the numbers above are what a
|
||||
future consumer would be choosing to pay.
|
||||
|
||||
**Hued variants are on demand, always.** `static/3922/h33` is generated when something on the wire
|
||||
actually carries hue 33. The cross product of 49,152 statics and 3,000 hues is not a set anyone
|
||||
enumerates.
|
||||
|
||||
**Everything deeper than the catalogue is the same protocol at a deeper key, and is refused rather
|
||||
than served** — §11.2. That sentence was written expecting phase 6 to serve it; what phase 6
|
||||
actually found was that no consumer exists, so the depth stays named and unserved.
|
||||
|
||||
### 11.1 What phase 5 measured, and the two traps it found
|
||||
|
||||
The sizing above was an estimate taken off `art.mul`'s length. Measured through the reader itself,
|
||||
@@ -1085,7 +1140,7 @@ than throwing, so the over-run counted silently as "empty slots".)
|
||||
**81 MB is small enough to reopen the bulk question, and the answer is still no.** Not on size — on
|
||||
what the transfer buys. Base64 puts it at 108 MB through a 512 KB single-slot channel, roughly 210
|
||||
round trips, to store 43,433 pictures of which a live shard displays a few hundred. On-demand stays
|
||||
right; phase 6's bulk-fill switch is where an operator who wants the lot says so.
|
||||
right, and phase 6 did not build the bulk-fill switch this sentence promised — see §11.2.
|
||||
|
||||
#### The library's cache poisons a hued sprite
|
||||
|
||||
@@ -1147,6 +1202,65 @@ call** — §4.1. It is the only decoder that reaches the `hasExtra: true` branc
|
||||
corrupts the process on the second id. Adding gump art later means fixing that path first,
|
||||
deliberately; it is additive under the same key scheme (`gump/<id>`), which is the point of §5.
|
||||
|
||||
### 11.2 What phase 6 measured, and why the deep keys are not built
|
||||
|
||||
Everything above about full animations was an estimate — ~119,000 frames, ~117 MB, from a
|
||||
151-frame average over six bodies. Phase 6 decoded and PNG-encoded **every action of every body at
|
||||
the catalogue's direction**, which is the set a complete one-direction animation store would hold:
|
||||
|
||||
| | Estimated (§11) | Measured |
|
||||
|---|---|---|
|
||||
| Bodies with art at *some* action | 1,022 | **1,096** |
|
||||
| Action keys (`body/<id>/a<n>`) with frames | — | **26,274** (mean 25.4 per body, max 35) |
|
||||
| Frames | ~119,000 | **174,453** |
|
||||
| As PNG | ~117 MB | **281.5 MB** (mean 1,692 B, largest single frame 71.5 KB) |
|
||||
| Per body | — | median **118 KB** / 210 frames; p90 536 KB; max **9.6 MB** (body 826) |
|
||||
| Decode + encode the lot | — | **104 s** |
|
||||
|
||||
Two facts fall out of that walk that any frame-serving design has to answer.
|
||||
|
||||
**Frames are not a sequence of pictures; they are a sequence of *placed* pictures.** Every frame
|
||||
carries its own centre offset and its own dimensions, and **23,818 of the 26,274 actions have
|
||||
frames that differ in size** (centres span x −51..270, y −217..184). A key that returned only a PNG
|
||||
would produce an animation that jitters, and nothing downstream could tell — the same shape of
|
||||
silent wrongness as every other trap in this document.
|
||||
|
||||
**At 281.5 MB the transfer is ~375 MB of base64 through a 512 KiB single-slot channel** (§3.2),
|
||||
which is upward of 750 round trips for a store of 174,453 files.
|
||||
|
||||
### The decision: the site shows still pictures, so frames wait for a consumer (org lead, 2026-09-11)
|
||||
|
||||
§11 justified the deep keys with "the future project", not with anything on this site — and the
|
||||
site does not display animation anywhere, nor is it planned to. So phase 6 builds **none** of it:
|
||||
no frame keys, no per-body frames manifest, no bulk-fill switch, no store sized for 281 MB, and no
|
||||
admin surface for any of that. What would have been the consumer-side machinery is exactly the part
|
||||
that would have been guessed at, and when a consumer does exist it can say what shape it wants.
|
||||
|
||||
`body/<id>/a<n>/f<k>` stays defined in §5 and refused in practice. Reviving it costs the frames
|
||||
manifest, the alignment fields and a store — not a protocol change, which is the whole point of
|
||||
§5's key scheme.
|
||||
|
||||
### What phase 6 built instead: the 73 bodies nobody could see
|
||||
|
||||
The measurement did turn up something the still-picture site wants. **73 bodies have no art at
|
||||
action 0 and real art at a later one** — 66 reached through the UOP packages, 7 through the legacy
|
||||
files. Body 820's first drawn action is 23, and it is a horse. Until this phase they were absent
|
||||
from the catalogue and rendered as text on the bestiary.
|
||||
|
||||
So the catalogue now falls back to **the first action that has art**, and the key names it
|
||||
(§5). The count goes **1,022 → 1,095**, a full cold scan of all 2,047 bodies goes 1,409 ms → 2,090 ms,
|
||||
and `EXTRACTOR_VERSION` goes **2 → 3** — unchanged input, a different answer, which is what that
|
||||
number is for.
|
||||
|
||||
Two honesties about those 73. The seven legacy ones and roughly a dozen of the UOP ones are
|
||||
recognisable creatures — a horse, a spider, a dragon, a phoenix, a turkey. **Most of the rest are
|
||||
thin flat sprites** with entries at actions 1 and 3 only, all anchored at centre x = 63: object or
|
||||
equipment art rather than creatures. They are correct by construction (a UOP entry is addressed by
|
||||
the hash of a name carrying body *and* action, and the payload re-declares the body), they cost
|
||||
about 200 bytes of row each, and nothing surfaces them unless a spawn file names a creature class
|
||||
that resolves to one of those ids. And the fallback is the one walk in this protocol that moves
|
||||
along the action axis, which is why §4.10's ceiling had to ship with it rather than after it.
|
||||
|
||||
---
|
||||
|
||||
## 12. Where it lands on the website
|
||||
@@ -1201,13 +1315,29 @@ past (org lead, 2026-09-10):
|
||||
is actually enforced — one spread, in one place, applied on every rebuild rather than only at
|
||||
import time.
|
||||
|
||||
**Two details worth not rediscovering.** The derivation joins on the catalogue key
|
||||
(`a.asset_key = CONCAT('body/', b.body, '/a0')`), not on `a.body = b.body`: today one body has
|
||||
exactly one asset and the simpler join is correct, and it stops being correct the moment phase 6
|
||||
adds `body/400/a2/f0`, at which point one slug matches dozens of rows and whichever the engine
|
||||
returned last becomes the portrait. And the stored filename is **content-addressed**
|
||||
(`uo-body-34-a0-<sha8>.png`), because a stable name overwritten in place leaves every browser and
|
||||
CDN serving last month's client's sprite from cache with the database row perfectly correct.
|
||||
**Two details worth not rediscovering.** The derivation joins on the catalogue **key**, not on
|
||||
`a.body = b.body`: today one body has exactly one asset and the simpler join is correct, and it
|
||||
stops being correct the day a deeper key (`body/400/a2/f0`) is stored, at which point one slug
|
||||
matches dozens of rows and whichever the engine returned last becomes the portrait. And the stored
|
||||
filename is **content-addressed** (`uo-body-34-a0-<sha8>.png`), because a stable name overwritten
|
||||
in place leaves every browser and CDN serving last month's client's sprite from cache with the
|
||||
database row perfectly correct.
|
||||
|
||||
Phase 6 changed that join, and the way it changed is the point. It read
|
||||
`a.asset_key = CONCAT('body/', b.body, '/a0')`, which stopped being right the moment a body could
|
||||
be catalogued at another action — it would have dropped exactly the 73 creatures §11.2 added, a
|
||||
horse among them, silently. It now reads the row's own action:
|
||||
|
||||
```sql
|
||||
JOIN shard_assets a ON a.body = b.body AND a.family = 'body'
|
||||
AND a.asset_key = CONCAT('body/', b.body, '/a', COALESCE(a.action, 0))
|
||||
```
|
||||
|
||||
`COALESCE` because a row written before the column existed has NULL there, and a NULL inside
|
||||
`CONCAT` makes the whole comparison NULL — which would have taken every portrait off the site on
|
||||
upgrade, with the database perfectly correct and nothing to see in a log. The `action` is stored
|
||||
rather than parsed back out of the key because this join needs it in SQL, and re-deriving it there
|
||||
would put a second, weaker parser of §5's key scheme in the schema.
|
||||
|
||||
`shard_creature_bodies` also answers §8 without a schema change on the atlas side:
|
||||
`shard_spawn_creatures.name` already holds the ServUO **class name** — the atlas build picks the
|
||||
@@ -1279,6 +1409,14 @@ discovers the gap as a refusal *per key, per pass, forever*, with no picture eve
|
||||
warning in the log every few minutes. With it, that is one reported state carrying a sentence naming
|
||||
the fix.
|
||||
|
||||
**Phase 6 added one field and no command.** Manifest and fetch rows carry `action` — which action
|
||||
of the body the thumbnail came from (§11.2). Additive, so **the protocol stays 8**; a consumer that
|
||||
ignores it sees the catalogue it always saw plus 73 rows, and one that reads it can build the right
|
||||
URL for a body catalogued at `a23`. `EXTRACTOR_VERSION` goes **2 → 3**, which is the change every
|
||||
consumer does see. A fetch for a key naming an action the catalogue did not choose is answered
|
||||
`unsupported` with the chosen action alongside it — never by decoding the asked-for action, which
|
||||
is §4.10's wrong picture reached politely.
|
||||
|
||||
**Phase 4 added one field and no command.** `source` on a manifest or fetch row is `legacy` or
|
||||
`uop` — which reader produced the bytes (§4.9). It is additive, so **the protocol stays 8**: a
|
||||
consumer that does not read it is unaffected, and one that does can say which half of the extractor
|
||||
@@ -1326,7 +1464,7 @@ disagree, so a split bump means the next bundle silently fails to compose.
|
||||
| 3 | **DONE 2026-09-10.** Body resolution (§8) + the **787**-body catalogue (§4.8), `assets.manifest` / `assets.fetch` / `assets.bodies` and their REST mirrors, `shard_spawn_creatures.art` filled and rendered (§8.1, §12.1). **787 rows in one 734 ms page; 455 types resolved at ~190 ms per 100 on the Core thread; zero mobiles leaked.** `UOFIDDLER.md` deleted, two phases early | servuo-plugins, **link**, module-uo |
|
||||
| 4 | **DONE 2026-09-11.** The UOP animation decoder (§4.3, §4.9): `BridgeUop` + a PNG encoder that never touches `System.Drawing`, wired in beneath the legacy reader. **Two of the eight player bodies turned out to exist** (gargoyles 666/667); the other six are in no client file, and ghost ids left the player-body set (§5.2, §17.9). The same fallback added **233 other bodies**: the catalogue is **1,022 rows, 1,409 ms cold**, and all six player bodies have art for the first time. `EXTRACTOR_VERSION` 1 → 2 | servuo-plugins |
|
||||
| 5 | **DONE 2026-09-11.** Item statics and land on demand (§11.1): the `static` and `land` families, hue applied on the shard from `tiledata.mul`, the byte-bounded art cache, `assets.fetch` made family-aware, `families` on `assets.sources`. Website side: the warm pass, per-row `catalog` staleness, and pictures on the marketplace and the character sheet. **39,189 statics and 4,244 land tiles served; the only refusals are the 9,963 + 12,140 empty index slots §4.5 predicted.** Two traps found — the library's bitmap cache poisons a hued sprite, and `PartialHue` decides the picture from a file only the shard has. Protocol stays 8; `EXTRACTOR_VERSION` stays 2 | servuo-plugins, 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 |
|
||||
| 6 | **DONE 2026-09-11, and not what this row said.** The measurement came first and changed the phase: a complete one-direction animation set is **174,453 frames / 281.5 MB**, not the ~119,000 estimated, and **the site displays still pictures** — so the deep keys and the bulk-fill switch were **not built** (§11.2, org lead 2026-09-11). What shipped is what the still-picture site was missing: the **73 bodies with no art at action 0 and real art deeper** (a horse at `body/820/a23`), the catalogue key carrying its action, the atlas join that reads it, and §4.10's per-body **action ceiling** — without which the fallback walk itself would serve **452 validated pictures of the next body**. Catalogue **1,022 → 1,095**; `EXTRACTOR_VERSION` 2 → 3; protocol stays 8 | 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 |
|
||||
@@ -1337,6 +1475,11 @@ a client that has been patched — *before* building eight phases on top of it.
|
||||
§1.1 were run from PowerShell against a stock client; neither of those is the environment this will
|
||||
actually run in.
|
||||
|
||||
**Phase 6's deep keys are not deferred to a later phase; they are out of the plan** until something
|
||||
wants them. §11.2 has the measurement a future consumer would be choosing to pay and the two facts
|
||||
it would have to answer (per-frame centres, and 750 round trips through a single-slot channel).
|
||||
Nothing about reviving them needs a protocol change — that is what §5's key scheme bought.
|
||||
|
||||
Phase 4 sat **after** the catalogue rather than inside it on purpose. The catalogue was useful with
|
||||
783 of its 787 bodies, the UOP reader is the one piece of genuinely new format work in this
|
||||
protocol, and putting it on the critical path would have held up every website-side phase behind
|
||||
@@ -1453,3 +1596,24 @@ in the document.
|
||||
from a complete one. The door §4.4 wanted open stays open; phase 4 simply does not walk
|
||||
through it.
|
||||
|
||||
11. **§11.2: phase 6 stopped being the phase it was planned as — settled 2026-09-11.** Put to the
|
||||
org lead with the measurement in hand, and the answer removed most of the phase:
|
||||
|
||||
- **The deep animation keys and the bulk-fill switch are not built.** The org lead's own scope
|
||||
note — "I hadn't planned to use the animations on the site, just static images" — is the
|
||||
whole argument: §11 justified them with a future project rather than with anything on this
|
||||
site, and 281.5 MB of store, a warm pass and an admin surface for a consumer that does not
|
||||
exist yet would all have been guesses. `body/<id>/a<n>/f<k>` stays named in §5 and refused in
|
||||
practice; reviving it is a reader and a store, not a protocol change.
|
||||
- **The catalogue falls back to the first action that has art, and the key names that action.**
|
||||
73 bodies on a stock client have nothing at action 0 and real art deeper — body 820's action
|
||||
23 is a horse — and they rendered as text on the bestiary. Keeping the `a0` spelling for
|
||||
them was rejected for the reason §5 gives: a key that lies about its content is this
|
||||
protocol's recurring failure, not a shortcut.
|
||||
- **Only that one exception**, over a per-body pose override that would let an operator pick a
|
||||
better-looking action. One rule with one mechanical exception; the setting can come the day
|
||||
somebody wants a specific creature to look different.
|
||||
- **§4.10's action ceiling ships in the same phase**, because the fallback is the walk that
|
||||
would otherwise hit it: one action past a body's band is 643 validated pictures and 452
|
||||
byte-identical copies of the next body.
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ Creature artwork read from the shard's own UO client ([`link/v8.md`](../../link/
|
||||
|
||||
| Table | Shape |
|
||||
|---|---|
|
||||
| `shard_assets` | `asset_key` VARCHAR PK (§5's key, e.g. `body/34/a0`, `static/3922/h33`, `land/3`), `family`, `sha256`, `bytes`, `width`, `height`, `body`, `direction`, `file`, `catalog`, `imported_at` |
|
||||
| `shard_assets` | `asset_key` VARCHAR PK (§5's key, e.g. `body/34/a0`, `body/820/a23`, `static/3922/h33`, `land/3`), `family`, `sha256`, `bytes`, `width`, `height`, `body`, `action`, `direction`, `file`, `catalog`, `imported_at` |
|
||||
| `shard_creature_bodies` | `slug` PK, `type_name` (the ServUO class name asked), `body` nullable, `status`, `resolved_at` |
|
||||
| `shard_asset_meta` | Singleton (`id = 1`), `payload` JSON (catalogue id, extractor version, source fingerprint, counts), `imported_at` |
|
||||
|
||||
|
||||
@@ -233,10 +233,12 @@ custom creatures) and points each `shard_spawn_creatures.art` at its picture.
|
||||
Boot never calls the shard for this: the files change when an operator patches
|
||||
their client, which is an event they know about and the site does not.
|
||||
|
||||
On this machine's stock client that is **1,022 creature portraits**, about a
|
||||
megabyte in total — 787 out of the legacy `anim*.mul` files and 235 more out of
|
||||
On this machine's stock client that is **1,095 creature portraits**, about a
|
||||
megabyte in total — 787 out of the legacy `anim*.mul` files, 235 more out of
|
||||
`AnimationFrame*.uop`, which ServUO's own decoder never opens
|
||||
([`../link/v8.md`](../link/v8.md) §4.9).
|
||||
([`../link/v8.md`](../link/v8.md) §4.9), and 73 more that have no art at the
|
||||
walk's first action and real art at a later one, which the import now falls back
|
||||
to (§11.2). Body 820 is one of them, and it is a horse.
|
||||
|
||||
**NULL stays a first-class state, and always will be.** An install with no shard
|
||||
link has never imported one; a Linux shard host without `libgdiplus` cannot
|
||||
|
||||
Reference in New Issue
Block a user