diff --git a/server/src/blocks.rs b/server/src/blocks.rs index 44a188e..0ee0c3a 100644 --- a/server/src/blocks.rs +++ b/server/src/blocks.rs @@ -1,23 +1,24 @@ extern crate test; -use std::{sync::Arc, ops::Sub, collections::HashMap}; +use std::{collections::HashMap, num::NonZero, ops::Sub, sync::Arc}; -use anyhow::{Ok, anyhow}; +use anyhow::{anyhow, Context, Ok}; use nalgebra::Vector3; use rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope}; use serde::{Deserialize, Serialize}; use tokio::sync::{RwLock, OwnedRwLockReadGuard, OwnedRwLockWriteGuard}; -use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}}; +use crate::{pallette::{self, Pallette}, paths::{self, TRANSPARENT}, turtle::TurtleCommand}; const CHUNK_SIZE: usize = 8; const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; const CHUNK_VEC: Vec3 = Vec3::new(CHUNK_SIZE as i32, CHUNK_SIZE as i32, CHUNK_SIZE as i32); #[derive(Serialize, Deserialize)] -pub struct World { // TODO: make r-trees faster than this, for my sanity +pub struct World { index: HashMap, data: Vec, last: Option, + pallette: Pallette, } impl World { @@ -26,11 +27,12 @@ impl World { index: HashMap::new(), data: Vec::new(), last: None, + pallette: Pallette::new(), } } pub fn get(&self, block: Vec3) -> Option { let chunk = self.get_chunk(block)?; - Some(chunk.get(block)?) + Some(chunk.get(block, &self.pallette)?) } pub fn set(&mut self, block: Block) { @@ -45,11 +47,11 @@ impl World { match chunk { Some(chunk) => { - self.data[chunk].set(block).unwrap(); + self.data[chunk].set(block, &mut self.pallette).unwrap(); }, None => { let mut new_chunk = Chunk::new(chunk_coords); - new_chunk.set(block).unwrap(); + new_chunk.set(block, &mut self.pallette).unwrap(); self.data.push(new_chunk); self.index.insert(chunk_coords, self.data.len() - 1); }, @@ -113,31 +115,33 @@ pub struct Block { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Chunk { pos: Vec3, /// position in chunk coordinates (world/16) - data: [[[Option;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE] + data: [[[Option>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE] } impl Chunk { fn new(pos: Vec3) -> Self { - let data :[[[Option;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]= Default::default(); + let data :[[[Option>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]= Default::default(); Self { pos, data } } - fn set(&mut self, pos: Block) -> anyhow::Result<()> { + fn set(&mut self, pos: Block, pallette: &mut Pallette) -> anyhow::Result<()> { let chunk = self.pos.component_mul(&CHUNK_VEC); if !self.contains(&pos.pos) { return Err(anyhow!("out of bounds")); } let local: Vector3 = (pos.pos - chunk).map(|n| n as usize); - self.data[local.x][local.y][local.z] = Some(pos.name); + let idx = pallette.number(&pos.name); + + self.data[local.x][local.y][local.z] = Some(idx); Ok(()) } - fn get(&self, pos: Vec3) -> Option { + fn get(&self, pos: Vec3, pallette: &Pallette) -> Option { let chunk = self.pos.component_mul(&CHUNK_VEC); let local = pos - chunk; if !self.contains(&pos) { @@ -145,8 +149,10 @@ impl Chunk { } let local = local.map(|n| n as usize); + let idx = self.data[local.x][local.y][local.z]?; + Some(Block { - name: self.data[local.x][local.y][local.z].clone()?, + name: pallette.name(idx)?, pos, }) } diff --git a/server/src/main.rs b/server/src/main.rs index 611aeba..ff1df8c 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -30,6 +30,7 @@ use indoc::formatdoc; use crate::blocks::Block; mod blocks; +mod pallette; mod names; mod mine; mod fell; diff --git a/server/src/pallette.rs b/server/src/pallette.rs new file mode 100644 index 0000000..539b161 --- /dev/null +++ b/server/src/pallette.rs @@ -0,0 +1,30 @@ +use std::{collections::HashMap, hash::{DefaultHasher, Hasher, SipHasher}, num::NonZero}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct Pallette { + names: HashMap, +} + +impl Pallette { + pub fn new() -> Self { + let names = HashMap::new(); + Self { names } + } + + pub fn number(&mut self, name: &str) -> NonZero { + let mut hasher = DefaultHasher::new(); + hasher.write(name.as_bytes()); + + let mut key = hasher.finish() as u32; + if key == 0 {key += 1}; // double the collisions! + self.names.insert(key,name.to_owned()); + let key = unsafe { NonZero::new_unchecked(key) }; // SAFETY: see + // two lines above + key + } + + pub fn name(&self, number: NonZero) -> Option { + self.names.get(&number.get()).map(|s| s.to_owned()) + } +}