2024-11-25 17:44:56 +03:00
|
|
|
pub mod lua_bindings;
|
|
|
|
|
2024-11-24 16:20:45 +03:00
|
|
|
use std::{
|
2024-11-25 18:34:30 +03:00
|
|
|
borrow::Cow,
|
2024-11-24 16:20:45 +03:00
|
|
|
cell::Cell,
|
|
|
|
ffi::{c_char, c_int, CStr},
|
2024-11-24 23:12:35 +03:00
|
|
|
mem, slice,
|
2024-11-25 17:44:56 +03:00
|
|
|
sync::LazyLock,
|
2024-11-24 16:20:45 +03:00
|
|
|
};
|
|
|
|
|
2024-11-24 23:12:35 +03:00
|
|
|
use eyre::{bail, Context, OptionExt};
|
2024-11-25 17:44:56 +03:00
|
|
|
use lua_bindings::{lua_CFunction, lua_State, Lua51, LUA_GLOBALSINDEX};
|
2024-11-21 17:08:03 +03:00
|
|
|
|
2024-11-25 18:34:30 +03:00
|
|
|
use crate::{Color, ComponentID, EntityID, Obj};
|
|
|
|
|
2024-11-24 16:20:45 +03:00
|
|
|
thread_local! {
|
|
|
|
static CURRENT_LUA_STATE: Cell<Option<LuaState>> = Cell::default();
|
|
|
|
}
|
|
|
|
|
2024-11-25 17:44:56 +03:00
|
|
|
pub static LUA: LazyLock<Lua51> = LazyLock::new(|| unsafe {
|
|
|
|
let lib = libloading::Library::new("./lua51.dll").expect("library to exist");
|
|
|
|
Lua51::from_library(lib).expect("library to be lua")
|
|
|
|
});
|
|
|
|
|
2024-11-21 17:08:03 +03:00
|
|
|
#[derive(Clone, Copy)]
|
2024-11-25 02:33:02 +03:00
|
|
|
pub struct LuaState {
|
2024-11-21 18:50:10 +03:00
|
|
|
lua: *mut lua_State,
|
|
|
|
}
|
2024-11-21 17:08:03 +03:00
|
|
|
|
|
|
|
impl LuaState {
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn new(lua: *mut lua_State) -> Self {
|
2024-11-21 18:50:10 +03:00
|
|
|
Self { lua }
|
|
|
|
}
|
|
|
|
|
2024-11-24 16:20:45 +03:00
|
|
|
/// Returns a lua state that is considered "current". Usually set when we get called from noita.
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn current() -> eyre::Result<Self> {
|
2024-11-24 16:20:45 +03:00
|
|
|
CURRENT_LUA_STATE
|
|
|
|
.get()
|
|
|
|
.ok_or_eyre("No current lua state available")
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn make_current(self) {
|
2024-11-24 16:20:45 +03:00
|
|
|
CURRENT_LUA_STATE.set(Some(self));
|
|
|
|
}
|
|
|
|
|
2024-11-25 17:44:56 +03:00
|
|
|
pub fn raw(&self) -> *mut lua_State {
|
2024-11-21 18:50:10 +03:00
|
|
|
self.lua
|
2024-11-21 17:08:03 +03:00
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn to_integer(&self, index: i32) -> isize {
|
2024-11-21 18:50:10 +03:00
|
|
|
unsafe { LUA.lua_tointeger(self.lua, index) }
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn to_number(&self, index: i32) -> f64 {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_tonumber(self.lua, index) }
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn to_bool(&self, index: i32) -> bool {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_toboolean(self.lua, index) > 0 }
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn to_string(&self, index: i32) -> eyre::Result<String> {
|
2024-11-24 23:12:35 +03:00
|
|
|
let mut size = 0;
|
|
|
|
let buf = unsafe { LUA.lua_tolstring(self.lua, index, &mut size) };
|
|
|
|
if buf.is_null() {
|
|
|
|
bail!("Expected a string, but got a null pointer");
|
|
|
|
}
|
|
|
|
let slice = unsafe { slice::from_raw_parts(buf as *const u8, size) };
|
|
|
|
|
|
|
|
Ok(String::from_utf8(slice.to_owned())
|
|
|
|
.context("Attempting to get lua string, expecting it to be utf-8")?)
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn to_cfunction(&self, index: i32) -> lua_CFunction {
|
2024-11-21 18:50:10 +03:00
|
|
|
unsafe { LUA.lua_tocfunction(self.lua, index) }
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn push_number(&self, val: f64) {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_pushnumber(self.lua, val) };
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn push_integer(&self, val: isize) {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_pushinteger(self.lua, val) };
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn push_bool(&self, val: bool) {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_pushboolean(self.lua, val as i32) };
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn push_string(&self, s: &str) {
|
2024-11-24 16:20:45 +03:00
|
|
|
unsafe {
|
2024-11-24 23:12:35 +03:00
|
|
|
LUA.lua_pushlstring(self.lua, s.as_bytes().as_ptr() as *const c_char, s.len());
|
2024-11-24 16:20:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn push_nil(&self) {
|
|
|
|
unsafe { LUA.lua_pushnil(self.lua) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn call(&self, nargs: i32, nresults: i32) {
|
2024-11-24 23:12:35 +03:00
|
|
|
unsafe { LUA.lua_call(self.lua, nargs, nresults) };
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn get_global(&self, name: &CStr) {
|
2024-11-21 18:50:10 +03:00
|
|
|
unsafe { LUA.lua_getfield(self.lua, LUA_GLOBALSINDEX, name.as_ptr()) };
|
|
|
|
}
|
|
|
|
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn pop_last(&self) {
|
2024-11-21 18:50:10 +03:00
|
|
|
unsafe { LUA.lua_settop(self.lua, -2) };
|
2024-11-21 17:08:03 +03:00
|
|
|
}
|
2024-11-25 02:33:02 +03:00
|
|
|
pub fn pop_last_n(&self, n: i32) {
|
|
|
|
unsafe { LUA.lua_settop(self.lua, -1 - (n)) };
|
|
|
|
}
|
2024-11-24 16:20:45 +03:00
|
|
|
|
|
|
|
/// Raise an error with message `s`
|
|
|
|
///
|
|
|
|
/// This takes String so that it gets deallocated properly, as this functions doesn't return.
|
|
|
|
unsafe fn raise_error(&self, s: String) -> ! {
|
|
|
|
self.push_string(&s);
|
|
|
|
mem::drop(s);
|
|
|
|
unsafe { LUA.lua_error(self.lua) };
|
|
|
|
// lua_error does not return.
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-25 18:34:30 +03:00
|
|
|
/// Used for types that can be returned from functions that were defined in rust to lua.
|
2024-11-25 17:44:56 +03:00
|
|
|
pub trait LuaFnRet {
|
2024-11-24 16:20:45 +03:00
|
|
|
fn do_return(self, lua: LuaState) -> c_int;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Function intends to return several values that it has on stack.
|
2024-11-25 17:44:56 +03:00
|
|
|
pub struct ValuesOnStack(pub c_int);
|
2024-11-24 16:20:45 +03:00
|
|
|
|
|
|
|
impl LuaFnRet for ValuesOnStack {
|
|
|
|
fn do_return(self, _lua: LuaState) -> c_int {
|
|
|
|
self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaFnRet for () {
|
|
|
|
fn do_return(self, _lua: LuaState) -> c_int {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<R: LuaFnRet> LuaFnRet for eyre::Result<R> {
|
|
|
|
fn do_return(self, lua: LuaState) -> c_int {
|
|
|
|
match self {
|
|
|
|
Ok(ok) => ok.do_return(lua),
|
|
|
|
Err(err) => unsafe {
|
2024-11-25 17:44:56 +03:00
|
|
|
lua.raise_error(format!("Error in rust call: {:?}", err));
|
2024-11-24 16:20:45 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2024-11-21 17:08:03 +03:00
|
|
|
}
|
2024-11-25 18:34:30 +03:00
|
|
|
|
|
|
|
/// Trait for arguments that can be put on lua stack.
|
|
|
|
pub(crate) trait LuaPutValue {
|
|
|
|
fn put(&self, lua: LuaState);
|
|
|
|
fn is_non_empty(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for i32 {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_integer(*self as isize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for isize {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_integer(*self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for u32 {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_integer(unsafe { mem::transmute::<_, i32>(*self) as isize });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for f64 {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_number(*self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for bool {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_bool(*self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for Cow<'_, str> {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_string(self.as_ref());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for str {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
lua.push_string(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for EntityID {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
self.0.put(lua);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for ComponentID {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
self.0.put(lua);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for Color {
|
|
|
|
fn put(&self, _lua: LuaState) {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaPutValue for Obj {
|
|
|
|
fn put(&self, _lua: LuaState) {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: LuaPutValue> LuaPutValue for Option<T> {
|
|
|
|
fn put(&self, lua: LuaState) {
|
|
|
|
match self {
|
|
|
|
Some(val) => val.put(lua),
|
|
|
|
None => lua.push_nil(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_non_empty(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Some(val) => val.is_non_empty(),
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|