diff --git a/blob_guy/src/chunk.rs b/blob_guy/src/chunk.rs index ff3241f1..443bdc75 100644 --- a/blob_guy/src/chunk.rs +++ b/blob_guy/src/chunk.rs @@ -1,6 +1,7 @@ use crate::blob_guy::OFFSET; use crate::{CHUNK_AMOUNT, CHUNK_SIZE}; use eyre::{ContextCompat, eyre}; +use noita_api::heap; use noita_api::noita::types; use noita_api::noita::world::ParticleWorldState; use rayon::iter::{ @@ -228,7 +229,7 @@ impl ChunkOps for ParticleWorldState { let world_x = x + i; let world_y = y + j; let cell = pixel_array.get_mut_raw(shift_x + i, shift_y + j); - let new = Box::leak(blob_cell.clone()); + let new = heap::place_new_ref(*blob_cell.clone()); new.x = world_x; new.y = world_y; *cell = (new as *mut types::LiquidCell).cast(); diff --git a/ewext/src/lib.rs b/ewext/src/lib.rs index 4ac8c905..d9a82f29 100644 --- a/ewext/src/lib.rs +++ b/ewext/src/lib.rs @@ -8,6 +8,7 @@ use modules::{Module, ModuleCtx, entity_sync::EntitySync}; use net::NetManager; use noita_api::add_lua_fn; use noita_api::addr_grabber::Globals; +use noita_api::heap::raw_new; use noita_api::noita::types::EntityManager; use noita_api::noita::world::ParticleWorldState; use noita_api::{ @@ -357,6 +358,7 @@ pub(crate) fn print_error(error: eyre::Report) -> eyre::Result<()> { pub unsafe extern "C" fn luaopen_ewext(lua: *mut lua_State) -> c_int { #[cfg(debug_assertions)] println!("Initializing ewext"); + raw_new(1); if let Err(_e) = KEEP_SELF_LOADED.as_ref() { #[cfg(debug_assertions)] diff --git a/ewext/src/modules/world_sync.rs b/ewext/src/modules/world_sync.rs index b3917edd..f92c0da9 100644 --- a/ewext/src/modules/world_sync.rs +++ b/ewext/src/modules/world_sync.rs @@ -3,6 +3,7 @@ use crate::{WorldSync, my_peer_id}; use eyre::{ContextCompat, eyre}; use noita_api::noita::types::{CellType, FireCell, GasCell, LiquidCell, Vec2i}; use noita_api::noita::world::ParticleWorldState; +use noita_api::{game_print, heap}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use shared::NoitaOutbound; use shared::world_sync::{ @@ -98,13 +99,14 @@ impl WorldData for ParticleWorldState { else { return Err(eyre!("chunk not loaded")); }; + let mut chunk_iter = chunk.iter_mut(); let (shift_x, shift_y) = self.get_shift::(cx, cy); - for ((j, i), p) in (shift_x..shift_x + CHUNK_SIZE as isize) - .flat_map(|i| (shift_y..shift_y + CHUNK_SIZE as isize).map(move |j| (i, j))) - .zip(chunk.iter_mut()) - { - *p = pixel_array.get_pixel(i, j); + for j in shift_y..shift_y + CHUNK_SIZE as isize { + for i in shift_x..shift_x + CHUNK_SIZE as isize { + *chunk_iter.next().unwrap() = pixel_array.get_pixel(i, j); + } } + Ok(()) } unsafe fn decode_world(&self, chunk: NoitaWorldUpdate) -> eyre::Result<()> { @@ -126,37 +128,39 @@ impl WorldData for ParticleWorldState { let cell = pixel_array.get_mut_raw(shift_x + x, shift_y + y); let xs = start_x + x; let ys = start_y + y; - let Some(mat) = self.material_list.get_static(pixel.mat() as usize) else { - return Err(eyre!("mat does not exist")); - }; - match mat.cell_type { - CellType::None => { - *cell = ptr::null_mut(); - } - CellType::Liquid => { - let liquid = Box::leak(Box::new(unsafe { - LiquidCell::create(mat, self.cell_vtables.liquid(), self.world_ptr) - })); - liquid.x = xs; - liquid.y = ys; - *cell = (liquid as *mut LiquidCell).cast(); - } - CellType::Gas => { - let gas = Box::leak(Box::new(unsafe { - GasCell::create(mat, self.cell_vtables.gas(), self.world_ptr) - })); - gas.x = xs; - gas.y = ys; - *cell = (gas as *mut GasCell).cast(); - } - CellType::Solid => {} - CellType::Fire => { - let fire = Box::leak(Box::new(unsafe { - FireCell::create(mat, self.cell_vtables.fire(), self.world_ptr) - })); - fire.x = xs; - fire.y = ys; - *cell = (fire as *mut FireCell).cast(); + if pixel.is_air() { + *cell = ptr::null_mut(); + } else { + let Some(mat) = self.material_list.get_static(pixel.mat() as usize) else { + return Err(eyre!("mat does not exist")); + }; + match mat.cell_type { + CellType::None => {} + CellType::Liquid => { + let mut liquid = unsafe { + LiquidCell::create(mat, self.cell_vtables.liquid(), self.world_ptr) + }; + liquid.x = xs; + liquid.y = ys; + *cell = heap::place_new(liquid).cast(); + } + CellType::Gas => { + let mut gas = unsafe { + GasCell::create(mat, self.cell_vtables.gas(), self.world_ptr) + }; + gas.x = xs; + gas.y = ys; + *cell = heap::place_new(gas).cast(); + } + CellType::Solid => {} + CellType::Fire => { + let mut fire = unsafe { + FireCell::create(mat, self.cell_vtables.fire(), self.world_ptr) + }; + fire.x = xs; + fire.y = ys; + *cell = heap::place_new(fire).cast(); + } } } } @@ -232,14 +236,14 @@ pub fn test_world() { celldata.material_type = rand::random::() as isize; list[i] = celldata.material_type; let cell = Cell::create( - Box::leak(Box::new(celldata)), + heap::place_new_ref(celldata), CellVTable { none: &NoneCellVTable { unknown: [ptr::null_mut(); 41], }, }, ); - *d = Box::leak(Box::new(cell)); + *d = heap::place_new(cell); } let chunk = Chunk { data: unsafe { std::mem::transmute::<&mut _, &'static mut _>(&mut data) }, @@ -254,14 +258,14 @@ pub fn test_world() { for d in data.iter_mut() { let celldata = CellData::default(); let cell = Cell::create( - Box::leak(Box::new(celldata)), + heap::place_new_ref(celldata), CellVTable { none: &NoneCellVTable { unknown: [ptr::null_mut(); 41], }, }, ); - *d = Box::leak(Box::new(cell)); + *d = heap::place_new_ref(cell); } let chunk = Chunk { data: unsafe { std::mem::transmute::<&mut _, &'static mut _>(&mut data) }, diff --git a/noita_api/src/heap.rs b/noita_api/src/heap.rs new file mode 100644 index 00000000..51491cce --- /dev/null +++ b/noita_api/src/heap.rs @@ -0,0 +1,41 @@ +use std::sync::LazyLock; + +struct Msvcr { + op_new: unsafe extern "C" fn(n: std::os::raw::c_uint) -> *mut std::os::raw::c_void, + // op_delete: unsafe extern "C" fn(*const std::os::raw::c_void), + // op_delete_array: unsafe extern "C" fn(*const std::os::raw::c_void), +} + +static MSVCR: LazyLock = LazyLock::new(|| unsafe { + let lib = libloading::Library::new("./msvcr120.dll").expect("library to exist"); + let op_new = *lib.get(b"??2@YAPAXI@Z\0").expect("symbol to exist"); + // let op_delete = *lib.get(b"operator_delete\0").expect("symbol to exist"); + // let op_delete_array = *lib.get(b"operator_delete[]\0").expect("symbol to exist"); + Msvcr { + op_new, + // op_delete, + // op_delete_array, + } +}); + +/// Allocate some memory, using the same allocator noita uses. +pub fn raw_new(size: usize) -> *mut std::os::raw::c_void { + let size = size as std::os::raw::c_uint; + assert!(size > 0, "Doesn't make sense to allocate memory of size 0"); + unsafe { (MSVCR.op_new)(size) } +} + +/// Allocates memory using noita's allocator and moves *value* to it. +pub fn place_new(value: T) -> *mut T { + let size = size_of::(); + let place = raw_new(size) as *mut T; + unsafe { + place.copy_from_nonoverlapping(&value, size); + } + place +} + +/// Same as place_new, but returns &'static mut +pub fn place_new_ref(value: T) -> &'static mut T { + unsafe { &mut *place_new(value) } +} diff --git a/noita_api/src/lib.rs b/noita_api/src/lib.rs index 2751cef2..eaf06f8d 100644 --- a/noita_api/src/lib.rs +++ b/noita_api/src/lib.rs @@ -17,6 +17,7 @@ pub mod lua; pub mod serialize; pub use noita_api_macro::add_lua_fn; pub mod addr_grabber; +pub mod heap; pub mod noita; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/noita_api/src/noita/types.rs b/noita_api/src/noita/types.rs index a4081b9b..c2f13052 100644 --- a/noita_api/src/noita/types.rs +++ b/noita_api/src/noita/types.rs @@ -18,6 +18,8 @@ use std::fmt::{Debug, Display, Formatter}; use std::ops::{Index, IndexMut}; use std::{alloc, ptr, slice}; pub use world::*; + +use crate::heap; #[repr(C)] union Buffer { buffer: *const u8, @@ -71,8 +73,8 @@ impl From<&str> for StdString { size: value.len(), }; if res.capacity > 16 { - let buffer = Box::leak(Box::new(value)); - res.buffer.buffer = buffer.as_ptr(); + let buffer = heap::place_new(value); + res.buffer.buffer = buffer.cast(); } else { let mut iter = value.as_bytes().iter(); res.buffer.sso_buffer = std::array::from_fn(|_| iter.next().copied().unwrap_or(0)) @@ -157,8 +159,8 @@ pub struct CString(pub *const u8); impl From<&str> for CString { fn from(value: &str) -> Self { let value = value.to_owned() + "\0"; - let str = Box::leak(Box::new(value)); - CString(str.as_ptr()) + let str = heap::place_new(value).cast(); + CString(str) } } impl CString { @@ -472,7 +474,7 @@ pub struct StdMap { impl Default for StdMap { fn default() -> Self { Self { - root: Box::leak(Box::new(StdMapNode::default())), + root: unsafe { &mut *heap::place_new(StdMapNode::default()) }, len: 0, } } @@ -520,7 +522,7 @@ impl StdMap { if self.is_empty() { self.len += 1; let node = StdMapNode::new(key, value); - self.root.parent = Box::leak(Box::new(node)) as *mut _; + self.root.parent = heap::place_new(node); None } else { todo!() diff --git a/noita_api/src/noita/types/component.rs b/noita_api/src/noita/types/component.rs index 35b193e8..0a3c5870 100644 --- a/noita_api/src/noita/types/component.rs +++ b/noita_api/src/noita/types/component.rs @@ -1,5 +1,8 @@ -use crate::noita::types::{ - BitSet, CString, Component, Entity, EntityManager, StdMap, StdString, StdVec, TagManager, +use crate::{ + heap, + noita::types::{ + BitSet, CString, Component, Entity, EntityManager, StdMap, StdString, StdVec, TagManager, + }, }; use std::ptr; #[repr(C)] @@ -141,7 +144,7 @@ impl ComponentBuffer { unk3: StdVec::null(), unk4: 0, }); - let com = Box::leak(Box::new(com)); + let com = heap::place_new(com); let index = self.component_list.len(); self.component_list.push((com as *mut C).cast()); if self.entities.len() > index { @@ -184,7 +187,7 @@ impl ComponentBuffer { } self.next[off] = self.end; } - com + unsafe { &mut *com } } pub fn iter_components(&self, entry: usize) -> ComponentIter { if let Some(off) = self.entity_entry.get(entry) { diff --git a/noita_api/src/noita/types/entity.rs b/noita_api/src/noita/types/entity.rs index 1024a232..d34da929 100644 --- a/noita_api/src/noita/types/entity.rs +++ b/noita_api/src/noita/types/entity.rs @@ -1,3 +1,4 @@ +use crate::heap; use crate::noita::types::component::{ComponentBuffer, ComponentData}; use crate::noita::types::{ Component, ComponentTypeManager, Inventory2Component, StdMap, StdString, StdVec, Vec2, @@ -20,7 +21,7 @@ impl EntityManager { children: std::ptr::null_mut(), parent: std::ptr::null_mut(), }; - let ent = Box::leak(Box::new(ent)); + let ent = heap::place_new_ref(ent); if let Some(entry) = self.free_ids.pop() { ent.entry = entry; self.entities[entry] = ent; diff --git a/noita_api/src/noita/types/world.rs b/noita_api/src/noita/types/world.rs index d10d1224..1b60886d 100644 --- a/noita_api/src/noita/types/world.rs +++ b/noita_api/src/noita/types/world.rs @@ -1,3 +1,4 @@ +use crate::heap; use crate::noita::types::objects::{ConfigExplosion, ConfigGridCosmeticParticle}; use crate::noita::types::{StdMap, StdString, StdVec, ThiscallFn, Vec2, Vec2i}; use shared::world_sync::{Pixel, PixelFlags}; @@ -727,7 +728,7 @@ impl ChunkMap { #[inline] pub fn insert(&mut self, x: isize, y: isize, chunk: Chunk) { let index = (((y - 256) & 511) << 9) | ((x - 256) & 511); - self.chunk_array[index.cast_unsigned()] = Box::leak(Box::new(chunk)) + self.chunk_array[index.cast_unsigned()] = heap::place_new(chunk) } } diff --git a/shared/src/world_sync.rs b/shared/src/world_sync.rs index d7f29188..9be6243a 100644 --- a/shared/src/world_sync.rs +++ b/shared/src/world_sync.rs @@ -62,6 +62,10 @@ impl Pixel { pub fn flags(self) -> PixelFlags { unsafe { std::mem::transmute((self.0 >> 12) as u8) } } + + pub fn is_air(&self) -> bool { + self.mat() == 0 + } } #[derive(Debug, Encode, Decode, Clone)]