Files
docs/link/SHARD_PREREQS.md
wtclaude 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
2026-09-10 08:32:58 -05:00

7.1 KiB

Shard prerequisites

Repairs the target shard (<servuo>, ServUO 57.4) required before the bridge could load. These are deletions and edits of existing files, so they cannot be expressed as an overlay copy. They are recorded here, and where practical as diffs under patches/.

Applied 2026-07-10. Backups on the Desktop: servuo_saves_backup_2026-07-10_032608, servuo_bin_backup_2026-07-10_032608, servuo_removed_files_2026-07-10.


The symptom

Scripts.dll had not been rebuilt since 2026-05-30 17:01. Every script change after that — including all of Scripts/Custom/Named/, MyStats.cs, and SearchAdd.cs — had never executed.

ScriptCompiler.Compile() (Server/ScriptCompiler.cs:38-58) shells out to dotnet build, prints the output, ignores the exit code, then Assembly.LoadFrom("Scripts.dll") and returns true. A failing script build is invisible: the stale DLL simply reloads. The retry loop at Main.cs:525 never trips.

Four independent breakages, all introduced between 17:14 and 21:55 on 2026-05-30.


1. Stray Server/Gumps/Gumps.cs

A byte-identical copy of Scripts/Services/Pet Training/Gumps.cs (75,468 bytes), sitting in the Server project. It declares namespace Server.Mobiles and extends BaseGump, referencing BaseCreature, PlayerMobile, TrainingPoint — all defined in Scripts. Server cannot reference Scripts, so Server.csproj failed with 35 errors.

Action: deleted. The canonical copy under Scripts/Services/Pet Training/ was edited 10 minutes later and is the one that matters.

2. Eleven duplicate creature classes

Scripts/Custom/{Named,Legendary}/ redefined classes already present in Scripts/Mobiles/Normal/, producing CS0111 / CS0579.

NamedEowmu, SkeletalCat, Windrunner. The stock files each define two types: the mount and an ICreatureStatuette item (EowmuStatue, …) that Scripts/Services/UltimaStore/UltimaStore.cs references. Deleting the stock files outright would have re-broken the build.

Action: removed only the duplicate mount class from each stock file; kept the statues.

LegendaryFireSteed, Kirin, Nightmare, OsseinRam, Phoenix, PolarBear, ShadowWyrm, TsukiWolf. Clean 1:1 pairs. All custom versions sit in namespace Server.Mobiles, so the serialized type name is unchanged, and each Deserialize guards on version and migrates from 0 (ShadowWyrm: if (version >= 1); FireSteed: if (version < 1) skill-cap migration; Kirin: if (version == 0) AI fixup).

Action: deleted the eight stock files. Custom wins.

3. PolarBear — a base-class change, not a version bump

Custom PolarBear : BaseMount; stock PolarBear : BaseCreature. The saved world contained a bear serialized through the BaseCreature chain, so loading it as a BaseMount misaligned the stream. World load aborted at Server.Mobiles.PolarBear serial 0x00000412 with Delete the object? (y/n).

Changing a saved type's base class is not version-migratable. The custom class also carried [TypeAlias("Server.Mobiles.Polarbear")], which would have hijacked the same records.

Action: restored stock PolarBear : BaseCreature; renamed the custom mount to LegendaryPolarBear and dropped the TypeAlias. Stock scripts referencing typeof(PolarBear) (TalismanSlayer, SpeedInfo, RoyalZooDonationBox, SummonCreature, PetTrainingHelper) continue to resolve to the BaseCreature.

Note: Scripts/Custom/Legendary/PolarBear.cs was renamed to LegendaryPolarBear.cs.

4. AnimalLore.cs referenced a package that does not exist

Scripts/Skills/AnimalLore.cs had using ShrinkSystem; and two IShrinkItem branches. No ShrinkSystem namespace exists anywhere in the repo, and IShrinkItem appears nowhere in the stale Scripts.dllthe code had never compiled or run. (Scripts/Misc/ShrinkTable.cs is unrelated stock: namespace Server, class ShrinkTable.)

Action: removed the using and collapsed the shrink branches back to the BaseCreature path. This restores exactly the behavior the shard was already running.


Verification

After the repairs, dotnet build Scripts/Scripts.csproj -c Release -p:Platform=x64 succeeded with 0 warnings, 0 errors. Rebuilding ServUO.exe and Ultima.dll from current source produced byte-identical binaries (same SHA-256), confirming the core was never stale in content — only Scripts.dll was.

With Phase 0 applied, a plain boot shows:

Core: Compiling scripts...
Build succeeded.
Core: Verified 6023 item and 1385 mobile types
World: Loading...
...done (206208 items, 42771 mobiles, 0 customs)

Unrelated, still open

DllNotFoundException: zlibwapi64 crashed this shard once (Crash 6-5-2026-22-38-3.log) while sending a packed gump. zlibwapi64.dll is present in the repo root, so this is a working-directory / native-load-path problem. It will bite the bridge if the bridge ever triggers a gump send. Resolve before load testing.


Host prerequisite: libgdiplus on Linux (Protocol 8)

Everything above is a repair to one shard's scripts. This one is different in kind: it is a requirement on the host, it applies to every shard, and only to Linux ones.

The Asset Bridge (v8.md §4.4) has the shard read art out of the operator's own UO client files. ServUO targets net48, so on Linux it runs under Mono, and Mono's System.Drawing is a thin layer over libgdiplus — which sits in the decode path and not merely the encode: Ultima.Frame writes ARGB1555 through a LockBits pointer. Without that library a Linux shard cannot read a sprite at all.

Windows shard hosts need nothing. System.Drawing ships with .NET Framework.

Host Get it with
Debian / Ubuntu sudo apt-get install libgdiplus — in Debian since bullseye (6.0.4) and bookworm/trixie (6.1), and in Ubuntu universe
Fedora / RHEL sudo dnf install libgdiplus (EPEL or the Mono repository)
Docker RUN apt-get update && apt-get install -y libgdiplus in the shard image
Alpine, or a distro with no package Build from source. This is the awkward case, and it is worth avoiding by choosing a Debian-based image

Upstream is https://github.com/mono/libgdiplus. That repository was archived in March 2025 and is read-only; distributions still package and patch it, so installing it is a normal supported thing to do today, but nobody is maintaining it upstream. It is the strongest long-term argument for eventually moving extraction off System.Drawing.

Its absence is not an error and never a crash. The shard reports a named status on the source gate — the first call any import makes — so an operator meets this while setting the shard up rather than as an empty bestiary weeks later:

imaging: { ok: false, code: "NO_IMAGING",
           reason: "This shard host cannot render images — Mono's System.Drawing needs
                    libgdiplus. Install it (apt-get install libgdiplus) and re-run the
                    import. Cliloc and atlas import are unaffected." }

Clilocs and the ServUO tree files are genuinely unaffected: neither touches a pixel. The installer's doctor checks for this alongside its other host checks.