/dnd clears a standing AFK first, as every chat type but AFK does (#186)
- fix: clear a standing AFK when /dnd is sent - The AFK clear runs once, ahead of the type arms, and a test drives /afk then /dnd through the real drain Fixes #185 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-Authored-By: Sam <samuv.eth@gmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Claude Opus 5.5
Sam
parent
2f35f63274
commit
4ebc2a9f23
@@ -314,36 +314,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `/afk` then `/dnd` prints three lines: the auto-clear skips only type `0x14` (`0x49f4f6`),
|
||||
/// so `/dnd` (`0x15`) clears AFK first.
|
||||
#[test]
|
||||
fn afk_then_dnd_prints_three_lines() {
|
||||
let mut mirror = AfkMirror::default();
|
||||
let mut out = Vec::new();
|
||||
|
||||
let first = afk_line("", mirror, &strings);
|
||||
out.push(first.line.clone().unwrap());
|
||||
mirror.0 = first.mirror.unwrap();
|
||||
|
||||
// `/dnd` takes the generic path first: the clear fires because the mirror is set.
|
||||
let cleared = auto_clear_line(mirror, true, &strings).expect("the implicit clear fires");
|
||||
out.push(cleared);
|
||||
mirror.0 = 0;
|
||||
// Then the DND arm: `0x49f3c7` sits above the type dispatch.
|
||||
assert!(!mirror.is_afk(), "the clear really cleared it");
|
||||
out.push(dnd_line("", false, &strings).line.unwrap());
|
||||
|
||||
assert_eq!(
|
||||
out,
|
||||
vec![
|
||||
"You are now AFK: Away from Keyboard",
|
||||
"You are no longer AFK.",
|
||||
"You are now DND: Do not Disturb.",
|
||||
],
|
||||
"the reference capture, line for line"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_auto_clear_is_gated_both_ways() {
|
||||
assert!(
|
||||
|
||||
@@ -1398,11 +1398,25 @@ pub(super) fn drain_addon_chat_sends(
|
||||
}
|
||||
// ── The away commands, and the AFK clear the other sends carry ───────────────────────
|
||||
//
|
||||
// `SendChatMessage` (`0x49f1e0`): AFK and DND each have an arm ahead of the generic send,
|
||||
// and every type but AFK (`0x14`) first clears a standing AFK. The system lines, the
|
||||
// default text and the mirror are `super::away`'s.
|
||||
// `SendChatMessage` (`0x49f1e0`): every type but AFK (`0x14`) first clears a standing AFK
|
||||
// (`0x49f4f6 jne 0x49f3c7`), `/dnd` included; then AFK and DND each have an arm ahead of
|
||||
// the generic send. The system lines, the default text and the mirror are `super::away`'s.
|
||||
let strings = |key: &str| crate::ui_chat::combat::global_string(&script, key);
|
||||
let wire = kind.wire();
|
||||
if !matches!(wire, crate::net::ChatKind::Afk) {
|
||||
if let Some(line) =
|
||||
super::away::auto_clear_line(*mirror, auto_clear_afk(&cvars), &strings)
|
||||
{
|
||||
super::away::push_system(&mut chat_log, line);
|
||||
mirror.0 = 0;
|
||||
// The empty `0x14` that tells the server, ahead of the message's own packet.
|
||||
let _ = commands.0.send(ClientCommand::Chat {
|
||||
kind: crate::net::ChatKind::Afk,
|
||||
target: None,
|
||||
text: String::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
let text = match wire {
|
||||
crate::net::ChatKind::Afk => {
|
||||
let out = super::away::afk_line(&send.text, *mirror, &strings);
|
||||
@@ -1415,31 +1429,14 @@ pub(super) fn drain_addon_chat_sends(
|
||||
out.body
|
||||
}
|
||||
crate::net::ChatKind::Dnd => {
|
||||
// DND has no mirror (`0x49f3f0`): the live descriptor bit. The reference's DND
|
||||
// also clears a standing AFK first (`0x49f3d6`); this arm does not.
|
||||
// DND has no mirror (`0x49f3f0`): the live descriptor bit.
|
||||
let out = super::away::dnd_line(&send.text, is_dnd(&self_q), &strings);
|
||||
if let Some(line) = out.line {
|
||||
super::away::push_system(&mut chat_log, line);
|
||||
}
|
||||
out.body
|
||||
}
|
||||
// Clear a standing AFK first (`0x49f3d6` skips only type `0x14`), then send the
|
||||
// line's own packet.
|
||||
_ => {
|
||||
if let Some(line) =
|
||||
super::away::auto_clear_line(*mirror, auto_clear_afk(&cvars), &strings)
|
||||
{
|
||||
super::away::push_system(&mut chat_log, line);
|
||||
mirror.0 = 0;
|
||||
// The empty `0x14` that tells the server, alongside the message's own packet.
|
||||
let _ = commands.0.send(ClientCommand::Chat {
|
||||
kind: crate::net::ChatKind::Afk,
|
||||
target: None,
|
||||
text: String::new(),
|
||||
});
|
||||
}
|
||||
send.text
|
||||
}
|
||||
_ => send.text,
|
||||
};
|
||||
let cmd = ClientCommand::Chat {
|
||||
kind: wire,
|
||||
|
||||
@@ -2392,6 +2392,67 @@ fn the_chat_cache_restore_is_finished_before_player_login() {
|
||||
let _ = std::fs::remove_dir_all(&tmp);
|
||||
}
|
||||
|
||||
/// `/afk` then `/dnd` through the real drain: every type but AFK clears a standing AFK first
|
||||
/// (`0x49f4f6 jne 0x49f3c7`), so `/dnd` (`0x15`) prints the clear and sends the empty `0x14` ahead
|
||||
/// of its own line and packet, three lines in all.
|
||||
#[test]
|
||||
fn afk_then_dnd_clears_the_afk_first() {
|
||||
use crate::net::{ChatKind, ClientCommand, NetCommands};
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
|
||||
let mut world = bevy::prelude::World::new();
|
||||
world.insert_non_send_resource(benilla_ui::script::UiScript::new().expect("VM"));
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
world.insert_resource(NetCommands(tx));
|
||||
world.init_resource::<super::feed::ChatLog>();
|
||||
world.init_resource::<super::away::AfkMirror>();
|
||||
world.init_resource::<crate::cvars::Cvars>();
|
||||
world.init_resource::<super::edit::ChannelState>();
|
||||
// The five `GlobalStrings.lua` keys the away lines read, at their enUS values.
|
||||
world
|
||||
.non_send_resource::<benilla_ui::script::UiScript>()
|
||||
.run(
|
||||
r#"
|
||||
MARKED_AFK_MESSAGE = "You are now AFK: %s"
|
||||
CLEARED_AFK = "You are no longer AFK."
|
||||
MARKED_DND = "You are now DND: %s."
|
||||
DEFAULT_AFK_MESSAGE = "Away from Keyboard"
|
||||
DEFAULT_DND_MESSAGE = "Do not Disturb"
|
||||
SendChatMessage("", "AFK")
|
||||
SendChatMessage("", "DND")
|
||||
"#,
|
||||
)
|
||||
.expect("lua");
|
||||
world
|
||||
.run_system_once(super::input::drain_addon_chat_sends)
|
||||
.expect("drain");
|
||||
|
||||
assert_eq!(
|
||||
world.resource::<super::feed::ChatLog>().pending_lines(),
|
||||
vec![
|
||||
"You are now AFK: Away from Keyboard",
|
||||
"You are no longer AFK.",
|
||||
"You are now DND: Do not Disturb.",
|
||||
]
|
||||
);
|
||||
let sent: Vec<(ChatKind, String)> = rx
|
||||
.try_iter()
|
||||
.map(|c| match c {
|
||||
ClientCommand::Chat { kind, text, .. } => (kind, text),
|
||||
other => panic!("unexpected command {other:?}"),
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(
|
||||
sent,
|
||||
vec![
|
||||
(ChatKind::Afk, "Away from Keyboard".into()),
|
||||
(ChatKind::Afk, String::new()),
|
||||
(ChatKind::Dnd, "Do not Disturb".into()),
|
||||
]
|
||||
);
|
||||
assert!(!world.resource::<super::away::AfkMirror>().is_afk());
|
||||
}
|
||||
|
||||
/// `SendChatMessage`'s `CHANNEL` target through the real drain: `SStrToInt` into `0x49be50`
|
||||
/// (`0x49f4d9`-`0x49f4ea`), so the packet carries the numbered slot's name, and a number naming
|
||||
/// no confirmed slot, or a name, sends nothing at all.
|
||||
|
||||
Reference in New Issue
Block a user