diff --git a/README.md b/README.md index 5a451747..5959912f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ๐ŸŒ‘ ``rglua`` [![cratesio](https://img.shields.io/crates/v/rglua.svg)](https://crates.io/crates/rglua) ![Build Status](https://github.com/Vurv78/rglua/actions/workflows/ci.yml/badge.svg) [![License](https://img.shields.io/github/license/Vurv78/rglua?color=red)](https://opensource.org/licenses/Apache-2.0) [![github/Vurv78](https://img.shields.io/discord/824727565948157963?label=Discord&logo=discord&logoColor=ffffff&labelColor=7289DA&color=2c2f33)](https://discord.gg/epJFC6cNsw) -This is a crate that allows interop with the luajit c api as well as the source sdk through libloading and vtable bindings. +This is a crate that allows interop with the (g)luajit c api as well as the source sdk through libloading and vtable bindings. You can then use these for binary modules or manually injected code, like with [Autorun-rs](https://github.com/Vurv78/Autorun-rs) More information on binary modules can be found on the garrysmod wiki: [Creating Binary Modules](https://wiki.facepunch.com/gmod/Creating_Binary_Modules) and examples [can be found here.](https://github.com/Vurv78/rglua/tree/master/examples) @@ -8,11 +8,8 @@ More information on binary modules can be found on the garrysmod wiki: [Creating If you are targeting 32 bit make sure to install the toolchain and build to it: ```bash rustup target add i686-pc-windows-msvc -cargo build --release --target=i686-pc-windows-msvc +cargo build --target=i686-pc-windows-msvc ``` -## Acknowledgements -### [garrysmod_common](https://github.com/danielga/garrysmod_common) -This is heavily based off of garrysmod_common, in how we export the lua_shared functions and trying to replicate everything from the Lua C Api. ## Comparison There are actually a decent amount of libraries out there for gmod development. @@ -34,6 +31,10 @@ Here's a comparison and why you could use this one. | Real world examples | โœ”๏ธ | โŒ | โœ”๏ธ | โŒ | | Github Stars | ๐Ÿ˜ข | ๐Ÿ‘ | ๐Ÿ‘‘ | ๐Ÿคทโ€โ™‚๏ธ | +__*You can help with that last one ๐Ÿ˜‰*__ + \* They technically do, but they depend on autogenerated bindings which is inaccurate for gmod, leading to missing functions. (See lua_resume_real) -__*You can help with that last one ๐Ÿ˜‰*__ \ No newline at end of file +## Acknowledgements +### [garrysmod_common](https://github.com/danielga/garrysmod_common) +This is heavily based off of garrysmod_common, in how we export the lua_shared functions and trying to replicate everything from the Lua C Api. \ No newline at end of file diff --git a/examples/is_even/Cargo.toml b/examples/is_even/Cargo.toml index 6e13df62..a18ae821 100644 --- a/examples/is_even/Cargo.toml +++ b/examples/is_even/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gmod_is_even" description = "Binary module that exposes a function called 'is_even' that does what it says." -version = "0.2.0" +version = "0.3.0" edition = "2021" publish = false @@ -9,4 +9,4 @@ publish = false crate-type = ["cdylib"] [dependencies] -rglua = { path = "../.." } \ No newline at end of file +rglua = { path = "../../rglua" } \ No newline at end of file diff --git a/examples/is_even/src/lib.rs b/examples/is_even/src/lib.rs index cef6ca59..933a8a55 100644 --- a/examples/is_even/src/lib.rs +++ b/examples/is_even/src/lib.rs @@ -1,7 +1,8 @@ use rglua::prelude::*; // The functions we want to provide to lua -extern "C" fn is_even(l: LuaState) -> i32 { +#[lua_function] +fn is_even(l: LuaState) -> i32 { let num = luaL_checkinteger(l, 1); // Ask for the first argument of the function. // If this is the wrong type or missing, an error will be thrown to lua (if you don't want this, use the lua_to* functions) @@ -12,13 +13,16 @@ extern "C" fn is_even(l: LuaState) -> i32 { 1 } -extern "C" fn is_odd(l: LuaState) -> i32 { +#[lua_function] +fn is_odd(l: LuaState) -> i32 { let num = luaL_checkinteger(l, 1); lua_pushboolean(l, (num % 2 != 0) as i32); 1 } +// Note that since this is #[gmod_open] the name of the function does not matter +// This is the same for #[gmod_close] #[gmod_open] fn open(l: LuaState) -> i32 { // Print to the gmod console @@ -31,7 +35,7 @@ fn open(l: LuaState) -> i32 { ]; // Register our functions in ``_G.math`` - // This WILL NOT overwrite _G.math if it already exists () + // This WILL NOT overwrite _G.math if it already exists (which it should..) luaL_register(l, cstr!("math"), lib.as_ptr()); 1 } diff --git a/examples/vector/Cargo.toml b/examples/vector/Cargo.toml new file mode 100644 index 00000000..76f3edf9 --- /dev/null +++ b/examples/vector/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "vector" +description = "Binary module that adds some extra functions to the gmod Vector type" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +rglua = { path = "../../rglua" } \ No newline at end of file diff --git a/examples/vector/README.md b/examples/vector/README.md new file mode 100644 index 00000000..cbcc194f --- /dev/null +++ b/examples/vector/README.md @@ -0,0 +1,8 @@ +# ๐Ÿ”ข ``vector`` +Binary module that adds some extra functions to the ``Vector`` type. + +## Vector:IsPositive() +Returns if the vector consists of positive numbers + +## Vector:GetPow(n: number) +Returns the vector multiplied by itself n times (exp ^) \ No newline at end of file diff --git a/examples/vector/src/lib.rs b/examples/vector/src/lib.rs new file mode 100644 index 00000000..a15c9d4d --- /dev/null +++ b/examples/vector/src/lib.rs @@ -0,0 +1,44 @@ +use rglua::prelude::*; + +#[lua_function] +fn is_positive(l: LuaState) -> i32 { + let vec = luaL_checkvector(l, 1); + lua_pushboolean(l, (vec.x > 0.0 && vec.y > 0.0 && vec.z > 0.0) as i32); + 1 +} + +#[lua_function] +fn get_pow(l: LuaState) -> i32 { + let vec = luaL_checkvector(l, 1); + let by = luaL_checknumber(l, 2) as f32; + + lua_pushvector(l, Vector::new(vec.x.powf(by), vec.y.powf(by), vec.z.powf(by))); + 1 +} + +// Note that since this is #[gmod_open] the name of the function does not matter +// This is the same for #[gmod_close] +#[gmod_open] +fn open(l: LuaState) -> i32 { + // Create a library consisting of functions to export to gmod. + let lib = reg! [ + "IsPositive" => is_positive, + "GetPow" => get_pow + ]; + + // Get the ``Vector`` metatable from the lua registry and put it onto the stack. + luaL_getmetatable(l, cstr!("Vector")); + + // Give a null pointer as the libname so that luaL_register knows we are trying to instead add these functions to the value on top of the stack; + // This being the Vector metatable at (-1). + luaL_register(l, std::ptr::null(), lib.as_ptr()); + + // Return nothing (0 objects) + 0 +} + +#[gmod_close] +fn close(l: LuaState) -> i32 { + printgm!(l, "Goodbye garrysmod!"); + 0 +} \ No newline at end of file diff --git a/rglua/Cargo.toml b/rglua/Cargo.toml index bdde4cd8..8a0be18c 100644 --- a/rglua/Cargo.toml +++ b/rglua/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rglua" description = "Toolkit for garrysmod development with the source sdk and luajit api" -version = "1.0.0" +version = "2.0.0-beta" authors = ["Vurv "] keywords = ["glua", "garrysmod", "lua", "gmod"] categories = ["api-bindings", "external-ffi-bindings", "development-tools::ffi", "game-development", "accessibility"] diff --git a/rglua/src/interface/common.rs b/rglua/src/interface/common.rs index 213c1de8..7cb00aff 100644 --- a/rglua/src/interface/common.rs +++ b/rglua/src/interface/common.rs @@ -1,3 +1,5 @@ +use super::prelude::*; + #[repr(C)] #[allow(non_snake_case)] pub struct PlayerInfo { @@ -18,3 +20,27 @@ pub struct PlayerInfo { filesDownloaded: u8, pad: [i8; 304], } + +#[repr(C)] +pub struct StudioHdr { + pub id: c_int, + pub version: c_int, + pub checksum: c_int, + + pub name: [c_char; 64], + pub length: c_int, + + pub eyeposition: Vector, + pub illumposition: Vector, + pub hull_min: Vector, + pub hull_max: Vector, + pub view_bbmin: Vector, + pub view_bbmax: Vector, + pub flags: c_int, + pub numbones: c_int, + pub boneindex: c_int, + pub numbonecontrollers: c_int, + pub bonecontrollerindex: c_int, + pub numhitboxsets: c_int, + pub hitboxsetindex: c_int, +} diff --git a/rglua/src/interface/cvar.rs b/rglua/src/interface/cvar.rs index 01bfc6dc..b80ae52f 100644 --- a/rglua/src/interface/cvar.rs +++ b/rglua/src/interface/cvar.rs @@ -11,8 +11,8 @@ pub struct ConCommandBase { } iface! { - /// VEngineCvar007 - /// vstdlib.dll + #[version("VEngineCvar007")] + #[file("vstdlib.dll")] pub abstract struct ICVar {}; } diff --git a/rglua/src/interface/engine.rs b/rglua/src/interface/engine.rs index 447cc043..2abaf2b7 100644 --- a/rglua/src/interface/engine.rs +++ b/rglua/src/interface/engine.rs @@ -4,8 +4,8 @@ use super::prelude::*; use std::os::raw::c_char; iface! { - /// VEngineClient015 - /// Offsets are confirmed correct as of 12/19/2021 + #[version("VEngineClient015")] + #[file("engine.dll")] pub abstract struct EngineClient {}; } diff --git a/rglua/src/interface/lua/mod.rs b/rglua/src/interface/lua/mod.rs index f52a3f10..c605b09e 100644 --- a/rglua/src/interface/lua/mod.rs +++ b/rglua/src/interface/lua/mod.rs @@ -1,14 +1,21 @@ pub(crate) use super::prelude::{self, iface, VTable}; iface! { + #[version("")] + #[file("")] /// + /// You do not use this as a typical interface, it is just a type returned by other iface functions. pub abstract struct ILuaObject {}; + #[version("")] + #[file("")] /// /// Basically what is given to ordinary C++ binary modules that do not interface with lua_shared. /// You can use this but should really just use the lua_shared bindings. pub abstract struct ILuaInterface {}; + #[version("LUASHARED003")] + #[file("")] /// pub abstract struct CLuaShared {}; } diff --git a/rglua/src/interface/materials.rs b/rglua/src/interface/materials.rs index f34e3a4a..929c4297 100644 --- a/rglua/src/interface/materials.rs +++ b/rglua/src/interface/materials.rs @@ -1,7 +1,13 @@ use super::prelude::*; iface! { + #[version("")] + #[file("")] + /// You do not get this through creating an interface, it is instead exported by other interface functions. pub abstract struct IMaterial {}; + + #[version("VMaterialSystem080")] + #[file("materialsystem.dll")] pub abstract struct IMaterialSystem {}; } diff --git a/rglua/src/interface/mdl.rs b/rglua/src/interface/mdl.rs new file mode 100644 index 00000000..f36d6f88 --- /dev/null +++ b/rglua/src/interface/mdl.rs @@ -0,0 +1,109 @@ +use super::common::StudioHdr; +use super::prelude::*; + +iface! { + #[version("MDLCache004")] + #[file("datacache.dll")] + pub abstract struct IMdlCache {}; + + #[version("")] + #[file("")] + pub abstract struct IMdlCacheNotify {}; +} + +#[repr(C)] +pub enum MDLCacheDataType { + // Callbacks to get called when data is loaded or unloaded for these: + StudioHDR = 0, + StudioHWData, + VCollide, + + // Callbacks NOT called when data is loaded or unloaded for these: + AnimBlock, + VirtualModel, + Vertexes, + DecodedAnimBlock, +} + +impl IMdlCacheNotify { + #[virtual_index(0)] + /// Called right after data is loaded + pub fn OnDataLoaded(&self, ty: MDLCacheDataType, handle: MDLHandle) -> () {} + + #[virtual_index(1)] + /// Called right before data is unloaded + pub fn OnDataUnloaded(&self, ty: MDLCacheDataType, handle: MDLHandle) -> () {} +} + +pub type MDLHandle = c_ushort; +pub type VirtualModel = c_void; // Todo? + +const MAX_NUM_LODS: usize = 8; + +#[repr(C)] +pub struct VertexFileHeader { + id: c_int, + version: c_int, + checksum: c_int, + numLODs: c_int, + numLODVertexes: [c_int; MAX_NUM_LODS], + numFixups: c_int, + fixupTableStart: c_int, + vertexDataStart: c_int, + tangentDataStart: c_int, +} + +impl IMdlCache { + #[virtual_index(0)] + pub fn SetCacheNotify(&self, pNotify: *mut IMdlCacheNotify) -> () {} + + #[virtual_index(1)] + pub fn FindMDL(&self, pMDLRelativePath: *const c_char) -> MDLHandle {} + + #[virtual_index(2)] + pub fn AddRef(&self, handle: MDLHandle) -> c_int {} + + #[virtual_index(3)] + pub fn Release(&self, handle: MDLHandle) -> c_int {} + + #[virtual_index(4)] + pub fn GetRef(&self, handle: MDLHandle) -> c_int {} + + #[virtual_index(5)] + pub fn GetStudioHdr(&self, handle: MDLHandle) -> *mut StudioHdr {} + + #[virtual_index(9)] + pub fn GetVirtualModel(&self, handle: MDLHandle) -> *mut VirtualModel {} + + #[virtual_index(11)] + pub fn GetVertexData(&self, handle: MDLHandle) -> *mut VertexFileHeader {} + + #[virtual_index(12)] + pub fn TouchAllData(&self, handle: MDLHandle) -> () {} + + #[virtual_index(13)] + pub fn SetUserData(&self, handle: MDLHandle, pData: *mut c_void) -> () {} + + #[virtual_index(14)] + pub fn GetUserData(&self, handle: MDLHandle) -> *mut c_void {} + + #[virtual_index(15)] + pub fn IsErrorModel(&self, handle: MDLHandle) -> bool {} + + #[virtual_index(18)] + pub fn GetModelName(&self, handle: MDLHandle) -> *const c_char {} + + #[virtual_index(19)] + pub fn GetVirtualModelFast( + &self, + pStudioHdr: *const StudioHdr, + handle: MDLHandle, + ) -> *mut VirtualModel { + } + + #[virtual_index(20)] + pub fn BeginLock(&self) -> () {} + + #[virtual_index(21)] + pub fn EndLock(&self) -> () {} +} diff --git a/rglua/src/interface/mod.rs b/rglua/src/interface/mod.rs index 7c04d8ac..a0108c78 100644 --- a/rglua/src/interface/mod.rs +++ b/rglua/src/interface/mod.rs @@ -12,11 +12,13 @@ pub(crate) mod prelude { macro_rules! iface { ( - $(#[$outer:meta])* + #[ version ( $ver:literal ) ] + #[ file ( $file:literal ) ] + $(#[$attr:meta])* $vis:vis abstract struct $iface:ident {}; $($rest:tt)* ) => { - $(#[$outer])* + $(#[$attr])* #[derive(VTable)] #[vtables_derive::has_vtable] $vis struct $iface { @@ -35,12 +37,14 @@ mod cvar; mod engine; mod lua; mod materials; +mod mdl; mod panel; pub use cvar::ICVar; pub use engine::EngineClient; pub use lua::{CLuaShared, ILuaInterface, ILuaObject}; pub use materials::IMaterialSystem; +pub use mdl::{IMdlCache, IMdlCacheNotify}; pub use panel::IPanel; use crate::try_cstr; diff --git a/rglua/src/interface/panel.rs b/rglua/src/interface/panel.rs index dc1cae20..96b60c1a 100644 --- a/rglua/src/interface/panel.rs +++ b/rglua/src/interface/panel.rs @@ -1,6 +1,8 @@ use super::prelude::*; iface! { + #[version("VGUI_Panel009")] + #[file("vgui2.dll")] pub abstract struct IPanel {}; } diff --git a/rglua/src/lib.rs b/rglua/src/lib.rs index e9adec73..c6696eb6 100644 --- a/rglua/src/lib.rs +++ b/rglua/src/lib.rs @@ -6,13 +6,6 @@ pub mod interface; #[macro_use] pub mod lua; -#[deprecated(since = "0.8.0", note = "Use rglua::lua instead")] -pub use lua as lua_shared; - -#[deprecated( - since = "0.8.0", - note = "Use rglua::lua::* or rglua::lua::types instead" -)] pub use lua::types; pub use rglua_macros::*; diff --git a/rglua/src/lua/globals.rs b/rglua/src/lua/globals.rs index 24901a58..21314a1e 100644 --- a/rglua/src/lua/globals.rs +++ b/rglua/src/lua/globals.rs @@ -1,46 +1,99 @@ -use crate::types::*; +use crate::lua::types::*; -/// Index of the lua registry. What you'd get from debug.getregistry() +/// Index of the lua registry. +/// What you'd get from debug.getregistry() pub const REGISTRYINDEX: c_int = -10000; -/// Index of the lua environment. -/// This is like getfenv() or _ENV in later lua versions + +/// Index of the lua environment. +/// This is like ``getfenv()`` or ``_ENV`` in later lua versions pub const ENVIRONINDEX: c_int = -10001; + /// Index of _G pub const GLOBALSINDEX: c_int = -10002; /// Number of returns to use in functions like lua_pcall to represent 0 or more. pub const MULTRET: c_int = -1; +/// Number of primitive lua types (excluding garrysmod userdata types) pub const NUMTYPES: c_int = 9; pub const NUMTAGS: c_int = NUMTYPES; +/// 'None' / 'No value' type. pub const TNONE: c_int = -1; + +/// 'nil' type pub const TNIL: c_int = 0; + +/// Boolean type pub const TBOOLEAN: c_int = 1; + +/// 'Light' Userdata type. +/// This is just a pointer to something owned by C without a custom metatable, as a [TUSERDATA] may have. pub const TLIGHTUSERDATA: c_int = 2; + +/// Number type. +/// This is a double, or [f64] pub const TNUMBER: c_int = 3; + +/// String type, this is a [LuaString] pub const TSTRING: c_int = 4; + +/// Table type created by [super::lua_newtable] pub const TTABLE: c_int = 5; + +/// Function type created by [super::lua_pushcfunction], [super::lua_pushcclosure] or retrieved from lua. pub const TFUNCTION: c_int = 6; + +/// 'Heavy' Userdata type managed by lua. +/// Created by [super::lua_newuserdata] pub const TUSERDATA: c_int = 7; + +/// Thread / Coroutine type, created by [super::lua_newthread] pub const TTHREAD: c_int = 8; +/// Minimum number of stack levels guaranteed to C whenever it is called into by lua. pub const MINSTACK: c_int = 20; +/// OK status code used by several functions like [super::lua_status], [super::lua_pcall] pub const OK: c_int = 0; + +/// YIELD status code used by [super::lua_status] pub const YIELD: c_int = 1; + +/// Runtime error, code used by functions like [super::lua_pcall] pub const ERRRUN: c_int = 2; + +/// Syntax error, code used by functions like [super::lua_load] pub const ERRSYNTAX: c_int = 3; + +/// Memory allocation, error code used by many functions like [super::lua_load] pub const ERRMEM: c_int = 4; + +/// Error when running the error handler, code used by functions like [super::lua_pcall] pub const ERRERR: c_int = 5; +/// Enum used with [super::lua_gc] - Stops the garbage collector. pub const GCSTOP: c_int = 0; + +/// Enum used with [super::lua_gc] - Restarts the garbage collector pub const GCRESTART: c_int = 1; + +/// Enum used with [super::lua_gc] - Restarts the garbage collector pub const GCCOLLECT: c_int = 2; + +/// Enum used with [super::lua_gc] - Returns the total number of live Lua objects in the current Lua state pub const GCCOUNT: c_int = 3; + +/// Enum used with [super::lua_gc] - Returns the total number of live Lua objects in the current Lua state, plus the total number of Lua objects in unreachable threads pub const GCCOUNTB: c_int = 4; + +/// Enum used with [super::lua_gc] - Performs a single step of the garbage collector. pub const GCSTEP: c_int = 5; + +/// Enum used with [super::lua_gc] - Sets `lua_gc`'s pause threshold. pub const GCSETPAUSE: c_int = 6; + +/// Enum used with [super::lua_gc] - Sets `lua_gc`'s step multiplier. pub const GCSETSTEPMUL: c_int = 7; pub const HOOKCALL: c_int = 0; @@ -49,126 +102,62 @@ pub const HOOKLINE: c_int = 2; pub const HOOKCOUNT: c_int = 3; pub const HOOKTAILRET: c_int = 4; +/// Enum used by [super::lua_sethook] pub const MASKCALL: c_int = 1 << HOOKCALL; +/// Enum used by [super::lua_sethook] pub const MASKRET: c_int = 1 << HOOKRET; +/// Enum used by [super::lua_sethook] pub const MASKLINE: c_int = 1 << HOOKLINE; +/// Enum used by [super::lua_sethook] pub const MASKCOUNT: c_int = 1 << HOOKCOUNT; -/// Size of LuaDebug.short_src +/// Size of [LuaDebug].short_src pub const IDSIZE: usize = 128; -// This is libc's default so we'll roll with it +/// This is libc's default so we'll roll with it +/// Used internally for [LuaBuffer]. pub const BUFFERSIZE: usize = 8192; -// Rust doesn't work well with C Enums. So I'm just going to ditch the idea. -#[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] -#[allow(non_snake_case)] -pub mod Type { - #![allow(non_upper_case_globals)] - - pub const None: i32 = -1; - pub const Nil: i32 = 0; - pub const Bool: i32 = 1; - pub const LUserdata: i32 = 2; - pub const Number: i32 = 3; - pub const String: i32 = 4; - pub const Table: i32 = 5; - pub const Function: i32 = 6; - pub const Userdata: i32 = 7; - pub const Thread: i32 = 8; -} - -#[repr(i32)] -#[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] -pub enum Status { - Ok = 0, - Yield, - ErrRun, - ErrSyntax, - ErrMem, - ErrErr, -} - -// Garbage collection -#[repr(i32)] -#[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] -pub enum Gc { - Stop = 0, - Restart, - Collect, - Count, - CountB, - Step, - SetPause, - SetStepMul, - IsRunning, - Gen, - Inc, // 11 -} - -// To be used with debug.sethook -#[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] -pub enum Hook { - Call = 0, - Ret, - Line, - Count, - TailCall, -} - -#[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] -pub enum Mask { - #[allow(deprecated)] - Call = (1 << Hook::Call as i32), - #[allow(deprecated)] - Ret = (1 << Hook::Ret as i32), - #[allow(deprecated)] - Line = (1 << Hook::Line as i32), - #[allow(deprecated)] - Count = (1 << Hook::Count as i32), -} - +/// LuaJIT specific global constants pub mod jit { use super::c_int; + /// Version of LuaJIT that garrysmod uses pub const VERSION: &str = "LuaJIT 2.0.4"; + /// Semver number pub const VERSION_NUM: c_int = 20004; /* Version 2.0.4 = 02.00.04. */ + /// Enum used by [crate::lua::luaJIT_setmode] pub const MODE_MASK: c_int = 0x00ff; - pub const MODE_ENGINE: c_int = 1; /* Set mode for whole JIT engine. */ - pub const MODE_DEBUG: c_int = 2; /* Set debug mode (idx = level). */ - pub const MODE_FUNC: c_int = 3; /* Change mode for a function. */ - pub const MODE_ALLFUNC: c_int = 4; /* Recurse into subroutine protos. */ - pub const MODE_ALLSUBFUNC: c_int = 5; /* Change only the subroutines. */ - pub const MODE_TRACE: c_int = 6; /* Flush a compiled trace. */ - pub const MODE_WRAPCFUNC: c_int = 0x10; /* Set wrapper mode for C function calls. */ + /// Enum used by [crate::lua::luaJIT_setmode] -- Set mode for the whole JIT engine + pub const MODE_ENGINE: c_int = 1; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Set debug mode (idx = level). + pub const MODE_DEBUG: c_int = 2; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Change mode for a function. + pub const MODE_FUNC: c_int = 3; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Recurse into subroutine protos. + pub const MODE_ALLFUNC: c_int = 4; + /// Enum used by [crate::lua::luaJIT_setmode] -- Change only the subroutines. + pub const MODE_ALLSUBFUNC: c_int = 5; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Flush a compiled trace. + pub const MODE_TRACE: c_int = 6; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Set wrapper mode for C function calls. + pub const MODE_WRAPCFUNC: c_int = 0x10; + pub const MODE_MAX: c_int = MODE_WRAPCFUNC + 1; - pub const MODE_OFF: c_int = 0x0000; /* Turn feature off. */ - pub const MODE_ON: c_int = 0x0100; /* Turn feature on. */ - pub const MODE_FLUSH: c_int = 0x0200; /* Flush JIT-compiled code. */ - - #[repr(i32)] - #[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] - pub enum Mode { - ENGINE, - DEBUG, - FUNC, - ALLFUNC, - ALLSUBFUNC, - TRACE, - WRAPCFUNC = 0x10, - MAX, - MASK = 0x0ff, // LUAJIT_MODE_MASK - } - - #[deprecated(since = "0.9.1", note = "Use rglua::lua::T* instead")] - #[allow(deprecated)] - // Associated Constants, woah - impl Mode { - pub const OFF: c_int = 0x0000; - pub const ON: c_int = 0x0100; - pub const FLUSH: c_int = 0x0200; - } + /// Enum used by [crate::lua::luaJIT_setmode] -- Turn a feature off + pub const MODE_OFF: c_int = 0x0000; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Turn a feature on + pub const MODE_ON: c_int = 0x0100; + + /// Enum used by [crate::lua::luaJIT_setmode] -- Flush JIT compiled code + pub const MODE_FLUSH: c_int = 0x0200; } diff --git a/rglua/src/lua/mod.rs b/rglua/src/lua/mod.rs index b52e5744..5bc52532 100644 --- a/rglua/src/lua/mod.rs +++ b/rglua/src/lua/mod.rs @@ -12,13 +12,12 @@ pub use globals::*; pub mod types; pub use types::*; -// Keep separate in case needed by crates. -pub static GMOD_DIR: Lazy = Lazy::new(|| { +static GMOD_DIR: Lazy = Lazy::new(|| { // Get the attached process. If you inject or run a binary module, will always GarrysMod directory. If not then you did something wrong. std::env::current_dir().expect("Couldn't get current_dir.") }); -// Let me know if there's a neater way to do this. +/// Path to lua_shared.dll relative to [std::env::current_dir()] pub static LUA_SHARED_PATH: Lazy> = Lazy::new(|| { let mut full: PathBuf; @@ -46,6 +45,9 @@ pub static LUA_SHARED_PATH: Lazy> = Lazy::new(|| { Some(full) }); +/// Path to lua_shared.dll relative to [std::env::current_dir()] +/// This tries to retrieve [LUA_SHARED_PATH], creates a [libloading::Library] to it and returns it. +/// If it could not find lua_shared.dll or create a [libloading::Library], this will panic! pub static LUA_SHARED_RAW: Lazy = Lazy::new(|| { let path = LUA_SHARED_PATH .as_ref() diff --git a/rglua/src/lua/shared.rs b/rglua/src/lua/shared.rs index a067bd7f..4367984f 100644 --- a/rglua/src/lua/shared.rs +++ b/rglua/src/lua/shared.rs @@ -1,6 +1,6 @@ use crate::{ - lua::{self, GLOBALSINDEX}, - types::*, + lua::{self, *}, + userdata::{Angle, Vector}, }; use super::LUA_SHARED_RAW; @@ -19,6 +19,18 @@ macro_rules! dyn_symbols { dyn_symbols!( $($rest)* ); }; + ( + $(#[$outer:meta])* + $vis:vis extern $abi:literal fn $name:ident <$generic:ident>( $($arg:ident : $argty:ty),* $(,)? ) -> $ret:ty; $($rest:tt)* + ) => { + $(#[$outer])* + #[allow(non_upper_case_globals)] + pub static $name: Lazy $ret> = Lazy::new(|| unsafe { + std::mem::transmute( LUA_SHARED_RAW.get:: $ret>( stringify!($name).as_bytes() ).unwrap() ) + }); + dyn_symbols!( $($rest)* ); + }; + ( $(#[$outer:meta])* $vis:vis extern $abi:literal fn $name:ident ( $($arg:ident : $argty:ty),+ , ... ) -> $ret:ty; $($rest:tt)* @@ -45,13 +57,18 @@ macro_rules! lua_macros { $vis fn $name( $($arg: $argty),* ) -> $ret $body lua_macros!( $($rest)* ); }; + () => () } // Create Lazy cells that'll find the functions at runtime when called. -// Special thanks to https://pgl.yoyo.org/luai/i/about for excellent examples and descriptions of the lua c api that could be integrated here. -// (Were tweaked to be more concise and fit with rglua of course.) +// Credit to https://pgl.yoyo.org/luai/i/about for most of the documentation below here. +// (Of course they were tweaked to be more concise and fit for this library) + +// Loading functions dyn_symbols! { + /// Function used by [luaL_loadbuffer]. + /// pub extern "C" fn luaL_loadbufferx( l: LuaState, code: LuaString, @@ -60,6 +77,8 @@ dyn_symbols! { mode: LuaString, ) -> c_int; + /// Loads a buffer as a Lua chunk. + /// This function uses [lua_load] to load the chunk in the buffer pointed to by buff with size ``sz``. pub extern "C" fn luaL_loadbuffer( l: LuaState, code: LuaString, @@ -67,54 +86,246 @@ dyn_symbols! { id: LuaString, ) -> c_int; + /// Loads a Lua chunk. + /// If there are no errors, lua_load pushes the compiled chunk as a Lua function on top of the stack. + /// Otherwise, it pushes an error message. + /// # Parameters + /// * `l` - Lua state, + /// * `reader` - [LuaReader] function used to read the chunk. + /// * `data` - Opaque value (userdata) passed to the reader function + /// * `chunkname` - Name to identify the chunk, used in error messages / debugging. + /// # Returns + /// * 0 - No errors, [OK] + /// * [ERRSYNTAX] - Syntax error, + /// * [ERRMEM] - Memory allocation error, + /// # Notes + /// * This function only loads a chunk; it does not run it. + /// * [lua_load] automatically detects whether the chunk is text or binary, and loads it accordingly. + pub extern "C" fn lua_load( + l: LuaState, + reader: LuaReader, + data: *mut c_void, + chunkname: LuaString + ) -> c_int; + + /// Function used by [lua_load] internally. + /// ``mode`` is whether to take the chunk as bytecode or as text. + /// You should just use [lua_load] instead though. + pub extern "C" fn lua_loadx( + l: LuaState, + reader: LuaReader, + dt: *mut c_void, + chunkname: LuaString, + mode: LuaString, + ) -> c_int; + + /// Loads a string as a Lua chunk. This function uses [lua_load] to load the chunk in the zero-terminated string ``s``. + /// This function returns the same results as [lua_load]. + /// Also as [lua_load], this function only loads the chunk; it does not run it. pub extern "C" fn luaL_loadstring(l: LuaState, code: LuaString) -> c_int; - pub extern "C" fn luaL_loadfile(l: LuaState, filename: LuaString) -> c_int; + + /// Loads a file as a Lua chunk. + /// This function uses [lua_load] to load the chunk in the file named ``filename``. + /// If filename is None, then it loads from the standard input. + /// The first line in the file is ignored if it starts with a # (shebang) + pub extern "C" fn luaL_loadfile(l: LuaState, filename: Option) -> c_int; + + /// Same as how [lua_loadx] is to [lua_load]. + /// You should probably use [luaL_loadfile] instead. pub extern "C" fn luaL_loadfilex(l: LuaState, filename: LuaString, mode: LuaString) -> c_int; +} + +// Calling lua code +dyn_symbols! { + /// Calls a function in protected mode. + /// Both nargs and nresults have the same meaning as in [lua_call]. + /// If there are no errors during the call, [lua_pcall] behaves exactly like [lua_call]. + /// + /// However, if there is any error, [lua_pcall] catches it, pushes a single value on the stack (the error message), and returns an error code. + /// Like [lua_call], [lua_pcall] always removes the function and its arguments from the stack. + /// + /// If errfunc is 0, then the error message returned on the stack is exactly the original error message. + /// Otherwise, errfunc is the stack index of an error handler function. (In the current implementation, this index cannot be a pseudo-index like [GLOBALSINDEX]) + /// In case of runtime errors, this function will be called with the error message and its return value will be the message returned on the stack by [lua_pcall]. + /// + /// Typically, the error handler function is used to add more debug information to the error message, such as a stack traceback. + /// Such information cannot be gathered after the return of lua_pcall, since by then the stack has unwound. + /// # Returns + /// This function returns 0 in case of success or these error codes: + /// * [ERRRUN] - There was an error at runtime + /// * [ERRMEM] - There was a memory allocation error + /// * [ERRERR] - Error when running the error handler + pub extern "C" fn lua_pcall(l: LuaState, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int; + - // Call lua code - pub extern "C" fn lua_pcall(l: LuaState, nargs: c_int, nresults: c_int, msgh: c_int) -> c_int; + /// Calls a function. + /// To call a function you must use the following protocol: first, the function to be called is pushed onto the stack; + /// then, the arguments to the function are pushed in direct order -- that is, the first argument is pushed first. + /// + /// Finally you call [lua_call]. + /// # Params + /// * `l` - The lua state. + /// * `nargs` - The number of arguments that you pushed onto the stack. + /// * `nresults` - Number of expected results to push onto the stack, or [MULTRET] to push all results. + /// # Stack Behavior + /// All arguments and the function value are popped from the stack when the function is called. + /// The function results are pushed onto the stack when the function returns in direct order, so the last result is on the top of the stack. pub extern "C" fn lua_call(l: LuaState, nargs: c_int, nresults: c_int) -> c_int; + + /// Calls the C function func in protected mode. + /// ``func`` starts with only one element in its stack, a light userdata containing ud. + /// In case of errors, this returns the same error codes as lua_pcall, plus the error object on the top of the stack. + /// Otherwise, it returns zero, and does not change the stack. + /// All values returned by func are discarded. pub extern "C" fn lua_cpcall(l: LuaState, func: LuaCFunction, userdata: *mut c_void) -> c_int; + + /// Calls a metamethod. + /// If the object at index obj has a metatable and this metatable has a field e, this function calls this field and passes the object as its only argument. + /// # Returns + /// In this case this function returns 1 and pushes onto the stack the value returned by the call. + /// If there is no metatable or no metamethod, this function returns 0 (without pushing any value on the stack). pub extern "C" fn luaL_callmeta(l: LuaState, obj: c_int, name: LuaString) -> c_int; +} - // Setters +dyn_symbols! { + /// Does the equivalent to t\[k\] = v, where t is the value at the given valid index and v is the value at the top of the stack. + /// This function pops the value from the stack. + /// As in Lua, this function may trigger the __newindex metamethod. pub extern "C" fn lua_setfield(l: LuaState, idx: c_int, name: LuaString) -> (); + /// Pops a table from the stack and sets it as the new metatable for the value at the given acceptable index. pub extern "C" fn lua_setmetatable(l: LuaState, idx: c_int) -> (); + + /// Accepts any acceptable index, or 0, and sets the stack top to this index. + /// If the new top is larger than the old one, then the new elements are filled with nil. + /// If index is 0, then all stack elements are removed. pub extern "C" fn lua_settop(l: LuaState, ind: c_int) -> (); + + /// Pops a table from the stack and sets it as the new environment for the value at the given index. + /// # Returns + /// If the value at the given index is neither a function nor a thread nor a userdata, returns 0. + /// Otherwise returns 1. pub extern "C" fn lua_setfenv(l: LuaState, idx: c_int) -> c_int; + + /// Does the equivalent to t\[k\] = v, where t is the value at the given valid index, v is the value at the top of the stack, and k is the value just below the top. pub extern "C" fn lua_settable(l: LuaState, idx: c_int) -> (); - pub extern "C" fn lua_rawset(l: LuaState, idx: c_int) -> (); // lua_settable but no metamethods called - pub extern "C" fn lua_rawseti(l: LuaState, idx: c_int, n: c_int) -> (); // t[n] = v - // Getters + /// Same as lua_settable, but without calling any metamethods. + pub extern "C" fn lua_rawset(l: LuaState, idx: c_int) -> (); + + /// Does the equivalent of t\[n\] = v, where t is the value at the given valid index and v is the value at the top of the stack. + /// This function pops the value from the stack. The assignment is raw; that is, it does not invoke metamethods. + pub extern "C" fn lua_rawseti(l: LuaState, idx: c_int, n: c_int) -> (); +} + +// Getters +dyn_symbols! { + /// Pushes onto the stack the value t\[k\], where t is the value at the given valid index and k is the value at the top of the stack. + /// This function pops the key from the stack (putting the resulting value in its place). As in Lua, this function may trigger a metamethod for the "index" event (see ยง2.8). pub extern "C" fn lua_gettable(l: LuaState, idx: c_int) -> (); - pub extern "C" fn lua_rawget(l: LuaState, idx: c_int) -> (); // lua_gettable but no metamethods called - pub extern "C" fn lua_rawgeti(l: LuaState, idx: c_int, n: c_int) -> (); // lua_gettable but no metamethods called + + /// This is the same as lua_gettable, but without calling any metamethods + pub extern "C" fn lua_rawget(l: LuaState, idx: c_int) -> (); + + /// Pushes onto the stack the value t\[n\], where t is the value at the given valid index. + /// The access is raw; that is, it does not invoke metamethods. + pub extern "C" fn lua_rawgeti(l: LuaState, idx: c_int, n: c_int) -> (); + + /// Pushes onto the stack the environment table of the value at the given index. pub extern "C" fn lua_getfenv(l: LuaState, idx: c_int) -> (); + /// Pushes onto the stack the metatable of the value at the given acceptable index. + /// If the index is not valid, or if the value does not have a metatable, the function returns 0 and pushes nothing on the stack. + pub extern "C" fn lua_getmetatable(l: LuaState, idx: c_int) -> c_int; + + /// Pushes onto the stack the value t\[k\], where t is the value at ``idx``. + /// As in Lua, this function may trigger a metamethod for the "index" event (see ยง2.8). pub extern "C" fn lua_getfield(l: LuaState, idx: c_int, key: LuaString) -> (); +} - // Non-stack getters +// Non-stack getters +dyn_symbols! { + /// Returns the type of the value in the given acceptable index, or LUA_TNONE for a non-valid index (that is, an index to an "empty" stack position). + /// The types returned by lua_type are coded by the following constants: + /// [TNIL], [TNUMBER], [TBOOLEAN], [TSTRING], [TTABLE], [TFUNCTION], [TUSERDATA], [TTHREAD], and [TLIGHTUSERDATA]. pub extern "C" fn lua_type(l: LuaState, idx: c_int) -> c_int; + + /// Returns the name of the type ``typeid`` which must be one the values returned by [lua_type]. + /// Use [luaL_typename] if you want to get it directly from a value in the stack. pub extern "C" fn lua_typename(l: LuaState, typeid: c_int) -> LuaString; // To be used with the return value of lua_type // Type conversion getters - pub extern "C" fn lua_tolstring(l: LuaState, ind: c_int, size: SizeT) -> LuaString; + + /// Converts the Lua value at the given index to a C string. + /// If len is not 0, it also sets *len with the string length. + /// The Lua value must be a string or a number; otherwise, the function returns a nullptr. + /// If the value is a number, then lua_tolstring also changes the actual value in the stack to a string. + /// (This change confuses lua_next when lua_tolstring is applied to keys during a table traversal.) + pub extern "C" fn lua_tolstring(l: LuaState, ind: c_int, size: SizeT) -> Option; + + /// Converts the Lua value at the given acceptable index to a C boolean value (0 or 1). + /// Like all tests in Lua, lua_toboolean returns 1 for any Lua value different from false and nil; otherwise returning 0. + /// This also returns 0 when called with a non-valid index. (If you want to accept only actual boolean values, use [lua_isboolean] to test the value's type.) pub extern "C" fn lua_toboolean(l: LuaState, idx: c_int) -> c_int; - pub extern "C" fn lua_tocfunction(l: LuaState, idx: c_int) -> LuaCFunction; + + /// Converts a value at the given acceptable index to a C function. + /// That value must be a C function; otherwise, returns None. + /// # Example + /// ```rust + /// use rglua::prelude::*; + /// #[gmod_open] + /// fn entry(l: LuaState) -> i32 { + /// lua_getglobal(l, cstr!("CurTime")); + /// let curtime = lua_tocfunction(l, -1).unwrap(); + /// 0 + /// } + /// ``` + pub extern "C" fn lua_tocfunction(l: LuaState, idx: c_int) -> Option; + + /// Converts the Lua value at the given acceptable index to the signed integral type [LuaInteger]. + /// The Lua value must be a number or a string convertible to a number; otherwise, this returns 0. + /// If the number is not an integer, it is truncated in some non-specified way. pub extern "C" fn lua_tointeger(l: LuaState, idx: c_int) -> LuaInteger; + + /// Converts the Lua value at the given acceptable index to a [LuaNumber]. + /// The Lua value must be a number or a string convertible to a number; otherwise, this returns 0. pub extern "C" fn lua_tonumber(l: LuaState, idx: c_int) -> LuaNumber; - pub extern "C" fn lua_topointer(l: LuaState, idx: c_int) -> *mut c_void; - pub extern "C" fn lua_tothread(l: LuaState, idx: c_int) -> LuaState; - pub extern "C" fn lua_touserdata(l: LuaState, idx: c_int) -> *mut c_void; - // Push functions + /// Converts the value at the given acceptable index to a generic C pointer (void*). + /// The value can be a userdata, a table, a thread, or a function; otherwise this returns None. + /// Different objects will give different pointers. + /// There is no way to convert the pointer back to its original value. + pub extern "C" fn lua_topointer(l: LuaState, idx: c_int) -> Option<*mut c_void>; + + /// Converts the value at the given acceptable index to a Lua thread (represented as lua_State*). + /// This value must be a thread; otherwise, the function returns None. + pub extern "C" fn lua_tothread(l: LuaState, idx: c_int) -> Option; + + /// Returns the value at the given index assuming it is a userdata. + /// # Returns + /// If the value at the given acceptable index is a full userdata, returns its block address. + /// If the value is a light userdata, returns its pointer. + /// Otherwise, returns None. + pub extern "C" fn lua_touserdata(l: LuaState, idx: c_int) -> Option<*mut c_void>; +} + +dyn_symbols! { + /// Pushes the zero-terminated string pointed to by s onto the stack. Lua makes (or reuses) an internal copy of the given string, so the memory at s can be freed or reused immediately after the function returns. The string cannot contain embedded zeros; it is assumed to end at the first zero. pub extern "C" fn lua_pushstring(l: LuaState, s: LuaString) -> (); + + /// Pushes a boolean onto the stack. Note this is still a [c_int] so use 0 for false and 1 for true. pub extern "C" fn lua_pushboolean(l: LuaState, s: c_int) -> (); + + /// Pushes a string of length ``sz`` onto the stack. pub extern "C" fn lua_pushlstring(l: LuaState, s: LuaString, sz: SizeT) -> (); + + /// Pushes a `nil` value onto the stack. pub extern "C" fn lua_pushnil(l: LuaState) -> (); + + /// Pushes the number ``num`` onto the stack. pub extern "C" fn lua_pushnumber(l: LuaState, num: LuaNumber) -> (); + + /// Pushes a copy of the element at the given valid index onto the stack. pub extern "C" fn lua_pushvalue(l: LuaState, idx: c_int) -> (); /// Pushes a c function on the stack with associated values. /// # Parameters @@ -122,70 +333,126 @@ dyn_symbols! { /// * `f` - Lua function /// * `n` - Number of upvalues to associate and pull from stack with the function pub extern "C" fn lua_pushcclosure(l: LuaState, fnc: LuaCFunction, nargs: c_int) -> (); + + /// Pushes a light userdata onto the stack. + /// Userdata represent C values in Lua. + /// A light userdata represents a pointer. + /// It is a value (like a number): you do not create it, it has no individual metatable, and it is not collected (as it was never created). + /// A light userdata is equal to "any" light userdata with the same C address. pub extern "C" fn lua_pushlightuserdata(l: LuaState, p: *mut c_void) -> (); + /// Pushes a given thread (representing ``l``) to the stack. /// # Parameters /// * `l` - The thread to push. /// # Returns /// 1 if the thread is the main thread of the state. pub extern "C" fn lua_pushthread(l: LuaState) -> c_int; - /// Pushes a formatted LuaString to the stack + + /// Pushes a formatted [LuaString] to the stack pub extern "C" fn lua_pushfstring(l: LuaState, fmt: LuaString, ...) -> LuaString; + + /// Pushes a number with value ``n`` onto the stack. pub extern "C" fn lua_pushinteger(l: LuaState, n: LuaInteger) -> (); +} - // Type checking getters +// Type checking getters +dyn_symbols! { /// Same as luaL_checknumber, but casts it to an integer. pub extern "C" fn luaL_checkinteger(l: LuaState, narg: c_int) -> LuaInteger; /// Checks whether the value at stack index 'narg' is a number and returns this number. /// If it is not a lua number, will throw an error to Lua. pub extern "C" fn luaL_checknumber(l: LuaState, narg: c_int) -> LuaNumber; + + /// Checks whether the function argument ``narg`` is a string and returns this string. + /// If len is not 0 fills *len with the string's length. pub extern "C" fn luaL_checklstring(l: LuaState, narg: c_int, len: SizeT) -> LuaString; - // Type checking getters that push to stack - pub extern "C" fn luaL_checkstack(l: LuaState, size: c_int, msg: LuaString) -> (); + /// Checks whether the function has an argument of any type (including nil) at position narg. pub extern "C" fn luaL_checkany(l: LuaState, narg: c_int) -> (); + + /// Checks whether the function argument narg has type ``t``. + /// See [lua_type] for the encoding of types for ``t``. pub extern "C" fn luaL_checktype(l: LuaState, narg: c_int, typeid: c_int) -> (); - pub extern "C" fn luaL_checkudata(l: LuaState, narg: c_int, len: SizeT) -> (); - // Creation - pub extern "C" fn luaL_newstate() -> LuaState; - pub extern "C" fn lua_newstate(f: LuaAlloc, ud: *mut c_void) -> c_int; + /// Checks whether the function argument narg is a userdata of the type tname (see luaL_newmetatable). + pub extern "C" fn luaL_checkudata(l: LuaState, ud: c_int, tname: LuaString) -> *mut Userdata; +} + +// Creation +dyn_symbols! { + /// Creates a new Lua state. + /// This calls [lua_newstate] with an allocator based on the standard C realloc function and then sets a panic function (see lua_atpanic) that prints an error message to the standard error output in case of fatal errors. + /// # Returns + /// The newly created [LuaState], or None if the allocation failed (due to memory). + pub extern "C" fn luaL_newstate() -> Option; + + /// Creates a new, independent state. + /// Note you might be looking for [luaL_newstate], which has no parameters + /// Returns None if cannot create the state (due to lack of memory). + /// The argument f is the allocator function; + /// Lua does all memory allocation for this state through this function. + /// The second argument, ud, is an opaque pointer that Lua simply passes to the allocator in every call. + pub extern "C" fn lua_newstate(f: LuaAlloc, ud: *mut c_void) -> Option; + + /// Creates a new empty table and pushes it onto the stack. + /// The new table has space pre-allocated for ``narr`` array elements and ``nrec`` non-array elements. + /// This pre-allocation is useful when you know exactly how many elements the table will have. + /// Otherwise you can use the function [lua_newtable]. pub extern "C" fn lua_createtable(l: LuaState, narr: c_int, nrec: c_int) -> (); +} - // Destruction +// Destruction +dyn_symbols! { /// Destroys the given lua state. /// You *probably* don't want to do this, unless you just want to self destruct the server / your client. pub extern "C" fn lua_close(l: LuaState) -> (); +} - // JIT - // Returns 1 for success, 0 for failure - pub extern "C" fn luaJIT_setmode(l: LuaState, idx: c_int, jit_mode: c_int) -> c_int; - pub extern "C" fn luaJIT_profile_stop(l: LuaState) -> (); - - pub extern "C" fn luaJIT_profile_start( - l: LuaState, - mode: LuaString, - cb: LuaJITProfileCallback, - data: *mut c_void, - ) -> (); - pub extern "C" fn luaJIT_profile_dumpstack( - l: LuaState, - fmt: LuaString, - depth: c_int, - len: SizeT, - ) -> LuaString; +// LuaJIT +dyn_symbols! { + /// This is a C API extension to allow control of the VM from "C" + /// # Parameters + /// * `l` - Lua state + /// * `idx` - Stack index of the function to set the mode of. None to set the mode of the entirety of luajit. + /// * `mode` - The mode to set, 'or'ed with a flag from [lua::jit] + /// # Returns + /// 1 for success, 0 for failure. + pub extern "C" fn luaJIT_setmode(l: LuaState, idx: Option, jit_mode: c_int) -> c_int; +} - // Coroutines +// Coroutines +dyn_symbols! { + /// Yields a coroutine. + /// This function should only be called as the return expression of a C function, as follows: + /// ```ignore + /// return lua_yield (L, nresults); + /// ``` + /// When a function calls [lua_yield] in that way, the running coroutine suspends its execution, and the call to [lua_resume] that started this coroutine returns. + /// The parameter nresults is the number of values from the stack that are passed as results to [lua_resume]. pub extern "C" fn lua_yield(l: LuaState, nresults: c_int) -> c_int; + + /// Returns the status of the thread/coroutine l. + /// # Returns + /// 0 for a normal thread, error code if it's finished with an error, or [lua::YIELD] if it is suspended. pub extern "C" fn lua_status(l: LuaState) -> c_int; + /// Starts and resumes a coroutine in a given thread. /// Blame garry for the _real pub extern "C" fn lua_resume_real(l: LuaState, narg: c_int) -> c_int; +} - // Comparison +// Comparison +dyn_symbols! { + /// Returns 1 or 0 for if the two values at given indices are equal, calling ``__eq`` metamethods along the way unlike [lua_rawequal]. + /// Also returns 0 if any of the indices are non valid. pub extern "C" fn lua_equal(l: LuaState, ind1: c_int, ind2: c_int) -> c_int; // Returns 1 or 0 bool + + /// Returns 1 or 0 for if the two values at given indices are equal, without calling metamethods, as [lua_equal] does. + /// Also returns 0 if any of the indices are non valid. pub extern "C" fn lua_rawequal(l: LuaState, ind1: c_int, ind2: c_int) -> c_int; +} +dyn_symbols! { // Raising Errors /// Generates an error with a message like the following: /// ```text @@ -193,15 +460,26 @@ dyn_symbols! { /// ``` /// where location is produced by luaL_where, func is the name of the current function, and rt is the type name of the actual argument. pub extern "C" fn luaL_typerror(l: LuaState, narg: c_int, typename: LuaString) -> !; + + /// Raises an error. + /// The error message format is given by fmt plus any extra arguments, following the same rules of [lua_pushfstring]. + /// It also adds at the beginning of the message the file name and the line number where the error occurred, if this information is available. pub extern "C" fn luaL_error(l: LuaState, fmt: LuaString, ...) -> !; + /// Raises an error with the following message, where func is retrieved from the call stack: /// ```text /// bad argument # to () /// ``` /// This function never returns pub extern "C" fn luaL_argerror(l: LuaState, narg: c_int, extramsg: LuaString) -> !; + + /// Generates a Lua error. + /// The error message (which can actually be a Lua value of any type) must be on the stack top.T + /// This function does a long jump, and therefore never returns. (see [luaL_error]). pub extern "C" fn lua_error(l: LuaState) -> !; +} +dyn_symbols! { // Libraries /// Opens the standard 'table' library for a lua state pub extern "C" fn luaopen_table(l: LuaState) -> c_int; @@ -226,14 +504,19 @@ dyn_symbols! { /// Internally called by luaL_register, opens given list of LuaRegs with number of functions provided explicitly pub extern "C" fn luaL_openlib(l: LuaState, libname: LuaString, l: *const LuaReg, nup: c_int) -> (); - /// When called with libname as nullptr, it simply registers all functions in the list l reg! into the table on the top of the stack. + /// Registers a ``reg`` of functions onto the Lua State's _G\[libname\]. + /// For example you could set libname to cstr!("math") to add functions onto the ``math`` table or create it if it does not exist. + /// + /// When called with libname as std::ptr::null(), it simply registers all functions in the list ``lib`` into the table on the top of the stack. /// # Example /// ```rust /// use rglua::prelude::*; - /// extern "C" fn add(l: LuaState) -> i32 {0} - /// extern "C" fn sub(l: LuaState) -> i32 {0} /// - /// extern "C" fn gmod13_open(l: LuaState) -> i32 { + /// #[lua_function] fn add(l: LuaState) -> i32 {0} + /// #[lua_function] fn sub(l: LuaState) -> i32 {0} + /// + /// #[gmod_open] + /// fn entry(l: LuaState) -> i32 { /// let lib = reg! [ /// "add" => add, /// "subtract" => sub @@ -242,40 +525,106 @@ dyn_symbols! { /// 0 /// } /// ``` - pub extern "C" fn luaL_register(l: LuaState, libname: LuaString, l: *const LuaReg) -> (); + pub extern "C" fn luaL_register(l: LuaState, libname: LuaString, lib: *const LuaReg) -> (); +} - // Ref +dyn_symbols! { + /// Creates and returns a reference, in the table at index t, for the object at the top of the stack (and pops the object). + /// A reference is a unique integer key. + /// As long as you do not manually add integer keys into table t, luaL_ref ensures the uniqueness of the key it returns. + /// You can retrieve an object referred by reference r by calling lua_rawgeti(L, t, r). + /// Function luaL_unref frees a reference and its associated object. + /// + /// If the object at the top of the stack is nil, luaL_ref returns the constant LUA_REFNIL. + /// The constant LUA_NOREF is guaranteed to be different from any reference returned by luaL_ref. pub extern "C" fn luaL_ref(l: LuaState, t: c_int) -> c_int; + + /// Releases reference ref from the table at index t (see luaL_ref). + /// The entry is removed from the table, so that the referred object can be collected. + /// The reference ref is also freed to be used again. + /// If ref is LUA_NOREF or LUA_REFNIL, this does nothing. pub extern "C" fn luaL_unref(l: LuaState, t: c_int, r: c_int) -> (); +} - // Metatables +// Metatables +dyn_symbols! { + /// If the registry already has the key tname, returns 0. Otherwise, creates a new table to be used as a metatable for userdata, adds it to the registry with key tname, and returns 1. + /// In both cases pushes onto the stack the final value associated with ``tname`` in the registry. pub extern "C" fn luaL_newmetatable(l: LuaState, tname: LuaString) -> c_int; - pub extern "C" fn luaL_newmetatable_type(l: LuaState, tname: LuaString, typ: c_int) -> c_int; + + /// Creates a metatable with type and typeid + /// Same as luaL_newmetatable, but also sets the MetaName and MetaID fields of the metatable + /// # Parameters + /// * `l` - LuaState + /// * `tname` - TypeName to be added to the metatable + /// * `tid` - TypeID to be applied to the metatable + pub extern "C" fn luaL_newmetatable_type(l: LuaState, tname: LuaString, tid: c_int) -> c_int; + + /// Pushes onto the stack the field ``e`` from the metatable of the object at index ``obj``. + /// If the object does not have a metatable, or if the metatable does not have this field, returns 0 and pushes nothing. pub extern "C" fn luaL_getmetafield(l: LuaState, obj: c_int, e: LuaString) -> c_int; +} - // Optional / Default to ``d`` +// Optional +dyn_symbols! { + /// If the function argument ``narg`` is a number, returns this number cast to a [LuaInteger]. + /// If this argument is absent or is nil, returns d. Otherwise, raises an error. pub extern "C" fn luaL_optinteger(l: LuaState, narg: c_int, d: LuaInteger) -> c_int; - pub extern "C" fn luaL_optlstring(l: LuaState, arg: c_int, d: LuaString, l: SizeT) + + /// If the function argument narg is a string, returns this string. + /// If this argument is absent or is nil, returns d. Otherwise, raises an error. + /// + /// If ``sz`` is not 0, fills the position *``sz`` with the results's length. + pub extern "C" fn luaL_optlstring(l: LuaState, arg: c_int, d: LuaString, sz: SizeT) -> LuaString; + + /// If the function argument ``arg`` is a number, returns this number. + /// If this argument is absent or is nil, returns ``d``. Otherwise, raises an error. pub extern "C" fn luaL_optnumber(l: LuaState, arg: c_int, d: LuaNumber) -> LuaNumber; +} +dyn_symbols! { // x / ref functions - pub extern "C" fn lua_tointegerx(l: LuaState, index: c_int, isnum: *mut c_int) -> LuaInteger; - pub extern "C" fn lua_tonumberx(l: LuaState, index: c_int, isnum: *mut c_int) -> LuaNumber; + /// Converts the Lua value at the given index to the signed integral type lua_Integer. + /// The Lua value must be an integer, or a number or string convertible to an integer; otherwise, this returns 0. + /// If ``isnum`` is not None, its referent is assigned a boolean value that indicates whether the operation succeeded. + pub extern "C" fn lua_tointegerx(l: LuaState, index: c_int, isnum: Option<*mut c_int>) -> LuaInteger; + - // Debug + /// Converts the Lua value at the given index to a LuaNumber (f64). + /// The Lua value must be a number or a string convertible to a number; otherwise, this returns 0. + /// If ``isnum`` is not None, its referent is assigned a boolean value that indicates whether the operation succeeded. + pub extern "C" fn lua_tonumberx(l: LuaState, index: c_int, isnum: Option<*mut c_int>) -> LuaNumber; +} + +dyn_symbols! { + /// Creates and pushes a traceback of the stack L1. + /// If msg is not None it is appended at the beginning of the traceback. + /// The level parameter tells at which level to start the traceback. pub extern "C" fn luaL_traceback( l: LuaState, state1: LuaState, - msg: LuaString, + msg: Option, level: c_int, ) -> (); + + /// Pushes onto the stack a string identifying the current position of the control at level ``lvl`` in the call stack. + /// Typically this string has the following format: + /// ```text + /// chunkname:currentline: + /// ``` + /// Level 0 is the running function, level 1 is the function that called the running function, etc. + /// This function is used to build a prefix for error messages. pub extern "C" fn luaL_where(l: LuaState, lvl: c_int) -> (); - // Misc - pub extern "C" fn luaL_testudata(l: LuaState, arg: c_int, tname: LuaString) -> (); + /// This function produces the return values for process-related functions in the standard library (os.execute and io.close). + /// Although, those don't exist in gmod.. pub extern "C" fn luaL_execresult(l: LuaState, stat: c_int) -> c_int; + + /// This function produces the return values for file-related functions in the standard library (like File:seek) pub extern "C" fn luaL_fileresult(l: LuaState, stat: c_int, fname: LuaString) -> c_int; + + /// Function used internally by lua pub extern "C" fn luaL_findtable( l: LuaState, idx: c_int, @@ -287,7 +636,7 @@ dyn_symbols! { /// If there are no more elements in the table, then lua_next returns 0 (and pushes nothing). /// /// # Safety - /// Do not call lua_tolstring on a string while traversing a table. This will confuse ``next`` since it modifies the key. + /// Do not call [lua_tolstring] on a string while traversing a table. This will confuse ``next`` since it modifies the key. /// /// # Examples /// ```rust @@ -317,10 +666,26 @@ dyn_symbols! { /// Otherwise returns 0. Also returns 0 if any of the indices is non valid. pub extern "C" fn lua_lessthan(l: LuaState, idx1: c_int, idx2: c_int) -> c_int; + /// Ensures that there are at least extra free stack slots in the stack. + /// It returns C 'false' if it cannot grow the stack to that size. + /// This function never shrinks the stack; if the stack is already larger than the new size, it is left unchanged. pub extern "C" fn lua_checkstack(l: LuaState, extra: c_int) -> c_int; - /// Sets the error handler for the lua state. + + /// Sets a new panic function and returns the old one. + /// If an error happens outside any protected environment, Lua calls a panic function and then calls exit(EXIT_FAILURE), thus exiting the host application. + /// Your panic function can avoid this exit by never returning (e.g., doing a long jump). + /// The panic function can access the error message at the top of the stack. + /// # Returns + /// The old panic function. pub extern "C" fn lua_atpanic(l: LuaState, panicf: LuaCFunction) -> LuaCFunction; + + /// Returns the index of the top element in the stack. + /// Because indices start at 1, this result is equal to the number of elements in the stack (and so 0 means an empty stack). pub extern "C" fn lua_gettop(l: LuaState) -> c_int; + + /// Removes the element at the given valid index, shifting down the elements above this index to fill the gap. + /// Cannot be called with a pseudo-index, because a pseudo-index is not an actual stack position. + /// (Example of pseudoindices are LUA_GLOBALSINDEX and globals::REGISTRYINDEX) pub extern "C" fn lua_remove(l: LuaState, index: c_int) -> (); /// Controls lua's garbage collector @@ -349,6 +714,17 @@ dyn_symbols! { /// There is no explicit function to close or to destroy a thread. Threads are subject to garbage collection, like any Lua object. pub extern "C" fn lua_newthread(l: LuaState) -> LuaState; + /// This function allocates a new block of memory with the given size, pushes onto the stack a new full userdata with the block address, and returns this address. + /// + /// Userdata represent C values in Lua. + /// A full userdata represents a block of memory. + /// It is an object (like a table): you must create it, it can have its own metatable, and you can detect when it is being collected. + /// A full userdata is only equal to itself (under raw equality). + /// + /// When Lua collects a full userdata with a gc metamethod, Lua calls the metamethod and marks the userdata as finalized. + /// When this userdata is collected again then Lua frees its corresponding memory. + pub extern "C" fn lua_newuserdata(l: LuaState, size: SizeT) -> *mut Userdata; + /// Returns information about a specific function or function invocation. /// /// To get information about a function you push it onto the stack and start the what string with the character '>'. @@ -380,11 +756,33 @@ dyn_symbols! { /// For userdata, this is the size of the block of memory allocated for the userdata; /// For other values, it is 0. pub extern "C" fn lua_objlen(l: LuaState, idx: c_int) -> SizeT; +} +// Lua Debug Library +dyn_symbols! { + /// Returns the current hook function. pub extern "C" fn lua_gethook(l: LuaState) -> LuaHook; + /// Returns the current hook count. pub extern "C" fn lua_gethookcount(l: LuaState) -> c_int; + /// Returns the current hook mask. pub extern "C" fn lua_gethookmask(l: LuaState) -> c_int; + /// Sets the debugging hook function. + /// # Parameters + /// + /// * `l` - [LuaState] + /// * `func` [LuaHook] function + /// * `mask` - Specifies on which events the hook will be called: it is formed by a bitwise or of the constants [MASKCALL], [MASKRET], [MASKLINE], and [MASKCOUNT] + /// * `count` - Only meaningful when the mask includes [MASKCOUNT]. For each event, the hook is called as explained below: + /// + /// **The call hook**: called when the interpreter calls a function. The hook is called just after Lua enters the new function, before the function gets its arguments. + /// **The return hook**: called when the interpreter returns from a function. The hook is called just before Lua leaves the function. You have no access to the values to be returned by the function. + /// **The line hook**: is called when the interpreter is about to start the execution of a new line of code, or when it jumps back in the code (even to the same line). (This event only happens while Lua is executing a Lua function.) + /// **The count hook**: is called after the interpreter executes every count instructions. (This event only happens while Lua is executing a Lua function.) + /// + /// A hook is disabled by setting ``mask`` to zero. + pub extern "C" fn lua_sethook(l: LuaState, func: LuaHook, mask: c_int, count: c_int) -> c_int; + /// Gets information about a local variable of a given activation record. /// The parameter ar must be a valid activation record that was filled by a previous call to lua_getstack or given as argument to a hook (see lua_Hook). /// The index n selects which local variable to inspect (1 is the first parameter or active local variable, and so on, until the last active local variable). @@ -396,6 +794,18 @@ dyn_symbols! { /// Get information about the interpreter runtime stack. /// This function fills in the priv part of the LuaDebug structure with information about the function that is running at the given level. pub extern "C" fn lua_getstack(l: LuaState, level: c_int, ar: *mut LuaDebug) -> c_int; + + /// Gets information about a closure's upvalue. This is basically debug.getlocal. + /// (For Lua functions, upvalues are the external local variables that the function uses, and that are consequently included in its closure.) + /// # Parameters + /// * `idx` - Index of the upvalue to push the value of onto the stack and return the name of (like debug.getlocal) + /// * `fidx` - Points to the closure in the stack. + /// # Note + /// Upvalues have no particular order, as they are active through the whole function. + /// So, they are numbered in an arbitrary order. + /// # Returns + /// The name of the upvalue at given index `idx`, or NULL (and pushes nothing) if the index is greater than the number of upvalues. + /// For C functions (functions not created in lua), this returns an empty string for the name of all upvalues pub extern "C" fn lua_getupvalue(l: LuaState, fidx: c_int, idx: c_int) -> LuaString; /// Sets the value of a closure's upvalue. Parameters funcindex and n are as in lua_getupvalue (see lua_getupvalue). It assigns the value at the top of the stack to the upvalue and returns its name. It also pops the value from the stack. @@ -406,7 +816,9 @@ dyn_symbols! { /// lua_setlocal assigns the value at the top of the stack to the variable and returns its name. /// It also pops the value from the stack. pub extern "C" fn lua_setlocal(l: LuaState, ar: *mut LuaDebug, n: c_int) -> LuaString; +} +dyn_symbols! { /// Creates a copy of string 's' by replacing any occurrence of the string 'p' with the string 'r' /// Pushes the resulting string on the stack and returns it pub extern "C" fn luaL_gsub(s: LuaString, pattern: LuaString, replace: LuaString) -> LuaString; @@ -414,11 +826,21 @@ dyn_symbols! { /// Exchange values between different threads of the same global state. /// This function pops `n` values from the stack `from`, and pushes them onto the stack `to`. pub extern "C" fn lua_xmove(from: LuaState, to: LuaState, n: c_int) -> (); +} +dyn_symbols! { + /// Returns an unique identifier for the upvalue numbered n from the closure at index funcindex. + /// Parameters funcindex and n are as in the lua_getupvalue (see lua_getupvalue) (but n cannot be greater than the number of upvalues). + /// These unique identifiers allow a program to check whether different closures share upvalues. + /// Lua closures that share an upvalue (that is, that access a same external local variable) will return identical ids for those upvalue indices. pub extern "C" fn lua_upvalueid(l: LuaState, fidx: c_int, n: c_int) -> *mut c_void; + /// Make the ``n1`` upvalue of the Lua closure at index ``fidx1`` refer to the ``n2`` upvalue of the Lua closure at index ``fidx2``. pub extern "C" fn lua_upvaluejoin(l: LuaState, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int) -> (); +} +// Buffer functions +dyn_symbols! { /// Initializes a buffer `b`. /// This function does not allocate any space; the buffer must be declared as a variable. pub extern "C" fn luaL_buffinit(l: LuaState, b: *mut LuaBuffer) -> (); @@ -441,21 +863,41 @@ dyn_symbols! { /// Finishes the use of buffer `b` leaving the final string on the top of the stack. pub extern "C" fn luaL_pushresult(b: *mut LuaBuffer) -> (); +} +dyn_symbols! { /// Returns the memory-allocation function of a given state. /// If ud is not NULL, Lua stores in *ud the opaque pointer passed to lua_newstate. pub extern "C" fn lua_getallocf(l: LuaState, ud: *mut *mut c_void) -> LuaAlloc; /// Changes the allocator function of a given state to f with user data ud. pub extern "C" fn lua_setallocf(l: LuaState, f: LuaAlloc, ud: *mut c_void) -> (); +} - pub extern "C" fn lua_loadx( - L: LuaState, - reader: LuaReader, - dt: *mut c_void, - chunkname: LuaString, - mode: LuaString, - ) -> c_int; +// Misc +dyn_symbols! { + /// Dumps a function as a binary chunk. + /// Receives a Lua function on the top of the stack and produces a binary chunk that, if loaded again, results in a function equivalent to the one dumped. As it produces parts of the chunk, lua_dump calls function writer (see lua_Writer) with the given data to write them. + pub extern "C" fn lua_dump(l: LuaState, writer: LuaWriter, data: *mut c_void) -> c_int; + + /// Grows the stack size to top + sz elements, raising an error if the stack cannot grow to that size. msg is an additional text to go into the error message. + /// # Note + /// You may be looking for [lua_checkstack] + pub extern "C" fn luaL_checkstack(l: LuaState, size: c_int, msg: LuaString) -> (); +} + +dyn_symbols! { + /// Returns 1 if the value at the given acceptable index is a number or a string convertible to a number, and 0 otherwise. + pub extern "C" fn lua_isnumber(l: LuaState, idx: c_int) -> c_int; + + /// Returns 1 if the value at the given acceptable index is a string or a number (which is always convertible to a string), and 0 otherwise. + pub extern "C" fn lua_isstring(l: LuaState, idx: c_int) -> c_int; + + /// Returns 1 if the value at the given acceptable index is a C function, and 0 otherwise. + pub extern "C" fn lua_iscfunction(l: LuaState, idx: c_int) -> c_int; + + /// Returns 1 if the value at the given acceptable index is a userdata (either full or light), and 0 otherwise. + pub extern "C" fn lua_isuserdata(l: LuaState, idx: c_int) -> c_int; } // Inline functions to mirror the C macros that come with the lua api @@ -483,7 +925,8 @@ lua_macros! { }; /// Equivalent to lua_tolstring with len equal to 0 - pub fn lua_tostring(l: LuaState, idx: c_int) -> LuaString { + /// This may return None if the value at ``idx`` is not a string or a number, use [luaL_optstring] instead if you do not desire an Option<> or unwrap when you are absolutely sure of the type. + pub fn lua_tostring(l: LuaState, idx: c_int) -> Option { lua_tolstring(l, idx, 0) }; @@ -543,7 +986,7 @@ lua_macros! { /// Returns if the code was successfully executed /// Error will be left on the stack if the code failed to execute pub fn luaL_dofile(l: LuaState, filename: LuaString) -> bool { - luaL_loadfile(l, filename) == 0 || lua_pcall(l, 0, lua::MULTRET, 0) == 0 + luaL_loadfile(l, Some(filename)) == 0 || lua_pcall(l, 0, lua::MULTRET, 0) == 0 }; /// Returns value at [crate::lua::REGISTRYINDEX] with name 'name' @@ -562,4 +1005,121 @@ lua_macros! { pub fn luaL_typename(l: LuaState, i: c_int) -> LuaString { lua_typename(l, lua_type(l, i)) }; + + /// Asserts that a string argument exists at index 'i' + pub fn luaL_checkstring(l: LuaState, i: c_int) -> LuaString { + luaL_checklstring(l, i, 0) + }; + + /// Like lua_tostring or luaL_checkstring, but instead of returning an invalid string / erroring, + /// It returns the given `default` string. + pub fn luaL_optstring(l: LuaState, i: c_int, default: LuaString) -> LuaString { + luaL_optlstring(l, i, default, 0) + }; + + /// Sets the C function ``f`` as the value of global name ``name``. + pub fn lua_register(l: LuaState, name: LuaString, f: LuaCFunction) -> () { + lua_pushcfunction(l, f); + lua_setglobal(l, name); + }; + + /// Creates a new empty table and pushes it onto the stack. + /// It is equivalent to ``lua_createtable(l, 0, 0)``. + pub fn lua_newtable(l: LuaState) -> () { + lua_createtable(l, 0, 0); + }; +} + +// Userdata helpers +lua_macros! { + pub fn luaL_checkvector(l: LuaState, narg: c_int) -> Vector { + unsafe { *( (*luaL_checkudata(l, narg, cstr!("Vector"))).data as *mut _) } + }; + + pub fn luaL_checkangle(l: LuaState, narg: c_int) -> crate::userdata::Angle { + unsafe { *( (*luaL_checkudata(l, narg, cstr!("Angle"))).data as *mut _) } + }; +} +/// Pushes a vector onto the stack +/// # Example +/// Creates a LuaCFunction that will take three number arguments and return a glua Vector type. +/// ```rust +/// use rglua::prelude::*; +/// #[lua_function] +/// fn new_vector(l: LuaState) -> i32 { +/// let x = luaL_checknumber(l, 1) as f32; +/// let y = luaL_checknumber(l, 2) as f32; +/// let z = luaL_checknumber(l, 3) as f32; +/// lua_pushvector(l, Vector::new(x, y, z)); +/// // Return one value -- the new vector +/// 1 +/// } +/// ``` +pub fn lua_pushvector(l: LuaState, v: Vector) { + let ptr = lua_newuserdata(l, std::mem::size_of::()); + + // I am an actual maniac for doing this + unsafe { + let ty = std::ptr::addr_of_mut!((*ptr).typ); + ty.write(LuaType::Vector); + + let data = std::ptr::addr_of_mut!((*ptr).data); + // FIXME: This may leak memory.. need to make sure lua actually cleans it up. + // I am assuming this will be fine since Vectors are primitive and this is lua managed userdata. + data.write(Box::into_raw(Box::new(v)) as *mut c_void); + } + + luaL_getmetatable(l, cstr!("Vector")); + lua_setmetatable(l, -2); +} + +/// Pushes an angle onto the stack. +pub fn lua_pushangle(l: LuaState, v: Angle) { + let ptr = lua_newuserdata(l, std::mem::size_of::()); + + unsafe { + let ty = std::ptr::addr_of_mut!((*ptr).typ); + ty.write(LuaType::Angle); + + let data = std::ptr::addr_of_mut!((*ptr).data); + data.write(Box::into_raw(Box::new(v)) as *mut c_void); + } + + luaL_getmetatable(l, cstr!("Angle")); + lua_setmetatable(l, -2); +} + +#[inline(always)] +#[allow(non_snake_case)] +/// Tries to see if the given value at index ``arg`` is nil or none, if so, returns ``default`` value. +/// Otherwise, runs ``func``, passing the lua state and arg indent and returns the value of that +/// # Returns +/// Type ``T`` either from the function or default value. +pub fn luaL_opt T>(l: LuaState, arg: c_int, default: T, func: F) -> T { + if lua_isnoneornil(l, arg) { + default + } else { + func(l, arg) + } +} + +/// This function works like luaL_checkudata, except that, when the test fails, it returns None instead of throwing an error. +/// Adapted from Lua 5.3, note this does not actually exist in gluajit +#[allow(non_snake_case)] +pub fn luaL_testudata(l: LuaState, arg: c_int, tname: LuaString) -> Option<*mut super::Userdata> { + if lua_isuserdata(l, arg) == 1 { + lua_getmetatable(l, arg); // Object metatable + luaL_getmetatable(l, tname); // Desired global metatable + if lua_rawequal(l, -1, -2) == 1 { + return lua_touserdata(l, arg).map(|ud| ud as *mut super::Userdata); + } + } + None +} + +#[inline(always)] +#[allow(non_snake_case)] +pub fn lua_tovector<'a>(l: LuaState, i: c_int) -> Option<&'a mut Vector> { + luaL_testudata(l, i, cstr!("Vector")) + .map(|x: *mut Userdata| unsafe { &mut *(x as *mut Vector) }) } diff --git a/rglua/src/lua/types.rs b/rglua/src/lua/types.rs index 27d0ae19..717ac358 100644 --- a/rglua/src/lua/types.rs +++ b/rglua/src/lua/types.rs @@ -1,7 +1,5 @@ -use std::os::raw as ffi; - -// C FFI Types -pub use ffi::{c_char, c_int, c_long, c_void}; +// Maybe replace with libc in the future +pub use std::os::raw::{c_char, c_int, c_void}; pub type LuaString = *const c_char; // const i8 pub type SizeT = usize; @@ -24,6 +22,12 @@ pub type LuaAlloc = pub type LuaReader = extern "C" fn(LuaState, ud: *mut c_void, sz: *mut SizeT) -> *const u8; +/// The type of the writer function used by lua_dump. +/// Every time it produces another piece of chunk, lua_dump calls the writer, passing along the buffer to be written (p), its size (sz), and the data parameter supplied to lua_dump. +/// # Returns +/// The writer returns an error code: 0 means no errors; any other value means an error and stops lua_dump from calling the writer again. +pub type LuaWriter = extern "C" fn(LuaState, p: *const c_void, sz: SizeT, ud: *mut c_void) -> c_int; + /// luaL_Reg type, used for defining large amounts of functions with names to be - /// registered into lua with luaL_register / openlibs. #[repr(C)] @@ -84,3 +88,68 @@ impl Default for LuaDebug { } } } + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +/// Taken from +pub enum LuaType { + None = -1, + Nil = 0, + Bool, + LightUserdata, + Number, + String, + Table, + Function, + Userdata, + Thread, + + // End of LUA_T* types + /// Entity and entity sub-classes including Player, Weapon, NPC, Vehicle, CSEnt, and NextBot + Entity, + Vector, + Angle, + PhysObj, + Save, + Restore, + DamageInfo, + EffectData, + MoveData, + RecipientFilter, + UserCmd, + #[deprecated = "Leftover from gmod13 beta"] + ScriptedVehicle, + Material, + Panel, + Particle, + ParticleEmitter, + Texture, + UserMsg, + ConVar, + IMesh, + Matrix, + Sound, + PixelVisHandle, + DLight, + Video, + File, + Locomotion, + Path, + NavArea, + SoundHandle, + NavLadder, + ParticleSystem, + ProjectedTexture, + PhysCollide, + SurfaceInfo, + + /// Amount of LuaType enums (44) + Count, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct Userdata { + pub data: *mut c_void, + pub typ: LuaType, +} diff --git a/rglua/src/prelude.rs b/rglua/src/prelude.rs index 64666cda..1fd163fe 100644 --- a/rglua/src/prelude.rs +++ b/rglua/src/prelude.rs @@ -1,5 +1,6 @@ pub use crate::lua::*; pub use crate::types::{LuaCFunction, LuaInteger, LuaNumber, LuaState, LuaString}; +pub use crate::userdata::{Angle, Vector}; pub use crate::util::dump_stack; pub use crate::{cstr, printgm, reg, rstr, try_cstr, try_rstr}; diff --git a/rglua/src/userdata/mod.rs b/rglua/src/userdata/mod.rs index e8aebb10..c7d1e864 100644 --- a/rglua/src/userdata/mod.rs +++ b/rglua/src/userdata/mod.rs @@ -34,6 +34,14 @@ macro_rules! udata { $fieldvis $field: $ty ),* } + + impl $name { + pub fn new( $($field: $ty),* ) -> $name { + $name { + $($field),* + } + } + } udata!( $($rest)* ); }; () => () @@ -41,6 +49,7 @@ macro_rules! udata { udata! { // https://github.com/danielga/sourcesdk-minimal/blob/cab3e07edc4a41e7e69ea645ea51c1e5c5d1be71/public/mathlib/vector.h#L66 + /// Floating point vector type created by the Vector() function in lua and Vector::new() in Rust. pub struct Vector { pub x: f32, pub y: f32, @@ -48,7 +57,8 @@ udata! { } // https://github.com/danielga/sourcesdk-minimal/blob/cab3e07edc4a41e7e69ea645ea51c1e5c5d1be71/public/mathlib/vector.h#L1765 - /// QAngle + /// Euler angle type. + /// This is a QAngle in the source engine. pub struct Angle { pub p: f32, pub y: f32, diff --git a/rglua/src/util/mod.rs b/rglua/src/util/mod.rs index 419bbb82..f05c8daf 100644 --- a/rglua/src/util/mod.rs +++ b/rglua/src/util/mod.rs @@ -154,7 +154,7 @@ pub fn dump_stack(l: LuaState) -> Result { write!(&mut buf, "[{}] '{}' = ", i, rstr!(luaL_typename(l, i))); match lua_type(l, i) { TNUMBER => write!(&mut buf, "{}", lua_tonumber(l, i)), - TSTRING => write!(&mut buf, "{}", rstr!(lua_tostring(l, i))), + TSTRING => write!(&mut buf, "{}", rstr!(lua_tostring(l, i).unwrap())), TBOOLEAN => write!( &mut buf, "{}", @@ -166,7 +166,9 @@ pub fn dump_stack(l: LuaState) -> Result { ), TNIL => write!(&mut buf, "nil"), TNONE => write!(&mut buf, "none"), - _ => write!(&mut buf, "{:p}", lua_topointer(l, i)), + TUSERDATA | TLIGHTUSERDATA => write!(&mut buf, "{:p}", lua_touserdata(l, i).unwrap()), + TTHREAD => write!(&mut buf, "{:p}", lua_tothread(l, i).unwrap()), + _ => write!(&mut buf, "Unknown type"), }? }