Merge pull request #6518 from xFedeT/feat/native-server-announce

🤖 Agent: queued at b7e01926a.
This commit is contained in:
Ludovic Marie authored and GitHub committed 2026-11-02 03:15:01 +01:00
commit cbbd4151c8
5 files changed
+44 -3

No files matched your search

@@ -390,6 +390,18 @@ impl Game<'_> {
host.hub.send_to(g.conn, &packet);
}
}
/// Posts a line to the dedicated server's event stream (`GET /api/v1/events`)
/// as an `announce` event, which horizon-server's Discord bridge relays when
/// `[discord] events` lists `announce`: `name` and `uuid` say who it is
/// about (empty and 0 for no one), `message` is the line (`§` codes colour it).
/// Only on a dedicated server; false elsewhere and for an empty message.
pub(super) fn native_server_event(&self, name: &str, uuid: u128, message: &str) -> bool {
if self.options.dedicated.is_none() || message.trim().is_empty() {
return false;
}
super::dedicated::events::record("announce", name, uuid, json!({ "message": message }));
true
}
/// A line of the command's answer, inside `native_perform_command`:
/// sent to the player whose command it is after the call, as vanilla
/// commands' feedback is.
+23 -1
View File
@@ -20,7 +20,7 @@ const BATCH: Duration = Duration::from_secs(2);
const GAP: Duration = Duration::from_secs(2);
/// Lines in one picture; more in a burst are counted and skipped.
const MAX_LINES: usize = 20;
const EVENTS: [&str; 5] = ["chat", "join", "leave", "death", "kick"];
const EVENTS: [&str; 6] = ["chat", "join", "leave", "death", "kick", "announce"];
/// Zero-width joiner: after an `@` or a `<`, Discord no longer reads a mention.
const ZWJ: char = '\u{200D}';
@@ -159,6 +159,7 @@ struct Run {
const WHITE: u32 = 0xFFFFFF;
const YELLOW: u32 = 0xFFFF55;
const GRAY: u32 = 0xAAAAAA;
const GOLD: u32 = 0xFFAA00;
/// ChatFormatting's colours, by their code `0`-`f`.
const PALETTE: [u32; 16] = [
0x000000, 0x0000AA, 0x00AA00, 0x00AAAA, 0xAA0000, 0xAA00AA, 0xFFAA00, 0xAAAAAA, 0x555555,
@@ -176,6 +177,8 @@ impl Line {
"join" => vec![Run::plain(&format!("{name} joined the game"), YELLOW)],
"leave" => vec![Run::plain(&format!("{name} left the game"), YELLOW)],
"kick" => vec![Run::plain(&format!("{name} was kicked"), YELLOW)],
// A line a native server mod posts (`native_server_event`), drawn like a broadcast.
"announce" => legacy(text("message"), GOLD),
// The game writes this message itself: should a nickname stand for the player there, the real name
// follows it. Lines say who someone is only by `name` (their account), never by a nickname.
"death" if !text("message").contains(name.as_str()) => {
@@ -539,6 +542,25 @@ mod tests {
}
}
#[test]
fn an_announcement_is_its_message_in_gold_and_cannot_ping() {
let line = Line::from_event(
&json!({"type": "announce", "name": "", "message": "Jackpot! @everyone"}),
)
.unwrap();
assert_eq!(line.plain(), "Jackpot! @everyone");
assert!(line.runs.iter().all(|r| r.color == GOLD));
assert!(!defuse(&line.plain(), false).contains("@everyone"));
assert!(
Settings {
events: vec!["announce".into()],
..Settings::default()
}
.check()
.is_ok()
);
}
#[test]
fn section_codes_colour_the_runs() {
let runs = legacy("a§cred§lbold§rplain§9blue§zx", WHITE);
+1 -1
View File
@@ -385,7 +385,7 @@ Integrations → Webhooks → New Webhook → Copy Webhook URL, then:
```toml
[discord]
webhook = "https://discord.com/api/webhooks/..." # a secret: whoever has it can post in the channel
events = ["chat", "join", "leave", "death"] # and "kick"
events = ["chat", "join", "leave", "death"] # and "kick", "announce" (lines native mods post)
enabled = true # false pauses it and keeps the webhook
```
+2 -1
View File
@@ -1287,7 +1287,8 @@ For mods that run where the world is simulated: a dedicated server, a LAN host,
- Helpers: `native_server_players()` (uuid, name, level, dimension, position and game mode of each player on);
`native_tell(uuid, text)`, a system line to one player, also the acting player inside their hook;
`native_broadcast(text)`, to every player and the server's
log; `native_feedback(text)`, the command's answer to the player whose command it is; `native_world_dir()`, the
log; `native_server_event(name, uuid, message)`, an `announce` event on a dedicated server's event stream, which the Discord
bridge relays when `[discord] events` lists `announce`; `native_feedback(text)`, the command's answer to the player whose command it is; `native_world_dir()`, the
open world's folder, where a server mod keeps its data in a folder of its own so the world's backups carry it.
- **`native_item_damaged(player, name, item, amount, cancel)`**: an item of a player is about to lose durability, on a
server (`ItemStack.hurtAndBreak`: tools, armour, shield, rod, elytra), after Unbreaking has had its say. Change
@@ -0,0 +1,6 @@
{
"hooks": [],
"rust_summary": "a server mod's line for the event stream and the Discord bridge (`native_server_event`).",
"doc_summary": "`native_server_event(name, uuid, message)` posts an `announce` event to a dedicated server's event stream (`GET /api/v1/events`); horizon-server's Discord bridge relays it, in gold, when `[discord] events` lists `announce` (off by default). False elsewhere and for an empty message. Bundles that use it need \"hooks\": @API:server_event@.",
"comments": ["crates/horizon-client/src/game/native_server.rs"]
}