All checks were successful
PR checks / checks (pull_request) Successful in 2m37s
PLAN.md §16 phase 17, built on the org lead's answers of 2026-10-22, recorded as D75-D79 (count of record 79). - D75: core marketing pages describe what a game module adds in core's words. "Game intelligence" became "What a game module adds"; each game's own list moved to src/data/games/ and renders only on the new /modules/uo/ and /modules/rust/. /modules/ is a card per game. - The page-side capability check is per game (assertGameCoverage), the item phase 13 moved here. - D76: the homepage keeps its game screenshot, labelled. screens.mjs entries may carry `game` (D67); Screenshot.astro and the app grid print the game's name and mark the figure data-game. - D77: /app/ describes the Rust screens (released the same day). - D78: /runicnpc/, the standalone page, is in the header and footer; the narrow header was re-measured so five links keep two rows. - D79: checkTerms.mjs --built reads every built page's visible text after the build; game routes and data-game elements allow their game's words. Proven against ten plants. Still the twelfth check. - Re-read fixes: /architecture/'s "two machines is also the maximum", Discord "guild" on /integrations/, and collection.mjs against what Steam sign-in and the Rust module keep (PLAY_DATA_SAFETY.md regenerated; the Play answers are unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
166 lines
5.0 KiB
Plaintext
166 lines
5.0 KiB
Plaintext
---
|
||
import Base from '../../layouts/Base.astro';
|
||
import PageHeader from '../../components/PageHeader.astro';
|
||
import GameFeatures from '../../components/GameFeatures.astro';
|
||
import Screenshot from '../../components/Screenshot.astro';
|
||
|
||
import platform from '../../data/platform.json';
|
||
import { features, screens } from '../../data/games/uo.mjs';
|
||
import { assertGameCoverage } from '../../data/games/index.mjs';
|
||
|
||
/**
|
||
* `/modules/uo/` — PLAN.md §16, phase 17.
|
||
*
|
||
* The Ultima Online module's own page, and so one of the few marketing pages that may use
|
||
* that game's words (D63; `GAME_ROUTES` in `checkTerms.mjs`). The feature list is
|
||
* `games/uo.mjs`, held to the module's declared capabilities by the call below, and the
|
||
* screenshots are the ones `/features/` showed until this phase — every one tagged with its
|
||
* game in `screens.mjs` (D67).
|
||
*
|
||
* Versions are read from `platform.json`, which `checkFacts.mjs` holds to the module's
|
||
* release, the installer's bundle and both halves of the bridge.
|
||
*/
|
||
assertGameCoverage('uo');
|
||
|
||
const uo = platform.games.uo;
|
||
const gitea = `${platform.gitea.base}/${platform.gitea.org}`;
|
||
const docs = `${gitea}/docs/src/branch/main`;
|
||
|
||
const pieces = [
|
||
{
|
||
role: 'The module',
|
||
name: uo.module.repo,
|
||
version: platform.releases['Module-uo'],
|
||
what: 'Every Ultima Online screen on the site, installed from Admin → Modules.',
|
||
},
|
||
{
|
||
role: 'The sidecar',
|
||
name: 'uo-link',
|
||
version: uo.bundle.sidecar,
|
||
what: 'Beside the shard, and the only part of the bridge that listens on a network.',
|
||
},
|
||
{
|
||
role: 'The bridge plugin',
|
||
name: uo.pluginRepo,
|
||
version: uo.bundle.overlay,
|
||
what: `An overlay compiled by ServUO at boot, on ServUO ${uo.bundle.servuoMin} or newer.`,
|
||
},
|
||
];
|
||
|
||
const title = 'The Ultima Online module';
|
||
const description =
|
||
'What the Ultima Online module adds to a Runic Gateway site: live shard status, the ' +
|
||
'player-vendor marketplace, the spawn atlas, guilds, houses and more, from a running ServUO shard.';
|
||
---
|
||
|
||
<Base title={title} description={description}>
|
||
<PageHeader eyebrow="Game module" title="Ultima Online">
|
||
<p>
|
||
The <code>uo</code> module turns a Runic Gateway site into an Ultima Online shard’s
|
||
site, fed live from a running ServUO shard. It was the first module, and the one the
|
||
seam was cut around: this support was extracted out of the site, and every URL it had
|
||
before the move it still has.
|
||
</p>
|
||
<p>
|
||
The shard side is installed with the installer as one bundle —
|
||
{' '}<strong>{uo.bundle.tag}</strong>, speaking protocol {uo.protocol} — and the site
|
||
side is a paste in the admin panel.
|
||
</p>
|
||
</PageHeader>
|
||
|
||
<section class="page section pieces" aria-labelledby="pieces-heading">
|
||
<h2 id="pieces-heading">The pieces</h2>
|
||
<ul class="pieces__list">
|
||
{
|
||
pieces.map((piece) => (
|
||
<li class="panel">
|
||
<p class="pieces__role">{piece.role}</p>
|
||
<p class="pieces__name">
|
||
<code>{piece.name}</code>
|
||
<span class="chip chip--version">{piece.version}</span>
|
||
</p>
|
||
<p class="pieces__what">{piece.what}</p>
|
||
</li>
|
||
))
|
||
}
|
||
</ul>
|
||
</section>
|
||
|
||
<GameFeatures
|
||
title="What it adds"
|
||
lede="Everything here is published from the live shard, and every public surface passes the visibility settings first — each one is the operator's to switch on."
|
||
items={features}
|
||
/>
|
||
|
||
<section class="page section shots" aria-label="Screenshots">
|
||
{screens.map((id) => <Screenshot id={id} />)}
|
||
</section>
|
||
|
||
<section class="page section next">
|
||
<h2>Setting it up</h2>
|
||
<p class="prose">
|
||
The documentation’s Ultima Online group starts from connecting a ServUO shard and ends
|
||
at the reference for every file the bridge reads.
|
||
</p>
|
||
<div class="next__links">
|
||
<a class="btn btn--primary" href="/docs/ultima-online/">The Ultima Online docs</a>
|
||
<a class="btn btn--ghost" href={`${docs}/modules/uo/README.md`} rel="noopener noreferrer">
|
||
The module in depth
|
||
</a>
|
||
<a class="btn btn--ghost" href="/modules/">All modules</a>
|
||
</div>
|
||
</section>
|
||
</Base>
|
||
|
||
<style>
|
||
.pieces h2,
|
||
.next h2 {
|
||
margin: 0 0 0.75rem;
|
||
font-size: clamp(1.5rem, 3vw, 1.95rem);
|
||
}
|
||
|
||
.pieces__list {
|
||
display: grid;
|
||
gap: 1rem;
|
||
margin: 1.25rem 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
|
||
}
|
||
|
||
.pieces__role {
|
||
margin: 0;
|
||
color: var(--dim);
|
||
font-size: 0.78rem;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.pieces__name {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
margin: 0.5rem 0 0;
|
||
color: var(--gold);
|
||
}
|
||
|
||
.pieces__what {
|
||
margin: 0.6rem 0 0;
|
||
color: var(--muted);
|
||
font-size: 0.94rem;
|
||
}
|
||
|
||
.next p {
|
||
margin: 0;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.next__links {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.7rem;
|
||
margin-top: 1.5rem;
|
||
}
|
||
</style>
|