docs(book): a mod is a plugin too, and RCON is not the Rust answer
The kit taught, to an audience outside this org, that Rust needs "no game-side plugin to write at all" because it ships RCON. That is overruled: the Rust dry run reaches the game through a MOD - a plugin loaded by the server's own framework, hooking events and dialling out - exactly as the ServUO overlay does (docs#170). Chapter 3's section kept its question and lost its example, which turned out to improve it. The useful test is not "does my game expose a protocol" but "does it DELIVER EVENTS": a remote-control channel is built for an operator typing commands and tells you what you asked about, when you ask, and a website needs what happened whether or not anyone was listening. A channel that answers questions can only be polled, and polling turns "someone left the clan at 14:02" into "the count was different at 14:03". Rust now appears in that section as the counter-example rather than the example, and carries the finding that is actually worth having: its server is a BINARY where ServUO is source you compile, and the three-part shape survives that unchanged. The plugin-dials-out arrangement is not a property of having source access. Chapter 4 said a game with a remote-control protocol may not need any of it, and that its worked example is source you build. Both now say what is true - the rules in that chapter are properties of being inside a game loop, and apply identically to a mod in a closed server. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -43,9 +43,11 @@ in the contract ([`MODULE_API.md`][api] §2.7, `MODULE_API_VERSION` 1.4.0), not
|
|||||||
style preference, and chapter 3 is mostly about why. The short version: the
|
style preference, and chapter 3 is mostly about why. The short version: the
|
||||||
website is the internet-facing process and your game is not; the sidecar persists
|
website is the internet-facing process and your game is not; the sidecar persists
|
||||||
before it forwards, so a website that is down or mid-deploy loses nothing; and a
|
before it forwards, so a website that is down or mid-deploy loses nothing; and a
|
||||||
game must never block on a web request. A game that already exposes a
|
game must never block on a web request. A game that genuinely delivers events on a
|
||||||
remote-control surface — Rust's RCON over WebSocket, say — needs a *thin* sidecar,
|
surface of its own needs a *thin* sidecar, not none — but check that it delivers
|
||||||
not none.
|
events rather than answering questions, because a channel built for an operator
|
||||||
|
typing commands can only be polled, and polling turns "someone left at 14:02" into
|
||||||
|
"the count was different at 14:03".
|
||||||
|
|
||||||
## Start here
|
## Start here
|
||||||
|
|
||||||
|
|||||||
@@ -132,28 +132,37 @@ forwarded, a lossy live feed, an authenticated read API with a version on it.
|
|||||||
|
|
||||||
## "But my game already speaks a remote-control protocol"
|
## "But my game already speaks a remote-control protocol"
|
||||||
|
|
||||||
Then your sidecar is **thin**, not absent.
|
Then your sidecar is **thin**, not absent — and be sure the surface you are
|
||||||
|
thinking of actually carries what your module needs, because that is where this
|
||||||
|
question usually goes wrong.
|
||||||
|
|
||||||
Rust — the survival game — is the worked example here, in
|
A remote-control channel is built for an operator typing commands: it tells you
|
||||||
[`rust-dryrun.md`][dryrun]: a module designed on paper for a game chosen for how
|
what you asked about, when you ask. What a website needs is what *happened* —
|
||||||
little it shares with Ultima Online. Rust ships RCON over WebSocket, so a
|
every kill, every join, every departure, delivered whether or not anyone was
|
||||||
`rust-link` has no protocol to invent and no game-side plugin to write at all. It
|
listening at that moment. Those are different products, and a channel that
|
||||||
keeps:
|
answers the first can only approximate the second by polling it, which turns
|
||||||
|
"someone left the clan at 14:02" into "the count was different at 14:03".
|
||||||
|
|
||||||
- the RCON connection, its credentials and its reconnect loop, **out of an Express
|
So the honest test is not *does my game expose a protocol* but **does it deliver
|
||||||
process** — where the failure mode is a wedged request handler;
|
events**. If it does, your sidecar keeps that connection, its credentials and its
|
||||||
- a store, so the site is not blank whenever the game is restarting, which for that
|
reconnect loop out of an Express process, keeps a store so the site is not blank
|
||||||
genre is a daily scheduled event;
|
whenever the game restarts, and presents your module one versioned HTTP + WS
|
||||||
- an HTTP + WS API with a version on it, so the module talks to one shape of thing
|
shape. That is what thin means: less code, the same architecture.
|
||||||
regardless of what the game speaks.
|
|
||||||
|
|
||||||
It drops the bespoke wire protocol and the plugin. That is what "thin" means: less
|
**Rust is the worked example, and it is not that case.** The dry run in
|
||||||
code, not a different architecture.
|
[`rust-dryrun.md`][dryrun] designs a module for it precisely because it shares so
|
||||||
|
little with Ultima Online — and its answer is a **mod**: a plugin loaded by the
|
||||||
|
server's own mod framework, hooking the game's events and dialling out to a
|
||||||
|
sidecar, exactly as the ServUO overlay does. The Rust server is a *binary*, where
|
||||||
|
ServUO is source a shard owner compiles, so the way in is a published hook API
|
||||||
|
rather than a file you edit. **The three-part shape survives that unchanged**,
|
||||||
|
which is the more useful finding: the plugin-dials-out arrangement is not a
|
||||||
|
property of having source access.
|
||||||
|
|
||||||
That document originally concluded the opposite — "no sidecar, the module dials
|
That document reached the game over RCON until 2026-08-19, and before that
|
||||||
RCON directly" — and it carries a dated correction saying so, rather than having
|
concluded there should be **no sidecar at all**. It carries both corrections,
|
||||||
been quietly rewritten. The value of a dry run is the record of what it found,
|
dated, rather than having been quietly rewritten — the value of a dry run is the
|
||||||
including where it was overruled.
|
record of what it found, including where it was overruled.
|
||||||
|
|
||||||
## Building yours
|
## Building yours
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,17 @@ The chapter with the least code and the highest stakes. Everything else in this
|
|||||||
book fails by showing an operator a broken web page; this part fails by taking the
|
book fails by showing an operator a broken web page; this part fails by taking the
|
||||||
game down while people are playing it.
|
game down while people are playing it.
|
||||||
|
|
||||||
If your game already speaks a remote-control protocol, you may not need any of
|
If your game already **delivers events** on a surface of its own, you may not need
|
||||||
this — see the end of [chapter 3](03-sidecar.md). If it does not, something has to
|
any of this — see the end of [chapter 3](03-sidecar.md), and read the test there
|
||||||
run inside the game and feed your sidecar, and the rules below are what keep that
|
before deciding, because a channel that answers questions is not the same thing.
|
||||||
something from being the reason the server froze.
|
Otherwise something has to run inside the game and feed your sidecar, and the
|
||||||
|
rules below are what keep that something from being the reason the server froze.
|
||||||
|
|
||||||
|
**That something does not have to be source you compile.** The worked example
|
||||||
|
below is an overlay built into a server whose code you have; the dry run for Rust
|
||||||
|
is a **mod** loaded by a closed server's own framework, hooking published events.
|
||||||
|
Every rule in this chapter applies identically to both — they are properties of
|
||||||
|
being inside a game loop, not of how you got there.
|
||||||
|
|
||||||
The worked example is `servuo-plugins`, the Ultima Online shard plugin, whose link
|
The worked example is `servuo-plugins`, the Ultima Online shard plugin, whose link
|
||||||
layer is one file: `overlay/Scripts/Custom/Bridge/BridgeLink.cs`. It is C# against
|
layer is one file: `overlay/Scripts/Custom/Bridge/BridgeLink.cs`. It is C# against
|
||||||
|
|||||||
Reference in New Issue
Block a user