use super::*;
use crate::test_support::harness::{Harness, expect, on_both};
use crate::test_support::server::{no_interfaces, tf2_binding};
use source_sdk_2013::tf2::damage::DamageType;
use std::cell::RefCell;
use std::ffi::c_void;
thread_local! {
static CALLS: RefCell<Vec<(&'static str, usize)>> = const { RefCell::new(Vec::new()) };
}
#[repr(C)]
struct Player {
vtable: *mut *mut c_void,
}
impl Player {
fn of_new_class(killed: EventKilled) -> Box<Self> {
let slots = Vec::leak(vec![killed as *mut c_void; EVENT_KILLED_SLOT + 1]);
Box::new(Self {
vtable: slots.as_mut_ptr(),
})
}
fn ptr(&mut self) -> NonNull<sys::CBaseEntity> {
NonNull::from(self).cast()
}
}
#[test]
fn deaths_reach_each_class_hook_after_the_game() {
on_both(|harness| {
let api = harness.api();
let mut player = Player::of_new_class(game_killed);
let mut bot = Player::of_new_class(game_killed);
let (player_address, bot_address) = (player.ptr().addr().get(), bot.ptr().addr().get());
for object in [player.ptr(), bot.ptr()] {
unsafe { api.install_killed(object, tf2_binding(no_interfaces), on_killed) }.unwrap();
}
assert!(matches!(
unsafe { api.install_killed(player.ptr(), tf2_binding(no_interfaces), on_killed) },
Err(DeathHookError::Hook(HookError::AlreadyInstalled))
));
assert_eq!(
kill(harness, &mut player),
[("game", player_address), ("killed", player_address)]
);
assert_eq!(
kill(harness, &mut bot),
[("game", bot_address), ("killed", bot_address)]
);
});
}
unsafe extern "C" fn game_killed(this: *mut sys::CBaseEntity, info: *const sys::CTakeDamageInfo) {
let amount = unsafe { (&raw const (*info).m_flDamage).read() };
expect(amount == 11.0, "the game received another damage record");
CALLS.with_borrow_mut(|calls| calls.push(("game", this.addr())));
}
fn kill(harness: &Harness, player: &mut Player) -> Vec<(&'static str, usize)> {
let info = DamageInfo::new(11.0, DamageType::BULLET);
CALLS.take();
harness.call::<EventKilled>(player.ptr().as_ptr(), EVENT_KILLED_SLOT, (info.as_ptr(),));
assert_eq!(
info.amount(),
11.0,
"a const source record must never be overwritten"
);
CALLS.take()
}
fn on_killed(_server: Server<'_>, victim: Entity<'_>, info: &DamageInfo) {
expect(
info.amount() == 11.0,
"the callback saw another damage record",
);
expect(
info.damage_type() == DamageType::BULLET,
"the callback saw another damage type",
);
CALLS.with_borrow_mut(|calls| calls.push(("killed", victim.as_ptr().addr())));
}