use crate::server::{Game, InterfaceFactory, Module, Server, ServerBinding};
use std::cell::RefCell;
use std::ffi::{CStr, CString, c_char, c_int, c_void};
use std::ptr::null_mut;
thread_local! {
static INTERFACES: RefCell<Vec<(Module, CString, *mut c_void)>> = const { RefCell::new(Vec::new()) };
}
unsafe extern "C" fn engine_factory(name: *const c_char, _return_code: *mut c_int) -> *mut c_void {
find(Module::Engine, name)
}
pub fn export<T>(module: Module, version: &CStr, interface: *mut T) {
INTERFACES.with_borrow_mut(|interfaces| {
interfaces.push((module, version.to_owned(), interface.cast()));
});
}
fn find(module: Module, name: *const c_char) -> *mut c_void {
let name = unsafe { CStr::from_ptr(name) };
INTERFACES.with_borrow(|interfaces| {
interfaces
.iter()
.find(|(owner, version, _)| *owner == module && version.as_c_str() == name)
.map_or(null_mut(), |&(_, _, interface)| interface)
})
}
unsafe extern "C" fn game_server_factory(
name: *const c_char,
_return_code: *mut c_int,
) -> *mut c_void {
find(Module::GameServer, name)
}
pub fn mock_binding() -> ServerBinding {
unsafe {
ServerBinding::new(
InterfaceFactory::new(engine_factory),
InterfaceFactory::new(game_server_factory),
Game::TeamFortress2,
)
}
}
pub fn mock_server<S: ?Sized>(scope: &S) -> Server<'_> {
unsafe { mock_binding().server(scope) }
}
unsafe extern "C" fn no_interface(_: *const c_char, _: *mut c_int) -> *mut c_void {
null_mut()
}
pub fn null_server<S: ?Sized>(game: Game, scope: &S) -> Server<'_> {
let factory = InterfaceFactory::new(no_interface);
unsafe { Server::new(factory, factory, game, scope) }
}