mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-10-19 15:13:16 +00:00
get all vtables, more ewext world data setup
This commit is contained in:
parent
29abf3f26f
commit
3d530ca648
12 changed files with 327 additions and 195 deletions
|
@ -203,7 +203,6 @@ impl ChunkOps for ParticleWorldState {
|
||||||
if !chunk.modified {
|
if !chunk.modified {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let (shift_x, shift_y) = self.get_shift::<CHUNK_SIZE>(cx, cy);
|
|
||||||
let Some(pixel_array) = unsafe { self.world_ptr.as_mut() }
|
let Some(pixel_array) = unsafe { self.world_ptr.as_mut() }
|
||||||
.wrap_err("no world")?
|
.wrap_err("no world")?
|
||||||
.chunk_map
|
.chunk_map
|
||||||
|
@ -212,12 +211,13 @@ impl ChunkOps for ParticleWorldState {
|
||||||
else {
|
else {
|
||||||
return Err(eyre!("chunk not loaded"));
|
return Err(eyre!("chunk not loaded"));
|
||||||
};
|
};
|
||||||
|
let (shift_x, shift_y) = self.get_shift::<CHUNK_SIZE>(cx, cy);
|
||||||
let x = cx * CHUNK_SIZE as isize;
|
let x = cx * CHUNK_SIZE as isize;
|
||||||
let y = cy * CHUNK_SIZE as isize;
|
let y = cy * CHUNK_SIZE as isize;
|
||||||
let blob_cell = unsafe {
|
let blob_cell = unsafe {
|
||||||
Box::new(types::LiquidCell::create(
|
Box::new(types::LiquidCell::create(
|
||||||
&self.material_list[blob as usize],
|
&self.material_list[blob as usize],
|
||||||
self.cell_vtable,
|
self.cell_vtables.liquid(),
|
||||||
self.world_ptr,
|
self.world_ptr,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
1
ewext/Cargo.lock
generated
1
ewext/Cargo.lock
generated
|
@ -142,6 +142,7 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
"noita_api",
|
"noita_api",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rayon",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shared",
|
"shared",
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,6 +25,7 @@ libloading = "0.8.6"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
rustc-hash = "2.0.0"
|
rustc-hash = "2.0.0"
|
||||||
bimap = "0.6.3"
|
bimap = "0.6.3"
|
||||||
|
rayon = "1.10.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
#enables cross-compilation on older systems (for example, when compiling on ubuntu 20.04)
|
#enables cross-compilation on older systems (for example, when compiling on ubuntu 20.04)
|
||||||
|
|
|
@ -1,19 +1,44 @@
|
||||||
use crate::WorldSync;
|
use crate::WorldSync;
|
||||||
use crate::modules::{Module, ModuleCtx};
|
use crate::modules::{Module, ModuleCtx};
|
||||||
|
use eyre::{ContextCompat, eyre};
|
||||||
|
use noita_api::noita::types::{CellType, FireCell, GasCell, LiquidCell};
|
||||||
use noita_api::noita::world::ParticleWorldState;
|
use noita_api::noita::world::ParticleWorldState;
|
||||||
|
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use shared::NoitaOutbound;
|
use shared::NoitaOutbound;
|
||||||
use shared::world_sync::{ChunkCoord, NoitaWorldUpdate, ProxyToWorldSync, WorldSyncToProxy};
|
use shared::world_sync::{
|
||||||
|
CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, PixelFlags, ProxyToWorldSync, RawPixel,
|
||||||
|
WorldSyncToProxy,
|
||||||
|
};
|
||||||
|
use std::ptr;
|
||||||
impl Module for WorldSync {
|
impl Module for WorldSync {
|
||||||
fn on_world_update(&mut self, ctx: &mut ModuleCtx) -> eyre::Result<()> {
|
fn on_world_update(&mut self, ctx: &mut ModuleCtx) -> eyre::Result<()> {
|
||||||
let mut update = NoitaWorldUpdate {
|
let update = NoitaWorldUpdate {
|
||||||
coord: ChunkCoord(0, 0),
|
coord: ChunkCoord(0, 0),
|
||||||
runs: vec![],
|
runs: Vec::with_capacity(16384),
|
||||||
};
|
};
|
||||||
unsafe {
|
let upd0 = std::array::from_fn(|_| RawPixel {
|
||||||
|
material: 0,
|
||||||
|
flags: PixelFlags::Unknown,
|
||||||
|
});
|
||||||
|
let upd1 = std::array::from_fn(|_| RawPixel {
|
||||||
|
material: 0,
|
||||||
|
flags: PixelFlags::Unknown,
|
||||||
|
});
|
||||||
|
let upd2 = std::array::from_fn(|_| RawPixel {
|
||||||
|
material: 0,
|
||||||
|
flags: PixelFlags::Unknown,
|
||||||
|
});
|
||||||
|
let upd3 = std::array::from_fn(|_| RawPixel {
|
||||||
|
material: 0,
|
||||||
|
flags: PixelFlags::Unknown,
|
||||||
|
});
|
||||||
|
let mut arr = [upd0, upd1, upd2, upd3];
|
||||||
|
arr.par_iter_mut().try_for_each(|upd| unsafe {
|
||||||
self.particle_world_state
|
self.particle_world_state
|
||||||
.assume_init_ref()
|
.assume_init_ref()
|
||||||
.encode_world(ChunkCoord(0, 0), &mut update)?
|
.encode_world(ChunkCoord(-2, -7), upd)
|
||||||
};
|
})?;
|
||||||
|
std::hint::black_box(arr);
|
||||||
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(())
|
||||||
|
@ -24,22 +49,25 @@ impl WorldSync {
|
||||||
match msg {
|
match msg {
|
||||||
ProxyToWorldSync::Updates(updates) => {
|
ProxyToWorldSync::Updates(updates) => {
|
||||||
for chunk in updates {
|
for chunk in updates {
|
||||||
|
let time = std::time::Instant::now();
|
||||||
unsafe {
|
unsafe {
|
||||||
self.particle_world_state
|
self.particle_world_state
|
||||||
.assume_init_ref()
|
.assume_init_ref()
|
||||||
.decode_world(chunk)?
|
.decode_world(chunk)?
|
||||||
}
|
}
|
||||||
|
noita_api::print!("de {}", time.elapsed().as_micros());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub const SCALE: isize = (512 / CHUNK_SIZE as isize).ilog2() as isize;
|
||||||
trait WorldData {
|
trait WorldData {
|
||||||
unsafe fn encode_world(
|
unsafe fn encode_world(
|
||||||
&self,
|
&self,
|
||||||
coord: ChunkCoord,
|
coord: ChunkCoord,
|
||||||
chunk: &mut NoitaWorldUpdate,
|
chunk: &mut [RawPixel; CHUNK_SIZE * CHUNK_SIZE],
|
||||||
) -> eyre::Result<()>;
|
) -> eyre::Result<()>;
|
||||||
unsafe fn decode_world(&self, chunk: NoitaWorldUpdate) -> eyre::Result<()>;
|
unsafe fn decode_world(&self, chunk: NoitaWorldUpdate) -> eyre::Result<()>;
|
||||||
}
|
}
|
||||||
|
@ -47,16 +75,87 @@ impl WorldData for ParticleWorldState {
|
||||||
unsafe fn encode_world(
|
unsafe fn encode_world(
|
||||||
&self,
|
&self,
|
||||||
coord: ChunkCoord,
|
coord: ChunkCoord,
|
||||||
chunk: &mut NoitaWorldUpdate,
|
chunk: &mut [RawPixel; CHUNK_SIZE * CHUNK_SIZE],
|
||||||
) -> eyre::Result<()> {
|
) -> eyre::Result<()> {
|
||||||
chunk.coord = coord;
|
let (cx, cy) = (coord.0 as isize, coord.1 as isize);
|
||||||
let runs = &mut chunk.runs;
|
let Some(pixel_array) = unsafe { self.world_ptr.as_mut() }
|
||||||
runs.clear();
|
.wrap_err("no world")?
|
||||||
|
.chunk_map
|
||||||
|
.chunk_array
|
||||||
|
.get(cx >> SCALE, cy >> SCALE)
|
||||||
|
else {
|
||||||
|
return Err(eyre!("chunk not loaded"));
|
||||||
|
};
|
||||||
|
let (shift_x, shift_y) = self.get_shift::<CHUNK_SIZE>(cx, cy);
|
||||||
|
for ((i, j), 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_raw_pixel(i, j);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
unsafe fn decode_world(&self, chunk: NoitaWorldUpdate) -> eyre::Result<()> {
|
unsafe fn decode_world(&self, chunk: NoitaWorldUpdate) -> eyre::Result<()> {
|
||||||
std::hint::black_box(chunk);
|
let chunk_coord = chunk.coord;
|
||||||
//TODO
|
let (cx, cy) = (chunk_coord.0 as isize, chunk_coord.1 as isize);
|
||||||
|
let Some(pixel_array) = unsafe { self.world_ptr.as_mut() }
|
||||||
|
.wrap_err("no world")?
|
||||||
|
.chunk_map
|
||||||
|
.chunk_array
|
||||||
|
.get_mut(cx >> SCALE, cy >> SCALE)
|
||||||
|
else {
|
||||||
|
return Err(eyre!("chunk not loaded"));
|
||||||
|
};
|
||||||
|
let (shift_x, shift_y) = self.get_shift::<CHUNK_SIZE>(cx, cy);
|
||||||
|
let start_x = cx * CHUNK_SIZE as isize;
|
||||||
|
let start_y = cy * CHUNK_SIZE as isize;
|
||||||
|
let mut x = 0;
|
||||||
|
let mut y = 0;
|
||||||
|
for run in chunk.runs {
|
||||||
|
for _ in 0..run.length {
|
||||||
|
if let Some(cell) = pixel_array.get_mut(shift_x + x, shift_y + y) {
|
||||||
|
let xs = start_x + x;
|
||||||
|
let ys = start_y + y;
|
||||||
|
let mat = &self.material_list[run.data.material as usize];
|
||||||
|
match mat.cell_type {
|
||||||
|
CellType::None => {
|
||||||
|
cell.0 = 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.0 = (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.0 = (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.0 = (fire as *mut FireCell).cast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x == CHUNK_SIZE as isize {
|
||||||
|
x = 0;
|
||||||
|
y += 1;
|
||||||
|
} else {
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ use world::WorldManager;
|
||||||
use crate::lobby_code::LobbyKind;
|
use crate::lobby_code::LobbyKind;
|
||||||
use crate::mod_manager::{ModmanagerSettings, get_mods};
|
use crate::mod_manager::{ModmanagerSettings, get_mods};
|
||||||
use crate::net::world::world_model::ChunkData;
|
use crate::net::world::world_model::ChunkData;
|
||||||
use crate::net::world::world_model::chunk::{Pixel, PixelFlags};
|
|
||||||
use crate::player_cosmetics::{PlayerPngDesc, create_player_png, get_player_skin};
|
use crate::player_cosmetics::{PlayerPngDesc, create_player_png, get_player_skin};
|
||||||
use crate::steam_helper::LobbyExtraData;
|
use crate::steam_helper::LobbyExtraData;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -38,7 +37,7 @@ use crate::{
|
||||||
bookkeeping::save_state::{SaveState, SaveStateEntry},
|
bookkeeping::save_state::{SaveState, SaveStateEntry},
|
||||||
};
|
};
|
||||||
use shared::des::ProxyToDes;
|
use shared::des::ProxyToDes;
|
||||||
use shared::world_sync::{ChunkCoord, ProxyToWorldSync};
|
use shared::world_sync::{ChunkCoord, PixelFlags, ProxyToWorldSync, RawPixel};
|
||||||
use tangled::Reliability;
|
use tangled::Reliability;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
mod audio;
|
mod audio;
|
||||||
|
@ -1486,7 +1485,7 @@ pub struct ExplosionData {
|
||||||
ray: u64,
|
ray: u64,
|
||||||
hole: bool,
|
hole: bool,
|
||||||
liquid: bool,
|
liquid: bool,
|
||||||
mat: Pixel,
|
mat: RawPixel,
|
||||||
prob: u8,
|
prob: u8,
|
||||||
}
|
}
|
||||||
impl ExplosionData {
|
impl ExplosionData {
|
||||||
|
@ -1510,7 +1509,7 @@ impl ExplosionData {
|
||||||
ray,
|
ray,
|
||||||
hole,
|
hole,
|
||||||
liquid,
|
liquid,
|
||||||
mat: Pixel {
|
mat: RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: mat,
|
material: mat,
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,10 +12,7 @@ use std::time::Duration;
|
||||||
use std::{cmp, mem, thread};
|
use std::{cmp, mem, thread};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use wide::f32x8;
|
use wide::f32x8;
|
||||||
use world_model::{
|
use world_model::{ChunkData, ChunkDelta, WorldModel, chunk::Chunk};
|
||||||
ChunkData, ChunkDelta, WorldModel,
|
|
||||||
chunk::{Chunk, Pixel},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::bookkeeping::save_state::{SaveState, SaveStateEntry};
|
use crate::bookkeeping::save_state::{SaveState, SaveStateEntry};
|
||||||
|
|
||||||
|
@ -1185,7 +1182,7 @@ impl WorldManager {
|
||||||
let start = x - radius;
|
let start = x - radius;
|
||||||
let end = x + radius;
|
let end = x + radius;
|
||||||
|
|
||||||
let air_pixel = Pixel {
|
let air_pixel = RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: 0,
|
material: 0,
|
||||||
};
|
};
|
||||||
|
@ -1270,7 +1267,7 @@ impl WorldManager {
|
||||||
let dm2 = ((dmx.unsigned_abs() as u64 * dmx.unsigned_abs() as u64
|
let dm2 = ((dmx.unsigned_abs() as u64 * dmx.unsigned_abs() as u64
|
||||||
+ dmy.unsigned_abs() as u64 * dmy.unsigned_abs() as u64) as f64)
|
+ dmy.unsigned_abs() as u64 * dmy.unsigned_abs() as u64) as f64)
|
||||||
.recip();
|
.recip();
|
||||||
let air_pixel = Pixel {
|
let air_pixel = RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: 0,
|
material: 0,
|
||||||
};
|
};
|
||||||
|
@ -1421,7 +1418,7 @@ impl WorldManager {
|
||||||
(y - r).div_euclid(CHUNK_SIZE as i32),
|
(y - r).div_euclid(CHUNK_SIZE as i32),
|
||||||
(y + r).div_euclid(CHUNK_SIZE as i32),
|
(y + r).div_euclid(CHUNK_SIZE as i32),
|
||||||
);
|
);
|
||||||
let air_pixel = Pixel {
|
let air_pixel = RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: mat.unwrap_or(0),
|
material: mat.unwrap_or(0),
|
||||||
};
|
};
|
||||||
|
@ -1727,7 +1724,7 @@ impl WorldManager {
|
||||||
list: Vec<(u64, u64, Option<ChunkCoord>)>,
|
list: Vec<(u64, u64, Option<ChunkCoord>)>,
|
||||||
hole: bool,
|
hole: bool,
|
||||||
liquid: bool,
|
liquid: bool,
|
||||||
mat: Pixel,
|
mat: RawPixel,
|
||||||
prob: u8,
|
prob: u8,
|
||||||
r: u64,
|
r: u64,
|
||||||
) -> Vec<ExRet> {
|
) -> Vec<ExRet> {
|
||||||
|
@ -1744,7 +1741,7 @@ impl WorldManager {
|
||||||
(y - r as i32).div_euclid(CHUNK_SIZE as i32),
|
(y - r as i32).div_euclid(CHUNK_SIZE as i32),
|
||||||
(y + r as i32).div_euclid(CHUNK_SIZE as i32),
|
(y + r as i32).div_euclid(CHUNK_SIZE as i32),
|
||||||
);
|
);
|
||||||
let air_pixel = Pixel {
|
let air_pixel = RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: 0,
|
material: 0,
|
||||||
};
|
};
|
||||||
|
@ -2050,7 +2047,7 @@ impl WorldManager {
|
||||||
grouped.entry(key).or_default().push((a, b));
|
grouped.entry(key).or_default().push((a, b));
|
||||||
}
|
}
|
||||||
let data: Vec<(usize, Vec<(usize, u64)>)> = grouped.into_iter().collect();
|
let data: Vec<(usize, Vec<(usize, u64)>)> = grouped.into_iter().collect();
|
||||||
let air_pixel = Pixel {
|
let air_pixel = RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: PixelFlags::Normal,
|
||||||
material: 0,
|
material: 0,
|
||||||
};
|
};
|
||||||
|
@ -3075,12 +3072,13 @@ fn test_explosion_img_big_many() {
|
||||||
}*/
|
}*/
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::net::LiquidType;
|
use crate::net::LiquidType;
|
||||||
use crate::net::world::world_model::chunk::PixelFlags;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use shared::world_sync::{CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, WorldSyncToProxy};
|
use shared::world_sync::{
|
||||||
|
CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, PixelFlags, RawPixel, WorldSyncToProxy,
|
||||||
|
};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
|
@ -3398,4 +3396,4 @@ impl WorldManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,10 +2,12 @@ use std::num::NonZeroU16;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bitcode::{Decode, Encode};
|
use bitcode::{Decode, Encode};
|
||||||
use chunk::{Chunk, CompactPixel, Pixel, PixelFlags};
|
use chunk::Chunk;
|
||||||
use encoding::PixelRunner;
|
use encoding::PixelRunner;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use shared::world_sync::{CHUNK_SIZE, ChunkCoord, NoitaWorldUpdate, PixelRun};
|
use shared::world_sync::{
|
||||||
|
CHUNK_SIZE, ChunkCoord, CompactPixel, NoitaWorldUpdate, PixelRun, RawPixel,
|
||||||
|
};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
pub(crate) mod chunk;
|
pub(crate) mod chunk;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
|
@ -53,8 +55,8 @@ impl ChunkData {
|
||||||
let mut runner = PixelRunner::new();
|
let mut runner = PixelRunner::new();
|
||||||
for _ in 0..CHUNK_SIZE * CHUNK_SIZE {
|
for _ in 0..CHUNK_SIZE * CHUNK_SIZE {
|
||||||
runner.put_pixel(
|
runner.put_pixel(
|
||||||
Pixel {
|
RawPixel {
|
||||||
flags: PixelFlags::Normal,
|
flags: shared::world_sync::PixelFlags::Normal,
|
||||||
material: mat,
|
material: mat,
|
||||||
}
|
}
|
||||||
.to_compact(),
|
.to_compact(),
|
||||||
|
@ -131,7 +133,7 @@ impl WorldModel {
|
||||||
update: NoitaWorldUpdate,
|
update: NoitaWorldUpdate,
|
||||||
changed: &mut FxHashSet<ChunkCoord>,
|
changed: &mut FxHashSet<ChunkCoord>,
|
||||||
) {
|
) {
|
||||||
fn set_pixel(pixel: Pixel, chunk: &mut Chunk, offset: usize) -> bool {
|
fn set_pixel(pixel: RawPixel, chunk: &mut Chunk, offset: usize) -> bool {
|
||||||
let current = chunk.pixel(offset);
|
let current = chunk.pixel(offset);
|
||||||
if current != pixel {
|
if current != pixel {
|
||||||
chunk.set_pixel(offset, pixel);
|
chunk.set_pixel(offset, pixel);
|
||||||
|
@ -149,11 +151,6 @@ impl WorldModel {
|
||||||
let mut chunk_coord = update.coord;
|
let mut chunk_coord = update.coord;
|
||||||
let mut chunk = self.chunks.entry(update.coord).or_default();
|
let mut chunk = self.chunks.entry(update.coord).or_default();
|
||||||
for run in update.runs {
|
for run in update.runs {
|
||||||
let flags = if run.data.flags > 0 {
|
|
||||||
PixelFlags::Fluid
|
|
||||||
} else {
|
|
||||||
PixelFlags::Normal
|
|
||||||
};
|
|
||||||
for _ in 0..run.length {
|
for _ in 0..run.length {
|
||||||
let xs = start_x + x;
|
let xs = start_x + x;
|
||||||
let ys = start_y + y;
|
let ys = start_y + y;
|
||||||
|
@ -163,9 +160,9 @@ impl WorldModel {
|
||||||
chunk = self.chunks.entry(chunk_coord).or_default();
|
chunk = self.chunks.entry(chunk_coord).or_default();
|
||||||
}
|
}
|
||||||
if set_pixel(
|
if set_pixel(
|
||||||
Pixel {
|
RawPixel {
|
||||||
material: run.data.material,
|
material: run.data.material,
|
||||||
flags,
|
flags: run.data.flags,
|
||||||
},
|
},
|
||||||
chunk,
|
chunk,
|
||||||
offset,
|
offset,
|
||||||
|
@ -193,7 +190,7 @@ impl WorldModel {
|
||||||
let mut runner = PixelRunner::new();
|
let mut runner = PixelRunner::new();
|
||||||
for j in 0..CHUNK_SIZE {
|
for j in 0..CHUNK_SIZE {
|
||||||
for i in 0..CHUNK_SIZE {
|
for i in 0..CHUNK_SIZE {
|
||||||
runner.put_pixel(chunk.pixel(i + j * CHUNK_SIZE).to_raw())
|
runner.put_pixel(chunk.pixel(i + j * CHUNK_SIZE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updates.push(NoitaWorldUpdate {
|
updates.push(NoitaWorldUpdate {
|
||||||
|
|
|
@ -1,99 +1,10 @@
|
||||||
use std::num::NonZeroU16;
|
|
||||||
|
|
||||||
use super::{ChunkData, encoding::PixelRunner};
|
use super::{ChunkData, encoding::PixelRunner};
|
||||||
use bitcode::{Decode, Encode};
|
use shared::world_sync::{CHUNK_SIZE, CompactPixel, RawPixel};
|
||||||
use crossbeam::atomic::AtomicCell;
|
|
||||||
use shared::world_sync::{CHUNK_SIZE, RawPixel};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode)]
|
|
||||||
pub enum PixelFlags {
|
|
||||||
/// Actual material isn't known yet.
|
|
||||||
#[default]
|
|
||||||
Unknown,
|
|
||||||
Normal,
|
|
||||||
Fluid,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode)]
|
|
||||||
pub struct Pixel {
|
|
||||||
pub flags: PixelFlags,
|
|
||||||
pub material: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pixel {
|
|
||||||
pub fn to_raw(self) -> RawPixel {
|
|
||||||
RawPixel {
|
|
||||||
material: if self.flags != PixelFlags::Unknown {
|
|
||||||
self.material
|
|
||||||
} else {
|
|
||||||
u16::MAX
|
|
||||||
},
|
|
||||||
flags: if self.flags == PixelFlags::Normal {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_compact(self) -> CompactPixel {
|
|
||||||
let flag_bit = if self.flags == PixelFlags::Normal {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
let material = (self.material + 1) & 2047; // 11 bits for material
|
|
||||||
let raw = if self.flags == PixelFlags::Unknown {
|
|
||||||
CompactPixel::UNKNOWN_RAW
|
|
||||||
} else {
|
|
||||||
(material << 1) | flag_bit
|
|
||||||
};
|
|
||||||
CompactPixel(NonZeroU16::new(raw).unwrap())
|
|
||||||
}
|
|
||||||
fn from_compact(compact: CompactPixel) -> Self {
|
|
||||||
let raw = u16::from(compact.0);
|
|
||||||
let material = (raw >> 1) - 1;
|
|
||||||
let flags = if raw & 1 == 1 {
|
|
||||||
PixelFlags::Fluid
|
|
||||||
} else {
|
|
||||||
PixelFlags::Normal
|
|
||||||
};
|
|
||||||
if raw == CompactPixel::UNKNOWN_RAW {
|
|
||||||
Pixel {
|
|
||||||
flags: PixelFlags::Unknown,
|
|
||||||
material: 0,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Pixel { flags, material }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An entire pixel packed into 12 bits.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct CompactPixel(pub NonZeroU16);
|
|
||||||
|
|
||||||
impl CompactPixel {
|
|
||||||
const UNKNOWN_RAW: u16 = 4095;
|
|
||||||
fn from_raw(val: u16) -> Self {
|
|
||||||
CompactPixel(NonZeroU16::new(val).unwrap())
|
|
||||||
}
|
|
||||||
fn raw(self) -> u16 {
|
|
||||||
u16::from(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CompactPixel {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(NonZeroU16::new(CompactPixel::UNKNOWN_RAW).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pixels: [u16; CHUNK_SQUARE],
|
pixels: [u16; CHUNK_SQUARE],
|
||||||
changed: Changed<bool, CHUNK_SQUARE>,
|
changed: Changed<bool, CHUNK_SQUARE>,
|
||||||
any_changed: bool,
|
any_changed: bool,
|
||||||
crc: AtomicCell<Option<u64>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Changed<T: Default, const N: usize>([T; N]);
|
struct Changed<T: Default, const N: usize>([T; N]);
|
||||||
|
@ -106,8 +17,6 @@ impl Changed<u128, CHUNK_SIZE> {
|
||||||
self.0[n / CHUNK_SIZE] |= 1 << (n % CHUNK_SIZE)
|
self.0[n / CHUNK_SIZE] |= 1 << (n % CHUNK_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
const _: () = assert!(u128::BITS as usize == CHUNK_SIZE);
|
|
||||||
const CHUNK_SQUARE: usize = CHUNK_SIZE * CHUNK_SIZE;
|
const CHUNK_SQUARE: usize = CHUNK_SIZE * CHUNK_SIZE;
|
||||||
impl Changed<bool, CHUNK_SQUARE> {
|
impl Changed<bool, CHUNK_SQUARE> {
|
||||||
fn get(&self, n: usize) -> bool {
|
fn get(&self, n: usize) -> bool {
|
||||||
|
@ -147,22 +56,21 @@ impl Default for Chunk {
|
||||||
pixels: [4095; CHUNK_SQUARE],
|
pixels: [4095; CHUNK_SQUARE],
|
||||||
changed: Changed([false; CHUNK_SQUARE]),
|
changed: Changed([false; CHUNK_SQUARE]),
|
||||||
any_changed: false,
|
any_changed: false,
|
||||||
crc: None.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Chunk of pixels. Stores pixels and tracks if they were changed.
|
/// Chunk of pixels. Stores pixels and tracks if they were changed.
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn pixel(&self, offset: usize) -> Pixel {
|
pub fn pixel(&self, offset: usize) -> RawPixel {
|
||||||
Pixel::from_compact(CompactPixel::from_raw(self.pixels[offset]))
|
RawPixel::from_compact(CompactPixel::from_raw(self.pixels[offset]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact_pixel(&self, offset: usize) -> CompactPixel {
|
pub fn compact_pixel(&self, offset: usize) -> CompactPixel {
|
||||||
CompactPixel::from_raw(self.pixels[offset])
|
CompactPixel::from_raw(self.pixels[offset])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pixel(&mut self, offset: usize, pixel: Pixel) {
|
pub fn set_pixel(&mut self, offset: usize, pixel: RawPixel) {
|
||||||
let px = pixel.to_compact().raw();
|
let px = pixel.to_compact().raw();
|
||||||
if self.pixels[offset] != px {
|
if self.pixels[offset] != px {
|
||||||
self.pixels[offset] = px;
|
self.pixels[offset] = px;
|
||||||
|
@ -184,7 +92,6 @@ impl Chunk {
|
||||||
pub fn mark_changed(&mut self, offset: usize) {
|
pub fn mark_changed(&mut self, offset: usize) {
|
||||||
self.changed.set(offset);
|
self.changed.set(offset);
|
||||||
self.any_changed = true;
|
self.any_changed = true;
|
||||||
self.crc.store(None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_changed(&mut self) {
|
pub fn clear_changed(&mut self) {
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use crate::noita::types;
|
use crate::noita::types;
|
||||||
|
use crate::noita::types::{
|
||||||
|
CellVTable, CellVTables, FireCellVTable, GasCellVTable, LiquidCellVTable, NoneCellVTable,
|
||||||
|
SolidCellVTable,
|
||||||
|
};
|
||||||
use eyre::ContextCompat;
|
use eyre::ContextCompat;
|
||||||
use object::{Object, ObjectSection};
|
use object::{Object, ObjectSection};
|
||||||
use std::arch::asm;
|
use std::arch::asm;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
pub fn get_functions() -> eyre::Result<(&'static types::CellVTable, *mut types::GameGlobal)> {
|
pub fn get_functions() -> eyre::Result<(types::CellVTables, *mut types::GameGlobal)> {
|
||||||
let exe = std::env::current_exe()?;
|
let exe = std::env::current_exe()?;
|
||||||
let mut file = File::open(exe)?;
|
let mut file = File::open(exe)?;
|
||||||
let mut vec = Vec::with_capacity(15460864);
|
let mut vec = Vec::with_capacity(15460864);
|
||||||
|
@ -20,29 +24,25 @@ pub fn get_functions() -> eyre::Result<(&'static types::CellVTable, *mut types::
|
||||||
let ptr = unsafe { start.add(game_global) };
|
let ptr = unsafe { start.add(game_global) };
|
||||||
let game_global_ptr = get_rela_call(ptr, offset);
|
let game_global_ptr = get_rela_call(ptr, offset);
|
||||||
let game_global_ptr = get_global(game_global_ptr);
|
let game_global_ptr = get_global(game_global_ptr);
|
||||||
let rdata = obj.section_by_name(".rdata").wrap_err("obj err")?;
|
let cellvtables = unsafe {
|
||||||
let data = rdata.data()?;
|
let none = CellVTable {
|
||||||
let cellvtable: &[u8] = &[
|
none: (0xff2040 as *const NoneCellVTable).as_ref().unwrap(),
|
||||||
0x20, 0xaf, 0x70, 0x00, 0xa0, 0x01, 0x5b, 0x00, 0x50, 0xb0, 0x70, 0x00, 0x60, 0xb0, 0x70,
|
};
|
||||||
0x00, 0xc0, 0x01, 0x5b, 0x00, 0xd0, 0x01, 0x5b, 0x00, 0x90, 0xd0, 0x70, 0x00, 0xe0, 0x01,
|
let liquid = CellVTable {
|
||||||
0x5b, 0x00, 0x00, 0x02, 0x5b, 0x00, 0xf0, 0x01, 0x5b, 0x00, 0x70, 0xb0, 0x70, 0x00, 0xb0,
|
liquid: (0x100bb90 as *const LiquidCellVTable).as_ref().unwrap(),
|
||||||
0xb0, 0x70, 0x00, 0xd0, 0xc0, 0x4a, 0x00, 0xb0, 0xd0, 0x70, 0x00, 0x60, 0xbf, 0x4a, 0x00,
|
};
|
||||||
0xa0, 0xd1, 0x70, 0x00, 0xe0, 0xd1, 0x70, 0x00, 0x80, 0xd1, 0x70, 0x00, 0x40, 0xcb, 0x70,
|
let gas = CellVTable {
|
||||||
0x00, 0x80, 0xcd, 0x70, 0x00, 0xd0, 0xcd, 0x70, 0x00, 0xe0, 0xc6, 0x70, 0x00, 0xb0, 0x01,
|
gas: (0x1007bcc as *const GasCellVTable).as_ref().unwrap(),
|
||||||
0x5b, 0x00, 0x90, 0xbf, 0x4a, 0x00, 0xa0, 0xbf, 0x4a, 0x00, 0x10, 0xb1, 0x70, 0x00, 0x20,
|
};
|
||||||
0xb1, 0x70, 0x00, 0x60, 0xb1, 0x70, 0x00, 0xb0, 0xf5, 0x70, 0x00, 0xd0, 0xf5, 0x70, 0x00,
|
let solid = CellVTable {
|
||||||
0xf0, 0xcd, 0x70, 0x00, 0x50, 0xf7, 0x70, 0x00, 0xe0, 0xc0, 0x4a, 0x00, 0xf0, 0xf7, 0x70,
|
solid: (0xff8a6c as *const SolidCellVTable).as_ref().unwrap(),
|
||||||
0x00, 0x20, 0xc0, 0x4a, 0x00, 0x60, 0xf1, 0x70, 0x00, 0xf0, 0xea, 0x70, 0x00, 0x90, 0xef,
|
};
|
||||||
0x70, 0x00, 0x60, 0xf3, 0x70, 0x00, 0x50, 0xaf, 0x70, 0x00, 0xd0, 0xb1, 0x70,
|
let fire = CellVTable {
|
||||||
0x00,
|
fire: (0x10096e0 as *const FireCellVTable).as_ref().unwrap(),
|
||||||
//TODO i should search for a function in the vtable then find the vtable prob
|
};
|
||||||
];
|
CellVTables([none, liquid, gas, solid, fire])
|
||||||
let start = rdata.address() as *const c_void;
|
};
|
||||||
let cellvtable_ptr = unsafe {
|
Ok((cellvtables, game_global_ptr))
|
||||||
(start.add(find_pattern(data, cellvtable)?) as *const types::CellVTable).as_ref()
|
|
||||||
}
|
|
||||||
.wrap_err("cell data err")?;
|
|
||||||
Ok((cellvtable_ptr, game_global_ptr))
|
|
||||||
}
|
}
|
||||||
fn get_global(global: *const c_void) -> *mut types::GameGlobal {
|
fn get_global(global: *const c_void) -> *mut types::GameGlobal {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -56,11 +56,11 @@ fn get_global(global: *const c_void) -> *mut types::GameGlobal {
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn find_pattern(data: &[u8], pattern: &[u8]) -> eyre::Result<usize> {
|
/*fn find_pattern(data: &[u8], pattern: &[u8]) -> eyre::Result<usize> {
|
||||||
data.windows(pattern.len())
|
data.windows(pattern.len())
|
||||||
.position(|window| window == pattern)
|
.position(|window| window == pattern)
|
||||||
.wrap_err("match err")
|
.wrap_err("match err")
|
||||||
}
|
}*/
|
||||||
fn find_pattern_global(data: &[u8], pattern: &[u8], other: &[u8]) -> eyre::Result<(usize, isize)> {
|
fn find_pattern_global(data: &[u8], pattern: &[u8], other: &[u8]) -> eyre::Result<(usize, isize)> {
|
||||||
let r = data
|
let r = data
|
||||||
.windows(pattern.len() + 4 + other.len())
|
.windows(pattern.len() + 4 + other.len())
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use shared::world_sync::{PixelFlags, RawPixel};
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
@ -28,20 +29,49 @@ unsafe impl Sync for ChunkPtr {}
|
||||||
unsafe impl Send for ChunkPtr {}
|
unsafe impl Send for ChunkPtr {}
|
||||||
|
|
||||||
impl ChunkPtr {
|
impl ChunkPtr {
|
||||||
|
#[inline]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &CellPtr> {
|
pub fn iter(&self) -> impl Iterator<Item = &CellPtr> {
|
||||||
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }.iter()
|
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }.iter()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn get(&self, x: isize, y: isize) -> Option<&Cell> {
|
pub fn get(&self, x: isize, y: isize) -> Option<&Cell> {
|
||||||
let index = (y << 9) | x;
|
let index = (y << 9) | x;
|
||||||
unsafe { self.0.offset(index).as_ref().and_then(|c| c.0.as_ref()) }
|
unsafe { self.0.offset(index).as_ref().and_then(|c| c.0.as_ref()) }
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn get_mut(&mut self, x: isize, y: isize) -> Option<&mut CellPtr> {
|
pub fn get_mut(&mut self, x: isize, y: isize) -> Option<&mut CellPtr> {
|
||||||
unsafe { self.get_mut_raw(x, y).as_mut() }
|
unsafe { self.get_mut_raw(x, y).as_mut() }
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn get_mut_raw(&mut self, x: isize, y: isize) -> *mut CellPtr {
|
pub fn get_mut_raw(&mut self, x: isize, y: isize) -> *mut CellPtr {
|
||||||
let index = (y << 9) | x;
|
let index = (y << 9) | x;
|
||||||
unsafe { self.0.offset(index) }
|
unsafe { self.0.offset(index) }
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_raw_pixel(&self, x: isize, y: isize) -> RawPixel {
|
||||||
|
if let Some(cell) = self.get(x, y) {
|
||||||
|
if cell.material.cell_type == CellType::Liquid {
|
||||||
|
RawPixel {
|
||||||
|
material: cell.material.material_type as u16,
|
||||||
|
flags: if cell.get_liquid().is_static == cell.material.liquid_static {
|
||||||
|
PixelFlags::Normal
|
||||||
|
} else {
|
||||||
|
PixelFlags::Abnormal
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RawPixel {
|
||||||
|
material: cell.material.material_type as u16,
|
||||||
|
flags: PixelFlags::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RawPixel {
|
||||||
|
material: 0,
|
||||||
|
flags: PixelFlags::Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -61,16 +91,20 @@ pub struct ChunkArrayPtr(pub *mut ChunkPtrPtr);
|
||||||
unsafe impl Sync for ChunkArrayPtr {}
|
unsafe impl Sync for ChunkArrayPtr {}
|
||||||
unsafe impl Send for ChunkArrayPtr {}
|
unsafe impl Send for ChunkArrayPtr {}
|
||||||
impl ChunkArrayPtr {
|
impl ChunkArrayPtr {
|
||||||
|
#[inline]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &ChunkPtrPtr> {
|
pub fn iter(&self) -> impl Iterator<Item = &ChunkPtrPtr> {
|
||||||
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }.iter()
|
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }.iter()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn slice(&self) -> &'static [ChunkPtrPtr] {
|
pub fn slice(&self) -> &'static [ChunkPtrPtr] {
|
||||||
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }
|
unsafe { std::slice::from_raw_parts(self.0, 512 * 512) }
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn get(&self, x: isize, y: isize) -> Option<&ChunkPtr> {
|
pub fn get(&self, x: isize, y: isize) -> Option<&ChunkPtr> {
|
||||||
let index = (((y - 256) & 511) << 9) | ((x - 256) & 511);
|
let index = (((y - 256) & 511) << 9) | ((x - 256) & 511);
|
||||||
unsafe { self.0.offset(index).as_ref().and_then(|c| c.0.as_ref()) }
|
unsafe { self.0.offset(index).as_ref().and_then(|c| c.0.as_ref()) }
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
pub fn get_mut(&mut self, x: isize, y: isize) -> Option<&mut ChunkPtr> {
|
pub fn get_mut(&mut self, x: isize, y: isize) -> Option<&mut ChunkPtr> {
|
||||||
let index = (((y - 256) & 511) << 9) | ((x - 256) & 511);
|
let index = (((y - 256) & 511) << 9) | ((x - 256) & 511);
|
||||||
unsafe { self.0.offset(index).as_mut().and_then(|c| c.0.as_mut()) }
|
unsafe { self.0.offset(index).as_mut().and_then(|c| c.0.as_mut()) }
|
||||||
|
@ -118,14 +152,15 @@ struct AABB {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
//ptr is 0x17f83e30, seems not constant
|
||||||
pub struct GridWorldThreadedVTable {
|
pub struct GridWorldThreadedVTable {
|
||||||
//TODO find some data maybe
|
//TODO find some data maybe
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct GridWorldThreaded {
|
pub struct GridWorldThreaded {
|
||||||
grid_world_threaded_vtable: &'static GridWorldThreadedVTable,
|
pub grid_world_threaded_vtable: &'static GridWorldThreadedVTable,
|
||||||
unknown: [isize; 287],
|
unknown: [isize; 287],
|
||||||
update_region: AABB,
|
update_region: AABB,
|
||||||
}
|
}
|
||||||
|
@ -139,7 +174,7 @@ pub struct GridWorld {
|
||||||
pub world_update_count: isize,
|
pub world_update_count: isize,
|
||||||
pub chunk_map: ChunkMap,
|
pub chunk_map: ChunkMap,
|
||||||
unknown2: [isize; 41],
|
unknown2: [isize; 41],
|
||||||
m_thread_impl: *mut GridWorldThreaded,
|
pub m_thread_impl: *mut GridWorldThreaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -462,19 +497,40 @@ impl Default for CellData {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct CellVTables(pub [CellVTable; 5]);
|
pub struct CellVTables(pub [CellVTable; 5]);
|
||||||
|
|
||||||
|
impl CellVTables {
|
||||||
|
pub fn none(&self) -> &'static NoneCellVTable {
|
||||||
|
unsafe { self.0[0].none }
|
||||||
|
}
|
||||||
|
pub fn liquid(&self) -> &'static LiquidCellVTable {
|
||||||
|
unsafe { self.0[1].liquid }
|
||||||
|
}
|
||||||
|
pub fn gas(&self) -> &'static GasCellVTable {
|
||||||
|
unsafe { self.0[2].gas }
|
||||||
|
}
|
||||||
|
pub fn solid(&self) -> &'static SolidCellVTable {
|
||||||
|
unsafe { self.0[3].solid }
|
||||||
|
}
|
||||||
|
pub fn fire(&self) -> &'static FireCellVTable {
|
||||||
|
unsafe { self.0[4].fire }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub union CellVTable {
|
pub union CellVTable {
|
||||||
//ptr is 0xff2040
|
//ptr is 0xff2040
|
||||||
none: NoneCellVTable,
|
pub none: &'static NoneCellVTable,
|
||||||
//ptr is 0x100bb90
|
//ptr is 0x100bb90
|
||||||
liquid: LiquidCellVTable,
|
pub liquid: &'static LiquidCellVTable,
|
||||||
gas: GasCellVTable,
|
//ptr is 0x1007bcc
|
||||||
|
pub gas: &'static GasCellVTable,
|
||||||
//ptr is 0xff8a6c
|
//ptr is 0xff8a6c
|
||||||
solid: SolidCellVTable,
|
pub solid: &'static SolidCellVTable,
|
||||||
//ptr is 0x10096e0
|
//ptr is 0x10096e0
|
||||||
fire: FireCellVTable,
|
pub fire: &'static FireCellVTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for CellVTable {
|
impl Debug for CellVTable {
|
||||||
|
@ -645,7 +701,7 @@ impl FireCell {
|
||||||
///# Safety
|
///# Safety
|
||||||
pub unsafe fn create(
|
pub unsafe fn create(
|
||||||
mat: &'static CellData,
|
mat: &'static CellData,
|
||||||
vtable: &'static CellVTable,
|
vtable: &'static FireCellVTable,
|
||||||
world: *mut GridWorld,
|
world: *mut GridWorld,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let lifetime = if let Some(world) = unsafe { world.as_mut() } {
|
let lifetime = if let Some(world) = unsafe { world.as_mut() } {
|
||||||
|
@ -655,7 +711,12 @@ impl FireCell {
|
||||||
} else {
|
} else {
|
||||||
-1
|
-1
|
||||||
};
|
};
|
||||||
let mut cell = Cell::create(mat, vtable);
|
let mut cell = Cell::create(mat, unsafe {
|
||||||
|
(vtable as *const FireCellVTable)
|
||||||
|
.cast::<CellVTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
cell.is_burning = true;
|
cell.is_burning = true;
|
||||||
Self {
|
Self {
|
||||||
cell,
|
cell,
|
||||||
|
@ -689,7 +750,7 @@ impl GasCell {
|
||||||
///# Safety
|
///# Safety
|
||||||
pub unsafe fn create(
|
pub unsafe fn create(
|
||||||
mat: &'static CellData,
|
mat: &'static CellData,
|
||||||
vtable: &'static CellVTable,
|
vtable: &'static GasCellVTable,
|
||||||
world: *mut GridWorld,
|
world: *mut GridWorld,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (bool, lifetime) = if let Some(world) = unsafe { world.as_mut() } {
|
let (bool, lifetime) = if let Some(world) = unsafe { world.as_mut() } {
|
||||||
|
@ -703,7 +764,12 @@ impl GasCell {
|
||||||
} else {
|
} else {
|
||||||
(false, -1)
|
(false, -1)
|
||||||
};
|
};
|
||||||
let mut cell = Cell::create(mat, vtable);
|
let mut cell = Cell::create(mat, unsafe {
|
||||||
|
(vtable as *const GasCellVTable)
|
||||||
|
.cast::<CellVTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
cell.is_burning = true;
|
cell.is_burning = true;
|
||||||
Self {
|
Self {
|
||||||
cell,
|
cell,
|
||||||
|
@ -746,7 +812,7 @@ impl LiquidCell {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
pub unsafe fn create(
|
pub unsafe fn create(
|
||||||
mat: &'static CellData,
|
mat: &'static CellData,
|
||||||
vtable: &'static CellVTable,
|
vtable: &'static LiquidCellVTable,
|
||||||
world: *mut GridWorld,
|
world: *mut GridWorld,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let lifetime = if mat.lifetime > 0
|
let lifetime = if mat.lifetime > 0
|
||||||
|
@ -760,7 +826,12 @@ impl LiquidCell {
|
||||||
-1
|
-1
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
cell: Cell::create(mat, vtable),
|
cell: Cell::create(mat, unsafe {
|
||||||
|
(vtable as *const LiquidCellVTable)
|
||||||
|
.cast::<CellVTable>()
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
unknown1: 3,
|
unknown1: 3,
|
||||||
|
@ -806,7 +877,7 @@ pub struct CellFactory {
|
||||||
unknown1: [isize; 5],
|
unknown1: [isize; 5],
|
||||||
pub cell_data_len: usize,
|
pub cell_data_len: usize,
|
||||||
pub cell_data_ptr: *const CellData,
|
pub cell_data_ptr: *const CellData,
|
||||||
//likely more data
|
//TODO likely more data
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -831,12 +902,12 @@ pub struct GameGlobal {
|
||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
_unknown0: [u8; 8],
|
_unknown0: [u8; 8],
|
||||||
pub filename_index: u32,
|
pub filename_index: u32,
|
||||||
// More stuff, not that relevant currently.
|
//TODO More stuff, not that relevant currently.
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct EntityManager {
|
pub struct EntityManager {
|
||||||
_fld: c_void,
|
_fld: c_void,
|
||||||
// Unknown
|
//TODO Unknown
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ThiscallFn(c_void);
|
pub struct ThiscallFn(c_void);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIter
|
||||||
pub struct ParticleWorldState {
|
pub struct ParticleWorldState {
|
||||||
pub world_ptr: *mut types::GridWorld,
|
pub world_ptr: *mut types::GridWorld,
|
||||||
pub material_list: &'static [types::CellData],
|
pub material_list: &'static [types::CellData],
|
||||||
pub cell_vtable: &'static types::CellVTable,
|
pub cell_vtables: types::CellVTables,
|
||||||
}
|
}
|
||||||
unsafe impl Sync for ParticleWorldState {}
|
unsafe impl Sync for ParticleWorldState {}
|
||||||
unsafe impl Send for ParticleWorldState {}
|
unsafe impl Send for ParticleWorldState {}
|
||||||
|
@ -15,12 +15,6 @@ impl ParticleWorldState {
|
||||||
let shift_y = (y * CHUNK_SIZE as isize).rem_euclid(512);
|
let shift_y = (y * CHUNK_SIZE as isize).rem_euclid(512);
|
||||||
(shift_x, shift_y)
|
(shift_x, shift_y)
|
||||||
}
|
}
|
||||||
pub fn get_cell_material_id(&self, cell: &mut types::Cell) -> u16 {
|
|
||||||
let offset = unsafe {
|
|
||||||
(cell.material as *const types::CellData).offset_from(self.material_list.as_ptr())
|
|
||||||
};
|
|
||||||
offset as u16
|
|
||||||
}
|
|
||||||
pub fn exists<const SCALE: isize>(&self, cx: isize, cy: isize) -> bool {
|
pub fn exists<const SCALE: isize>(&self, cx: isize, cy: isize) -> bool {
|
||||||
let Some(world) = (unsafe { self.world_ptr.as_mut() }) else {
|
let Some(world) = (unsafe { self.world_ptr.as_mut() }) else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -82,7 +76,7 @@ impl ParticleWorldState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn new() -> eyre::Result<Self> {
|
pub fn new() -> eyre::Result<Self> {
|
||||||
let (cell_vtable, global_ptr) = crate::noita::init_data::get_functions()?;
|
let (cell_vtables, global_ptr) = crate::noita::init_data::get_functions()?;
|
||||||
let global = unsafe { global_ptr.as_mut() }.wrap_err("no global?")?;
|
let global = unsafe { global_ptr.as_mut() }.wrap_err("no global?")?;
|
||||||
let cell_factory =
|
let cell_factory =
|
||||||
unsafe { global.m_cell_factory.as_mut() }.wrap_err("no cell factory?")?;
|
unsafe { global.m_cell_factory.as_mut() }.wrap_err("no cell factory?")?;
|
||||||
|
@ -93,7 +87,7 @@ impl ParticleWorldState {
|
||||||
Ok(ParticleWorldState {
|
Ok(ParticleWorldState {
|
||||||
world_ptr,
|
world_ptr,
|
||||||
material_list,
|
material_list,
|
||||||
cell_vtable,
|
cell_vtables,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use bitcode::{Decode, Encode};
|
use bitcode::{Decode, Encode};
|
||||||
|
use std::num::NonZeroU16;
|
||||||
/// Stores a run of pixels.
|
/// Stores a run of pixels.
|
||||||
/// Not specific to Noita side - length is an actual length
|
/// Not specific to Noita side - length is an actual length
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
||||||
|
@ -18,10 +19,74 @@ pub struct NoitaWorldUpdate {
|
||||||
pub runs: Vec<PixelRun<RawPixel>>,
|
pub runs: Vec<PixelRun<RawPixel>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode)]
|
||||||
|
pub enum PixelFlags {
|
||||||
|
/// Actual material isn't known yet.
|
||||||
|
#[default]
|
||||||
|
Unknown,
|
||||||
|
Normal,
|
||||||
|
Abnormal,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Encode, Decode, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, Encode, Decode, PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct RawPixel {
|
pub struct RawPixel {
|
||||||
pub material: u16,
|
pub material: u16,
|
||||||
pub flags: u8,
|
pub flags: PixelFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawPixel {
|
||||||
|
pub fn to_compact(self) -> CompactPixel {
|
||||||
|
let flag_bit = if self.flags == PixelFlags::Normal {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
let material = (self.material + 1) & 2047; // 11 bits for material
|
||||||
|
let raw = if self.flags == PixelFlags::Unknown {
|
||||||
|
CompactPixel::UNKNOWN_RAW
|
||||||
|
} else {
|
||||||
|
(material << 1) | flag_bit
|
||||||
|
};
|
||||||
|
CompactPixel(NonZeroU16::new(raw).unwrap())
|
||||||
|
}
|
||||||
|
pub fn from_compact(compact: CompactPixel) -> Self {
|
||||||
|
let raw = u16::from(compact.0);
|
||||||
|
let material = (raw >> 1) - 1;
|
||||||
|
let flags = if raw & 1 == 1 {
|
||||||
|
PixelFlags::Abnormal
|
||||||
|
} else {
|
||||||
|
PixelFlags::Normal
|
||||||
|
};
|
||||||
|
if raw == CompactPixel::UNKNOWN_RAW {
|
||||||
|
RawPixel {
|
||||||
|
flags: PixelFlags::Unknown,
|
||||||
|
material: 0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RawPixel { flags, material }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An entire pixel packed into 12 bits.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct CompactPixel(pub NonZeroU16);
|
||||||
|
|
||||||
|
impl CompactPixel {
|
||||||
|
const UNKNOWN_RAW: u16 = 4095;
|
||||||
|
pub fn from_raw(val: u16) -> Self {
|
||||||
|
CompactPixel(NonZeroU16::new(val).unwrap())
|
||||||
|
}
|
||||||
|
pub fn raw(self) -> u16 {
|
||||||
|
u16::from(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CompactPixel {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(NonZeroU16::new(CompactPixel::UNKNOWN_RAW).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Encode, Decode, Clone)]
|
#[derive(Debug, Encode, Decode, Clone)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue