pub mod cheats;
pub mod incoming;
use crate::bitbuf::BitWriter;
use sdk_raw::bitbuf::BfWrite;
use sdk_raw::test_support::{mock_vtable, unexpected_call};
use std::cell::{Cell, RefCell};
use std::ffi::{CStr, CString, c_char, c_int};
use std::ptr::NonNull;
thread_local! {
static ACCEPTS: Cell<bool> = const { Cell::new(true) };
static SENT: RefCell<Vec<(BitWriter, bool)>> = const { RefCell::new(Vec::new()) };
}
pub struct MockChannel {
channel: *mut sys::INetChannel,
}
impl MockChannel {
pub fn new() -> Self {
let vtable = unsafe {
mock_vtable::<sys::INetChannel__bindgen_vtable>(
unexpected_call as *const (),
|vtable| {
(&raw mut (*vtable).INetChannel_SendData).write(record_send_data);
},
)
};
let channel = Box::into_raw(Box::new(sys::INetChannel {
vtable_: Box::into_raw(vtable),
}));
SENT.set(Vec::new());
ACCEPTS.set(true);
Self { channel }
}
pub const fn as_ptr(&self) -> *mut sys::INetChannel {
self.channel
}
#[cfg(test)]
pub(crate) fn channel(&self) -> crate::net::NetChannel<'_> {
unsafe { crate::net::NetChannel::from_raw(NonNull::new(self.channel).unwrap()) }
}
pub fn refuse(&self) {
ACCEPTS.set(false);
}
pub fn take_sent(&self) -> Vec<(BitWriter, bool)> {
SENT.take()
}
}
impl Default for MockChannel {
fn default() -> Self {
Self::new()
}
}
unsafe extern "C" fn record_send_data(
_: *mut sys::INetChannel,
buffer: *mut sys::bf_write,
reliable: bool,
) -> bool {
let bits = unsafe { BfWrite::read_back(NonNull::new(buffer.cast()).unwrap()) }
.map(BitWriter::from)
.expect("a readable buffer");
SENT.with_borrow_mut(|sent| sent.push((bits, reliable)));
ACCEPTS.get()
}
pub struct MockMessage {
message: *mut MessageObject,
}
impl MockMessage {
pub fn new(id: c_int, group: c_int, name: &'static CStr, bits: BitWriter) -> Self {
let vtable = unsafe {
mock_vtable::<sys::INetMessage__bindgen_vtable>(
unexpected_call as *const (),
|vtable| {
(&raw mut (*vtable).INetMessage_GetGroup).write(message_group);
(&raw mut (*vtable).INetMessage_GetName).write(message_name);
(&raw mut (*vtable).INetMessage_GetType).write(message_type);
(&raw mut (*vtable).INetMessage_IsReliable).write(message_is_reliable);
(&raw mut (*vtable).INetMessage_ToString).write(message_to_string);
(&raw mut (*vtable).INetMessage_WriteToBuffer).write(message_write);
},
)
};
let description = format!("{}: {} bits", name.to_string_lossy(), bits.len());
let message = Box::into_raw(Box::new(MessageObject {
base: sys::INetMessage {
vtable_: Box::into_raw(vtable),
},
id,
group,
name,
description: CString::new(description).unwrap(),
bits,
reliable: Cell::new(true),
fails: Cell::new(false),
writes: Cell::new(0),
}));
Self { message }
}
pub const fn as_ptr(&self) -> *mut sys::INetMessage {
self.message.cast()
}
pub fn fail(&self) {
self.object().fails.set(true);
}
pub fn make_unreliable(&self) {
self.object().reliable.set(false);
}
fn object(&self) -> &MessageObject {
unsafe { &*self.message }
}
pub fn writes(&self) -> usize {
self.object().writes.get()
}
}
#[repr(C)]
struct MessageObject {
base: sys::INetMessage,
id: c_int,
group: c_int,
name: &'static CStr,
description: CString,
bits: BitWriter,
reliable: Cell<bool>,
fails: Cell<bool>,
writes: Cell<usize>,
}
unsafe extern "C" fn message_group(this: *const sys::INetMessage) -> c_int {
unsafe { (*this.cast::<MessageObject>()).group }
}
unsafe extern "C" fn message_is_reliable(this: *const sys::INetMessage) -> bool {
unsafe { (*this.cast::<MessageObject>()).reliable.get() }
}
unsafe extern "C" fn message_name(this: *const sys::INetMessage) -> *const c_char {
unsafe { (*this.cast::<MessageObject>()).name.as_ptr() }
}
unsafe extern "C" fn message_to_string(this: *const sys::INetMessage) -> *const c_char {
unsafe { (*this.cast::<MessageObject>()).description.as_ptr() }
}
unsafe extern "C" fn message_type(this: *const sys::INetMessage) -> c_int {
unsafe { (*this.cast::<MessageObject>()).id }
}
unsafe extern "C" fn message_write(
this: *mut sys::INetMessage,
buffer: *mut sys::bf_write,
) -> bool {
let message = unsafe { &*this.cast::<MessageObject>() };
message.writes.set(message.writes.get() + 1);
if message.fails.get() {
return false;
}
let buffer = BfWrite::from_sys(NonNull::new(buffer).expect("a buffer"));
unsafe { BfWrite::append(buffer, message.bits.as_words(), message.bits.len()) }
}