157e8034b1e6dc64d3df78f91d526e2fb1fd20e6
18 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 72a379f84c |
docs(link): 9a touched servuo-plugins too — the plugin README's file table
Its file table stopped at Phase 6, ten rows for a directory of 38. The nine files this workstream added are documented there now (servuo-plugins#35). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 57ecdc8c29 |
docs(link): the guide an integrator reads stopped at protocol 6 (Phase 9a)
The Asset Bridge's docs pass, and the acceptance walk that shaped it (v8.md §16 row 9a, §17.13-14). Phase 9 is three legs now: this one, the edge->main cutover, and the site. ## INTEGRATION.md had stopped at 6 and contradicted itself Its §2 said "the current version is 6" above examples already carrying `X-UOLink-Version: 8`, there was no protocol-7 paragraph, and `assets.` appeared zero times in 1,306 lines. It is the only document an integrator outside this org has, so it is carried the whole way: the version block corrected, v7 (the event plane's command half) and v8 (the asset plane) written, a §5 section for the five routes, 425/422 in the status table, and a caveat that the asset plane is a working set rather than a stream. Protocol 7's absence is the Events workstream's debt rather than this one's, but it cannot be stepped over on the way to 8. ## The operator-facing half `UPGRADE_NOTES.md` gains the entry an operator reads when this ships: what changed, the one required action on a Linux host, and the thing that will not announce itself -- nothing here happens on a restart, so a patched client keeps serving the old pictures until somebody presses a button. `installer/INSTALL.md` gains libgdiplus as a prerequisite row and the `doctor` row that checks it. The index rows for SPAWN_ATLAS and CLILOCS described the workflows this protocol deleted; v8.md now has an index row of its own, and v7 is marked as the released protocol. ## The walk Wiped every asset row and every imported sprite, then walked it as a new operator: 1,095 portraits in 3.18 s, 67,496 names in 1.42 s, 313 item pictures in 1.38 s, the atlas over the bridge in ~2.0 s, an Update with no drift answered in 0.99 s. Bestiary portraits are the right animals by eye; the marketplace shows hued item art with cliloc names. It found two defects (§17.14) and one cutover hazard: module-uo's `edge` is behind its `main`, missing #35, so the walk measured 0 of 6,455 spawners carrying a UniqueId. 9b's row says to sync before merging or the cutover ships a regression. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| fc2d4872f6 |
docs(link): the panel, and the two deletions it could see and no test could (Phase 8)
v8.md section 12.2 records the admin surface as built: one page, Admin -> Client
Files, over the creature catalogue, item and land art, and the cliloc table --
one page because they come off one client install and change on one event. The
four decisions are written down, including the one that kept the phase to a
single repo: section 14's "activity view" is the last import's own summary
rather than a filtered feed, which would have needed a core activity filter, a
`ctx.activity.list` helper and a MODULE_API bump to re-read rows the panel can
keep itself.
The part worth reading twice is what the walk found. Two of the three defects
were DELETIONS, both predate phase 8, and neither was reachable by a test that
did not already suspect them:
* the body import diffing its manifest against every family's rows, so 313
item pictures were staged for deletion under a sentence that was wrong about
what had happened;
* an approved vanish unlinking the sprite and keeping the row, so the key came
back for review forever and the count never moved.
The rule they share is now stated: after phase 5, `shard_assets` is three
collections in one table, and every read of it that feeds a decision has to say
which one it means. Two of the three that did not were deletions.
CLILOCS.md and SPAWN_ATLAS.md now name the button that exists (Admin -> Client
Files) instead of the one they promised, and modules/uo/API.md records that a
`needsReview` reply carries each vanished key's picture.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| 047ecb6771 |
docs(link): the empty-catalog fix is plane-wide, and the stock-install walk that proves it
10.3 recorded the empty-`catalog` defect as a tree-family bug. It was inline in three places -- the body catalogue, statics and land, and the tree -- and all three now share one comparison. Records why BridgeLeases keeps the other shape, and names the two defects as one kind of thing: a value meaning "I am not asserting anything" has two spellings on a wire, and a stock install finds whichever one you did not think about. Adds the whole-plane check against a stock ServUO install: every family asked with a real catalog, an absent one and an empty one, plus the cross-family and stale-catalog refusals and both of stock ServUO's empty decoration files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| dcf7be4975 |
docs(link): what a 4 MB spawn file does to a 1 MiB wire (Phase 7)
10 said the shard would serve `tree/<label>` -> bytes. Measured against a stock 57.4 tree it cannot: Spawns/trammel.xml is 4.03 MB, the sidecar discards any inbound line over 1 MiB, and that file as one base64 row is 5.4 MiB. It would be dropped, time out, and be re-requested forever with no error in it anywhere -- and two files on a STOCK tree are in that state. 10.1 records the three carriages measured before anything was written, and why the winner is not the smallest one: whole-file gzip is 1.21 MB against chunked gzip's 1.26 MB and is bounded by nothing, so it works on every tree anyone would test and fails on the first one nobody did. The chunk is the guarantee; the compression is only the saving. 10.2: it is a `tree` family on assets.fetch, not 14's separate tree.* commands -- phase 5's registry already owns the single slot, the envelope, the ceiling and the mid-import guard, so reusing it left `link` with nothing to do for the third phase running. Its CONSENT is its own, though: Bridge.TreeEnabled, because declining to serve an EA-licensed client is not the same as declining to serve the spawn files an operator wrote, and the atlas would have been the casualty. 10.3 records the two defects and which harness found which. An empty `catalog` is not an absent one. And GZipStream writes nothing at all for zero bytes of input, which stock ServUO's two empty decoration files walk straight into -- an offline probe called that a success, because .NET's own decompressor reads an empty stream as empty data and the declared length and hash both agree with it. Only a live walk through a reader on another runtime disagreed. 10.4: one canonical read order, because the decoration index keeps the first item id it sees and the two readers agreed by coincidence rather than by construction. PARSER_VERSION 4 -> 5. 10.5 has the end-to-end numbers against a live shard. 17 gains the phase's three decisions, including the one that departed from the recommendation: boot never calls the shard, so an install on the bridge has no automatic refresh at all. SPAWN_ATLAS.md is rewritten around the two sources and stops requiring a shared filesystem. Protocol stays 8; EXTRACTOR_VERSION stays 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| b84a73d0e7 |
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 |
|||
| a4b63d87d2 |
docs(link): the hue belongs where the files are, and the cache poisons it (Phase 5)
§11.1 is new and carries what phase 5 measured: 49,152 addressable static ids (not the 81,884 `artidx.mul` declares -- `FileIndex` sizes its table from its length ARGUMENT), 39,189 with art, 4,244 land tiles, 9,963 + 12,140 empty index slots, and the whole set at 81 MB decoding in 34 s. That last number reopens the bulk question and the answer is still no: 108 MB of base64 through a 512 KB single-slot channel to store 43,433 pictures a shard displays a few hundred of. Two traps, both §4.5's failure mode -- a confident, plausible, wrong picture: - `Art.GetStatic` hands back the SAME cached Bitmap and `Hue.ApplyTo` repaints in place, so hueing edits the library's own copy: the plain key comes back hued from then on, and the next hue stacks. `Files.CacheData` off process-wide fixes it and also stops a game server retaining 74 MB of Bitmap. Copying instead does not solve the retention, and `new Bitmap(src)` throws on ARGB1555 anyway. - `PartialHue` (13,259 of 65,536 ids) decides whether a hue repaints every pixel or only the grey ones, from a file only the shard has. Item 597 is a wooden screen with painted flowers; one mode reddens the flowers, the other the whole screen, and both decode. Hence land takes no hue segment and `h0` is not a key. Plus the namespace trap that compiled: unqualified `TileData` binds to ServUO's own `Server.TileData`, because the enclosing namespace beats `using Ultima;`. §14 records what the wire gained -- the `static` and `land` families, `families` on `assets.sources`, and `assets.fetch` becoming shared plumbing whose family is DERIVED from the keys (§5 made the key the address; a request naming its family too would have two places to be wrong and one of them silent). Additive, so the protocol stays 8 and EXTRACTOR_VERSION stays 2. §15 records that `link` needed nothing in phases 4 or 5: it forwards verbatim in both directions. §17.10 is the four org-lead decisions. §12 and modules/uo/SCHEMA.md carry the website side: `uploads/items/`, per-row `catalog` staleness, and why a key with no art writes no row at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 6ef4b06c76 |
docs(link): two of the eight player bodies existed, and 233 nobody asked about (Phase 4)
Phase 4 built 4.3's UOP animation reader. What it found first changed what the phase
was worth, so the plan is corrected rather than merely annotated.
- New 4.9: what phase 4 measured. Of the EIGHT player bodies 4.8 assigned this
phase, two are in the client at all -- gargoyles 666/667, in AnimationFrame3.uop.
The six ghost bodies are in no package, and that is established rather than
unfound: the five packages hold 10,724 entries and the
build/animationlegacyframe/%06d/%02d.bin scheme claims every one, leaving no room
for another naming. Also the format as read (AMOU, a per-frame ARGB1555 palette,
direction as a slice of the frame table), the nine bodies whose frame count is not
a multiple of five, the validate-as-we-go bounds and the measurement that says
they refuse nothing real, and the live rig.
- 5.2 rewritten: the player-body set is the LIVING pair per race, six ids not
twelve. Ghost ids left it because no client has art for any of them. Still asked
of the shard, never hardcoded -- only the question changed. And with phase 4 in,
all six have art for the first time.
- 4.3 rewritten against what was measured, including why searching five UOP packages
for one body is NOT the never-sweep rule being broken: a legacy index is addressed
by position, a UOP entry by the hash of a name carrying the body id, which the
payload then declares again.
- 11 sizing: the catalogue is 1,022, not 787. The mix is recorded because "add every
body" sounds like it changes what a catalogue is, and it does not -- the legacy
787 was already 366 equipment bodies.
- 14: manifest and fetch rows carry `source` (legacy/uop). Additive, so protocol
stays 8; EXTRACTOR_VERSION 1 -> 2 is the change consumers actually see.
- 16 phase 4 marked DONE; 17.9 records the four org-lead decisions (fallback applies
to every body; ghost ids leave the set; own PNG encoder; NO_IMAGING stays flat).
- 4.8 and 8.1 keep their numbers as the record of what those phases measured, with a
pointer to where the answer landed.
Two consumer docs repeated the ghost claim as fact and are corrected:
website/SPAWN_ATLAS.md (787 -> 1,022, and "two thirds of the playable ghost and
gargoyle bodies have no art" -> about half the addressable body range) and
modules/uo/API.md (same sentence).
Code: servuo-plugins#31.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| 1a7481e9f4 |
docs(link): the catalogue is real, and UOFiddler's last job is gone (Phase 3)
Phase 3 is built and walked on a live shard. What the walk measured, and the
two places the design of record needed correcting:
§8.1, new: the catalogue is 787 exactly as §4.8 predicted, and the whole scan
of bodies 1-2047 takes 734 ms cold -- so the wall-clock paging §11 designed
never fires on this client. Every §4.8/§5.2 prediction held when the bytes were
rendered and LOOKED at: 320, 607, 608 and 666 come back absent rather than as
another creature's picture, and the direction split is 783 at index 1 against 4
at index 0 -- four player bodies, not six.
44 of the 787 hashes are shared by two or three bodies, which is the exact
signature of the wrong-picture bug, so it was chased rather than assumed. It is
the client's own Body.def aliasing (83 {1}, 84 {1}, 106 {12, 59}), and the check
that settles it is at the source: Translate(ref body, ref hue) rewrites `body`
only when bit 31 is set, unlike the one-argument overload -- and ResolveAnimation
calls that same two-argument overload, so validator and decoder resolve the
identical record.
§12.1, new: **§12 is right about the outcome and wrong about the mechanism.**
`shard_spawn_creatures` is emptied and refilled by every atlas refresh, and a
refresh runs on every boot -- so an imported filename written to that row is
destroyed by an ordinary re-parse of the ServUO tree, and the next Update finds
the client files unchanged and never restores it. Three tables outside that
blast radius, and the atlas import re-derives `art` on the way past.
§14: **§16 listed phase 3 as servuo-plugins + module-uo and that was wrong.**
web.rs routes every command explicitly, so `link` is in the phase. Corrected in
both places.
UOFIDDLER.md is DELETED, two phases earlier than §9.1 predicted -- creature art
was the only thing still on it. SPAWN_ATLAS.md §Artwork is rewritten around the
import, keeping the operator's own map as the thing that wins; the module's
SCHEMA.md gains the three tables and API.md the two admin routes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| bbd69a8e2e |
docs(link): clilocs come over the bridge now, and UOFiddler's first job is gone
Phase 2 of the Asset Bridge is built, so the documentation stops telling an operator to install a GUI tool. `v8.md` gains §9.1 and §9.2 — what the port cost, what it measured, and where the base table comes from now. The measurement worth keeping: **67,496 rows in 290 ms**, which is exactly what UOFiddler's own `Ultima.dll` produced from this same client through the converter this phase deletes. An independent implementation agreeing to the row is the strongest check available that a format decoder is correct, and it is not something a subtly-wrong one produces. §17 records the four shapes the org lead settled before any of it was written. Two departed from the recommendation: **the bridge always wins** (no source setting — there is no version of that question an operator benefits from answering) and **import is admin-triggered** (boot does not call the shard at all). `CLILOCS.md` is rewritten around that: where the table comes from, what arrives and in how many pieces, the refusals — including the two the file pipeline had no equivalent of (a client patched mid-import, and the base's exemption from the vanished-source rule, which exists so an upgraded install is not asked to approve a change the upgrade itself made). `UOFIDDLER.md` loses Part 1 entirely rather than having it rewritten. What is left is creature art, which phase 5 takes, after which the page goes away. `v3.md` §8.6 keeps its reasoning with a note saying what superseded it, because the argument for why the manual step existed is still the argument for why this was worth building. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 1a048ae1be |
docs(link): phase 1 built the transport, and re-measured the catalogue
Asset Bridge phase 1, docs half. Code: RunicGateway/servuo-plugins#28, RunicGateway/link#41. ## The correction, which is most of this **New §4.8.** The animation path has §4.5's shared-buffer defect too, and 357 of the 1,144 bodies §1.1 counted are **wrong pictures on a stock client** — ids with `length 0` that return whichever body was decoded before them. Proved by decoding body 320 after a dragon (a dragon), a wolf (a wolf) and a human (a human). So the catalogue is **787 bodies**, and the numbers that were derived from 1,144 move with it: §11's working set, its ~173,000-frame full set (now ~119,000), phase 3's scope. **§5.2's table was wrong in the direction that matters.** The elf ghosts were listed as decoding; their index entry has no record, and what came back was the elf female. Four of twelve player bodies have art, not six — which takes phase 4's UOP decoder from six ids to eight. §1.1 now says outright that every "decoded" count in it is an upper bound. It is not a table to size anything from any more. ## What phase 1 settled - **§3.3** — the two numbers: a 512 KiB batch budget under a 1 MiB inbound line cap, with the factor of two load-bearing rather than cautious. - **§3.2** — flow control is enforced **on the shard**, as a single slot answering `bridge.busy`, not serialised in the sidecar and not left to the website as a convention. Records what it costs: a status poll shares the slot. - **§3.4, new** — one paging envelope (`more`/`cursor`/`cut`) for all five families that will page, defined before the first one needs it. `cut` because "short page" has three meanings and only one of them means finished. - **§6** — hashing had to come off the request path entirely. The gate is unchanged; what changed is that "the normal case must cost nothing" now also means "and the abnormal case must not time out", because the first hash of 1.06 GB does not fit in 10 s. - **§14** — which commands exist now, and which phase brings the rest. - **§16, §17** — phase 1 done; decisions 6 and 7. ## Elsewhere - **`SHARD_PREREQS.md`** gains the libgdiplus requirement (§4.4) — Linux hosts only, with the archived-upstream caveat and the `NO_IMAGING` status the shard now reports on the source gate. - **`INTEGRATION.md`** advertised `X-UOLink-Version: 6`. It was already two versions stale before this change; now 8. - [x] AI-assisted — Claude Code (Opus 5) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 018f1af5ff |
docs(link): phase 0 ran, and the fault it found is not a crash
Records the Asset Bridge phase 0 spike (§16) against v8.md, and closes the
last open decision.
§4's choice to call ServUO's vendored `Ultima` STANDS: nothing faulted on a
path this protocol calls, and §9's cliloc reader reproduced UOFiddler's
123,490-entry table byte for byte in 218 ms from inside the shard.
But the spike was looking for the wrong kind of failure. `LoadStatic` and
`LoadLand` decode out of a buffer that is reused, only ever grown, and
filled by a `Read` whose return value is discarded — so a short, absent or
out-of-bounds record does not throw, it renders the PREVIOUS asset. On the
stock, unmodified client on this machine that is 22,102 ids whose index
entry reads `lookup 0, length 0`, all of which the library returns a picture
for. §1.1's "32,766 decoded" was counting these.
New §4.5 states the rule that answers it — validate before calling — with
the six checks phase 0 implemented, the eight deliberate defects they caught
(seven of which the library rendered silently, including a verdata lookup
past verdata.mul's own end, which `Verdata.Seek` bounds-checks nowhere), and
the number that makes the boundary defensible: zero false refusals across
49,151 statics and 16,384 land tiles on a clean client.
New §4.6: `FileIndex`'s UOP constructor ends `MulPath = uopPath`, so
`artLegacyMUL.uop` wins outright and `art.mul` is never opened on a current
client. Bounding an offset against the wrong file is not approximate, it is
meaningless — the spike's first run refused 34,299 good statics that way,
and every refusal read like a real finding.
New §4.7: `Ultima.Gumps.GetGump(2)`, called once from inside a running
shard, made the ServUO process disappear — no catch reached, no console
line, the probe's checkpoint file the only record. §4.1's rule is earned.
§17 now has nothing open:
* item 4 — the default audience — SETTLED: an asset inherits the audience
of the page that uses it.
* item 5 is new: validate-before-calling, chosen ahead of the spike over
a child-process extractor and over reversing §4, and confirmed by it.
The dangerous failure turns out to be a wrong picture, which no
containment strategy would have caught.
§16 marks phase 0 done and adds the half it deliberately left unbuilt to
phase 1: the animation path has no validator, and the patched client's wolf
decoded something else in silence to prove it.
Full measurements and the rig recipe live in servuo-plugins
`tools/scaffolding/README.md`; the code is RunicGateway/servuo-plugins#27.
- [x] AI-assisted — Claude Code (Opus 5)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
|
|||
| 041e1f4069 |
docs(link): name it the Asset Bridge, and make libgdiplus a stated requirement
The work has a name now — the Asset Bridge — for commits, PR titles, branches (feat/asset-bridge-p<n>) and conversation. Protocol number stays 8 and the file stays docs/link/v8.md. §4.4 closes the last real open question rather than deferring it to phase 0, and takes all three answers instead of choosing one. ServUO targets net48, so a Linux host runs it under Mono, and Mono's System.Drawing is a thin layer over libgdiplus — which §4.2 put in the decode path, not just the encode. So on Linux it is a hard prerequisite for art. Cliloc and atlas import are unaffected; neither touches pixels. Windows hosts need nothing at all. It is now written down as: a SHARD_PREREQS.md entry, a doctor check in the installer, and a named NO_IMAGING status when it is missing, in the same family as the cliloc reader's COMPRESSED — never a stack trace, never a 500. Install routes per distro are in the section, apt-get install libgdiplus being the normal one. One fact recorded because depending on something unmaintained should be a conscious act: github.com/mono/libgdiplus was ARCHIVED in March 2025 and is read-only. Distributions still package and patch it, so installing it today is supported and ordinary — but it is the strongest long-term argument for moving extraction off System.Drawing eventually, and phase 4's UOP reader is written without it so that door stays open. §17 restructured: three settled items kept because each changes numbers elsewhere, and one genuinely open question (the default audience) that does not block starting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| d39e4163d1 |
docs(link): a UOP decoder for the player bodies, and the spider that proves we need one
The six player-character bodies the vendored Animations cannot reach are the player character, and the scope says player models, so they get a decoder rather than a caveat. New §4.3, new phase 4, scoped as narrowly as possible: a reader for AnimationFrame*.uop used ONLY for bodies the legacy path cannot resolve. Everything the vendored code already decodes keeps going through it. Verified genuinely absent rather than mis-addressed, and the way that was established is now the most important warning in the document. Bodyconv.def maps gargoyle 666 to anim5 and BodyConverter.Convert faithfully returns fileType 5, where this 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, while fileTypes 3 and 4 return misaligned colour fragments. All of it rendered and looked at, which is the only reason it was caught: every one of those reads reports success. So the extractor takes Convert's answer and reports nothing when that yields 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, with no error raised anywhere and nothing downstream able to detect it. A "0 rows" outcome is correct behaviour; a confident wrong picture is the failure this protocol most needs to avoid. Phase 4 sits after the catalogue, not inside it: the catalogue is useful with 1,138 of 1,144 bodies, and the UOP reader is the only genuinely new format work here, so putting it on the critical path would hold up every website-side phase behind it. Its acceptance test is that a gargoyle looks like a gargoyle, checked by eye. References available and license-compatible: ClassicUO's animation loader (GPL-3) and UOFiddler 4.22 (Beerware). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 44039e83d5 |
docs(link): call ServUO's decoders — the crash is on a branch we never take
Decision: no decoders are reimplemented. Extraction goes through ServUO's own vendored Ultima, which Scripts.csproj already references, so the art half of this protocol is plumbing and the Mythic cliloc reader (§9) becomes the only decoder Protocol 8 writes rather than calls. What makes that safe rather than merely cheap is a distinction §1.1 did not draw. All three decoders build a FileIndex, but only Gumps passes hasExtra: true — and FileIndex.cs's own comment says that branch exists FOR gumpartlegacy.uop, the one UOP layout with an extra field. Art passes hasExtra: false and probed 49,150 statics plus 16,384 land tiles with zero faults; Animations touches no UOP at all and probed 1,144 bodies clean. The access violation is a bug on a branch exactly one decoder reaches, and that decoder was already out of scope. So "nothing calls Ultima.Gumps" is now a safety rule, and adding gump art later means fixing that path first. §4.2 records the three costs this accepts: six of twelve stock player bodies have no art (UOP-only, not reachable by calling the existing code differently), System.Drawing stays in the decode path, and we inherit whatever Ultima a shard vendors — EXTRACTOR_VERSION already covers the last one. Phase 0 changes shape with it. It was going to prove new decoders byte-identical; it now tries to BREAK the vendored ones on purpose, from inside a running ServUO against a deliberately patched client, because the probes behind §1.1 ran in PowerShell against a stock client and neither is the real environment. §17 is down to one real question: libgdiplus on Linux/Mono shards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| b62d0b6307 |
docs(link): player bodies face you, everything else does not
Player character bodies take direction index 0 (head-on); every other body takes index 1 (front three-quarter). Direction still does not appear in the key — both indices are extractor configuration. The split follows the legibility caveat rather than fighting it: a humanoid reads fine head-on and a character portrait should look at you, while a wolf seen head-on is a dark blob and the same wolf at index 1 is obviously a wolf. §5.2 is new: which bodies count as player characters is asked of the shard, via Race.AllRaces and each race's MaleBody/FemaleBody/MaleGhostBody/FemaleGhostBody, never hardcoded. Twelve ids on stock 57.4. Two things say 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. And the finding that matters most: SIX OF THOSE TWELVE do not decode at all with ServUO's vendored Animations — both human ghosts and every gargoyle body, because they live in AnimationFrame*.uop which that library never reads. The one part of the scope with the most attention on it is the part the vendored library serves worst, which is now the strongest single argument for §4's recommendation to own the decoders. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 0c3f0d65cc |
docs(link): one direction, not five — and it is index 0
Only the viewer-facing direction is wanted for players and monsters, so direction leaves the key entirely (§5.1) rather than being a segment nobody varies. Every depth count in §11 falls by exactly 5x. Which index that is was rendered, not looked up: all five directions for a human, a wolf and a dragon. Index 0 is head-on — face, chest and front legs — and index 4 is directly away, with a quadruped's tail toward the camera. The small-thumbnail version of the same test suggested the opposite, which is why the finding is in the doc rather than in someone's head. Measured consequence: body 400 drops 1,050 -> 210 frames, and a complete one-direction set for all 1,144 bodies is ~173,000 frames (~170 MB) rather than ~865,000. That moves a bulk-fill-everything switch from "never" to something phase 5 should leave room for. One caveat kept as an open question: index 0 is the least legible view for four-legged creatures — a head-on wolf is a dark blob, a side-on wolf is a wolf — so the extractor takes the index as configuration defaulting to 0, and §17 asks whether the catalogue should default to 1 or 2 instead. No client-derived image is committed; the render was inspected and discarded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |
|||
| 556141341f |
docs(link): Protocol 8 — client assets over the bridge
The shard host already has the UO client — ServUO cannot boot without one, and Config/DataPath.cfg resolves into Server.Core.DataDirectories at runtime. So the cliloc table, the creature and item art, and the spawn atlas's own source files can all reach the website over the bridge that already exists, and UOFiddler, the desktop conversion step and the website's shared-filesystem view of the ServUO tree all go away. Design of record for the work: the shard extracts, the sidecar forwards, the website decides — which is the only arrangement that keeps the sidecar a dumb forwarder while still resolving creature slug -> body id, something only code running inside ServUO can do. Measured against this machine's ServUO 57.4 tree and client rather than assumed: Art.GetStatic and GetLand decode ~66,000 ids with no faults, 1,144 bodies have a decodable first frame, and body 400 alone is 1,050 frames across its actions and directions — which is what makes the bulk set one thumbnail per body and everything deeper on demand. Two findings shape the build. Gumps.GetGump(2) does not fail, it corrupts the process (AccessViolationException, 0xC0000005) — uncatchable on .NET Framework 4.8 and a shard crash in-process — so §4 recommends owning bounds-checked decoders rather than calling ServUO's vendored Ultima, which also removes the System.Drawing/libgdiplus dependency and the UOP gap that leaves gargoyle bodies 666/667 empty. And the shard -> sidecar direction has no line cap today, which Protocol 8 must close before it starts sending large lines deliberately. UOFiddler is Beerware, so its Mythic cliloc decompressor can be ported into this GPL-3.0-or-later tree and the conversion step retired entirely. Nine phases, four decisions still open in §17. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4 |