From 51f4d09898dbcf5000a8e74843c71daa5c7fa696 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 2 Nov 2023 05:42:31 +0100 Subject: [PATCH] chore: harder, better, faster, stronger --- src/chunk.rs | 50 +++++++++++++-------------- src/lib.rs | 16 ++++----- src/section.rs | 91 ++++---------------------------------------------- src/world.rs | 19 +++++------ 4 files changed, 45 insertions(+), 131 deletions(-) diff --git a/src/chunk.rs b/src/chunk.rs index a16c187..929ac64 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,8 +1,8 @@ -use std::io::{Cursor, Read, Seek}; +use std::io::{Cursor, Seek}; use pyo3::prelude::*; -use crate::section::{ChunkSectionModern, ChunkSection}; +use crate::section::{ChunkSection754, ChunkSection}; #[pyclass] #[derive(Debug, Clone)] @@ -12,14 +12,11 @@ pub struct Chunk { pub bitmask: u16, pub ground_up_continuous: bool, pub(crate) block_states: [[[u16; 16]; 256]; 16], - pub(crate) block_light: [[[u16; 16]; 256]; 16], - pub(crate) sky_light: [[[u16; 16]; 256]; 16], + // pub(crate) block_light: [[[u16; 16]; 256]; 16], + // pub(crate) sky_light: [[[u16; 16]; 256]; 16], // pub(crate) biomes: [u16; 256], pub(crate) block_entities: String, // TODO less jank way to store this NBT/JSON/PyDict ... } -// Biomes -// The biomes array is only present when ground-up continuous is set to true. Biomes cannot be changed unless a chunk is re-sent. -// The structure is an array of 256 bytes, each representing a Biome ID (it is recommended that 127 for "Void" is used if there is no set biome). The array is indexed by z * 16 | x. #[pymethods] impl Chunk { @@ -28,8 +25,8 @@ impl Chunk { Self { x, z, bitmask, ground_up_continuous, block_entities, block_states: [[[0u16; 16]; 256]; 16], - block_light: [[[0u16; 16]; 256]; 16], - sky_light: [[[0u16; 16]; 256]; 16], + // block_light: [[[0u16; 16]; 256]; 16], + // sky_light: [[[0u16; 16]; 256]; 16], // biomes: [0u16; 256], } } @@ -37,28 +34,20 @@ impl Chunk { pub fn read(&mut self, chunk_data: Vec) -> PyResult<()> { let mut c = Cursor::new(chunk_data); c.seek(std::io::SeekFrom::Start(0)).unwrap(); // just in case - let mut count = 0; - for i in 0..16 { - if ((self.bitmask >> i) & 1) != 0 { count += 1} - } - log::info!("chunk {}:{} contains {} sections", self.x, self.z, count); for i in 0..16 { if ((self.bitmask >> i) & 1) != 0 { - log::info!("reading section #{} of chunk {}:{}", i, self.x, self.z); - let mut section = ChunkSectionModern::default(); + let mut section = ChunkSection754::default(); section.read(&mut c)?; for ((x, y, z), state) in section { self.block_states[x][y + (i*16)][z] = state; } - log::info!("updated block states"); } } Ok(()) } - pub fn merge(&mut self, other: Chunk) -> Option { - let old_chunk = self.clone(); + pub fn merge(&mut self, other: &Chunk) { for i in 0..16 { if ((self.bitmask >> i) & 1) != 0 { for x in 0..16 { @@ -66,25 +55,32 @@ impl Chunk { for z in 0..16 { self.block_states[x][(i * 16) + y][z] = other.block_states[x][(i * 16) + y][z]; - self.block_light[x][(i * 16) + y][z] = - other.block_light[x][(i * 16) + y][z]; - self.sky_light[x][(i * 16) + y][z] = - other.sky_light[x][(i * 16) + y][z]; + // self.block_light[x][(i * 16) + y][z] = + // other.block_light[x][(i * 16) + y][z]; + // self.sky_light[x][(i * 16) + y][z] = + // other.sky_light[x][(i * 16) + y][z]; } } } } } - return Some(old_chunk); //TODO: is this really we want to return? } pub fn get_slice(&self, y:u8) -> Vec> { let mut slice = vec![vec![0u16;16];16]; - for x in 0..16 { - for z in 0..16 { - slice[x][z] = self.block_states[x][y as usize][z]; + for (x, row) in self.block_states.iter().enumerate() { + for (z, state) in row[y as usize].iter().enumerate() { + slice[x][z] = *state; } } slice } + + pub fn get(&self, x:usize, y:usize, z:usize) -> u16 { + self.block_states[x][y][z] + } + + pub fn entities(&self) -> &str { + &self.block_entities + } } diff --git a/src/lib.rs b/src/lib.rs index 847a08d..2196ffe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,9 +7,6 @@ use world::World; use pyo3::prelude::*; -/// A Python module implemented in Rust. The name of this function must match -/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to -/// import the module. #[pymodule] fn aiocraft(_py: Python<'_>, m: &PyModule) -> PyResult<()> { pyo3_log::init(); @@ -23,9 +20,9 @@ fn aiocraft(_py: Python<'_>, m: &PyModule) -> PyResult<()> { fn abs(v:i32, modulo:i32) -> i32 { if v < 0 { - return (modulo + (v % modulo)) % modulo; + (modulo + (v % modulo)) % modulo } else { - return v % modulo; + v % modulo } } @@ -40,16 +37,17 @@ pub fn bit_pack(data: Vec, bits: i32, size: i32) -> PyResult> { let mut cursor = 0; let mut buffer = 0; for el in data { + // TODO what did i know then that i don't know now ?????? if cursor + bits > size { let delta = (cursor + bits) - size; - buffer |= (el & (2 << (bits - delta) - 1)) << cursor; + buffer |= (el & (2 << ((bits - delta) - 1))) << cursor; out.push(buffer); - buffer = 0 | ((el >> (bits - delta)) & (2 << delta - 1)); + buffer = (el >> (bits - delta)) & (2 << (delta - 1)); cursor = delta; } else { - buffer |= (el & (2 << bits - 1)) << cursor; + buffer |= (el & (2 << (bits - 1))) << cursor; cursor += bits; } } - return Ok(out); + Ok(out) } diff --git a/src/section.rs b/src/section.rs index e03f7dc..f99816e 100644 --- a/src/section.rs +++ b/src/section.rs @@ -1,4 +1,4 @@ -use std::io::{Read, ErrorKind}; +use std::io::Read; use pyo3::prelude::*; @@ -26,7 +26,7 @@ pub trait ChunkSection : Default + Iterator { } #[derive(Debug, Clone, Default)] -pub struct ChunkSectionModern { +pub struct ChunkSection754 { pub block_count: i16, pub bits_per_block: u8, pub palette: Vec, @@ -38,7 +38,7 @@ pub struct ChunkSectionModern { y: usize, } -impl ChunkSectionModern { +impl ChunkSection754 { fn count_up_coordinates(&mut self) { self.x += 1; if self.x >= 16 { @@ -52,7 +52,7 @@ impl ChunkSectionModern { } } -impl Iterator for ChunkSectionModern { +impl Iterator for ChunkSection754 { type Item = ((usize, usize, usize), u16); fn next(&mut self) -> Option { @@ -88,21 +88,19 @@ impl Iterator for ChunkSectionModern { } } -impl ChunkSection for ChunkSectionModern { +impl ChunkSection for ChunkSection754 { fn read(&mut self, buffer: &mut R) -> PyResult<()> { let mut block_count_buf: [u8; 2] = [0; 2]; buffer.read_exact(&mut block_count_buf)?; self.block_count = i16::from_be_bytes(block_count_buf); - log::info!("block count is {}", self.block_count); let mut bits_per_block_buf: [u8; 1] = [0u8; 1]; buffer.read_exact(&mut bits_per_block_buf)?; self.bits_per_block = u8::from_be_bytes(bits_per_block_buf); - log::info!("bits per block is {}", self.bits_per_block); self.bits_per_block = match self.bits_per_block { 0..=4 => 4, 5..=8 => self.bits_per_block, - 9.. => ChunkSectionModern::max_bits(), + 9.. => ChunkSection754::max_bits(), }; self.palette = Vec::new(); @@ -112,10 +110,8 @@ impl ChunkSection for ChunkSectionModern { self.palette.push(read_varint(buffer)?); } } - log::info!("palette of len {}: {:?}", self.palette.len(), self.palette); let content_len = read_varint(buffer)? as usize; - log::info!("reading {} longs from buffer ({} bytes)", content_len, content_len * 8); let mut data_buf = vec![0u8; content_len * 8]; buffer.read_exact(&mut data_buf)?; @@ -146,78 +142,3 @@ pub struct ChunkSection340 { pub block_light: [[[u16; 16]; 16]; 16], pub sky_light: Option<[[[u16; 16]; 16]; 16]>, } - -#[allow(unused)] -fn read_paletted_container(buffer: &mut R) -> PyResult<[[[u16; 16]; 16]; 16]> { - let mut data: [u8; 1] = [0u8; 1]; - buffer.read_exact(&mut data)?; - // bits = UnsignedByte.read(buffer, ctx=ctx) # FIXME if bits > 4 it reads trash - // #logging.debug("[%d|%d@%d] Bits per block : %d", ctx.x, ctx.z, ctx.sec, bits) - let bits_raw = u8::from_be_bytes(data); - let bits = match bits_raw { - 0 => 0, - 1..=4 => 4, - 5..=8 => bits_raw, - 9.. => 13, // this should not be hardcoded but we have no way to calculate all possible block states - }; - log::info!("bits per block: {} -> {}", bits_raw, bits); - let palette_len = read_varint(buffer)?; - log::info!("palette len: {}", palette_len); - if bits == 0 { // single value palette: the length is the actual value and it fills the chunk - let _container_size = read_varint(buffer)? as usize; // this is still sent - assert_eq!(_container_size, 0); - return Ok([[[palette_len as u16; 16]; 16]; 16]); - } - let mut palette = vec![0; palette_len as usize]; - for p in 0..palette_len as usize { - palette[p] = read_varint(buffer)?; - } - // # logging.debug("[%d|%d@%d] Palette section : [%d] %s", ctx.x, ctx.z, ctx.sec, palette_len, str(palette)) - let container_size = read_varint(buffer)? as usize; - log::info!("reading off socket {}x8 = {} bytes (palette of {} bits)", container_size, container_size * 8, bits); - let mut block_data_buffer = vec![0u8; container_size * 8]; - buffer.read_exact(&mut block_data_buffer)?; - let block_data : Vec = block_data_buffer - .chunks_exact(8) - .map(|x| u64::from_be_bytes(x.try_into().unwrap())) // wtf rust!!! - .collect(); - let mut section = [[[0u16; 16]; 16]; 16]; - let max_val: u16 = (1 << bits) - 1; - let mut i = 0; - for y in 0..16 { - for z in 0..16 { - for x in 0..16 { - // let i = (y * 16 * 16) + (z * 16) + x; - let start_byte = (i * bits as usize) / 64; - let start_offset = (i * bits as usize) % 64; - let end_byte = ((i + 1) * bits as usize) / 64; - if start_byte >= container_size || end_byte >= container_size { - log::warn!("early exit? is this ok?"); - return Ok(section); // early exit? is this OK? - } - let value: u16; - if start_byte == end_byte { - value = ((block_data[start_byte as usize] //FIXME out of bounds? - >> start_offset) & max_val as u64) as u16; - } else { - let end_offset = 64 - start_offset; - value = (((block_data[start_byte as usize] as usize) - >> start_offset - | (block_data[end_byte as usize] as usize) << end_offset) // FIXME: out of bounds? - & max_val as usize) as u16; - } - if bits == 13 { - section[x][y][z] = value; - } else if value as i32 >= palette_len { - log::warn!("index out of palette bounds : {}/{} (bits {})", value, palette_len, bits); - section[x][y][z] = value; - } else { - section[x][y][z] = palette[value as usize] as u16; - } - - i += 1; - } - } - } - return Ok(section); -} diff --git a/src/world.rs b/src/world.rs index 890a865..80ec02c 100644 --- a/src/world.rs +++ b/src/world.rs @@ -5,6 +5,7 @@ use pyo3::prelude::*; use crate::{chunk::Chunk, abs}; #[pyclass] +#[derive(Debug, Default, Clone)] pub struct World { chunks: HashMap<(i32, i32), Chunk>, } @@ -14,18 +15,16 @@ impl World { #[new] pub fn new() -> Self { log::info!("Initializing world from Rust"); - World { - chunks: HashMap::new(), - } + Self::default() } pub fn __getitem__(&self, item: (i32, i32)) -> Option { - return self.get(item.0, item.1); + self.get(item.0, item.1) } pub fn get_block(&self, x: i32, y: i32, z: i32) -> Option { if y < 0 { - return Some(0); // TODO no longer the case after 1.17 + return None; // TODO no longer the case after 1.17 } let mut chunk_x = x / 16; let mut chunk_z = z / 16; @@ -49,18 +48,18 @@ impl World { let c = self.chunks.get_mut(&(chunk_x, chunk_z))?; let old_block = c.block_states[x_off][y as usize][z_off]; c.block_states[x_off][y as usize][z_off] = id; - return Some(old_block); + Some(old_block) } pub fn get(&self, x: i32, z: i32) -> Option { - return Some((self.chunks.get(&(x, z))?).clone()); + Some((self.chunks.get(&(x, z))?).clone()) } - pub fn put(&mut self, chunk: Chunk, x: i32, z: i32, merge: bool) -> Option { + pub fn put(&mut self, chunk: Chunk, x: i32, z: i32, merge: bool) { if merge && self.chunks.contains_key(&(x, z)) { - return self.chunks.get_mut(&(x, z)).unwrap().merge(chunk); + self.chunks.get_mut(&(x, z)).unwrap().merge(&chunk); } else { - return self.chunks.insert((x, z), chunk); + self.chunks.insert((x, z), chunk); } } }