#[cfg(test)]
#[path = "tests/edicts.rs"]
mod tests;
use crate::entities::Entity;
use crate::interfaces::ValveEngine;
use crate::{NotThreadSafe, Server};
use sdk_raw::edicts::{
FL_EDICT_ALWAYS, FL_EDICT_DONTSEND, FL_EDICT_FREE, FL_EDICT_FULLCHECK, FL_EDICT_PVSCHECK,
FL_EDICT_TRANSMIT_STATE,
};
use sdk_raw::util::cstr::borrow_cstr;
use sdk_raw::vcall;
use std::ffi::{CStr, c_int};
use std::marker::PhantomData;
use std::ptr::NonNull;
pub use sdk_raw::edicts::MAX_EDICTS;
#[doc(alias("edict_t"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Edict<'s> {
pointer: NonNull<sys::edict_t>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> Edict<'s> {
pub unsafe fn from_live(_server: Server<'s>, pointer: NonNull<sys::edict_t>) -> Self {
unsafe { Self::from_raw(pointer) }
}
pub(crate) const unsafe fn from_raw(pointer: NonNull<sys::edict_t>) -> Self {
Self {
pointer,
_scope: PhantomData,
_not_thread_safe: PhantomData,
}
}
pub const fn as_ptr(self) -> *mut sys::edict_t {
self.pointer.as_ptr()
}
#[doc(alias("GetClassName"))]
pub fn class_name(self) -> Option<&'s CStr> {
if self.is_free() {
return None;
}
let networkable =
NonNull::new(unsafe { (&raw const (*self.as_ptr())._base.m_pNetworkable).read() })?;
unsafe { borrow_cstr(vcall!(networkable.as_ptr() => IServerNetworkable_GetClassName())) }
}
#[doc(alias("GetBaseEntity", "GetUnknown"))]
pub fn entity(self) -> Option<Entity<'s>> {
if self.is_free() {
return None;
}
let unknown = NonNull::new(unsafe { (&raw const (*self.as_ptr())._base.m_pUnk).read() })?;
let entity = unsafe { vcall!(unknown.as_ptr() => IServerUnknown_GetBaseEntity()) };
NonNull::new(entity).map(|entity| unsafe { Entity::from_raw(entity) })
}
#[doc(alias("StateChanged"))]
pub fn full_state_changed(self, engine: ValveEngine<'_>) {
let accessor = engine.change_accessor(self);
unsafe { sdk_raw::edicts::full_state_changed(self.as_ptr(), accessor) };
}
#[doc(alias("ENTINDEX", "IndexOfEdict", "m_EdictIndex"))]
pub fn index(self) -> c_int {
let index = unsafe { (&raw const (*self.as_ptr())._base.m_EdictIndex).read() };
c_int::from(index)
}
#[doc(alias("FL_EDICT_FREE", "IsFree"))]
pub fn is_free(self) -> bool {
self.state_flags() & FL_EDICT_FREE != 0
}
#[doc(alias("SetTransmitState"))]
pub fn set_transmit_state(self, engine: ValveEngine<'_>, state: TransmitState) {
if self.is_free() {
return;
}
let flags = self.state_flags();
let changed = (flags & !FL_EDICT_TRANSMIT_STATE) | state.flag();
unsafe { (&raw mut (*self.as_ptr())._base.m_fStateFlags).write(changed) };
if (flags ^ changed) & FL_EDICT_DONTSEND != 0 {
engine.notify_edict_flags_change(self);
}
}
#[doc(alias("StateChanged"))]
pub fn state_changed(self, engine: ValveEngine<'_>, offset: u16) {
unsafe {
sdk_raw::edicts::state_changed(self.as_ptr(), offset, || {
engine
.change_accessor(self)
.zip(engine.shared_edict_change_info())
})
};
}
fn state_flags(self) -> c_int {
unsafe { (&raw const (*self.as_ptr())._base.m_fStateFlags).read() }
}
#[doc(alias("m_fStateFlags"))]
pub fn transmit_state(self) -> TransmitState {
TransmitState::from_flags(self.state_flags())
}
}
#[doc(alias("CCheckTransmitInfo"))]
#[derive(Debug, Clone, Copy)]
pub struct TransmitCheck<'s> {
pointer: NonNull<sys::CCheckTransmitInfo>,
_scope: PhantomData<&'s ()>,
_not_thread_safe: NotThreadSafe,
}
impl<'s> TransmitCheck<'s> {
pub unsafe fn from_live(
_server: Server<'s>,
pointer: NonNull<sys::CCheckTransmitInfo>,
) -> Self {
Self {
pointer,
_scope: PhantomData,
_not_thread_safe: PhantomData,
}
}
pub const fn as_ptr(self) -> *mut sys::CCheckTransmitInfo {
self.pointer.as_ptr()
}
#[doc(alias("m_pClientEnt"))]
pub fn client(self) -> Option<Edict<'s>> {
let client = unsafe { (&raw const (*self.as_ptr()).m_pClientEnt).read() };
NonNull::new(client).map(|client| unsafe { Edict::from_raw(client) })
}
#[doc(alias("m_pTransmitEdict"))]
pub fn is_sent(self, index: c_int) -> bool {
let sent = unsafe { (&raw const (*self.as_ptr()).m_pTransmitEdict).read() };
unsafe { sdk_raw::transmit::has_edict_bit(sent, index) }
}
}
#[doc(alias(
"FL_EDICT_ALWAYS",
"FL_EDICT_DONTSEND",
"FL_EDICT_FULLCHECK",
"FL_EDICT_PVSCHECK"
))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TransmitState {
Always,
DontSend,
FullCheck,
PvsCheck,
}
impl TransmitState {
pub const fn from_flags(flags: c_int) -> Self {
if flags & FL_EDICT_DONTSEND != 0 {
Self::DontSend
} else if flags & FL_EDICT_ALWAYS != 0 {
Self::Always
} else if flags & FL_EDICT_PVSCHECK != 0 {
Self::PvsCheck
} else {
Self::FullCheck
}
}
pub const fn flag(self) -> c_int {
match self {
Self::Always => FL_EDICT_ALWAYS,
Self::DontSend => FL_EDICT_DONTSEND,
Self::FullCheck => FL_EDICT_FULLCHECK,
Self::PvsCheck => FL_EDICT_PVSCHECK,
}
}
}