implement get_entity and get_entity_mut

This commit is contained in:
bgkillas 2025-07-15 14:47:06 -04:00
parent 2a7d3a5c46
commit e9faa77b28
4 changed files with 37 additions and 48 deletions

View file

@ -7,8 +7,7 @@ use eyre::{Context, OptionExt, bail};
use modules::{Module, ModuleCtx, entity_sync::EntitySync}; use modules::{Module, ModuleCtx, entity_sync::EntitySync};
use net::NetManager; use net::NetManager;
use noita_api::add_lua_fn; use noita_api::add_lua_fn;
use noita_api::addr_grabber::{grab_addrs, grabbed_fns, grabbed_globals}; use noita_api::addr_grabber::{grab_addrs, grabbed_globals};
use noita_api::noita::types::Entity;
use noita_api::noita::world::ParticleWorldState; use noita_api::noita::world::ParticleWorldState;
use noita_api::{ use noita_api::{
DamageModelComponent, EntityID, VariableStorageComponent, DamageModelComponent, EntityID, VariableStorageComponent,
@ -20,8 +19,6 @@ use noita_api::{
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use shared::des::{Gid, RemoteDes}; use shared::des::{Gid, RemoteDes};
use shared::{Destination, NoitaInbound, NoitaOutbound, PeerId, SpawnOnce, WorldPos}; use shared::{Destination, NoitaInbound, NoitaOutbound, PeerId, SpawnOnce, WorldPos};
#[cfg(target_arch = "x86")]
use std::arch::asm;
use std::array::IntoIter; use std::array::IntoIter;
use std::backtrace::Backtrace; use std::backtrace::Backtrace;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
@ -156,26 +153,12 @@ fn init_particle_world_state(lua: LuaState) -> eyre::Result<()> {
pub fn ephemerial(entity_id: u32) -> eyre::Result<()> { pub fn ephemerial(entity_id: u32) -> eyre::Result<()> {
unsafe { unsafe {
let entity_manager = grabbed_globals().entity_manager.read(); let entity_manager = grabbed_globals().entity_manager.as_ref().unwrap();
let entity: *mut Entity; if let Some(entity) = entity_manager
#[cfg(target_arch = "x86")] .as_ref()
asm!( .unwrap()
"mov ecx, {entity_manager}", .get_entity_mut(entity_id as isize)
"push {entity_id:e}",
"call {get_entity}",
entity_manager = in(reg) entity_manager,
get_entity = in(reg) grabbed_fns().get_entity,
entity_id = in(reg) entity_id,
clobber_abi("C"),
out("ecx") _,
out("eax") entity,
);
#[cfg(not(target_arch = "x86"))]
{ {
std::hint::black_box((entity_manager, grabbed_fns().get_entity));
entity = Default::default();
}
if let Some(entity) = entity.as_mut() {
entity.filename_index = 0; entity.filename_index = 0;
} else { } else {
bail!("Entity {entity_id} not found"); bail!("Entity {entity_id} not found");

View file

@ -15,14 +15,12 @@ impl Module for WorldSync {
runs: Vec::with_capacity(16384), runs: Vec::with_capacity(16384),
}; };
let mut upd = std::array::from_fn(|_| None); let mut upd = std::array::from_fn(|_| None);
let time = std::time::Instant::now();
unsafe { unsafe {
self.particle_world_state self.particle_world_state
.assume_init_ref() .assume_init_ref()
.encode_world(ChunkCoord(-2, -7), &mut upd)?; .encode_world(ChunkCoord(-2, -7), &mut upd)?;
} }
std::hint::black_box(upd); std::hint::black_box(upd);
noita_api::print!("{:?}", time.elapsed().as_micros());
let msg = NoitaOutbound::WorldSyncToProxy(WorldSyncToProxy::Updates(vec![update])); let msg = NoitaOutbound::WorldSyncToProxy(WorldSyncToProxy::Updates(vec![update]));
ctx.net.send(&msg)?; ctx.net.send(&msg)?;
Ok(()) Ok(())

View file

@ -1,7 +1,7 @@
use std::{mem, os::raw::c_void, ptr, sync::OnceLock}; use std::{os::raw::c_void, ptr, sync::OnceLock};
use crate::lua::LuaState; use crate::lua::LuaState;
use crate::noita::types::{EntityManager, ThiscallFn}; use crate::noita::types::EntityManager;
use iced_x86::{Decoder, DecoderOptions, Mnemonic}; use iced_x86::{Decoder, DecoderOptions, Mnemonic};
static GRABBED: OnceLock<Grabbed> = OnceLock::new(); static GRABBED: OnceLock<Grabbed> = OnceLock::new();
@ -32,7 +32,6 @@ pub(crate) unsafe fn grab_addr_from_instruction(
struct Grabbed { struct Grabbed {
globals: GrabbedGlobals, globals: GrabbedGlobals,
fns: GrabbedFns,
} }
// This only stores pointers that are constant, so should be safe to share between threads. // This only stores pointers that are constant, so should be safe to share between threads.
@ -44,36 +43,20 @@ pub struct GrabbedGlobals {
pub entity_manager: *const *mut EntityManager, pub entity_manager: *const *mut EntityManager,
} }
pub struct GrabbedFns {
pub get_entity: *const ThiscallFn, //unsafe extern "C" fn(*const EntityManager, u32) -> *mut Entity,
}
pub fn grab_addrs(lua: LuaState) { pub fn grab_addrs(lua: LuaState) {
lua.get_global(c"EntityGetFilename"); lua.get_global(c"EntityGetFilename");
let base = lua.to_cfunction(-1).unwrap() as *const c_void; let base = lua.to_cfunction(-1).unwrap() as *const c_void;
let get_entity = unsafe { let entity_manager: *const *mut EntityManager =
mem::transmute_copy(&grab_addr_from_instruction(
base,
0x0079782b - 0x00797570,
Mnemonic::Call,
))
};
let entity_manager =
unsafe { grab_addr_from_instruction(base, 0x00797821 - 0x00797570, Mnemonic::Mov).cast() }; unsafe { grab_addr_from_instruction(base, 0x00797821 - 0x00797570, Mnemonic::Mov).cast() };
lua.pop_last(); lua.pop_last();
GRABBED GRABBED
.set(Grabbed { .set(Grabbed {
globals: GrabbedGlobals { entity_manager }, globals: GrabbedGlobals { entity_manager },
fns: GrabbedFns { get_entity },
}) })
.ok(); .ok();
} }
pub fn grabbed_fns() -> &'static GrabbedFns {
&GRABBED.get().expect("to be initialized early").fns
}
pub fn grabbed_globals() -> &'static GrabbedGlobals { pub fn grabbed_globals() -> &'static GrabbedGlobals {
&GRABBED.get().expect("to be initialized early").globals &GRABBED.get().expect("to be initialized early").globals
} }

View file

@ -918,15 +918,40 @@ pub struct GameGlobal {
pub pause_state: isize, pub pause_state: isize,
} }
#[repr(C)] #[repr(C)]
#[derive(Debug)]
pub struct Entity { pub struct Entity {
_unknown0: [u8; 8], pub id: isize,
pub filename_index: u32, pub entry: isize,
pub filename_index: usize,
//TODO More stuff, not that relevant currently. //TODO More stuff, not that relevant currently.
} }
#[repr(C)] #[repr(C)]
#[derive(Debug)]
pub struct EntityManager { pub struct EntityManager {
_fld: c_void, unknown: [isize; 5],
pub list: *mut *mut Entity,
pub last: usize,
unknown2: [isize; 120],
//TODO Unknown //TODO Unknown
} }
impl EntityManager {
pub fn get_entity(&self, id: isize) -> Option<&'static Entity> {
let len = (self.last - self.list as usize) / 4;
let start = unsafe { self.list.offset(id - 1) };
let list = unsafe { std::slice::from_raw_parts(start, len - id as usize) };
list.iter()
.find_map(|c| unsafe { c.as_ref() }.filter(|c| c.id == id))
}
pub fn get_entity_mut(&self, id: isize) -> Option<&'static mut Entity> {
let len = (self.last - self.list as usize) / 4;
let start = unsafe { self.list.offset(id - 1) };
let list = unsafe { std::slice::from_raw_parts(start, len - id as usize) };
list.iter()
.find_map(|c| unsafe { c.as_mut() }.filter(|c| c.id == id))
}
}
#[repr(C)] #[repr(C)]
pub struct ThiscallFn(c_void); pub struct ThiscallFn(c_void);