use crate::MetamodApi;
use crate::hooks::HookError;
use crate::sys::plugin as raw;
use source_sdk_2013::ServerBinding;
use source_sdk_2013::commands::{CommandRegistrar, UnlinksBeforeUnload, route_client_command};
use source_sdk_2013::interfaces::ServerGameClients;
use source_sdk_2013::sys::ConCommandBase;
use std::cell::Cell;
use std::ffi::c_void;
use std::ptr::{self, NonNull};
#[derive(Debug, Clone, Copy)]
pub struct MetamodRegistrar<'callback> {
api: MetamodApi<'callback>,
plugin: NonNull<c_void>,
}
unsafe impl CommandRegistrar for MetamodRegistrar<'_> {
unsafe fn link(&self, command: NonNull<ConCommandBase>) {
unsafe {
self.api
.register_con_command_base(self.plugin, command.cast())
};
}
unsafe fn unlink(&self, command: NonNull<ConCommandBase>) {
unsafe {
self.api
.unregister_con_command_base(self.plugin, command.cast())
};
}
}
unsafe impl UnlinksBeforeUnload for MetamodRegistrar<'_> {}
struct RoutedServer(Cell<Option<ServerBinding>>);
unsafe impl Sync for RoutedServer {}
static ROUTED_SERVER: RoutedServer = RoutedServer(Cell::new(None));
impl<'callback> MetamodApi<'callback> {
pub fn command_registrar(self) -> Option<MetamodRegistrar<'callback>> {
let version = self.version().plugin_api_version();
if !raw::cpp_metamod_plugin_is_loaded(version) {
return None;
}
Some(MetamodRegistrar {
api: self,
plugin: NonNull::new(raw::cpp_metamod_plugin_for_version(version))?,
})
}
pub fn route_client_commands(
self,
clients: ServerGameClients<'_>,
binding: ServerBinding,
) -> Result<(), HookError> {
ROUTED_SERVER.0.set(Some(binding));
let status = unsafe {
raw::cpp_metamod_hook_client_commands(
self.version().plugin_api_version(),
clients.as_ptr().cast(),
route,
ptr::from_ref(&ROUTED_SERVER).cast_mut().cast(),
)
};
HookError::check(status)
}
}
unsafe extern "C" fn route(
context: *mut c_void,
edict: *mut c_void,
command: *const c_void,
) -> bool {
let (Some(edict), Some(command)) = (NonNull::new(edict), NonNull::new(command.cast_mut()))
else {
return false;
};
let Some(binding) = (unsafe { &*context.cast::<RoutedServer>() }).0.get() else {
return false;
};
unsafe { route_client_command(&binding, edict.cast(), command.cast()) }.is_handled()
}