Merge pull request 'feat(sidecar): protocol 12 — POST /titles (phase 17)' (#12) from feat/phase-17-integrations into edge
Reviewed-on: #12
This commit is contained in:
@@ -57,6 +57,7 @@ still authenticate). Every response carries `X-RustLink-Version`.
|
|||||||
| `GET /feed?since=&limit=` | store | **Oldest first, from a cursor.** For a consumer that must not miss a row. Omitting `since` asks where the end is |
|
| `GET /feed?since=&limit=` | store | **Oldest first, from a cursor.** For a consumer that must not miss a row. Omitting `since` asks where the end is |
|
||||||
| `GET /status` | plugin (RPC) | A live round trip. `503` with no plugin, `504` on no reply |
|
| `GET /status` | plugin (RPC) | A live round trip. `503` with no plugin, `504` on no reply |
|
||||||
| `GET /ws` | broadcast | The live feed. Sends `ws.hello` on connect |
|
| `GET /ws` | broadcast | The live feed. Sends `ws.hello` on connect |
|
||||||
|
| every later route | plugin (RPC) | One thin forward per command, from `POST /link/confirm` (protocol 3) to `POST /titles` (protocol 12). Each is listed beside its protocol in `src/web.rs`, and its body in `docs/rust-link/PROTOCOL.md` |
|
||||||
|
|
||||||
The split is the point: the store-backed reads answer while the game is off, which is what lets the
|
The split is the point: the store-backed reads answer while the game is off, which is what lets the
|
||||||
website render a server list during a wipe or a restart. `/status` is the one route that fails when
|
website render a server list during a wipe or a restart. `/status` is the one route that fails when
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ use tracing_subscriber::EnvFilter;
|
|||||||
///
|
///
|
||||||
/// `docs/rust-link/PROTOCOL.md` is the specification — §8 the read path, §9 identity, §10 the
|
/// `docs/rust-link/PROTOCOL.md` is the specification — §8 the read path, §9 identity, §10 the
|
||||||
/// mirror, §11 configuration, §12 clans, §13 the raid frame, §14 the leases, §15 the world verbs,
|
/// mirror, §11 configuration, §12 clans, §13 the raid frame, §14 the leases, §15 the world verbs,
|
||||||
/// §16 the rewards, §17 the map; this constant is one of its four declaration sites.
|
/// §16 the rewards, §17 the map, §18 the optional mods; this constant is one of its four declaration sites.
|
||||||
pub const PROTOCOL_VERSION: u32 = 11;
|
pub const PROTOCOL_VERSION: u32 = 12;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let args = match cli::parse(std::env::args().skip(1)) {
|
let args = match cli::parse(std::env::args().skip(1)) {
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ pub async fn serve(addr: &str, state: AppState) -> anyhow::Result<()> {
|
|||||||
.route("/map/chunk", get(map_chunk))
|
.route("/map/chunk", get(map_chunk))
|
||||||
.route("/map/render", post(map_render))
|
.route("/map/render", post(map_render))
|
||||||
.route("/map/live", get(map_live))
|
.route("/map/live", get(map_live))
|
||||||
|
// Protocol 12 (§33): the chat titles a player has earned. A whole set, replaced each time,
|
||||||
|
// held by the plugin in memory and read by BetterChat on the chat path. Nothing here knows
|
||||||
|
// what a title is.
|
||||||
|
.route("/titles", post(titles))
|
||||||
.route_layer(middleware::from_fn_with_state(state.clone(), gate));
|
.route_layer(middleware::from_fn_with_state(state.clone(), gate));
|
||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
@@ -565,6 +569,12 @@ async fn chat(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
|||||||
forward_object(&st, body, "chat.say", "a chat line").await
|
forward_object(&st, body, "chat.say", "a chat line").await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace the chat titles this server's plugin holds (protocol 12). The set is whole, so a retry
|
||||||
|
/// is harmless, and the plugin answers whether BetterChat is there to show them.
|
||||||
|
async fn titles(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
||||||
|
forward_object(&st, body, "titles.set", "a title set").await
|
||||||
|
}
|
||||||
|
|
||||||
/// What this map is, where its picture comes from, and its monuments (protocol 11, stage one).
|
/// What this map is, where its picture comes from, and its monuments (protocol 11, stage one).
|
||||||
/// Live, and never cached here: a wipe changes the answer, and the module compares its key and
|
/// Live, and never cached here: a wipe changes the answer, and the module compares its key and
|
||||||
/// hash against what it holds to decide whether to fetch at all.
|
/// hash against what it holds to decide whether to fetch at all.
|
||||||
@@ -1097,6 +1107,24 @@ mod tests {
|
|||||||
assert!(stamp(json!(["9"]), "tally.close", "r-4").is_none());
|
assert!(stamp(json!(["9"]), "tally.close", "r-4").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Protocol 12: a title set is forwarded whole. The markup inside each `text` is the module's
|
||||||
|
/// and the plugin's business, so it must arrive byte for byte as the website composed it.
|
||||||
|
#[test]
|
||||||
|
fn a_protocol_12_title_set_passes_through_untouched() {
|
||||||
|
let body = json!({
|
||||||
|
"cmd": "chat.say",
|
||||||
|
"setId": "s-1",
|
||||||
|
"titles": [{ "steamId": "76561198000000001", "text": "[#ff8800]Top Killer[/#]" }],
|
||||||
|
});
|
||||||
|
let stamped = stamp(body, "titles.set", "r-5").expect("an object is stamped");
|
||||||
|
|
||||||
|
assert_eq!(stamped["cmd"], "titles.set");
|
||||||
|
assert_eq!(stamped["reqId"], "r-5");
|
||||||
|
assert_eq!(stamped["setId"], "s-1");
|
||||||
|
assert_eq!(stamped["titles"][0]["text"], "[#ff8800]Top Killer[/#]");
|
||||||
|
assert!(stamp(json!("titles"), "titles.set", "r-6").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
/// Protocol 11's one opaque forward is stamped like the rest: a render cannot become a world
|
/// Protocol 11's one opaque forward is stamped like the rest: a render cannot become a world
|
||||||
/// placement by naming one.
|
/// placement by naming one.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user