chore: harder, better, faster, stronger
This commit is contained in:
parent
c1f9f7f262
commit
51f4d09898
4 changed files with 45 additions and 131 deletions
50
src/chunk.rs
50
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<u8>) -> 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<Chunk> {
|
||||
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<Vec<u16>> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
16
src/lib.rs
16
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<i32>, bits: i32, size: i32) -> PyResult<Vec<i32>> {
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -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<i32>,
|
||||
|
@ -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<Self::Item> {
|
||||
|
@ -88,21 +88,19 @@ impl Iterator for ChunkSectionModern {
|
|||
}
|
||||
}
|
||||
|
||||
impl ChunkSection for ChunkSectionModern {
|
||||
impl ChunkSection for ChunkSection754 {
|
||||
fn read<R: 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<R: Read>(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<u64> = 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);
|
||||
}
|
||||
|
|
19
src/world.rs
19
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<Chunk> {
|
||||
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<u16> {
|
||||
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<Chunk> {
|
||||
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<Chunk> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue