use crate::ffi::{NotThreadSafe, copy_cstr, vcall};
use crate::interfaces::ValveEngine;
use crate::net::NetChannel;
use crate::players::UserId;
use std::ffi::{CStr, CString, c_int};
use std::marker::PhantomData;
use std::ptr::NonNull;
#[doc(alias = "IServer")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GameServer<'s> {
raw: NonNull<sys::IServer>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> ValveEngine<'s> {
#[doc(alias = "GetIServer")]
pub fn game_server(self) -> Option<GameServer<'s>> {
let raw = NonNull::new(unsafe { vcall!(self.as_ptr() => IVEngineServer_GetIServer()) })?;
Some(GameServer {
raw,
_scope: PhantomData,
_not_thread_safe: PhantomData,
})
}
}
impl<'s> GameServer<'s> {
pub const fn as_ptr(self) -> *mut sys::IServer {
self.raw.as_ptr()
}
const fn as_const(self) -> *const sys::IServer {
self.raw.as_ptr().cast_const()
}
#[doc(alias = "GetClient")]
pub fn client(self, slot: c_int) -> Option<GameClient<'s>> {
if !(0..self.client_count()).contains(&slot) {
return None;
}
let raw = NonNull::new(unsafe { vcall!(self.as_ptr() => IServer_GetClient(slot)) })?;
Some(unsafe { GameClient::from_raw(raw) })
}
pub fn clients(self) -> impl Iterator<Item = GameClient<'s>> {
(0..self.client_count()).filter_map(move |slot| self.client(slot))
}
#[doc(alias = "GetClientCount")]
pub fn client_count(self) -> c_int {
unsafe { vcall!(self.as_ptr() => IServer_GetClientCount()) }
}
#[doc(alias = "GetMaxClients")]
pub fn max_clients(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetMaxClients()) }
}
#[doc(alias = "GetNumClients")]
pub fn connected_clients(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetNumClients()) }
}
#[doc(alias = "GetNumFakeClients")]
pub fn fake_clients(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetNumFakeClients()) }
}
#[doc(alias = "GetNumProxies")]
pub fn proxies(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetNumProxies()) }
}
#[doc(alias = "GetTick")]
pub fn tick(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetTick()) }
}
#[doc(alias = "GetTime")]
pub fn time(self) -> f32 {
unsafe { vcall!(self.as_const() => IServer_GetTime()) }
}
#[doc(alias = "GetName")]
pub fn name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_const() => IServer_GetName())) }
}
#[doc(alias = "GetMapName")]
pub fn map_name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_const() => IServer_GetMapName())) }
}
#[doc(alias = "GetSpawnCount")]
pub fn spawn_count(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetSpawnCount()) }
}
#[doc(alias = "GetLocalUDPPort")]
pub fn udp_port(self) -> c_int {
unsafe { vcall!(self.as_const() => IServer_GetLocalUDPPort()) }
}
#[doc(alias = "IsActive")]
pub fn is_active(self) -> bool {
unsafe { vcall!(self.as_const() => IServer_IsActive()) }
}
#[doc(alias = "IsLoading")]
pub fn is_loading(self) -> bool {
unsafe { vcall!(self.as_const() => IServer_IsLoading()) }
}
#[doc(alias = "IsPaused")]
pub fn is_paused(self) -> bool {
unsafe { vcall!(self.as_const() => IServer_IsPaused()) }
}
#[doc(alias = "IsDedicated")]
pub fn is_dedicated(self) -> bool {
unsafe { vcall!(self.as_const() => IServer_IsDedicated()) }
}
}
#[doc(alias = "IClient")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GameClient<'s> {
raw: NonNull<sys::IClient>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> GameClient<'s> {
pub(crate) const unsafe fn from_raw(raw: NonNull<sys::IClient>) -> Self {
Self {
raw,
_scope: PhantomData,
_not_thread_safe: PhantomData,
}
}
pub const fn as_ptr(self) -> *mut sys::IClient {
self.raw.as_ptr()
}
const fn as_const(self) -> *const sys::IClient {
self.raw.as_ptr().cast_const()
}
#[doc(alias = "GetPlayerSlot")]
pub fn slot(self) -> c_int {
unsafe { vcall!(self.as_const() => IClient_GetPlayerSlot()) }
}
pub fn entity_index(self) -> c_int {
self.slot() + 1
}
#[doc(alias = "GetUserID")]
pub fn user_id(self) -> Option<UserId> {
UserId::from_raw(unsafe { vcall!(self.as_const() => IClient_GetUserID()) }).ok()
}
#[doc(alias = "GetClientName")]
pub fn name(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_const() => IClient_GetClientName())) }
}
#[doc(alias = "GetNetworkIDString")]
pub fn network_id(self) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_const() => IClient_GetNetworkIDString())) }
}
#[doc(alias = "GetUserSetting")]
pub fn user_setting(self, name: &CStr) -> Option<CString> {
unsafe { copy_cstr(vcall!(self.as_const() => IClient_GetUserSetting(name.as_ptr()))) }
}
#[doc(alias = "GetNetChannel")]
pub fn net_channel(self) -> Option<NetChannel<'s>> {
let channel = NonNull::new(unsafe { vcall!(self.as_ptr() => IClient_GetNetChannel()) })?;
Some(unsafe { NetChannel::from_raw(channel) })
}
#[doc(alias = "GetRate")]
pub fn rate(self) -> c_int {
unsafe { vcall!(self.as_const() => IClient_GetRate()) }
}
#[doc(alias = "GetUpdateRate")]
pub fn update_rate(self) -> c_int {
unsafe { vcall!(self.as_const() => IClient_GetUpdateRate()) }
}
#[doc(alias = "IsConnected")]
pub fn is_connected(self) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsConnected()) }
}
#[doc(alias = "IsSpawned")]
pub fn is_spawned(self) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsSpawned()) }
}
#[doc(alias = "IsActive")]
pub fn is_active(self) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsActive()) }
}
#[doc(alias = "IsFakeClient")]
pub fn is_fake(self) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsFakeClient()) }
}
#[doc(alias = "IsHLTV")]
pub fn is_hltv(self) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsHLTV()) }
}
#[doc(alias = "IsHearingClient")]
pub fn is_hearing(self, speaker: GameClient<'_>) -> bool {
unsafe { vcall!(self.as_const() => IClient_IsHearingClient(speaker.slot())) }
}
#[doc(alias = "ClientPrintf")]
pub fn print(self, message: &CStr) {
unsafe { vcall!(self.as_ptr() => IClient_ClientPrintf(c"%s".as_ptr(), message.as_ptr())) };
}
#[doc(alias = "ExecuteStringCommand")]
pub unsafe fn execute_string_command(self, command: &CStr) -> bool {
unsafe { vcall!(self.as_ptr() => IClient_ExecuteStringCommand(command.as_ptr())) }
}
#[doc(alias = "Disconnect")]
pub unsafe fn disconnect(self, reason: &CStr) {
unsafe { vcall!(self.as_ptr() => IClient_Disconnect(c"%s".as_ptr(), reason.as_ptr())) };
}
}