docs(book): the game host already has the files your site wants (chapter 3 §2b) #12
@@ -135,6 +135,47 @@ clocks. And if the borrowed value lives in the game's own save file, the *hold*
|
||||
must be persisted and the timer re-armed at load; a restart preserves the change
|
||||
and destroys only the thing that would have undone it.
|
||||
|
||||
## 2b. The game host already has the files your site wants
|
||||
|
||||
There is a third kind of traffic, and it is worth knowing about before you decide
|
||||
your sidecar only ever forwards live state. Most games keep **content on the host**
|
||||
that a website wants to show: sprites, icons, portraits, localisation tables, map
|
||||
or spawn definitions. It is static, it is large, and it changes only when an
|
||||
operator patches the game.
|
||||
|
||||
The tempting answer is to make the operator's problem: export it on a desktop with
|
||||
some third-party tool, upload the result, repeat after every patch. It works once
|
||||
and rots immediately, because nothing reminds anyone to redo it.
|
||||
|
||||
The better answer costs less than it sounds like: **the game host already has those
|
||||
files, and you already have a channel to the game host.** Route them over it.
|
||||
|
||||
Four design notes, all learned the expensive way in `uo-link`'s protocol 8 (the
|
||||
"asset bridge", [`v8.md`][v8]):
|
||||
|
||||
- **This is request/reply, never events.** A sidecar that persists and broadcasts
|
||||
every event would write megabytes of sprite into its own store and fan it out to
|
||||
every connected client. Content must ride the same correlated round-trip a query
|
||||
uses — see [chapter 5](05-events.md) for the shape.
|
||||
- **Serve one at a time, and say so in the protocol.** Decoding assets costs the
|
||||
game host real memory. One in-flight request with an explicit "busy" answer is
|
||||
simpler and safer than a queue, and a caller that treats busy as flow control
|
||||
rather than failure gets a working import out of it.
|
||||
- **Two stages: what exists, then what changed.** A cheap call that returns a list
|
||||
with a hash per item and no content, then a second that fetches only the hashes
|
||||
that moved. The common case — a restart that changed nothing — must cost one
|
||||
small round trip, not a re-download of everything.
|
||||
- **Version your *derivation*, separately from the protocol.** If you improve how
|
||||
you read a file, the bytes you produce change while the source file's hash does
|
||||
not. `uo-link` carries an `EXTRACTOR_VERSION` for exactly that, and a consumer
|
||||
treats a change in it like a changed hash.
|
||||
|
||||
And one operational note, because it is the part that surprises people: **do not
|
||||
import on boot.** A patch is an event the operator knows about and your website does
|
||||
not. Re-reading hundreds of megabytes on every restart to discover that nothing
|
||||
changed pays for the rare case forever; a button an operator presses after they
|
||||
patch costs nothing and is honest about who knows what.
|
||||
|
||||
## 3. The wire is a versioned contract, not a build dependency
|
||||
|
||||
Your sidecar and your module ship separately, on different schedules, to hosts you
|
||||
@@ -246,4 +287,5 @@ it wrong takes the game down rather than the website.
|
||||
[api]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/website/MODULE_API.md
|
||||
[linkplan]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md
|
||||
[linkint]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md
|
||||
[v8]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/v8.md
|
||||
[dryrun]: https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/modules/rust-dryrun.md
|
||||
|
||||
Reference in New Issue
Block a user