--- import Base from '../layouts/Base.astro'; import PageHeader from '../components/PageHeader.astro'; import NotBuilt from '../components/NotBuilt.astro'; import platform from '../data/platform.json'; import { capabilityGroup } from '../data/capabilities.mjs'; /** * `/modules/` — PLAN.md §13 phase 4. * * §10 gives this page four jobs: what a module is, `module-uo` as the worked example, * writing your own, and the Integration Kit with its draft badge (D8). They are in that * order because they are increasing commitment — a reader deciding whether to install one, * a reader wondering what they get, a reader considering building one. * * --------------------------------------------------------------------------------------- * THE WORKED EXAMPLE READS ITS OWN CAPABILITIES * --------------------------------------------------------------------------------------- * The `module-uo` section lists what the module publishes, and it takes that list from * `capabilities.mjs` rather than retyping it — the same list the homepage names and * `/features/` expands, which is already checked against the module's own manifest through * `platform.json` (§12). A third hand-maintained copy on this page is exactly the failure * that machinery exists to prevent, and this is the page where it would be least visible. * * --------------------------------------------------------------------------------------- * THE DRAFT CHIP IS A DECISION, NOT A DISCLAIMER * --------------------------------------------------------------------------------------- * D8 marks the Integration Kit draft until a second module is successfully built against * it by somebody outside this project, and requires that status to carry its removal * condition. Both are here: the chip, and the sentence that says what takes it down. The * same absence appears in `notBuilt.mjs`, so a reader who scrolls past the chip meets it * again in the list of things that do not exist. */ const title = 'Modules'; const description = 'What a module is, what the Ultima Online module publishes, and what it takes to write ' + 'one for another game.'; const gitea = `${platform.gitea.base}/${platform.gitea.org}`; const docs = `${gitea}/docs/src/branch/main`; const gameIntelligence = capabilityGroup('game-intelligence'); /** The three ways a module reaches a running deployment. None of them is a build. */ const installPaths = [ { name: 'From the admin panel', body: 'Paste the URL of a release manifest into Admin → Modules and press restart when it ' + 'asks. The site downloads the artifact, verifies the checksum the manifest declares, ' + 'inspects the whole archive before writing a single file, and unpacks it.', fits: 'The click path, for a host you have no shell on.', }, { name: 'From your environment', body: 'Name the module and its version in one environment variable and the container ' + 'resolves it at every start. Already at that version means no network call at all, so ' + 'a restart with the internet down comes up unchanged.', fits: 'A compose-managed host, where the running set should be a line you version-control.', }, { name: 'By hand', body: 'Unpack the tarball into the modules directory and restart. The bundle is already ' + 'assembled — the client half is prebuilt and its one runtime dependency ships inside.', fits: 'Development, and any host where the other two do not fit.', }, ]; ---

A module is the entire game-specific half of a deployment, packaged: its routes, its screens, its database tables, its navigation rows and its slice of the API documentation. The core site holds accounts, Teams, the wiki, posts, moderation and the admin panel, and knows nothing about any game at all.

That division is not an aspiration bolted on afterwards. The Ultima Online support was extracted out of the site into a module, and every URL it had before the move it still has — which is the only version of this claim worth making.

What you get

What installing one actually does

Installing

Three ways in, and none of them is a build

Which one you use is a question about your host, not about the module. All three end the same way: a restart, and the module's screens appear in the navigation.

    { installPaths.map((path) => (
  1. {path.name}

    {path.body}

    {path.fits}

  2. )) }

The worked example

module-uo

{platform.releases['Module-uo']}

The Ultima Online module, and the reference every module that follows is measured against. It is what turns a general-purpose community site into something that knows what a shard is — and it is the proof that the seam described on the architecture page is real, because the code on the far side of it was moved there rather than designed there.

What it publishes

    {gameIntelligence.items.map((item) =>
  • {item.label}
  • )}

The same list features expands, read from one file that is checked against the module's own manifest on every build.

It connects to a real server

The module talks to the sidecar beside your game server, not to the game. You deploy that side with the installer and paste four values into the admin panel; nothing here requires the game to exist, and with no server configured the site renders normally and shows it offline.

It owns its own tables

Its schema is applied by the site on every boot and its data is its own. The module declares which versions of the core interface it speaks — the site runs {' '}{platform.moduleApi} — and refuses to load against one it does not.

It is a separate release

Versioned, tagged and published on its own cadence, independently of the site. Upgrading one does not mean upgrading the other, as long as the declared interface range still holds.

Writing your own

The Integration Kit

Draft

A four-chapter book on putting a different game on this platform — the module, the sidecar beside your game server, the plugin inside it — plus a template module that continuous integration builds against a pinned version of the core site, so the instructions cannot quietly stop working.

Why it says draft

Because nobody outside this project has yet followed it to a working module, and that is the only test of a set of instructions that counts. The badge comes off when somebody does — that is the stated condition, not a mood, and it is written down so a future reader knows when to take it down.

Everything it teaches is real and in use. What is untested is whether it is sufficient: whether someone with no access to this project's context can get from an empty repository to a running module using it alone.