# Rust-Plugins The **in-game half** of the Runic Gateway bridge for [Rust](https://rust.facepunch.com/): one Oxide plugin that dials out to a [rust-link](https://gitea.whitlocktech.com/RunicGateway/Rust-Link) sidecar and speaks newline-delimited JSON over it. It is the mirror of [`RunicGateway/servuo-plugins`](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins), which does the same job for Ultima Online — and it inherits that plugin's threading contract wholesale, because the reason for it is the same on both games. ## The threading contract Everything else in this repo depends on these three: - **`Emit` is called from the main thread. It formats nothing, blocks on nothing, and touches no socket.** It enqueues and returns. A slow, wedged, or absent sidecar cannot stall the game. - **One link thread owns the socket.** It connects, drains the queue, and reconnects with backoff. A single writer keeps event ordering intact. - **A reader thread parses inbound lines and hands each to the main thread** via `Interface.Oxide.NextTick`. The reader touches no Unity object, no `BasePlayer` and no `ConVar` — every one of those is main-thread-only, and reading one from the reader is the kind of bug that presents as a crash somewhere else entirely. The outbound queue is **bounded, drop-oldest**. On overflow the oldest record goes and is counted, because telemetry is worth less than the server's memory. ## Loopback is the trust boundary There is no token on the game link. The plugin and the sidecar share a host, and the sidecar binds `127.0.0.1` — that is the authentication. Pointing `Host` at anything routable puts an unauthenticated command channel on the network. ## Installing it ``` overlay/oxide/plugins/RunicGateway.cs → /oxide/plugins/RunicGateway.cs ``` Oxide compiles and loads it on the write, and writes `oxide/config/RunicGateway.json` on first load: ```json { "Host": "127.0.0.1", "Port": 7799, "QueueCap": 5000, "ServerId": "main" } ``` `ServerId` is this server's stable identity across wipes and restarts, as the website knows it. It is deliberately **not** derived from the hostname: an operator renames a server for a season, and the site must not lose its history for it. That is the developer's loop. An operator uses the [installer](https://gitea.whitlocktech.com/RunicGateway/installer), which syncs the released overlay tarball and installs the sidecar alongside it. ### Prerequisites The bridge itself needs nothing but Oxide. The features that follow it read four third-party plugins an operator installs from uMod — `Clans`, `Kits`, `PopupNotifications` and `ZoneManager`. They are listed in `overlay.toml` so the installer's `doctor` can report a missing one by name rather than leaving the site quietly short of a feature. ## Diagnosing it ``` rg.link ``` from the server console or over RCON. It reports the link's own counters: ``` protocol=1 serverId=main connected=True depth=0 sent=3 dropped=0 received=2 connects=1 writeErrors=0 bootId=boot-20260915T194502Z ``` This is the first thing to ask for when the website says a server is offline — it separates "the plugin is not loaded", "the plugin cannot reach the sidecar" and "the website cannot reach the sidecar", which look identical from the site. **`bootId` identifies the server PROCESS, not the plugin load.** It is the process start time, so `oxide.reload RunicGateway` does not change it. That matters more than it looks: the website watches this value to tell a game restart (everything an event put in the world is gone) from a bridge reconnect (nothing is lost), and a plugin reload is the second kind. ## The protocol is a contract `ProtocolVersion` in the plugin and `protocol` in `overlay.toml` must agree with the sidecar's `PROTOCOL_VERSION` and the module's own constant. The installer refuses to pair an overlay and a sidecar that disagree, so a bump landing in one repo and not the others fails to compose rather than half-deploying. The canonical spec is [`docs/rust-link/PROTOCOL.md`](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/rust-link/PROTOCOL.md). ## Licence GPL-3.0-or-later. See [LICENSE.md](LICENSE.md).