aboutsummaryrefslogtreecommitdiff
path: root/azalea-world/src/lib.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-05-15 01:46:11 +0000
committerGitHub <noreply@github.com>2022-05-15 01:46:11 +0000
commitd0ac62d85276bc48e4f8e0e60afdc35840681622 (patch)
treeff4996b89d6f34c7c452d1b2950e53d512bce3c1 /azalea-world/src/lib.rs
parentef3cbe27f2a7eed5c635924d6fa0401dd04eae77 (diff)
parentc16e958d0be671a17edf060aee9850faccbcfe14 (diff)
downloadazalea-drasl-d0ac62d85276bc48e4f8e0e60afdc35840681622.tar.xz
Merge pull request #6 from mat-1/chunk-decoding
Chunk decoding
Diffstat (limited to 'azalea-world/src/lib.rs')
-rw-r--r--azalea-world/src/lib.rs237
1 files changed, 237 insertions, 0 deletions
diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs
new file mode 100644
index 00000000..766c61f0
--- /dev/null
+++ b/azalea-world/src/lib.rs
@@ -0,0 +1,237 @@
+#![feature(int_roundings)]
+
+mod bit_storage;
+mod palette;
+
+use crate::palette::PalettedContainerType;
+use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
+use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
+pub use bit_storage::BitStorage;
+use palette::PalettedContainer;
+use std::{
+ io::{Read, Write},
+ ops::{Index, IndexMut},
+ sync::{Arc, Mutex},
+};
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ let result = 2 + 2;
+ assert_eq!(result, 4);
+ }
+}
+
+const SECTION_HEIGHT: u32 = 16;
+
+pub struct World {
+ pub storage: ChunkStorage,
+ pub height: u32,
+ pub min_y: i32,
+}
+
+impl World {
+ pub fn replace_with_packet_data(
+ &mut self,
+ pos: &ChunkPos,
+ data: &mut impl Read,
+ ) -> Result<(), String> {
+ if !self.storage.in_range(pos) {
+ println!(
+ "Ignoring chunk since it's not in the view range: {}, {}",
+ pos.x, pos.z
+ );
+ return Ok(());
+ }
+ // let existing_chunk = &self.storage[pos];
+
+ let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?));
+ println!("Loaded chunk {:?}", pos);
+ self.storage[pos] = Some(chunk);
+
+ Ok(())
+ }
+
+ pub fn update_view_center(&mut self, pos: &ChunkPos) {
+ self.storage.view_center = *pos;
+ }
+
+ pub fn get_block_state(&self, pos: &BlockPos) -> Option<u32> {
+ self.storage.get_block_state(pos, self.min_y)
+ }
+}
+impl Index<&ChunkPos> for World {
+ type Output = Option<Arc<Mutex<Chunk>>>;
+
+ fn index(&self, pos: &ChunkPos) -> &Self::Output {
+ &self.storage[pos]
+ }
+}
+impl IndexMut<&ChunkPos> for World {
+ fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
+ &mut self.storage[pos]
+ }
+}
+// impl Index<&BlockPos> for World {
+// type Output = Option<Arc<Mutex<Chunk>>>;
+
+// fn index(&self, pos: &BlockPos) -> &Self::Output {
+// let chunk = &self[ChunkPos::from(pos)];
+// // chunk.
+
+// }
+// }
+
+pub struct ChunkStorage {
+ view_center: ChunkPos,
+ chunk_radius: u32,
+ view_range: u32,
+ // chunks is a list of size chunk_radius * chunk_radius
+ chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
+}
+
+// java moment
+// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
+fn floor_mod(x: i32, y: u32) -> u32 {
+ if x < 0 {
+ y - ((-x) as u32 % y)
+ } else {
+ x as u32 % y
+ }
+}
+
+impl ChunkStorage {
+ pub fn new(chunk_radius: u32) -> Self {
+ let view_range = chunk_radius * 2 + 1;
+ ChunkStorage {
+ view_center: ChunkPos::new(0, 0),
+ chunk_radius,
+ view_range,
+ chunks: vec![None; (view_range * view_range) as usize],
+ }
+ }
+
+ fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
+ (floor_mod(chunk_pos.x, self.view_range) * self.view_range
+ + floor_mod(chunk_pos.z, self.view_range)) as usize
+ }
+
+ pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
+ (chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
+ && (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
+ }
+
+ pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<u32> {
+ let chunk_pos = ChunkPos::from(pos);
+ println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
+ let chunk = &self[&chunk_pos];
+ match chunk {
+ Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
+ None => None,
+ }
+ }
+}
+
+impl Index<&ChunkPos> for ChunkStorage {
+ type Output = Option<Arc<Mutex<Chunk>>>;
+
+ fn index(&self, pos: &ChunkPos) -> &Self::Output {
+ &self.chunks[self.get_index(pos)]
+ }
+}
+impl IndexMut<&ChunkPos> for ChunkStorage {
+ fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
+ let index = self.get_index(pos);
+ &mut self.chunks[index]
+ }
+}
+
+#[derive(Debug)]
+pub struct Chunk {
+ pub sections: Vec<Section>,
+}
+
+impl Chunk {
+ pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
+ Self::read_with_world_height(buf, data.height)
+ }
+
+ pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
+ let section_count = world_height / SECTION_HEIGHT;
+ let mut sections = Vec::with_capacity(section_count as usize);
+ for _ in 0..section_count {
+ let section = Section::read_into(buf)?;
+ sections.push(section);
+ }
+ Ok(Chunk { sections })
+ }
+
+ pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
+ // TODO: check the build height and stuff, this code will be broken if the min build height is 0
+ // (LevelHeightAccessor.getMinSection in vanilla code)
+ assert!(y >= 0);
+ let min_section_index = min_y.div_floor(16);
+ (y.div_floor(16) - min_section_index) as u32
+ }
+
+ pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> u32 {
+ let section_index = self.section_index(pos.y, min_y);
+ // TODO: make sure the section exists
+ let section = &self.sections[section_index as usize];
+ let chunk_section_pos = ChunkSectionBlockPos::from(pos);
+ let block_state = section.get(chunk_section_pos);
+ block_state
+ }
+}
+
+impl McBufWritable for Chunk {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ for section in &self.sections {
+ section.write_into(buf)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Section {
+ pub block_count: u16,
+ pub states: PalettedContainer,
+ pub biomes: PalettedContainer,
+}
+
+impl McBufReadable for Section {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let block_count = u16::read_into(buf)?;
+ // this is commented out because the vanilla server is wrong
+ // assert!(
+ // block_count <= 16 * 16 * 16,
+ // "A section has more blocks than what should be possible. This is a bug!"
+ // );
+ let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
+ let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
+ Ok(Section {
+ block_count,
+ states,
+ biomes,
+ })
+ }
+}
+
+impl McBufWritable for Section {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ self.block_count.write_into(buf)?;
+ self.states.write_into(buf)?;
+ self.biomes.write_into(buf)?;
+ Ok(())
+ }
+}
+
+impl Section {
+ // TODO: return a BlockState instead of a u32
+ fn get(&self, pos: ChunkSectionBlockPos) -> u32 {
+ self.states
+ .get(pos.x as usize, pos.y as usize, pos.z as usize)
+ }
+}