diff --git a/Cargo.lock b/Cargo.lock index da99691..1c24bdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,7 +135,7 @@ dependencies = [ [[package]] name = "gmod" -version = "11.0.2" +version = "11.1.1" dependencies = [ "cfg_table 1.0.0", "cstr", diff --git a/examples/printing-to-console/Cargo.toml b/examples/printing-to-console/Cargo.toml index 11b6afd..708b035 100644 --- a/examples/printing-to-console/Cargo.toml +++ b/examples/printing-to-console/Cargo.toml @@ -8,4 +8,4 @@ publish = false crate-type = ["cdylib"] [dependencies] -gmod = {version = "10.2.1", features = ["gmcl"], default-features = false} \ No newline at end of file +gmod = {version = "*", features = ["gmcl"], default-features = false} \ No newline at end of file diff --git a/gmod-macros/Cargo.toml b/gmod-macros/Cargo.toml index 3b7f661..346298d 100644 --- a/gmod-macros/Cargo.toml +++ b/gmod-macros/Cargo.toml @@ -2,7 +2,7 @@ name = "gmod-macros" version = "1.0.4" authors = ["William Venner "] -edition = "2018" +edition = "2021" license = "MIT" description = "Proc macros for gmod-rs" repository = "https://github.com/WilliamVenner/gmod-rs" diff --git a/gmod/Cargo.toml b/gmod/Cargo.toml index 3e65d3f..b99062d 100644 --- a/gmod/Cargo.toml +++ b/gmod/Cargo.toml @@ -2,7 +2,7 @@ name = "gmod" version = "11.1.1" authors = ["William Venner "] -edition = "2018" +edition = "2021" license = "MIT" description = "A swiss army knife for creating binary modules for Garry's Mod in Rust" repository = "https://github.com/WilliamVenner/gmod-rs" diff --git a/gmod/src/lua/import.rs b/gmod/src/lua/import.rs index c6664a1..bdcaab3 100644 --- a/gmod/src/lua/import.rs +++ b/gmod/src/lua/import.rs @@ -164,6 +164,11 @@ pub struct LuaShared { pub lua_topointer: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> *const c_void>, pub lua_newuserdata: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, size: usize) -> *mut c_void>, pub lual_newmetatable: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, name: LuaString) -> i32>, + pub lua_resume: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, narg: i32) -> i32>, + pub lua_newthread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> LuaState>, + pub lua_yield: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, nresults: i32) -> i32>, + pub lua_pushthread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> i32>, + pub lua_tothread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> LuaState>, } unsafe impl Sync for LuaShared {} impl LuaShared { @@ -231,6 +236,11 @@ impl LuaShared { lua_topointer: find_symbol!("lua_topointer"), lua_newuserdata: find_symbol!("lua_newuserdata"), lual_newmetatable: find_symbol!("luaL_newmetatable"), + lua_resume: find_symbol!("lua_resume_real"), + lua_newthread: find_symbol!("lua_newthread"), + lua_yield: find_symbol!("lua_yield"), + lua_pushthread: find_symbol!("lua_pushthread"), + lua_tothread: find_symbol!("lua_tothread"), } } } diff --git a/gmod/src/lua/lua_state.rs b/gmod/src/lua/lua_state.rs index 8e9cc85..3dc9ed2 100644 --- a/gmod/src/lua/lua_state.rs +++ b/gmod/src/lua/lua_state.rs @@ -166,6 +166,16 @@ impl LuaState { (LUA_SHARED.lua_pushnil)(*self) } + #[inline(always)] + pub unsafe fn push_thread(&self) -> i32 { + (LUA_SHARED.lua_pushthread)(*self) + } + + #[inline(always)] + pub unsafe fn to_thread(&self, index: i32) -> State { + (LUA_SHARED.lua_tothread)(*self, index) + } + #[inline(always)] pub unsafe fn pcall(&self, nargs: i32, nresults: i32, errfunc: i32) -> i32 { (LUA_SHARED.lua_pcall)(*self, nargs, nresults, errfunc) @@ -177,18 +187,27 @@ impl LuaState { pub unsafe fn pcall_ignore(&self, nargs: i32, nresults: i32) -> bool { let res = self.pcall(nargs, nresults, 0); if res == LUA_ERRRUN { - self.get_global(crate::lua_string!("ErrorNoHaltWithStack")); - if self.is_nil(-1) { - eprintln!("[ERROR] {:?}", self.get_string(-2)); - self.pop_n(2); - return false; - } - self.push_value(-2); - self.call(1, 0); + crate::lua_stack_guard!(self => { + self.get_global(crate::lua_string!("ErrorNoHaltWithStack")); + if self.is_nil(-1) { + eprintln!("[ERROR] {:?}", self.get_string(-2)); + self.pop(); + } else { + #[cfg(debug_assertions)] { + self.push_string(&format!("[pcall_ignore] {}", self.get_string(-2).expect("Expected a string here"))); + } + #[cfg(not(debug_assertions))] { + self.push_value(-2); + } + + self.call(1, 0); + } + }); self.pop(); - return false; + false + } else { + true } - true } pub unsafe fn load_string(&self, src: LuaString) -> Result<(), LuaError> { @@ -450,9 +469,25 @@ impl LuaState { (LUA_SHARED.lua_touserdata)(*self, index) } + #[inline(always)] + pub unsafe fn coroutine_new(&self) -> State { + (LUA_SHARED.lua_newthread)(*self) + } + + #[inline(always)] + pub unsafe fn coroutine_yield(&self, nresults: i32) -> i32 { + (LUA_SHARED.lua_yield)(*self, nresults) + } + + #[inline(always)] + pub unsafe fn coroutine_resume(&self, narg: i32) -> i32 { + (LUA_SHARED.lua_resume)(*self, narg) + } + /// Creates a new table in the registry with the given `name` as the key if it doesn't already exist, and pushes it onto the stack. /// /// Returns if the metatable was already present in the registry. + #[inline(always)] pub unsafe fn new_metatable(&self, name: LuaString) -> bool { (LUA_SHARED.lual_newmetatable)(*self, name) == 0 } @@ -480,6 +515,7 @@ impl LuaState { self.push_value(-2); self.set_metatable(-2); self.remove(self.get_top() - 1); + self.remove(self.get_top() - 1); } ptr.write(data); diff --git a/gmod/src/lua/mod.rs b/gmod/src/lua/mod.rs index a359a66..c86ade9 100644 --- a/gmod/src/lua/mod.rs +++ b/gmod/src/lua/mod.rs @@ -6,6 +6,9 @@ pub use import::*; mod lua_state; pub use lua_state::LuaState as State; +mod push; +pub use push::*; + #[derive(Debug, Clone)] pub enum LuaError { /// Out of memory @@ -73,7 +76,21 @@ macro_rules! lua_stack_guard { let ret = (|| $code)(); if top != $lua.get_top() { $lua.dump_stack(); - panic!("Stack is dirty!"); + panic!("Stack is dirty! Expected the stack to have {} elements, but it has {}!", top, $lua.get_top()); + } + ret + } + + #[cfg(not(debug_assertions))] + $code + }}; + + ( $lua:ident => $elem:literal => $code:block ) => {{ + #[cfg(debug_assertions)] { + let ret = (|| $code)(); + if $lua.get_top() != $elem { + $lua.dump_stack(); + panic!("Stack is dirty! Expected the stack to have ", $elem, " (fixed size) elements, but it has {}!", $lua.get_top()); } ret } diff --git a/gmod/src/lua/push.rs b/gmod/src/lua/push.rs new file mode 100644 index 0000000..d0cd9e4 --- /dev/null +++ b/gmod/src/lua/push.rs @@ -0,0 +1,154 @@ +use std::time::{Duration, SystemTime}; + +pub trait PushToLua: Sized { + /// Pushes this value to the Lua stack. + unsafe fn push_to_lua(self, lua: crate::lua::State); +} +pub trait TryPushToLua: Sized { + /// Checked `push_to_lua` for types that may not fit in an `i32` + unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self>; +} +pub trait ForcePushToLua: Sized { + /// `push_to_lua` but may result in loss of data + unsafe fn force_push_to_lua(self, lua: crate::lua::State); +} +pub trait PushCollectionToLua: Sized { + /// Pushes this collection to a table at the top of the Lua stack. + /// + /// **You must create the table yourself** + unsafe fn push_to_lua_table(self, lua: crate::lua::State); +} + +impl TryPushToLua for P { + #[inline] + unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> { + self.push_to_lua(lua); + Ok(()) + } +} +implForcePushToLua for P { + #[inline] + unsafe fn force_push_to_lua(self, lua: crate::lua::State) { + self.push_to_lua(lua); + } +} + +macro_rules! push_primitives { + {$($ty:ty => $fn:ident),*} => {$( + impl PushToLua for $ty { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + lua.$fn(self as _); + } + } + )*}; +} +macro_rules! try_push_primitives { + {$($ty:ty => $fn:ident / $forcefn:ident),*} => {$( + impl TryPushToLua for $ty { + #[inline] + unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> { + lua.$fn(match self.try_into() { + Ok(v) => v, + Err(e) => return Err(self) + }); + Ok(()) + } + } + impl ForcePushToLua for $ty { + #[inline] + unsafe fn force_push_to_lua(self, lua: crate::lua::State) { + lua.$forcefn(self as _); + } + } + )*}; +} + +push_primitives! { + &str => push_string, + bool => push_boolean, + f64 => push_number, + f32 => push_number, + u8 => push_integer, + i8 => push_integer, + u16 => push_integer, + i16 => push_integer, + i32 => push_integer +} +try_push_primitives! { + u32 => push_integer / push_number, + i64 => push_integer / push_number, + u64 => push_integer / push_number, + u128 => push_integer / push_number, + i128 => push_integer / push_number +} + +impl PushToLua for String { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + lua.push_string(&self); + } +} +impl PushToLua for Vec { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + lua.push_binary_string(&self); + } +} +impl PushToLua for &[u8] { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + lua.push_binary_string(&self); + } +} +impl PushToLua for Duration { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + lua.push_number(self.as_secs_f64()); + } +} +impl PushToLua for Option { + #[inline] + unsafe fn push_to_lua(self, lua: crate::lua::State) { + match self { + Some(val) => val.push_to_lua(lua), + None => lua.push_nil() + } + } +} +impl PushCollectionToLua for std::collections::BTreeMap { + #[inline] + unsafe fn push_to_lua_table(self, lua: crate::lua::State) { + for (k, v) in self { + k.push_to_lua(lua); + v.push_to_lua(lua); + lua.set_table(-3); + } + } +} +impl PushCollectionToLua for Vec { + #[inline] + unsafe fn push_to_lua_table(self, lua: crate::lua::State) { + iterator(lua, &mut self.into_iter()) + } +} + +impl TryPushToLua for SystemTime { + #[inline] + unsafe fn try_push_to_lua(self, lua: crate::lua::State) -> Result<(), Self> { + lua.push_number(self.duration_since(SystemTime::UNIX_EPOCH).map_err(|_| self)?.as_secs_f64()); + Ok(()) + } +} + +/// Pushes all elements in an iterator to a Lua table at the top of the stack. +/// +/// **You must create the table yourself** +#[inline] +pub unsafe fn iterator>(lua: crate::lua::State, iter: &mut I) { + for (i, val) in iter.enumerate() { + lua.push_integer((i + 1) as _); + val.push_to_lua(lua); + lua.set_table(-3); + } +} \ No newline at end of file