diff --git a/gmod/src/lib.rs b/gmod/src/lib.rs index 6b3fc9c..c4f33aa 100644 --- a/gmod/src/lib.rs +++ b/gmod/src/lib.rs @@ -16,6 +16,9 @@ pub mod msgc; /// Advanced dark magic utilities pub mod hax; +/// Userdata types +pub mod userdata; + /// Returns whether this client is running the x86-64 branch /// /// Current implementation checks the contents of the bin/ directory, so this is a blocking operation and requires syscalls, use sparingly diff --git a/gmod/src/lua/lua_state.rs b/gmod/src/lua/lua_state.rs index ff42307..e92508b 100644 --- a/gmod/src/lua/lua_state.rs +++ b/gmod/src/lua/lua_state.rs @@ -2,6 +2,8 @@ use std::{mem::MaybeUninit, borrow::Cow, ffi::c_void}; use crate::lua::*; +use crate::userdata::UserData; + #[repr(transparent)] #[derive(Clone, Copy, Debug)] pub struct LuaState(pub *mut std::ffi::c_void); diff --git a/gmod/src/lua/mod.rs b/gmod/src/lua/mod.rs index f858b12..66c898e 100644 --- a/gmod/src/lua/mod.rs +++ b/gmod/src/lua/mod.rs @@ -6,14 +6,6 @@ pub use import::*; mod lua_state; pub use lua_state::LuaState as State; -#[repr(C)] -#[derive(Debug)] -pub struct UserData -{ - pub data: *mut core::ffi::c_void, - pub r#type: u8 -} - #[derive(Debug, Clone)] pub enum LuaError { /// Out of memory diff --git a/gmod/src/userdata.rs b/gmod/src/userdata.rs new file mode 100644 index 0000000..219eb12 --- /dev/null +++ b/gmod/src/userdata.rs @@ -0,0 +1,112 @@ +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum UserData { + None = 255, + + Nil = 0, + Bool, + LightUserData, + Number, + String, + Table, + Function, + UserData, + Thread, + + // GMod Types + Entity, + Vector, + Angle, + PhysObj, + Save, + Restore, + DamageInfo, + EffectData, + MoveData, + RecipientFilter, + UserCmd, + ScriptedVehicle, + Material, + Panel, + Particle, + ParticleEmitter, + Texture, + UserMsg, + ConVar, + IMesh, + Matrix, + Sound, + PixelVisHandle, + DLight, + Video, + File, + Locomotion, + Path, + NavArea, + SoundHandle, + NavLadder, + ParticleSystem, + ProjectedTexture, + PhysCollide, + SurfaceInfo, + + MAX +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Vector { + x: f32, + y: f32, + z: f32 +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Angle { + p: f32, + y: f32, + r: f32 +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct TaggedUserData +{ + pub data: *mut core::ffi::c_void, + pub r#type: UserData +} + +pub trait CoercibleUserData {} + +macro_rules! userdata { + ($(UserData::$enum:ident => $struct:ident),+) => { + $(impl CoercibleUserData for $struct {})+ + + impl TaggedUserData { + /// Coerce this tagged UserData into its corresponding Rust struct, if possible. + /// + /// This will perform a type check to ensure that the tagged userdata matches the user data you are coercing to. + pub fn coerce<'a, T: CoercibleUserData>(&self) -> Result<&mut T, UserData> { + match self.r#type { + $(UserData::$enum => Ok(unsafe { &mut *(self.data as *mut T) }),)+ + _ => Err(self.r#type) + } + } + + /// Coerce this tagged UserData into its corresponding Rust struct, if possible. + /// + /// # Safety + /// This will NOT perform a type check to ensure that the tagged userdata matches the user data you are coercing to. + /// + /// Coercing to the wrong type is undefined behaviour and is likely to crash your program. + pub unsafe fn coerce_unchecked(&self) -> &mut T { + &mut *(self.data as *mut T) + } + } + }; +} +userdata! { + UserData::Vector => Vector, + UserData::Angle => Angle +} \ No newline at end of file