|
|
|
|
@@ -131,6 +131,15 @@ pub async fn serve(addr: &str, state: AppState) -> anyhow::Result<()> {
|
|
|
|
|
// Protocol 14 (§20.9): place one guarded crate. The plugin remembers the key; the crate is
|
|
|
|
|
// kept when the run ends (Guarded Crate cannot end one alone), so nothing is kept here.
|
|
|
|
|
.route("/gc/place", post(gc_place))
|
|
|
|
|
// Protocol 14 (§20.11): console commands a step's author typed. The plugin keeps the key
|
|
|
|
|
// so nothing runs twice, fills the placeholders and decides who is online; this process
|
|
|
|
|
// moves the line and knows nothing of what any command does (no allowlist, D384).
|
|
|
|
|
.route("/commands/run", post(commands_run))
|
|
|
|
|
// Protocol 14 (§20.12): one RaidableBases base for a run, and a spot's checks. The plugin
|
|
|
|
|
// keeps the key and the run's base in its world registry, so `world.revert` takes it back
|
|
|
|
|
// like a zone; nothing is kept here.
|
|
|
|
|
.route("/raid/spawn", post(raid_spawn))
|
|
|
|
|
.route("/raid/check", post(raid_check))
|
|
|
|
|
// Protocol 10 (§29): who took part, what they may redeem, and a line in chat. The tally
|
|
|
|
|
// is counted and held by the plugin, the kit catalogue is Kits' own, and whether a chat
|
|
|
|
|
// line was already said is the plugin's memory. These move lines, like everything above.
|
|
|
|
|
@@ -583,6 +592,29 @@ async fn gc_place(State(st): State<AppState>, Json(body): Json<Value>) -> Respon
|
|
|
|
|
forward_object(&st, body, "gc.place", "a guarded crate placement").await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Run the console commands a step's author typed (protocol 14): once, or once per online
|
|
|
|
|
/// recipient. `command.ran` (how many ran, the console's replies, who was skipped as offline, and
|
|
|
|
|
/// `repeat` for a key already seen) or `command.error` with a reason, `unknown-command` among
|
|
|
|
|
/// them. A step's `teardown` rides its run and the plugin keeps it with the key; `teardownOf`
|
|
|
|
|
/// asks for the teardown kept under another key (D385). The plugin remembers the key, so nothing
|
|
|
|
|
/// is remembered here.
|
|
|
|
|
async fn commands_run(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
|
|
|
|
forward_object(&st, body, "command.run", "a console command run").await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Spawn one RaidableBases base for a run's step (protocol 14). `raid.queued` (its id, when the
|
|
|
|
|
/// raids helper made it the run's, and `repeat` for a key already seen) or `raid.error` with a
|
|
|
|
|
/// reason. The plugin's world registry holds the run's base; nothing is remembered here.
|
|
|
|
|
async fn raid_spawn(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
|
|
|
|
forward_object(&st, body, "raid.spawn", "a raid base spawn").await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check a spot for a raid base without spawning one (protocol 14): `raid.checked`, with the
|
|
|
|
|
/// refusal's reason and the warnings, or `raid.error` when the spot cannot be checked at all.
|
|
|
|
|
async fn raid_check(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
|
|
|
|
forward_object(&st, body, "raid.check", "a raid base spot check").await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Start counting who takes part in a run. `tally.ok` or `tally.error` with a reason.
|
|
|
|
|
async fn tally_open(State(st): State<AppState>, Json(body): Json<Value>) -> Response {
|
|
|
|
|
forward_object(&st, body, "tally.open", "a tally").await
|
|
|
|
|
@@ -1346,6 +1378,70 @@ mod tests {
|
|
|
|
|
assert!(stamp(json!("Elite"), "gc.place", "r-63").is_none());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Protocol 14, console commands: a run is stamped `command.run` whatever its body names, so
|
|
|
|
|
/// the route runs commands and nothing else, and its key, commands and recipients pass through
|
|
|
|
|
/// byte for byte; the placeholders are the plugin's to fill.
|
|
|
|
|
#[test]
|
|
|
|
|
fn protocol_14_command_run_is_stamped() {
|
|
|
|
|
let body = json!({ "cmd": "gc.place", "reqId": "theirs", "key": "12:5",
|
|
|
|
|
"commands": ["sr add {steamid} 100", "say \"{name} won\""],
|
|
|
|
|
"recipients": [{ "steamId": "76561198000000001", "name": "Some \"One\"" }] });
|
|
|
|
|
let stamped = stamp(body, "command.run", "r-64").expect("an object is stamped");
|
|
|
|
|
|
|
|
|
|
assert_eq!(stamped["cmd"], "command.run");
|
|
|
|
|
assert_eq!(stamped["reqId"], "r-64");
|
|
|
|
|
assert_eq!(stamped["key"], "12:5");
|
|
|
|
|
assert_eq!(stamped["commands"][0], "sr add {steamid} 100");
|
|
|
|
|
assert_eq!(stamped["commands"][1], "say \"{name} won\"");
|
|
|
|
|
assert_eq!(stamped["recipients"][0]["name"], "Some \"One\"");
|
|
|
|
|
assert!(stamp(json!(["rbe"]), "command.run", "r-65").is_none());
|
|
|
|
|
|
|
|
|
|
// D385: a step's teardown rides its run, and a teardown asked for by key alone names the
|
|
|
|
|
// run it is for; both are the plugin's to read.
|
|
|
|
|
let run = stamp(
|
|
|
|
|
json!({ "key": "12:6", "commands": ["rbe 1"], "teardown": ["rbe stop"] }),
|
|
|
|
|
"command.run",
|
|
|
|
|
"r-66",
|
|
|
|
|
)
|
|
|
|
|
.expect("an object is stamped");
|
|
|
|
|
assert_eq!(run["teardown"][0], "rbe stop");
|
|
|
|
|
let teardown = stamp(
|
|
|
|
|
json!({ "key": "12:6:teardown", "teardownOf": "12:6" }),
|
|
|
|
|
"command.run",
|
|
|
|
|
"r-67",
|
|
|
|
|
)
|
|
|
|
|
.expect("an object is stamped");
|
|
|
|
|
assert_eq!(teardown["cmd"], "command.run");
|
|
|
|
|
assert_eq!(teardown["teardownOf"], "12:6");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Protocol 14, RaidableBases: a spawn is stamped `raid.spawn` and a check `raid.check`
|
|
|
|
|
/// whatever their bodies name, so each route does its one thing; the plugin's own names for
|
|
|
|
|
/// difficulties, profiles and bases (D386), the spot and the settings pass through untouched.
|
|
|
|
|
#[test]
|
|
|
|
|
fn protocol_14_raid_spawn_and_check_are_stamped() {
|
|
|
|
|
let body = json!({ "cmd": "world.revert", "reqId": "theirs", "key": "12:7", "runId": "12",
|
|
|
|
|
"difficulty": "Wobbly", "profile": "Wobbly Bases", "base": "wobbly1",
|
|
|
|
|
"x": 650.0, "z": 2037.0, "heightAdjust": 5.0,
|
|
|
|
|
"pvp": false, "npcs": true, "npcCount": 0, "despawnMinutes": 90, "lock": true });
|
|
|
|
|
let spawn = stamp(body.clone(), "raid.spawn", "r-68").expect("an object is stamped");
|
|
|
|
|
|
|
|
|
|
assert_eq!(spawn["cmd"], "raid.spawn");
|
|
|
|
|
assert_eq!(spawn["reqId"], "r-68");
|
|
|
|
|
assert_eq!(spawn["key"], "12:7");
|
|
|
|
|
assert_eq!(spawn["difficulty"], "Wobbly");
|
|
|
|
|
assert_eq!(spawn["base"], "wobbly1");
|
|
|
|
|
assert_eq!(spawn["heightAdjust"], 5.0);
|
|
|
|
|
assert_eq!(spawn["npcCount"], 0);
|
|
|
|
|
assert_eq!(spawn["pvp"], false);
|
|
|
|
|
assert!(stamp(json!("Wobbly"), "raid.spawn", "r-69").is_none());
|
|
|
|
|
|
|
|
|
|
let check = stamp(body, "raid.check", "r-70").expect("an object is stamped");
|
|
|
|
|
assert_eq!(check["cmd"], "raid.check");
|
|
|
|
|
assert_eq!(check["x"], 650.0);
|
|
|
|
|
assert_eq!(check["profile"], "Wobbly Bases");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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]
|
|
|
|
|
|