Compare commits
2 Commits
73f664c38e
...
c3c347d237
| Author | SHA1 | Date | |
|---|---|---|---|
| c3c347d237 | |||
| 4e1ce2316a |
BIN
public/screens/admin-client-files.webp
Normal file
BIN
public/screens/admin-client-files.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 67 KiB |
@@ -14,7 +14,7 @@
|
||||
* UOLINK_BASE http://127.0.0.1:8080 sidecar REST, written to Admin → Shard
|
||||
* UOLINK_WS ws://127.0.0.1:8080/ws sidecar WebSocket
|
||||
* UOLINK_TOKEN (unset) sidecar auth token; skipped when absent
|
||||
* UOLINK_PROTOCOL 4 wire protocol to pin — see the note below
|
||||
* UOLINK_PROTOCOL (platform.json) wire protocol to pin — see the note below
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* WHY THE SEED DRIVES THE API AND NEVER THE DATABASE
|
||||
@@ -45,6 +45,8 @@
|
||||
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import platform from '../src/data/platform.json' with { type: 'json' };
|
||||
|
||||
const BASE = (process.env.RG_BASE || 'http://localhost:3000').replace(/\/+$/, '');
|
||||
const API = `${BASE}/api/v1`;
|
||||
const ADMIN_USER = process.env.RG_ADMIN_USER || 'demoadmin';
|
||||
@@ -53,16 +55,21 @@ const DEMO_PASS = process.env.RG_DEMO_PASS || 'DemoReview!2026';
|
||||
const UOLINK_BASE = process.env.UOLINK_BASE || 'http://127.0.0.1:8080';
|
||||
const UOLINK_WS = process.env.UOLINK_WS || 'ws://127.0.0.1:8080/ws';
|
||||
const UOLINK_TOKEN = process.env.UOLINK_TOKEN || '';
|
||||
// The pinned wire protocol has to be STATED, not left to the module's default.
|
||||
// The pinned wire protocol, read from `platform.json` rather than written down here.
|
||||
//
|
||||
// `module-uo`'s schema fragment still carries `protocol INT NOT NULL DEFAULT 3`, from the
|
||||
// protocol-3 cutover; the sidecar on `link` `main` speaks 4. The module handles protocol 4's
|
||||
// frames — `guild.roster` and `guild.leave` ingest landed with the Teams cutover — but a
|
||||
// FRESH install pins 3, and the sidecar answers a 3 with `409 protocol version mismatch` on
|
||||
// every REST call. So a new deployment reads nothing from its shard until somebody edits the
|
||||
// number in Admin → Shard. Raised with the org lead rather than patched from here: the fix
|
||||
// belongs in `module-uo`, not in this repo's screenshot rig (PLAN.md §13 phase 9).
|
||||
const UOLINK_PROTOCOL = Number(process.env.UOLINK_PROTOCOL || 4);
|
||||
// It was a literal `4` until the Asset Bridge cutover, with a note explaining that
|
||||
// `module-uo` pinned 3 on a fresh install while the sidecar spoke 4, so a new deployment
|
||||
// read nothing from its shard until somebody edited the number in Admin → Shard. That debt
|
||||
// has since been paid: the module's schema fragment defaults the column to the protocol its
|
||||
// build speaks and carries a one-shot migration per bump, so both a fresh install and an
|
||||
// upgraded one land on the right number by themselves.
|
||||
//
|
||||
// What remains is the rig's own reason to state it: this seed points a demo deployment at a
|
||||
// sidecar, and if it pins the wrong number every REST call comes back `409`. Reading it from
|
||||
// `platform.json` means the number is the one `checkFacts.mjs` verified against `link`'s
|
||||
// `main` — so the rig cannot quietly drift two protocols behind the platform again, which is
|
||||
// exactly what the literal did.
|
||||
const UOLINK_PROTOCOL = Number(process.env.UOLINK_PROTOCOL || platform.protocol);
|
||||
|
||||
const DRY = process.argv.includes('--dry-run');
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export const docsSidebar = [
|
||||
{ label: 'Message templates', slug: 'docs/administration/message-templates' },
|
||||
{ label: 'Managing modules', slug: 'docs/administration/managing-modules' },
|
||||
{ label: 'The shard connection', slug: 'docs/administration/the-shard-connection' },
|
||||
{ label: 'Client files', slug: 'docs/administration/client-files' },
|
||||
{ label: 'Maintenance and upgrades', slug: 'docs/administration/maintenance-and-upgrades' },
|
||||
{ label: 'Troubleshooting', slug: 'docs/administration/troubleshooting' },
|
||||
],
|
||||
@@ -122,6 +123,7 @@ export const plannedSidebar = {
|
||||
'Message templates',
|
||||
'Managing modules',
|
||||
'The shard connection',
|
||||
'Client files',
|
||||
'Maintenance and upgrades',
|
||||
'Troubleshooting',
|
||||
],
|
||||
|
||||
103
src/content/docs/docs/administration/client-files.mdx
Normal file
103
src/content/docs/docs/administration/client-files.mdx
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Client files
|
||||
description: Creature portraits, item pictures and the game's own name table — where they come from, the one button that imports them, and why nothing here happens on a restart.
|
||||
---
|
||||
|
||||
import Screenshot from '../../../../components/Screenshot.astro';
|
||||
import { Aside } from '@astrojs/starlight/components';
|
||||
|
||||
Most of what a game shows you is not text. Ultima Online keeps its creature artwork, its
|
||||
item graphics and even its item *names* inside the client files, and a site that cannot read
|
||||
them shows a bestiary of words and a marketplace of numbers.
|
||||
|
||||
With the `uo` module installed, **Client files** appears in the admin sidebar at
|
||||
`/admin/uo/files`. It is where those three things arrive.
|
||||
|
||||
<Screenshot id="admin-client-files" />
|
||||
|
||||
## Where they come from
|
||||
|
||||
A ServUO shard cannot boot without a UO client — it resolves one at startup to read the
|
||||
world's own data. So the files were already on the shard host, and the shard reads and
|
||||
decodes them there, handing the results over the bridge like everything else.
|
||||
|
||||
**Nothing is converted on a desktop and nothing is uploaded.** Earlier versions of this
|
||||
platform asked an operator to install a third-party tool, build a converter against it and
|
||||
copy the output onto the web host. That path is gone.
|
||||
|
||||
## Three things, one page
|
||||
|
||||
| Section | Fills | How it arrives |
|
||||
|---|---|---|
|
||||
| **Creature portraits** | The bestiary and the spawn atlas | One picture per creature body, imported as a **set** |
|
||||
| **Item and land pictures** | Marketplace listings and character sheets | **One at a time**, shortly after a page asks for one |
|
||||
| **Item and title names (clilocs)** | Anywhere an item is named | The whole table at once — tens of thousands of names |
|
||||
|
||||
They are one page because they are one job: they come out of one client install, and they
|
||||
all change at the same moment — when you patch it.
|
||||
|
||||
<Aside type="caution" title="Nothing here happens on a restart">
|
||||
Boot deliberately never asks the shard for client files. A client patch is an event **you**
|
||||
know about and the website does not, and a site that re-read hundreds of megabytes on every
|
||||
restart to discover nothing had changed would pay for the rare case forever.
|
||||
|
||||
So after you patch your client, the site keeps serving the old pictures and the old names
|
||||
until somebody presses a button on this page. That is the whole reason the page exists.
|
||||
</Aside>
|
||||
|
||||
## Update, or re-import everything
|
||||
|
||||
Every section offers the same pair, and the difference is worth knowing:
|
||||
|
||||
- **Update** asks the shard what changed first and transfers only that. When nothing has, it
|
||||
costs one small round trip and answers *"unchanged"*.
|
||||
- **Re-import everything** fetches the lot. It is for the case the first cannot see — you
|
||||
restored a backup, or lost the uploads volume, and the database still remembers pictures
|
||||
that are no longer on disk.
|
||||
|
||||
Item and land pictures work differently, because there are tens of thousands of item
|
||||
graphics times every dye colour and importing them as a set would be absurd. They arrive
|
||||
lazily instead. The two buttons there — *Fetch waiting pictures* and *Refresh the ones I
|
||||
have* — exist for the two moments waiting is the wrong answer: you have just linked a shard,
|
||||
or you have just patched a client.
|
||||
|
||||
## When the page says something is wrong
|
||||
|
||||
Every one of these is a reported state with a reason, not an error. The site keeps serving
|
||||
whatever is already imported in all of them.
|
||||
|
||||
| What you see | What it means |
|
||||
|---|---|
|
||||
| **The shard is busy with another client-file request** | Not a fault. The shard serves one of these at a time, and an import — or the item-picture pass refilling itself — is holding it. It frees itself. |
|
||||
| **The shard is not answering for client files** | The ordinary bridge problem: see [The shard connection](/docs/administration/the-shard-connection/). |
|
||||
| **…set `AssetsEnabled` on the shard** | The asset plane is switched off in [`Bridge.cfg`](/docs/reference/bridge-cfg/). It is a separate switch on purpose — turning it on is consenting to the website reading this host's client files. |
|
||||
| **The shard host cannot render images** | A Linux host with no `libgdiplus`. Names are unaffected, because they have no pixels in them. |
|
||||
| **Waiting for you: *n* pictures … no longer offered** | The shard stopped offering artwork this site holds. A deletion is never silent here; it waits for you to approve or dismiss it. |
|
||||
|
||||
<Aside type="note" title="Linux shard hosts need one package">
|
||||
ServUO runs under Mono on Linux, and the library it decodes sprites with is a thin layer
|
||||
over **`libgdiplus`** — in the *decode* path, not merely the encode. Without it the shard
|
||||
cannot read a single sprite.
|
||||
|
||||
`sudo apt-get install libgdiplus`, or `dnf install libgdiplus`. `runicgateway doctor` checks
|
||||
for it, and Windows shard hosts need nothing. See
|
||||
[Requirements](/docs/getting-started/requirements/).
|
||||
</Aside>
|
||||
|
||||
## What it will not do
|
||||
|
||||
- **It never writes to the game.** Everything on this plane is a read.
|
||||
- **It never overwrites your own artwork.** A portrait you drew and named yourself always
|
||||
wins over an imported one.
|
||||
- **Creatures with no artwork stay as text.** That is normal rather than a failure — a stock
|
||||
client has no animation for most ghost and gargoyle bodies, and the shard reports nothing
|
||||
rather than guessing. A wrong picture is worse than no picture.
|
||||
|
||||
## Canonical documents
|
||||
|
||||
[`link/v8.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v8.md)
|
||||
is the asset plane's design of record;
|
||||
[`link/SHARD_PREREQS.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/SHARD_PREREQS.md)
|
||||
covers what a shard host needs first, and
|
||||
[`website/UPGRADE_NOTES.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/UPGRADE_NOTES.md)
|
||||
is what to do on a site that was running before this existed.
|
||||
@@ -116,6 +116,13 @@ when ServUO needs restarting — it never restarts your shard itself. Because it
|
||||
**bundle**, the sidecar and the plugin move together and cannot end up disagreeing about the
|
||||
protocol.
|
||||
|
||||
<Aside type="caution" title="After you patch the UO client, press one more button">
|
||||
Creature portraits, item pictures and the name table are read from that client, and the
|
||||
site deliberately never re-reads them on its own — a restart does not, and neither does
|
||||
`update`. They keep serving the old artwork until somebody presses *Update* on
|
||||
**Admin → Client files**. It is one round trip when nothing has changed.
|
||||
</Aside>
|
||||
|
||||
<Aside type="note" title="Update the two sides in either order, but verify after each">
|
||||
They are independent deployments joined by a version-checked contract: a mismatch is
|
||||
rejected with a `409` rather than mis-parsed. So the worst case is a bridge that refuses to
|
||||
|
||||
@@ -81,6 +81,13 @@ lines, and a duration in seconds. Re-posting the same id **replaces** that messa
|
||||
The id is the useful part — give a recurring announcement a stable one and you can update or
|
||||
withdraw it without waiting for it to expire.
|
||||
|
||||
## Client files
|
||||
|
||||
The other half of what the bridge carries has its own screen: creature portraits, item
|
||||
pictures and the game's own name table, read from the UO client on the shard host. It is
|
||||
**Client files**, at `/admin/uo/files`, and it is where an operator goes after patching that
|
||||
client — nothing imports on a restart. See [Client files](/docs/administration/client-files/).
|
||||
|
||||
## What reaches the public
|
||||
|
||||
Events from the shard fan out over two separate streams, and the split is a security
|
||||
|
||||
@@ -88,6 +88,23 @@ the tree and never compiles — and ServUO ignores the script build's exit code,
|
||||
looks clean. `doctor` catches it by comparing file hashes against the install record.
|
||||
</Aside>
|
||||
|
||||
## The bestiary has no pictures, or items show numbers
|
||||
|
||||
Those come out of the UO client on the shard host, and **nothing imports them on a
|
||||
restart** — a button on **Admin → Client files** is the only thing that does. Check that
|
||||
page first: it reports why rather than failing.
|
||||
|
||||
| What it says | What to do |
|
||||
|---|---|
|
||||
| Counts are zero and no import is recorded | Press *Update*. On a shard that was linked before this existed, nobody ever has. |
|
||||
| *…set `AssetsEnabled` on the shard* | The asset plane is off in `Bridge.cfg`. It is a separate switch on purpose. |
|
||||
| *The shard host cannot render images* | A Linux host with no `libgdiplus`. Install it and press *Update* again. Names are unaffected either way. |
|
||||
| *The shard is busy with another client-file request* | Not a fault. Something ordinary holds the slot; it frees itself. |
|
||||
| Pictures were fine and went blank | Check the uploads volume before anything else — the database still remembers pictures that are no longer on disk, and *Re-import everything* is the button for exactly that. |
|
||||
|
||||
Items reading as numbers rather than names is the same page, different section: it means the
|
||||
cliloc table has not been imported. See [Client files](/docs/administration/client-files/).
|
||||
|
||||
## Teams are missing
|
||||
|
||||
Check the sync panel on **Admin → Teams** before anything else: *last success: never* with
|
||||
|
||||
@@ -46,28 +46,35 @@ allowed to be chosen independently.
|
||||
## What a bump obliges
|
||||
|
||||
Changing a message shape means editing every side plus the specification. The most recent
|
||||
bump touched five repositories:
|
||||
bump — **8**, which taught the bridge to carry a game's own client files — touched four
|
||||
repositories:
|
||||
|
||||
| Repository | What had to change |
|
||||
|---|---|
|
||||
| `servuo-plugins` | The handlers, the caps and switches in `Bridge.cfg`, and `overlay.toml` |
|
||||
| `link` | `PROTOCOL_VERSION`, and the endpoints that carry the new commands |
|
||||
| `module-uo` | The verbs it declares, their option sources, and the ingest |
|
||||
| `website` | Core learned a shape it had not had — a lease aimed at one named target |
|
||||
| `servuo-plugins` | The extractors and the decoders they call, the switches and caps in `Bridge.cfg`, and `overlay.toml` |
|
||||
| `link` | `PROTOCOL_VERSION`, a cap on how large a line the shard may send, and the endpoints that carry the new commands |
|
||||
| `module-uo` | The importers, the admin screen, and the pages that render a picture |
|
||||
| `docs` | The protocol document and the integration guide |
|
||||
|
||||
The `website` row is the one worth noticing. Core holds no game connection and names no
|
||||
game noun, so most protocol bumps do not reach it at all — the two before this one did not.
|
||||
This one did, because what changed was not a game *noun* but the shape of a thing core owns
|
||||
the ledger for.
|
||||
**`website` is not on that list, and its absence is the interesting part.** Core holds no
|
||||
game connection and names no game noun, so most protocol bumps do not reach it at all. The
|
||||
one before this did, because what changed then was not a game *noun* but the shape of a
|
||||
thing core owns the ledger for. This one did not reach core because everything it needed —
|
||||
somewhere to put a picture — core already offered every module. Its entire share of eight
|
||||
phases of work was a **deletion**: a developer tool it no longer needed.
|
||||
|
||||
**A protocol bump can also require a store migration**, because the sidecar persists what it
|
||||
forwards. That is not automatic, and it has happened once: version 4 added a column to a
|
||||
table that already existed. Versions 5, 6 and 7 needed none, because every frame is
|
||||
table that already existed. Versions 5, 6, 7 and 8 needed none, because every frame is
|
||||
persisted whole — a bump that only widens a frame, or adds a kind, or adds a guarantee about
|
||||
how a command is executed, asks nothing of a store that defines no schema for a frame's
|
||||
contents. That is the dumb-forwarder property paying for itself.
|
||||
|
||||
Version 8 puts it more sharply still. It is the largest bump this protocol has had, and it
|
||||
moves megabytes of artwork rather than events — and it changed **no line** of the sidecar's
|
||||
store, because the things it carries are answers to requests rather than events to keep. A
|
||||
forwarder that holds no opinion about what it forwards has nothing to migrate.
|
||||
|
||||
## This is not the module API version
|
||||
|
||||
Two different numbers, versioning two different contracts, and confusing them is easy.
|
||||
@@ -102,9 +109,9 @@ What is worth inheriting is the **shape**:
|
||||
|
||||
## Canonical documents
|
||||
|
||||
[`link/v7.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v7.md)
|
||||
[`link/v8.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v8.md)
|
||||
is the current protocol's record, including its cross-repository obligations, and
|
||||
[`link/v6.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v6.md)
|
||||
[`link/v7.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v7.md)
|
||||
the one before it;
|
||||
[`link/PLAN.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md)
|
||||
§7 is the wire protocol, and
|
||||
|
||||
@@ -68,7 +68,7 @@ responsibility. The sidecar is a **dumb forwarder** — it makes no access-contr
|
||||
and holds no policy. Access control and the admin-toggleable visibility scope live on the
|
||||
**website**, where an administrator can see and change them.
|
||||
|
||||
## Two ways in
|
||||
## Three ways in
|
||||
|
||||
**Live events** arrive over an outbound **WebSocket** and are routed by the module's ingest
|
||||
dispatcher. Kinds are handled differently by nature: state-changing kinds update tables,
|
||||
@@ -77,6 +77,23 @@ than accumulating history.
|
||||
|
||||
**Point-in-time reads and commands** go over **REST**, through a client that never throws.
|
||||
|
||||
**Bulk reads** — a game's own client artwork, its string table, its spawn files — are the
|
||||
newest and the least obvious. They go over the request/reply path in **pages**, with **one
|
||||
request in flight at a time** and a hard cap on how large a single line may be.
|
||||
|
||||
<Aside type="note" title="Why bulk data must not ride the event stream">
|
||||
It is the tempting shortcut, and it is wrong for a structural reason rather than a
|
||||
performance one: the sidecar **persists every event and broadcasts it to every connected
|
||||
client**. That is exactly what you want for "a house went IDOC" and exactly what you do not
|
||||
want for hundreds of megabytes of artwork, which is an *answer to a question somebody
|
||||
asked* rather than news.
|
||||
|
||||
Sending it as replies instead is what let the same bump move megabytes without the sidecar's
|
||||
store changing by a line. The single slot is the other half: it is what keeps the queue
|
||||
between the game and the writer thread shallow, so rule 2 above still holds while a
|
||||
transfer is running.
|
||||
</Aside>
|
||||
|
||||
Every call carries `Authorization: Bearer <token>` and an `X-UOLink-Version` header. **A
|
||||
protocol mismatch fails fast with `409`** rather than being mis-parsed — see [Protocol
|
||||
versions](/docs/architecture/protocol-versions/).
|
||||
|
||||
@@ -36,11 +36,20 @@ the [`uo` module](/docs/getting-started/install-a-game-module/) and the installe
|
||||
|---|---|
|
||||
| **A working ServUO install** | It must currently boot and compile scripts cleanly. The installer deploys onto a healthy shard; it does not repair a broken one. |
|
||||
| **ServUO {platform.bundle.servuoMin}** *(patch tier only)* | The base install works on any reasonably current ServUO. The optional patch tier is written and tested against stock {platform.bundle.servuoMin}; on any other version it is unsupported, and skipping it still leaves you with a working bridge. |
|
||||
| **`libgdiplus`** *(Linux shard hosts only)* | Only needed for **artwork**. ServUO runs under Mono on Linux, and the library it decodes sprites with is a thin layer over this one — in the decode path, not merely the encode. Without it creature portraits and item pictures stay empty, and names and the spawn atlas are unaffected because neither touches a pixel. `sudo apt-get install libgdiplus`; `doctor` checks for it. **Windows hosts need nothing.** |
|
||||
| **The shard stopped** | `ServUO.exe` locks `Scripts.dll` and rewrites `Saves/` on exit. The installer refuses to deploy under a running shard. |
|
||||
| **Administrator / root** | It writes into system directories and registers a service. |
|
||||
| **Outbound HTTPS** | To fetch the bundle and its two artifacts. No Gitea account and no git client are needed. |
|
||||
| **The sidecar on the same host as the shard** | The shard connects to `127.0.0.1:7788`. Splitting them is not supported — that loopback socket *is* the trust boundary for inbound commands. |
|
||||
|
||||
<Aside type="note" title="You do not need to install a game client for this">
|
||||
You already have one. A ServUO shard cannot boot without a UO client — it resolves one at
|
||||
startup to read the world's own data — so the artwork, the animations and the name table
|
||||
the site shows are already sitting on that host. The shard reads them there and hands the
|
||||
results over the bridge; nothing is converted on a desktop and nothing is uploaded. See
|
||||
[Client files](/docs/administration/client-files/).
|
||||
</Aside>
|
||||
|
||||
<Aside type="caution" title="Back up before the shard install">
|
||||
The overlay overwrites `Scripts/Scripts.csproj`, a stock file, and the optional patch tier
|
||||
edits stock sources. A copy of `Scripts/` and `Config/` costs nothing and is the difference
|
||||
|
||||
@@ -86,11 +86,18 @@ So verify each link in the chain, in order. Each check tells you which one to fi
|
||||
|
||||
<Aside type="note" title="`runicgateway doctor` answers most of this in one command">
|
||||
Run on the shard host, it checks the install record, the ServUO tree, every overlay file
|
||||
hash, the patch tier, the sidecar, its service, `/health`, and that the sidecar and overlay
|
||||
agree on a protocol. Its output is the first thing anyone helping you will ask for. It
|
||||
hash, the patch tier, the sidecar, its service, `/health`, that the sidecar and overlay
|
||||
agree on a protocol, and — on Linux — that the host can decode an image at all. Its output is the first thing anyone helping you will ask for. It
|
||||
exits non-zero when a check failed, so a monitoring system can run it too.
|
||||
</Aside>
|
||||
|
||||
<Aside type="note" title="A bridge can be green and still show no artwork">
|
||||
Creature portraits and item pictures are a separate switch and a separate import, so a
|
||||
perfectly healthy bridge shows a bestiary of text until somebody presses *Update* on
|
||||
**Admin → Client files**. That is the expected first-run state, not a fault — see [Client
|
||||
files](/docs/administration/client-files/).
|
||||
</Aside>
|
||||
|
||||
## What "working" looks like a week later
|
||||
|
||||
- The public shard page shows live status, and the admin dashboard shows events arriving.
|
||||
|
||||
@@ -45,7 +45,8 @@ moved turns this page red rather than leaving a dead link.
|
||||
- **"Why is the module system like this?"** → `MODULE_SYSTEM.md`.
|
||||
- **"What does this API return?"** → your own deployment's `/api/docs`, then
|
||||
`BACKEND_DESIGN.md` §4.
|
||||
- **"What can the shard send?"** → `link/PLAN.md` §5, and `v7.md` for the current protocol.
|
||||
- **"What can the shard send?"** → `link/PLAN.md` §5, and `v8.md` for the current
|
||||
protocol — which is also where the asset plane is specified.
|
||||
- **"What may a scheduled event do to the world?"** → `website/EVENTS.md` for the model,
|
||||
`MODULE_API.md` for the verbs a module may declare, and `link/ADMIN_CONTROLS.md` for what
|
||||
the site may ask a game to do at all.
|
||||
|
||||
@@ -232,7 +232,8 @@ const gameIntelligence = {
|
||||
detail:
|
||||
'Every player vendor on the server and what is on it, searchable without logging ' +
|
||||
'in to the game. Item names arrive from the world as numeric ids and are resolved ' +
|
||||
"against the game's own string table, so they read as names rather than numbers.",
|
||||
"against the game's own string table, so they read as names rather than numbers — " +
|
||||
"beside the item's own picture, in the colour it was dyed.",
|
||||
},
|
||||
{
|
||||
label: 'Houses and IDOC decay',
|
||||
@@ -249,7 +250,8 @@ const gameIntelligence = {
|
||||
detail:
|
||||
"A bestiary and spawn map built by reading your shard's own spawn tables, so it " +
|
||||
"describes your server rather than someone else's idea of the game. Regions, " +
|
||||
'landmarks and champion altars come with it.',
|
||||
'landmarks and champion altars come with it, and each creature is shown as the ' +
|
||||
'artwork your own client draws it with.',
|
||||
},
|
||||
{
|
||||
label: 'Champion boards',
|
||||
|
||||
@@ -12,23 +12,23 @@
|
||||
"wrong protocol number in the first place."
|
||||
],
|
||||
|
||||
"verifiedOn": "2026-09-09",
|
||||
"verifiedOn": "2026-09-15",
|
||||
|
||||
"protocol": 7,
|
||||
"protocol": 8,
|
||||
|
||||
"moduleApi": "1.10.0",
|
||||
|
||||
"bundle": {
|
||||
"tag": "2026.09.10",
|
||||
"sidecar": "v2.2.0",
|
||||
"overlay": "v1.2.0",
|
||||
"tag": "2026.09.15",
|
||||
"sidecar": "v2.3.0",
|
||||
"overlay": "v1.3.0",
|
||||
"servuoMin": "57.4"
|
||||
},
|
||||
|
||||
"releases": {
|
||||
"link": "v2.2.0",
|
||||
"installer": "v0.1.1",
|
||||
"Module-uo": "v1.2.2",
|
||||
"link": "v2.3.0",
|
||||
"installer": "v0.2.0",
|
||||
"Module-uo": "v1.3.0",
|
||||
"Android-app": "v0.5.0"
|
||||
},
|
||||
|
||||
|
||||
@@ -201,6 +201,33 @@ export const bridgeCfg = {
|
||||
AccountNameMaxLength: 'Account name cap',
|
||||
AccountPasswordMaxLength: 'Account password cap',
|
||||
},
|
||||
'Client assets': {
|
||||
AssetsEnabled:
|
||||
'Whether the website may read the UO client files on this host \u2014 art, animations, ' +
|
||||
'the string table \u2014 over the link at all. Its own switch, because it is its own consent',
|
||||
AssetBatchBytes:
|
||||
'Byte budget for one reply page, inside the 1 MiB line cap the sidecar accepts',
|
||||
AssetBodyBatch:
|
||||
'Bodies one catalogue request may name. Counted in items rather than bytes, because ' +
|
||||
'what it bounds is building and deleting that many real mobiles on the core thread. ' +
|
||||
'A larger request is refused, never truncated',
|
||||
AssetFetchKeys:
|
||||
'How many keys one fetch may name. The byte budget above still decides where a page is cut',
|
||||
AssetScanMs: 'How long a catalogue scan may run before the page it has is returned',
|
||||
AssetPlayerDirection:
|
||||
'Which of the five directions a player body renders as. 0 is head-on, facing the viewer',
|
||||
AssetCreatureDirection:
|
||||
'The same for everything else. 1 is the front three-quarter \u2014 a wolf seen head-on ' +
|
||||
'is a dark blob',
|
||||
},
|
||||
'Spawn files': {
|
||||
TreeEnabled:
|
||||
'Whether the shard configuration itself \u2014 spawn files, regions, locations, champion ' +
|
||||
'spawns, decoration \u2014 may cross the bridge. A third switch for a third consent: ' +
|
||||
'this is the work of the operator rather than the game client. Off means the spawn ' +
|
||||
'atlas needs a shared filesystem again',
|
||||
TreeChunkBytes: 'How large a slice of one file may be before it is compressed and sent',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -224,7 +251,8 @@ export const canonicalDocs = {
|
||||
'website/TRUSTED_DEVICES_MFA.md': 'Trusted devices and the second factor',
|
||||
'link/PLAN.md': 'The sidecar design of record, the data catalog and the wire protocol',
|
||||
'link/INTEGRATION.md': 'Integrating with the sidecar',
|
||||
'link/v7.md': 'The current protocol, and its cross-repository obligations',
|
||||
'link/v8.md': 'The current protocol, and its cross-repository obligations',
|
||||
'link/SHARD_PREREQS.md': 'What a shard host needs before any of this works',
|
||||
'link/ADMIN_CONTROLS.md': 'What the site may command the game to do',
|
||||
'installer/INSTALL.md': 'The operator guide for setting a shard up',
|
||||
'installer/PLAN.md': "The installer's design of record",
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* A demo deployment of this platform, wired to a real ServUO shard over a real sidecar
|
||||
* (D42): the marketplace rows are player vendors the game actually holds, the atlas is
|
||||
* parsed from the shard's own spawn files, the guild rosters came over the bridge. The
|
||||
* parsed from the shard's own spawn files, the guild rosters came over the bridge, and
|
||||
* since protocol 8 the artwork in both came off that host's own UO client. The
|
||||
* deployment is branded "Runic Gateway Demo" rather than a real community's name (D43) —
|
||||
* the screenshots show the platform, not somebody's private shard.
|
||||
*
|
||||
@@ -81,19 +82,19 @@ export const screens = [
|
||||
route: '/uo/market',
|
||||
admin: false,
|
||||
scrollY: 470,
|
||||
alt: 'The marketplace page, listing items for sale by player vendors with their prices, shop names and locations, above a search box and price filters.',
|
||||
alt: 'The marketplace page, listing items for sale by player vendors — each row showing the item picture, its name, the shop, the seller, the location and the price — above a search box and price filters.',
|
||||
caption:
|
||||
'Player vendors, searchable from the website — the same index the in-game vendor search reads, honouring the same per-vendor opt-out.',
|
||||
'Player vendors, searchable from the website — the same index the in-game vendor search reads, honouring the same per-vendor opt-out. The names and the pictures both come out of the client on the shard host.',
|
||||
family: 'web',
|
||||
},
|
||||
{
|
||||
id: 'spawn-atlas',
|
||||
route: '/uo/atlas',
|
||||
admin: false,
|
||||
scrollY: 430,
|
||||
alt: 'The spawn atlas, listing creatures with how many of them spawn and on which facets, above a search box and facet filters.',
|
||||
scrollY: 466,
|
||||
alt: 'The spawn atlas, listing creatures — sea serpent, water elemental, orc, ettin, horse, goat, sheep — each with the artwork from the game client beside how many of them spawn and on which facets.',
|
||||
caption:
|
||||
"The spawn atlas is parsed from the shard's own spawn files, so it stays accurate whether or not the server is up.",
|
||||
"The spawn atlas is parsed from the shard's own spawn files, so it stays accurate whether or not the server is up. Every portrait was decoded on the shard host and came over the same bridge.",
|
||||
family: 'web',
|
||||
},
|
||||
{
|
||||
@@ -151,6 +152,15 @@ export const screens = [
|
||||
'The shard connection, showing a live sidecar. The token is write-only: it is never sent back to any client, including this screen.',
|
||||
family: 'web',
|
||||
},
|
||||
{
|
||||
id: 'admin-client-files',
|
||||
route: '/admin/uo/files',
|
||||
admin: true,
|
||||
alt: 'The client files screen, showing the creature portraits section: 1,095 pictures held of 1,095 catalogued, 746 of 800 creatures matched, the last import and the extractor version, above Update and Re-import everything buttons.',
|
||||
caption:
|
||||
'Client files, imported from the UO client on the shard host over the same bridge. Nothing here happens on a restart — these buttons are the only thing that imports.',
|
||||
family: 'web',
|
||||
},
|
||||
{
|
||||
id: 'admin-modules',
|
||||
route: '/admin/modules',
|
||||
|
||||
Reference in New Issue
Block a user