Allow returning errors from ewext functions to lua.

This commit is contained in:
IQuant 2024-11-24 16:20:45 +03:00
parent 134584dcf2
commit c781187e57
5 changed files with 109 additions and 15 deletions

25
ewext/Cargo.lock generated
View file

@ -40,14 +40,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ewext"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"backtrace",
"eyre",
"iced-x86",
"libloading",
"noita_api_macro",
]
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "gimli"
version = "0.31.1"
@ -69,6 +80,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "itoa"
version = "1.0.11"
@ -132,6 +149,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "proc-macro2"
version = "1.0.89"

View file

@ -1,6 +1,6 @@
[package]
name = "ewext"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
[lib]
@ -16,3 +16,4 @@ libloading = "0.8.5"
backtrace = "0.3.74"
iced-x86 = "1.21.0"
noita_api_macro = {path = "noita_api_macro"}
eyre = "0.6.12"

View file

@ -107,7 +107,9 @@ pub fn add_lua_fn(item: TokenStream) -> TokenStream {
let fn_name_c = proc_macro2::Literal::c_string(CString::new(fn_name).unwrap().as_c_str());
quote! {
unsafe extern "C" fn #bridge_fn_name(lua: *mut lua_State) -> c_int {
#fn_name_ident(LuaState::new(lua)) as c_int
let lua_state = LuaState::new(lua);
lua_state.make_current();
crate::lua_state::LuaFnRet::do_return(#fn_name_ident(lua_state), lua_state)
}
LUA.lua_pushcclosure(lua, Some(#bridge_fn_name), 0);

View file

@ -6,8 +6,9 @@ use std::{
};
use addr_grabber::{grab_addrs, grabbed_fns, grabbed_globals};
use eyre::bail;
use lua_bindings::{lua_State, Lua51};
use lua_state::LuaState;
use lua_state::{LuaState, ValuesOnStack};
use noita::{ntypes::Entity, NoitaPixelRun, ParticleWorldState};
use noita_api_macro::add_lua_fn;
@ -35,7 +36,7 @@ struct ExtState {
particle_world_state: Option<ParticleWorldState>,
}
fn init_particle_world_state(lua: LuaState) -> c_int {
fn init_particle_world_state(lua: LuaState) {
println!("\nInitializing particle world state");
let world_pointer = lua.to_integer(1);
let chunk_map_pointer = lua.to_integer(2);
@ -50,10 +51,9 @@ fn init_particle_world_state(lua: LuaState) -> c_int {
runner: Default::default(),
});
});
0
}
fn encode_area(lua: LuaState) -> c_int {
fn encode_area(lua: LuaState) -> ValuesOnStack {
let lua = lua.raw();
let start_x = unsafe { LUA.lua_tointeger(lua, 1) } as i32;
let start_y = unsafe { LUA.lua_tointeger(lua, 2) } as i32;
@ -67,10 +67,10 @@ fn encode_area(lua: LuaState) -> c_int {
let runs = unsafe { pws.encode_area(start_x, start_y, end_x, end_y, encoded_buffer) };
unsafe { LUA.lua_pushinteger(lua, runs as isize) };
});
1
lua_state::ValuesOnStack(1)
}
fn make_ephemerial(lua: LuaState) -> c_int {
fn make_ephemerial(lua: LuaState) -> eyre::Result<()> {
unsafe {
let entity_id = lua.to_integer(1) as u32;
@ -87,16 +87,16 @@ fn make_ephemerial(lua: LuaState) -> c_int {
out("ecx") _,
out("eax") entity,
);
if !entity.is_null() {
if entity.is_null() {
bail!("Entity {} not found", entity_id);
}
entity.cast::<c_void>().offset(0x8).cast::<u32>().write(0);
}
}
0
Ok(())
}
fn on_world_initialized(lua: LuaState) -> i32 {
fn on_world_initialized(lua: LuaState) {
grab_addrs(lua);
0
}
/// # Safety

View file

@ -1,10 +1,20 @@
use std::ffi::CStr;
use std::{
cell::Cell,
ffi::{c_char, c_int, CStr},
mem,
};
use eyre::OptionExt;
use crate::{
lua_bindings::{lua_CFunction, lua_State, LUA_GLOBALSINDEX},
LUA,
};
thread_local! {
static CURRENT_LUA_STATE: Cell<Option<LuaState>> = Cell::default();
}
#[derive(Clone, Copy)]
pub(crate) struct LuaState {
lua: *mut lua_State,
@ -15,6 +25,17 @@ impl LuaState {
Self { lua }
}
/// Returns a lua state that is considered "current". Usually set when we get called from noita.
pub(crate) fn current() -> eyre::Result<Self> {
CURRENT_LUA_STATE
.get()
.ok_or_eyre("No current lua state available")
}
pub(crate) fn make_current(self) {
CURRENT_LUA_STATE.set(Some(self));
}
pub(crate) fn raw(&self) -> *mut lua_State {
self.lua
}
@ -27,6 +48,12 @@ impl LuaState {
unsafe { LUA.lua_tocfunction(self.lua, index) }
}
pub(crate) fn push_string(&self, s: &str) {
unsafe {
LUA.lua_pushstring(self.lua, s.as_bytes().as_ptr() as *const c_char);
}
}
pub(crate) fn get_global(&self, name: &CStr) {
unsafe { LUA.lua_getfield(self.lua, LUA_GLOBALSINDEX, name.as_ptr()) };
}
@ -34,4 +61,45 @@ impl LuaState {
pub(crate) fn pop_last(&self) {
unsafe { LUA.lua_settop(self.lua, -2) };
}
/// 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!()
}
}
pub(crate) trait LuaFnRet {
fn do_return(self, lua: LuaState) -> c_int;
}
/// Function intends to return several values that it has on stack.
pub(crate) struct ValuesOnStack(pub(crate) c_int);
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 {
lua.raise_error(format!("Error in ewext call: {:?}", err));
},
}
}
}