use std::ffi::{c_char, c_int, c_void};
use std::mem::size_of;
#[derive(Debug)]
#[repr(C)]
pub struct PluginCallbacks {
pub load: unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_char, usize, bool) -> bool,
pub all_plugins_loaded: unsafe extern "C" fn(*mut c_void),
pub query_running: unsafe extern "C" fn(*mut c_void, *mut c_char, usize) -> bool,
pub unload: unsafe extern "C" fn(*mut c_void, *mut c_char, usize) -> bool,
pub pause: unsafe extern "C" fn(*mut c_void, *mut c_char, usize) -> bool,
pub unpause: unsafe extern "C" fn(*mut c_void, *mut c_char, usize) -> bool,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct PluginMetadata {
pub author: *const c_char,
pub name: *const c_char,
pub description: *const c_char,
pub url: *const c_char,
pub license: *const c_char,
pub version: *const c_char,
pub date: *const c_char,
pub log_tag: *const c_char,
}
unsafe extern "C" {
fn cpp_metamod_plugin_stable(
callbacks: *const PluginCallbacks,
metadata: PluginMetadata,
) -> *mut c_void;
fn cpp_metamod_plugin_dev(
callbacks: *const PluginCallbacks,
metadata: PluginMetadata,
) -> *mut c_void;
fn cpp_metamod_plugin_stable_instance() -> *mut c_void;
fn cpp_metamod_plugin_dev_instance() -> *mut c_void;
fn cpp_metamod_plugin_stable_is_loaded() -> bool;
fn cpp_metamod_plugin_dev_is_loaded() -> bool;
fn cpp_metamod_hook_client_commands_stable(
clients: *mut c_void,
callback: ClientCommandCallback,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_hook_client_commands_dev(
clients: *mut c_void,
callback: ClientCommandCallback,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_hook_game_frame_stable(
game_dll: *mut c_void,
callback: GameFrameCallback,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_hook_game_frame_dev(
game_dll: *mut c_void,
callback: GameFrameCallback,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_listen_levels_stable(
init: Option<LevelInitCallback>,
shutdown: Option<LevelShutdownCallback>,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_listen_levels_dev(
init: Option<LevelInitCallback>,
shutdown: Option<LevelShutdownCallback>,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_hook_net_messages_stable(
handler: *mut c_void,
slots: *const c_int,
kinds: c_int,
callback: NetMessageCallback,
context: *mut c_void,
) -> c_int;
fn cpp_metamod_hook_net_messages_dev(
handler: *mut c_void,
slots: *const c_int,
kinds: c_int,
callback: NetMessageCallback,
context: *mut c_void,
) -> c_int;
}
pub unsafe fn cpp_metamod_plugin(
api_version: c_int,
callbacks: *const PluginCallbacks,
metadata: PluginMetadata,
) -> *mut c_void {
match api_version {
16 => unsafe { cpp_metamod_plugin_stable(callbacks, metadata) },
18 => unsafe { cpp_metamod_plugin_dev(callbacks, metadata) },
_ => std::ptr::null_mut(),
}
}
pub fn cpp_metamod_plugin_for_version(api_version: c_int) -> *mut c_void {
match api_version {
16 => unsafe { cpp_metamod_plugin_stable_instance() },
18 => unsafe { cpp_metamod_plugin_dev_instance() },
_ => std::ptr::null_mut(),
}
}
pub fn cpp_metamod_plugin_is_loaded(api_version: c_int) -> bool {
match api_version {
16 => unsafe { cpp_metamod_plugin_stable_is_loaded() },
18 => unsafe { cpp_metamod_plugin_dev_is_loaded() },
_ => false,
}
}
pub type ClientCommandCallback =
unsafe extern "C" fn(context: *mut c_void, edict: *mut c_void, command: *const c_void) -> bool;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct HookStatus(pub c_int);
impl HookStatus {
pub const INSTALLED: Self = Self(0);
pub const NOT_BOUND: Self = Self(1);
pub const ALREADY_INSTALLED: Self = Self(2);
pub const INVALID_ARGUMENT: Self = Self(3);
pub const REFUSED: Self = Self(4);
pub const UNSUPPORTED: Self = Self(-1);
}
pub unsafe fn cpp_metamod_hook_client_commands(
api_version: c_int,
clients: *mut c_void,
callback: ClientCommandCallback,
context: *mut c_void,
) -> HookStatus {
HookStatus(match api_version {
16 => unsafe { cpp_metamod_hook_client_commands_stable(clients, callback, context) },
18 => unsafe { cpp_metamod_hook_client_commands_dev(clients, callback, context) },
_ => return HookStatus::UNSUPPORTED,
})
}
pub type GameFrameCallback = unsafe extern "C" fn(context: *mut c_void, simulating: bool);
pub type LevelInitCallback = unsafe extern "C" fn(context: *mut c_void, map: *const c_char);
pub type LevelShutdownCallback = unsafe extern "C" fn(context: *mut c_void);
pub type NetMessageCallback = unsafe extern "C" fn(
context: *mut c_void,
kind: c_int,
handler: *mut c_void,
message: *mut c_void,
) -> bool;
pub unsafe fn cpp_metamod_hook_game_frame(
api_version: c_int,
game_dll: *mut c_void,
callback: GameFrameCallback,
context: *mut c_void,
) -> HookStatus {
HookStatus(match api_version {
16 => unsafe { cpp_metamod_hook_game_frame_stable(game_dll, callback, context) },
18 => unsafe { cpp_metamod_hook_game_frame_dev(game_dll, callback, context) },
_ => return HookStatus::UNSUPPORTED,
})
}
pub unsafe fn cpp_metamod_listen_levels(
api_version: c_int,
init: Option<LevelInitCallback>,
shutdown: Option<LevelShutdownCallback>,
context: *mut c_void,
) -> HookStatus {
HookStatus(match api_version {
16 => unsafe { cpp_metamod_listen_levels_stable(init, shutdown, context) },
18 => unsafe { cpp_metamod_listen_levels_dev(init, shutdown, context) },
_ => return HookStatus::UNSUPPORTED,
})
}
pub unsafe fn cpp_metamod_hook_net_messages(
api_version: c_int,
handler: *mut c_void,
slots: &[c_int],
callback: NetMessageCallback,
context: *mut c_void,
) -> HookStatus {
let Ok(kinds) = c_int::try_from(slots.len()) else {
return HookStatus::INVALID_ARGUMENT;
};
HookStatus(match api_version {
16 => unsafe {
cpp_metamod_hook_net_messages_stable(handler, slots.as_ptr(), kinds, callback, context)
},
18 => unsafe {
cpp_metamod_hook_net_messages_dev(handler, slots.as_ptr(), kinds, callback, context)
},
_ => return HookStatus::UNSUPPORTED,
})
}
const _: () = {
let slot = size_of::<*const ()>();
assert!(size_of::<PluginCallbacks>() == 6 * slot);
assert!(size_of::<PluginMetadata>() == 8 * slot);
};