use core::ffi::c_void;
use crate::Entity;
use crate::abi::{RenameCall, Str, read_headed};
use crate::world::Player;
use crate::{Load, Off};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Rename {
pub entity: Entity,
pub player: Option<Player>,
pub text: String,
}
unsafe fn text_of<'a>(value: Str) -> &'a str {
unsafe { value.as_str() }.unwrap_or("")
}
unsafe extern "system" fn run_filter<F>(call: *const RenameCall, user: *mut c_void) -> u8
where
F: Fn(&Rename) -> bool,
{
let Some(call) = (unsafe { read_headed(call, size_of::<RenameCall>()) }) else {
return 1;
};
let handler = unsafe { &*user.cast::<F>() };
let rename = Rename {
entity: call.entity,
player: (call.known != 0).then(|| Player::from_info(&call.player)),
text: unsafe { text_of(call.text) }.to_owned(),
};
u8::from(crate::host::guard(
"panic in a rename filter",
|| true,
|| handler(&rename),
))
}
impl Load<'_> {
pub fn on_rename<F>(&mut self, filter: F) -> Result<(), Off>
where
F: Fn(&Rename) -> bool + Send + Sync + 'static,
{
let host = self.host();
crate::load::hand_over(
filter,
|user| (host.api().rename_filter)(host.handle(), run_filter::<F>, user),
"rename filter",
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::{Header, PlayerInfo};
fn filter_call<F: Fn(&Rename) -> bool>(filter: &F, call: &RenameCall) -> u8 {
let user = core::ptr::from_ref(filter).cast_mut().cast::<c_void>();
unsafe { run_filter::<F>(call, user) }
}
#[test]
fn a_filter_sees_the_rename_and_may_refuse_it() {
let mut name = [0u8; 64];
name[..3].copy_from_slice(b"Ann");
let filter = |rename: &Rename| rename.player.is_some() && rename.text != "no";
let mut call = RenameCall {
header: Header::of::<RenameCall>(),
entity: Entity(5),
known: 1,
player: PlayerInfo {
header: Header::of::<PlayerInfo>(),
slot: 1,
entity: Entity(7),
key: crate::PlayerKey::new(42),
permissions: 0,
name_len: 3,
name,
},
text: Str::new("Tools"),
};
assert_eq!(filter_call(&filter, &call), 1);
call.text = Str::new("no");
assert_eq!(filter_call(&filter, &call), 0);
call.known = 0;
call.text = Str::new("Tools");
assert_eq!(filter_call(&filter, &call), 0);
}
}