docs(link): phase 0 ran, and the fault it found is not a crash #235
Reference in New Issue
Block a user
No description provided.
Delete Branch "docs/asset-bridge-p0"
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?
Records the Asset Bridge phase 0 spike (§16) against
v8.md, and closes the last open decision. Code half: RunicGateway/servuo-plugins#27.§4's decision 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, with no UOFiddler installed.
But the spike was looking for the wrong kind of failure.
The finding
LoadStaticandLoadLanddecode out of a buffer that is reused, only ever grown, and filled by aReadwhose return value is discarded. So a short, absent or out-of-bounds record does not throw — it renders the previous asset. On this machine's stock, unmodified client that is 22,102 ids whose index entry readslookup 0, length 0;FileIndex.Seekrejectslookup < 0andlength < 0, and zero is neither, so it treats an empty slot as a hit. §1.1's "32,766 decoded" was counting these.A bulk import that trusted the library would have written 22,102 duplicate images into the site under ids that have no art.
What changed here
New §4.5 — validate before calling. The rule, 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 —Verdata.Seekis bounds-checked nowhere at all), and the number that makes the boundary defensible: zero false refusals across 49,151 statics and 16,384 land tiles on a clean client. Also the two other ways an id with no art yields a picture:GetStaticthrows above the index ceiling, and with the defaultcheckmaxid: truereturns item 0's picture instead.New §4.6 — the UOP wins outright.
FileIndex's UOP constructor ends with a bareMulPath = uopPath, soartLegacyMUL.uopwins andart.mulis 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. Second consequence, for operators: a shard adding custom art toart.mulwhile the UOP is present gets nothing, silently.New §4.7 — the gump crash, reproduced in the host process. One
Ultima.Gumps.GetGump(2)and the ServUO process disappeared. Nocatchreached, no console line; the probe's checkpoint file was the only record. §4.1's rule is earned rather than argued.§17 has nothing open
§16
Phase 0 marked done. The half it deliberately left unbuilt moves into phase 1: the animation path has no validator, and the patched client's verdata-broken wolf decoded something else in silence to prove it needs one.
GetAnimationalso allocatesnew int[frameCount]straight from a file-supplied int.Full measurements and the rig recipe live in
servuo-plugins/tools/scaffolding/README.md.🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
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