Why Pier exists¶
Pier started as levilamina-rust-loader, built to answer one question: can a LeviLamina mod be written in Rust?
It could. And answering it surfaced a harder problem, which is what Pier is for.
What the loader got wrong¶
The loader grew its interface by accretion, one function at a time as each was needed. That works until the shape underneath matters, and then four things went wrong that could not be patched out.
The layout forked per build target¶
Conditional blocks inside the function table meant its tail sat at a different offset on a client build than on a server one.
A marker in the high bits of the version number was added to compensate. It could not protect a mod nobody had rebuilt, so a mod compiled before the fork kept loading and every slot past the divergence landed on the wrong function pointer. Both sides compiled cleanly; the symptom was a call doing something unrelated.
One misalignment reached seven slots. Nothing reported it, because nothing could.
The center called out to every capability¶
The dispatch table named each package's functions directly. Every one of those names is a link edge from the center out to a capability, so removing a capability from a build broke the link.
"Optional" was therefore a word in the documentation and not a property of the code. The packages documented as optional could not actually be dropped.
Missing values became plausible ones¶
A field that could not be read came back as a zero, and a zero is a legitimate answer for most of the things being read.
The clearest case: a land protection mod could not read the dimension out of an event
payload, wrote unwrap_or(0), and every such event was treated as happening in the
overworld. Protection refused inside the overworld and allowed everything everywhere else.
Nothing was logged. The behaviour looked deliberate for as long as nobody tested a custom
dimension.
Event names matched on substrings¶
An event id was resolved with a substring search. The day upstream added an event sharing a stem with a synthetic one, every subscriber to the real event would be silently redirected to a synthetic event with a different payload shape, with no route back.
Nobody hit that one. It was waiting.
What that adds up to¶
Each of those is fixable in isolation, and several were patched. Together they say something a patch cannot fix: the interface was never designed, only grown, and the patches were defending a shape that should not have existed.
The loader was a test. It did what a test is for, which is to tell you what to build properly. It is not maintained.
What Pier does differently¶
Pier inverts the order. The header is written first and the implementation second, and each of the four problems above becomes a structural property rather than a rule to remember:
| The problem | What replaces it |
|---|---|
| Layout forked per target | One layout on every target; an absent capability is a NULL slot |
| The center called out to capabilities | Capabilities register inward through an SPI; dropping one changes no host code |
| Missing became plausible | Nothing answers with a value it could not determine; cannot-determine and no stay apart |
| Substring matching | An exact id or a namespaced suffix, one matcher, no fallthrough |
Each of these has a script in tools/checks/ that runs on every push. The scripts came
after a rule that was written down and believed was found broken in four packages; each
script states what it covers and what it cannot see.
How it is designed goes through the mechanisms.