docs(link): the Asset Bridge (Protocol 8) — client assets without UOFiddler #234
Reference in New Issue
Block a user
No description provided.
Delete Branch "docs/client-assets-v8"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Call it the Asset Bridge. Design of record for moving the cliloc table, the creature/item art and the spawn atlas's own source files off the operator's desktop and onto the bridge. Retires UOFiddler, the manual conversion step, the hand-written slug→filename map, and the website's shared-filesystem view of the ServUO tree.
No code yet — this is the doc to approve before any of it is built.
The premise
A ServUO shard cannot boot without a UO client.
Config/DataPath.cfgresolves intoServer.Core.DataDirectoriesat runtime, so the files the operator has been converting on their desktop are already on the shard host, in a directory the shard already knows, in the process the bridge already runs inside.Architecture
Shard extracts → sidecar forwards → website decides. It is the only arrangement that keeps the sidecar a dumb forwarder while still resolving creature slug → body id, which only code running inside ServUO can do (
BridgeWorld.cs:350already has the mechanism).Extraction calls ServUO's own vendored
Ultima—overlay/Scripts/Scripts.csproj:39already references it. Two decoders are written rather than called: the Mythic cliloc reader (§9) and a UOP animation reader scoped to the player bodies the legacy path cannot reach (§4.3).Measured, not assumed
Against this machine's ServUO 57.4 tree and client:
Art.GetStatic/GetLandAnimations.GetAnimation(0…2047)Gumps.GetGump(2)0xC0000005new StringList("enu", "Cliloc.enu")The crash, and why it does not block the decision
All three decoders build a
FileIndex, but onlyGumpspasseshasExtra: true— andFileIndex.cs's own comment says that branch exists forgumpartlegacy.uop, the one UOP layout carrying an extra field.Artpassesfalseand probed clean across ~66,000 ids;Animationstouches no UOP at all.So the access violation is a bug on a branch exactly one decoder reaches, and that decoder is already out of scope. "Nothing calls
Ultima.Gumps" is a safety rule (§4.1).The spider (§4.3) — the most dangerous finding here
Bodyconv.defmaps gargoyle 666 toanim5, andBodyConverter.Convertreturns 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 lives at that index inanim2.mul. FileTypes 3 and 4 return misaligned colour fragments. Rendered and confirmed by eye; every one of those reads reports success.The extractor takes
Convert's answer and reports nothing when that yields nothing. It must never sweep file types looking for a hit — that doesn't find missing art, it silently puts a spider on the gargoyle page, and nothing downstream can detect it.That is also how we know the six missing player bodies are genuinely UOP-only, which is why they get a decoder (phase 4) rather than a caveat.
Directions
One direction per body, not in the key: player bodies index 0 (head-on), everything else index 1 (front three-quarter — a head-on wolf is a dark blob). Which bodies are player characters is asked of
Race.AllRaces, never hardcoded —RaceDefinitions.cspasses the gargoyle's ghost bodies in the opposite order to the other races.Cuts every depth count 5×: body 400 goes 1,050 → 210 frames.
Requirement: libgdiplus on Linux hosts (§4.4)
ServUO targets
net48, so Linux runs it under Mono, and Mono'sSystem.Drawingis a thin layer over libgdiplus — which sits in the decode, not just the encode. Windows hosts need nothing. Handled three ways: aSHARD_PREREQS.mdentry, an installerdoctorcheck, and a namedNO_IMAGINGstatus rather than an error. Cliloc and atlas import are unaffected.Noted because depending on something unmaintained should be deliberate: mono/libgdiplus was archived in March 2025. Distros still package it, so
apt-get install libgdiplusis ordinary and supported — but it is the long-term argument for moving offSystem.Drawing, and phase 4's UOP reader is written without it so that door stays open.Other findings
The shard → sidecar direction has no line cap (
shard.rsusesread_lineunbounded), while sidecar → shard caps at 1 MiB. Protocol 8 closes that 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. Client files remain the operator's own, extracted on their own host — nothing committed, nothing redistributed.
Contents
Ten phases (§16). Phase 0 tries to break the vendored decoders on purpose, from inside a running ServUO against a deliberately patched client — the probes above ran in PowerShell against a stock client, and neither is the real environment.
§17: three items settled, one open (the default audience for asset serving), which does not block starting.
Bumps
PROTOCOL_VERSION7 → 8 inlink/andservuo-plugins/overlay.tomlin the same PR when the code lands.🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
docs(link): Protocol 8 — client assets over the bridgeto docs(link): the Asset Bridge (Protocol 8) — client assets without UOFiddler