use std::collections::HashMap;
use ndarray::{Array3};
use crate::block::Block;
use crate::error::Error;
#[derive(Debug, Copy, Clone)]
pub struct Light(u8);
#[derive(Debug, Clone)]
pub struct Entity {
pub tags: HashMap<String, fastnbt::Value>,
pub position: [f64; 3],
pub block_pos: [i32; 3],
}
#[derive(Debug, Clone)]
pub struct BlockEntity {
pub tags: HashMap<String, fastnbt::Value>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(dead_code)]
pub enum PendingTickInfo {
Fluid { id: String },
Block { id: String },
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[allow(dead_code)]
pub struct PendingTick {
pub priority: i32,
pub sub_tick: i64,
pub time: i32,
pub info: PendingTickInfo,
}
pub trait HasPalette {
fn palette(&self) -> &[Block];
fn find_in_palette(&self, block: &Block) -> Option<u16> {
for (idx, blk) in self.palette().iter().enumerate() {
if blk == block {
return Some(idx as u16);
}
}
return None;
}
fn block_index_of_air(&self) -> Option<u16> {
for (idx, blk) in self.palette().iter().enumerate() {
if blk.is_air() {
return Some(idx as u16);
}
}
return None;
}
fn block_index_of_structure_void(&self) -> Option<u16> {
for (idx, blk) in self.palette().iter().enumerate() {
if blk.is_structure_void() {
return Some(idx as u16);
}
}
return None;
}
}
pub trait WorldSlice {
fn shape(&self) -> [i32; 3];
fn contains_coord(&self, r_pos: [i32; 3]) -> bool {
for dim in 0..3 {
if r_pos[dim] >= 0 && r_pos[dim] < self.shape()[dim] {
continue;
}
return false;
}
return true;
}
fn volume(&self) -> u64 {
return self.shape()[0] as u64 * self.shape()[1] as u64 * self.shape()[2] as u64;
}
fn total_blocks(&self, include_air: bool) -> u64;
fn block_info_at(&self, r_pos: [i32; 3]) -> Option<(u16, &Block, Option<&BlockEntity>, &[PendingTick])> {
return Some((self.block_index_at(r_pos)?,
self.block_at(r_pos)?,
self.block_entity_at(r_pos),
self.pending_tick_at(r_pos),
));
}
fn block_index_at(&self, r_pos: [i32; 3]) -> Option<u16>;
fn block_at(&self, r_pos: [i32; 3]) -> Option<&Block>;
fn block_entity_at(&self, r_pos: [i32; 3]) -> Option<&BlockEntity>;
fn pending_tick_at(&self, r_pos: [i32; 3]) -> &[PendingTick];
}
pub trait HasOffset {
fn offset(&self) -> [i32; 3];
}
#[derive(Debug, Clone)]
pub struct Region {
pub name: String,
pub array_yzx: Array3<u16>,
pub palette: Vec<Block>,
pub block_entities: HashMap<[i32; 3], BlockEntity>,
pub pending_ticks: HashMap<[i32; 3], Vec<PendingTick>>,
pub entities: Vec<Entity>,
pub offset: [i32; 3],
}
impl Default for Light {
fn default() -> Self {
return Self(0xFF);
}
}
impl Light {
pub fn new(sky_light: u8, block_light: u8) -> Self {
return Self(sky_light << 4 | block_light);
}
pub fn sky_light(&self) -> u8 {
return (self.0 & 0xF0) >> 4;
}
pub fn block_light(&self) -> u8 {
return self.0 & 0x0F;
}
}
impl Entity {
pub fn new() -> Entity {
return Entity {
tags: HashMap::new(),
position: [0.0, 0.0, 0.0],
block_pos: [0, 0, 0],
};
}
pub fn pos_shift(&mut self, adder: [i32; 3]) {
for dim in 0..3 {
self.block_pos[dim] += adder[dim];
self.position[dim] += adder[dim] as f64;
}
}
}
impl BlockEntity {
pub fn new() -> BlockEntity {
return BlockEntity {
tags: HashMap::new(),
};
}
}
impl PendingTickInfo {
pub fn default() -> PendingTickInfo {
return PendingTickInfo::Block { id: "".to_string() };
}
}
impl HasPalette for Region {
fn palette(&self) -> &[Block] {
return &self.palette;
}
}
impl HasOffset for Region {
fn offset(&self) -> [i32; 3] {
return self.offset;
}
}
impl WorldSlice for Region {
fn shape(&self) -> [i32; 3] {
let shape = self.array_yzx.shape();
if shape.len() != 3 {
panic!("Invalid array dimensions: should be 3 but now it is {}", shape.len());
}
return Self::pos_yzx_to_xyz(&[shape[0] as i32, shape[1] as i32, shape[2] as i32]);
}
fn total_blocks(&self, include_air: bool) -> u64 {
let mut counter = 0;
for blk_id in &self.array_yzx {
if let Some(air_idx) = self.block_index_of_air() {
if *blk_id == air_idx {
if include_air {
counter += 1;
}
continue;
}
}
if let Some(sv_idx) = self.block_index_of_structure_void() {
if *blk_id == sv_idx {
counter += 1;
continue;
}
}
counter += 1;
}
return counter;
}
fn block_info_at(&self, r_pos: [i32; 3]) -> Option<(u16, &Block, Option<&BlockEntity>, &[PendingTick])> {
return if let Some(pid) = self.block_index_at(r_pos) {
Some((pid, &self.palette[pid as usize],
self.block_entities.get(&r_pos),
self.pending_tick_at(r_pos)))
} else {
None
};
}
fn block_index_at(&self, r_pos: [i32; 3]) -> Option<u16> {
if !self.contains_coord(r_pos) {
return None;
}
let x = r_pos[0] as usize;
let y = r_pos[1] as usize;
let z = r_pos[2] as usize;
let pid = self.array_yzx[[y, z, x]] as usize;
return Some(pid as u16);
}
fn block_at(&self, r_pos: [i32; 3]) -> Option<&Block> {
return if let Some(pid) = self.block_index_at(r_pos) {
Some(&self.palette[pid as usize])
} else {
None
};
}
fn block_entity_at(&self, r_pos: [i32; 3]) -> Option<&BlockEntity> {
return self.block_entities.get(&r_pos);
}
fn pending_tick_at(&self, r_pos: [i32; 3]) -> &[PendingTick] {
if let Some(pts) = self.pending_ticks.get(&r_pos) {
return &pts;
}
return &[];
}
}
#[allow(dead_code)]
impl Region {
pub fn pos_xyz_to_yzx<T>(pos: &[T; 3]) -> [T; 3]
where T: Copy {
return [pos[1], pos[2], pos[0]];
}
pub fn pos_yzx_to_xyz<T>(yzx: &[T; 3]) -> [T; 3]
where T: Copy {
return [yzx[2], yzx[0], yzx[1]];
}
pub fn new() -> Region {
return Self::with_shape([1, 1, 1]);
}
pub fn with_shape(shape_xyz: [i32; 3]) -> Region {
let shape_yzx = [shape_xyz[1] as usize, shape_xyz[2] as usize, shape_xyz[0] as usize];
let mut result = Region {
name: String::from("NewRegion"),
array_yzx: Array3::zeros(shape_yzx),
palette: Vec::new(),
block_entities: HashMap::new(),
pending_ticks: HashMap::new(),
entities: Vec::new(),
offset: [0, 0, 0],
};
result.find_or_append_to_palette(&Block::air());
return result;
}
pub fn i32_to_usize(pos: &[i32; 3]) -> [usize; 3] {
let x = pos[0] as usize;
let y = pos[1] as usize;
let z = pos[2] as usize;
return [x, y, z];
}
pub fn set_block(&mut self, r_pos: [i32; 3], block: &Block) -> Result<(), ()> {
if !self.contains_coord(r_pos) {
return Err(());
}
let mut blkid = self.palette.len();
for (idx, blk) in self.palette.iter().enumerate() {
if blk == block {
blkid = idx;
break;
}
}
if blkid >= self.palette.len() {
self.palette.push(block.clone());
}
if blkid >= 65536 {
return Err(());
}
let blkid = blkid as u16;
let pos_usize = Self::i32_to_usize(&r_pos);
self.array_yzx[Self::pos_xyz_to_yzx(&pos_usize)] = blkid;
return Ok(());
}
pub fn set_block_id(&mut self, r_pos: [i32; 3], block_id: u16) -> Result<(), ()> {
if !self.contains_coord(r_pos) {
return Err(());
}
if block_id as usize >= self.palette.len() {
return Err(());
}
let pos_usize = Self::i32_to_usize(&r_pos);
self.array_yzx[Self::pos_xyz_to_yzx(&pos_usize)] = block_id;
return Ok(());
}
pub fn reshape(&mut self, shape_xyz: &[i32; 3]) {
let mut usz: [usize; 3] = [0, 0, 0];
for idx in 0..3 {
let sz = shape_xyz[idx];
if sz < 0 {
panic!("Try resizing with negative size [{},{},{}]", shape_xyz[0], shape_xyz[1], shape_xyz[2]);
}
usz[idx] = sz as usize;
}
let shape_yzx = Self::pos_xyz_to_yzx(&usz);
self.array_yzx = Array3::zeros(shape_yzx);
}
pub fn shape_yzx(&self) -> [i32; 3] {
let shape = self.array_yzx.shape();
if shape.len() != 3 {
panic!("Invalid array dimensions: should be 3 but now it is {}", shape.len());
}
return [shape[0] as i32, shape[1] as i32, shape[2] as i32];
}
pub fn global_pos_to_relative_pos(&self, g_pos: [i32; 3]) -> [i32; 3] {
return [
g_pos[0] - self.offset[0],
g_pos[1] - self.offset[1],
g_pos[2] - self.offset[2],
];
}
pub fn relative_pos_to_global_pos(&self, r_pos: [i32; 3]) -> [i32; 3] {
return [
r_pos[0] + self.offset[0],
r_pos[1] + self.offset[1],
r_pos[2] + self.offset[2],
];
}
pub fn shrink_palette(&mut self) -> Result<(), Error> {
let mut block_counter: Vec<u64> = Vec::new();
block_counter.resize(self.palette.len(), 0);
for x in 0..self.shape()[0] {
for y in 0..self.shape()[1] {
for z in 0..self.shape()[2] {
let idx = self.array_yzx[[y as usize, z as usize, x as usize]];
if idx as usize >= self.palette.len() {
return Err(Error::BlockIndexOutOfRangeWriting {
r_pos: [x, y, z],
block_index: idx,
max_index: self.palette.len() as u16 - 1,
});
}
block_counter[idx as usize] += 1;
}
}
}
let mut id_map: Vec<u16> = Vec::new();
id_map.resize(self.palette.len(), 65535);
{
let mut counter: u16 = 0;
for id in 0..self.palette.len() {
if block_counter[id] <= 0 {
continue;
}
id_map[id] = counter;
counter += 1;
}
for id in (0..block_counter.len()).rev() {
if block_counter[id] <= 0 {
self.palette.remove(id);
}
}
}
for blkid in &mut self.array_yzx {
let new_id = id_map[*blkid as usize];
assert!((new_id as usize) < self.palette.len());
*blkid = new_id;
}
return Ok(());
}
pub fn find_in_palette(&self, block: &Block) -> Option<u16> {
for (idx, blk) in self.palette.iter().enumerate() {
if blk == block {
return Some(idx as u16);
}
}
return None;
}
pub fn find_or_append_to_palette(&mut self, block: &Block) -> u16 {
return match self.find_in_palette(block) {
Some(idx) => idx,
None => {
self.palette.push(block.clone());
(self.palette.len() - 1) as u16
}
}
}
pub fn fill_with(&mut self, block: &Block) {
let blk_id = self.find_or_append_to_palette(block);
self.array_yzx.fill(blk_id);
}
pub fn set_block_entity_at(&mut self, r_pos: [i32; 3], be: BlockEntity) -> Option<BlockEntity> {
return self.block_entities.insert(r_pos, be);
}
pub fn set_pending_tick_at(&mut self, r_pos: [i32; 3], value: Vec<PendingTick>) -> Option<Vec<PendingTick>> {
return self.pending_ticks.insert(r_pos, value);
}
pub fn block_info_at_mut(&mut self, r_pos: [i32; 3]) -> Option<(u16, &Block, Option<&mut BlockEntity>, &mut [PendingTick])> {
return if let Some(pid) = self.block_index_at(r_pos) {
Some((pid, &self.palette[pid as usize],
self.block_entities.get_mut(&r_pos),
if let Some(pts) = self.pending_ticks.get_mut(&r_pos) { pts.as_mut_slice() } else { &mut [] }
))
} else {
None
};
}
pub fn block_entity_at_mut(&mut self, r_pos: [i32; 3]) -> Option<&mut BlockEntity> {
return self.block_entities.get_mut(&r_pos);
}
pub fn pending_tick_at_mut(&mut self, r_pos: [i32; 3]) -> &mut [PendingTick] {
return if let Some(pts) = self.pending_ticks.get_mut(&r_pos) {
pts.as_mut_slice()
} else {
&mut []
};
}
}