mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add abstraction to support multiple world formats
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -2032,7 +2032,6 @@ dependencies = [
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"static_assertions",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -2656,12 +2655,6 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
||||
@@ -18,7 +18,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ futures = "0.3"
|
||||
flate2 = "1.0"
|
||||
serde.workspace = true
|
||||
serde_json = "1.0"
|
||||
static_assertions = "1.1.0"
|
||||
log.workspace = true
|
||||
|
||||
parking_lot.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::level::WorldError;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::block_registry::{Block, BlockCategory, BLOCKS};
|
||||
|
||||
@@ -21,16 +21,16 @@ impl BlockState {
|
||||
pub fn new(
|
||||
registry_id: &str,
|
||||
properties: Option<&HashMap<String, String>>,
|
||||
) -> Result<Self, WorldError> {
|
||||
) -> Result<Self, BlockStateError> {
|
||||
let block_registry = BLOCKS
|
||||
.get(registry_id)
|
||||
.ok_or(WorldError::BlockIdentifierNotFound)?;
|
||||
.ok_or(BlockStateError::BlockIdentifierNotFound)?;
|
||||
let mut block_states = block_registry.states.iter();
|
||||
|
||||
let block_state = match properties {
|
||||
Some(properties) => block_states
|
||||
.find(|state| &state.properties == properties)
|
||||
.ok_or_else(|| WorldError::BlockStateIdNotFound)?,
|
||||
.ok_or(BlockStateError::BlockStateIdNotFound)?,
|
||||
None => block_states
|
||||
.find(|state| state.is_default)
|
||||
.expect("Every Block should have at least 1 default state"),
|
||||
@@ -71,3 +71,11 @@ impl BlockState {
|
||||
self.category == category
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BlockStateError {
|
||||
#[error("The requested block identifier does not exist")]
|
||||
BlockIdentifierNotFound,
|
||||
#[error("The requested block state id does not exist")]
|
||||
BlockStateIdNotFound,
|
||||
}
|
||||
|
||||
144
pumpkin-world/src/chunk/anvil.rs
Normal file
144
pumpkin-world/src/chunk/anvil.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use std::{
|
||||
fs::OpenOptions,
|
||||
io::{Read, Seek},
|
||||
};
|
||||
|
||||
use flate2::bufread::{GzDecoder, ZlibDecoder};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::level::SaveFile;
|
||||
|
||||
use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError};
|
||||
|
||||
pub struct AnvilChunkReader {}
|
||||
|
||||
impl Default for AnvilChunkReader {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AnvilChunkReader {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Compression {
|
||||
Gzip,
|
||||
Zlib,
|
||||
None,
|
||||
LZ4,
|
||||
}
|
||||
|
||||
impl Compression {
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
match byte {
|
||||
1 => Some(Self::Gzip),
|
||||
2 => Some(Self::Zlib),
|
||||
3 => Some(Self::None),
|
||||
4 => Some(Self::LZ4),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_data(&self, compressed_data: Vec<u8>) -> Result<Vec<u8>, CompressionError> {
|
||||
match self {
|
||||
Compression::Gzip => {
|
||||
let mut z = GzDecoder::new(&compressed_data[..]);
|
||||
let mut chunk_data = Vec::with_capacity(compressed_data.len());
|
||||
z.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::GZipError)?;
|
||||
Ok(chunk_data)
|
||||
}
|
||||
Compression::Zlib => {
|
||||
let mut z = ZlibDecoder::new(&compressed_data[..]);
|
||||
let mut chunk_data = Vec::with_capacity(compressed_data.len());
|
||||
z.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::ZlibError)?;
|
||||
Ok(chunk_data)
|
||||
}
|
||||
Compression::None => Ok(compressed_data),
|
||||
Compression::LZ4 => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChunkReader for AnvilChunkReader {
|
||||
fn read_chunk(
|
||||
&self,
|
||||
save_file: &SaveFile,
|
||||
at: pumpkin_core::math::vector2::Vector2<i32>,
|
||||
) -> Result<super::ChunkData, ChunkReadingError> {
|
||||
let region = (
|
||||
((at.x as f32) / 32.0).floor() as i32,
|
||||
((at.z as f32) / 32.0).floor() as i32,
|
||||
);
|
||||
|
||||
let mut region_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(
|
||||
save_file
|
||||
.region_folder
|
||||
.join(format!("r.{}.{}.mca", region.0, region.1)),
|
||||
)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => ChunkReadingError::ChunkNotExist,
|
||||
kind => ChunkReadingError::IoError(kind),
|
||||
})?;
|
||||
|
||||
let mut location_table: [u8; 4096] = [0; 4096];
|
||||
let mut timestamp_table: [u8; 4096] = [0; 4096];
|
||||
|
||||
// fill the location and timestamp tables
|
||||
region_file
|
||||
.read_exact(&mut location_table)
|
||||
.map_err(|err| ChunkReadingError::IoError(err.kind()))?;
|
||||
region_file
|
||||
.read_exact(&mut timestamp_table)
|
||||
.map_err(|err| ChunkReadingError::IoError(err.kind()))?;
|
||||
|
||||
let modulus = |a: i32, b: i32| ((a % b) + b) % b;
|
||||
let chunk_x = modulus(at.x, 32) as u32;
|
||||
let chunk_z = modulus(at.z, 32) as u32;
|
||||
let table_entry = (chunk_x + chunk_z * 32) * 4;
|
||||
|
||||
let mut offset = vec![0u8];
|
||||
offset.extend_from_slice(&location_table[table_entry as usize..table_entry as usize + 3]);
|
||||
let offset = u32::from_be_bytes(offset.try_into().unwrap()) as u64 * 4096;
|
||||
let size = location_table[table_entry as usize + 3] as usize * 4096;
|
||||
|
||||
if offset == 0 && size == 0 {
|
||||
return Err(ChunkReadingError::ChunkNotExist);
|
||||
}
|
||||
|
||||
// Read the file using the offset and size
|
||||
let mut file_buf = {
|
||||
region_file
|
||||
.seek(std::io::SeekFrom::Start(offset))
|
||||
.map_err(|_| ChunkReadingError::RegionIsInvalid)?;
|
||||
let mut out = vec![0; size];
|
||||
region_file
|
||||
.read_exact(&mut out)
|
||||
.map_err(|_| ChunkReadingError::RegionIsInvalid)?;
|
||||
out
|
||||
};
|
||||
|
||||
// TODO: check checksum to make sure chunk is not corrupted
|
||||
let header = file_buf.drain(0..5).collect_vec();
|
||||
|
||||
let compression = Compression::from_byte(header[4])
|
||||
.ok_or_else(|| ChunkReadingError::Compression(CompressionError::UnknownCompression))?;
|
||||
|
||||
let size = u32::from_be_bytes(header[..4].try_into().unwrap());
|
||||
|
||||
// size includes the compression scheme byte, so we need to subtract 1
|
||||
let chunk_data = file_buf.drain(0..size as usize - 1).collect_vec();
|
||||
let decompressed_chunk = compression
|
||||
.decompress_data(chunk_data)
|
||||
.map_err(ChunkReadingError::Compression)?;
|
||||
|
||||
ChunkData::from_bytes(decompressed_chunk, at).map_err(ChunkReadingError::ParsingError)
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,53 @@ use std::ops::Index;
|
||||
use fastnbt::LongArray;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{
|
||||
block::{BlockId, BlockState},
|
||||
block::{block_state::BlockStateError, BlockId, BlockState},
|
||||
coordinates::{ChunkRelativeBlockCoordinates, Height},
|
||||
level::{ChunkNotGeneratedError, WorldError},
|
||||
level::SaveFile,
|
||||
WORLD_HEIGHT,
|
||||
};
|
||||
|
||||
pub mod anvil;
|
||||
|
||||
const CHUNK_AREA: usize = 16 * 16;
|
||||
const SUBCHUNK_VOLUME: usize = CHUNK_AREA * 16;
|
||||
const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT;
|
||||
|
||||
pub trait ChunkReader: Sync + Send {
|
||||
fn read_chunk(
|
||||
&self,
|
||||
save_file: &SaveFile,
|
||||
at: Vector2<i32>,
|
||||
) -> Result<ChunkData, ChunkReadingError>;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ChunkReadingError {
|
||||
#[error("Io error: {0}")]
|
||||
IoError(std::io::ErrorKind),
|
||||
#[error("Region is invalid")]
|
||||
RegionIsInvalid,
|
||||
#[error("Compression error {0}")]
|
||||
Compression(CompressionError),
|
||||
#[error("Tried to read chunk which does not exist")]
|
||||
ChunkNotExist,
|
||||
#[error("Failed to parse Chunk from bytes: {0}")]
|
||||
ParsingError(ChunkParsingError),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompressionError {
|
||||
#[error("Compression scheme not recognised")]
|
||||
UnknownCompression,
|
||||
#[error("Error while working with zlib compression: {0}")]
|
||||
ZlibError(std::io::Error),
|
||||
#[error("Error while working with Gzip compression: {0}")]
|
||||
GZipError(std::io::Error),
|
||||
}
|
||||
|
||||
pub struct ChunkData {
|
||||
pub blocks: ChunkBlocks,
|
||||
pub position: Vector2<i32>,
|
||||
@@ -188,18 +223,16 @@ impl Index<ChunkRelativeBlockCoordinates> for ChunkBlocks {
|
||||
}
|
||||
|
||||
impl ChunkData {
|
||||
pub fn from_bytes(chunk_data: Vec<u8>, at: Vector2<i32>) -> Result<Self, WorldError> {
|
||||
pub fn from_bytes(chunk_data: Vec<u8>, at: Vector2<i32>) -> Result<Self, ChunkParsingError> {
|
||||
if fastnbt::from_bytes::<ChunkStatus>(&chunk_data).expect("Failed reading chunk status.")
|
||||
!= ChunkStatus::Full
|
||||
{
|
||||
return Err(WorldError::ChunkNotGenerated(
|
||||
ChunkNotGeneratedError::IncompleteGeneration,
|
||||
));
|
||||
return Err(ChunkParsingError::ChunkNotGenerated);
|
||||
}
|
||||
|
||||
let chunk_data = match fastnbt::from_bytes::<ChunkNbt>(chunk_data.as_slice()) {
|
||||
Ok(v) => v,
|
||||
Err(err) => return Err(WorldError::ErrorDeserializingChunk(err.to_string())),
|
||||
Err(err) => return Err(ChunkParsingError::ErrorDeserializingChunk(err.to_string())),
|
||||
};
|
||||
|
||||
// this needs to be boxed, otherwise it will cause a stack-overflow
|
||||
@@ -221,7 +254,8 @@ impl ChunkData {
|
||||
Ok(state) => Ok(state.into()),
|
||||
},
|
||||
)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(ChunkParsingError::BlockStateError)?;
|
||||
|
||||
let block_data = match block_states.data {
|
||||
None => {
|
||||
@@ -277,3 +311,13 @@ impl ChunkData {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ChunkParsingError {
|
||||
#[error("BlockState error: {0}")]
|
||||
BlockStateError(BlockStateError),
|
||||
#[error("The chunk isn't generated yet")]
|
||||
ChunkNotGenerated,
|
||||
#[error("Error deserializing chunk: {0}")]
|
||||
ErrorDeserializingChunk(String),
|
||||
}
|
||||
@@ -1,13 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::OpenOptions,
|
||||
io::{Read, Seek},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
|
||||
use flate2::{bufread::ZlibDecoder, read::GzDecoder};
|
||||
use itertools::Itertools;
|
||||
use parking_lot::Mutex;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use rayon::prelude::*;
|
||||
@@ -15,7 +7,7 @@ use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{
|
||||
chunk::ChunkData,
|
||||
chunk::{anvil::AnvilChunkReader, ChunkData, ChunkReader, ChunkReadingError},
|
||||
world_gen::{get_world_gen, Seed, WorldGenerator},
|
||||
};
|
||||
|
||||
@@ -23,7 +15,7 @@ use crate::{
|
||||
///
|
||||
/// Key features include:
|
||||
///
|
||||
/// - **Chunk Loading:** Efficiently loads chunks from disk (Anvil format).
|
||||
/// - **Chunk Loading:** Efficiently loads chunks from disk.
|
||||
/// - **Chunk Caching:** Stores accessed chunks in memory for faster access.
|
||||
/// - **Chunk Generation:** Generates new chunks on-demand using a specified `WorldGenerator`.
|
||||
///
|
||||
@@ -31,77 +23,18 @@ use crate::{
|
||||
pub struct Level {
|
||||
save_file: Option<SaveFile>,
|
||||
loaded_chunks: Arc<Mutex<HashMap<Vector2<i32>, Arc<ChunkData>>>>,
|
||||
chunk_reader: Box<dyn ChunkReader>,
|
||||
world_gen: Box<dyn WorldGenerator>,
|
||||
}
|
||||
|
||||
struct SaveFile {
|
||||
pub struct SaveFile {
|
||||
#[expect(dead_code)]
|
||||
root_folder: PathBuf,
|
||||
region_folder: PathBuf,
|
||||
pub region_folder: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum WorldError {
|
||||
// using ErrorKind instead of Error, beacuse the function read_chunks and read_region_chunks is designed to return an error on a per-chunk basis, while std::io::Error does not implement Copy or Clone
|
||||
#[error("Io error: {0}")]
|
||||
IoError(std::io::ErrorKind),
|
||||
#[error("Region is invalid")]
|
||||
RegionIsInvalid,
|
||||
#[error("The chunk isn't generated yet: {0}")]
|
||||
ChunkNotGenerated(ChunkNotGeneratedError),
|
||||
#[error("Compression Error")]
|
||||
Compression(CompressionError),
|
||||
#[error("Error deserializing chunk: {0}")]
|
||||
ErrorDeserializingChunk(String),
|
||||
#[error("The requested block identifier does not exist")]
|
||||
BlockIdentifierNotFound,
|
||||
#[error("The requested block state id does not exist")]
|
||||
BlockStateIdNotFound,
|
||||
#[error("The block is not inside of the chunk")]
|
||||
BlockOutsideChunk,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ChunkNotGeneratedError {
|
||||
#[error("The region file does not exist.")]
|
||||
RegionFileMissing,
|
||||
|
||||
#[error("The chunks generation is incomplete.")]
|
||||
IncompleteGeneration,
|
||||
|
||||
#[error("Chunk not found.")]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompressionError {
|
||||
#[error("Compression scheme not recognised")]
|
||||
UnknownCompression,
|
||||
#[error("Error while working with zlib compression: {0}")]
|
||||
ZlibError(std::io::Error),
|
||||
#[error("Error while working with Gzip compression: {0}")]
|
||||
GZipError(std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Compression {
|
||||
Gzip,
|
||||
Zlib,
|
||||
None,
|
||||
LZ4,
|
||||
}
|
||||
|
||||
impl Compression {
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
match byte {
|
||||
1 => Some(Self::Gzip),
|
||||
2 => Some(Self::Zlib),
|
||||
3 => Some(Self::None),
|
||||
4 => Some(Self::LZ4),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub enum WorldError {}
|
||||
|
||||
impl Level {
|
||||
pub fn from_root_folder(root_folder: PathBuf) -> Self {
|
||||
@@ -120,6 +53,7 @@ impl Level {
|
||||
root_folder,
|
||||
region_folder,
|
||||
}),
|
||||
chunk_reader: Box::new(AnvilChunkReader::new()),
|
||||
loaded_chunks: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
} else {
|
||||
@@ -130,6 +64,7 @@ impl Level {
|
||||
Self {
|
||||
world_gen,
|
||||
save_file: None,
|
||||
chunk_reader: Box::new(AnvilChunkReader::new()),
|
||||
loaded_chunks: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
@@ -164,8 +99,8 @@ impl Level {
|
||||
let at = *at;
|
||||
let data = match &self.save_file {
|
||||
Some(save_file) => {
|
||||
match Self::read_chunk(save_file, at) {
|
||||
Err(WorldError::ChunkNotGenerated(_)) => {
|
||||
match self.chunk_reader.read_chunk(save_file, at) {
|
||||
Err(ChunkReadingError::ChunkNotExist) => {
|
||||
// This chunk was not generated yet.
|
||||
Ok(self.world_gen.generate_chunk(at))
|
||||
}
|
||||
@@ -186,103 +121,4 @@ impl Level {
|
||||
loaded_chunks.insert(at, data);
|
||||
})
|
||||
}
|
||||
|
||||
fn read_chunk(save_file: &SaveFile, at: Vector2<i32>) -> Result<ChunkData, WorldError> {
|
||||
let region = (
|
||||
((at.x as f32) / 32.0).floor() as i32,
|
||||
((at.z as f32) / 32.0).floor() as i32,
|
||||
);
|
||||
|
||||
let mut region_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(
|
||||
save_file
|
||||
.region_folder
|
||||
.join(format!("r.{}.{}.mca", region.0, region.1)),
|
||||
)
|
||||
.map_err(|err| match err.kind() {
|
||||
std::io::ErrorKind::NotFound => {
|
||||
WorldError::ChunkNotGenerated(ChunkNotGeneratedError::RegionFileMissing)
|
||||
}
|
||||
kind => WorldError::IoError(kind),
|
||||
})?;
|
||||
|
||||
let mut location_table: [u8; 4096] = [0; 4096];
|
||||
let mut timestamp_table: [u8; 4096] = [0; 4096];
|
||||
|
||||
// fill the location and timestamp tables
|
||||
region_file
|
||||
.read_exact(&mut location_table)
|
||||
.map_err(|err| WorldError::IoError(err.kind()))?;
|
||||
region_file
|
||||
.read_exact(&mut timestamp_table)
|
||||
.map_err(|err| WorldError::IoError(err.kind()))?;
|
||||
|
||||
let modulus = |a: i32, b: i32| ((a % b) + b) % b;
|
||||
let chunk_x = modulus(at.x, 32) as u32;
|
||||
let chunk_z = modulus(at.z, 32) as u32;
|
||||
let table_entry = (chunk_x + chunk_z * 32) * 4;
|
||||
|
||||
let mut offset = vec![0u8];
|
||||
offset.extend_from_slice(&location_table[table_entry as usize..table_entry as usize + 3]);
|
||||
let offset = u32::from_be_bytes(offset.try_into().unwrap()) as u64 * 4096;
|
||||
let size = location_table[table_entry as usize + 3] as usize * 4096;
|
||||
|
||||
if offset == 0 && size == 0 {
|
||||
return Err(WorldError::ChunkNotGenerated(
|
||||
ChunkNotGeneratedError::NotFound,
|
||||
));
|
||||
}
|
||||
|
||||
// Read the file using the offset and size
|
||||
let mut file_buf = {
|
||||
region_file
|
||||
.seek(std::io::SeekFrom::Start(offset))
|
||||
.map_err(|_| WorldError::RegionIsInvalid)?;
|
||||
let mut out = vec![0; size];
|
||||
region_file
|
||||
.read_exact(&mut out)
|
||||
.map_err(|_| WorldError::RegionIsInvalid)?;
|
||||
out
|
||||
};
|
||||
|
||||
// TODO: check checksum to make sure chunk is not corrupted
|
||||
let header = file_buf.drain(0..5).collect_vec();
|
||||
|
||||
let compression = Compression::from_byte(header[4])
|
||||
.ok_or_else(|| WorldError::Compression(CompressionError::UnknownCompression))?;
|
||||
|
||||
let size = u32::from_be_bytes(header[..4].try_into().unwrap());
|
||||
|
||||
// size includes the compression scheme byte, so we need to subtract 1
|
||||
let chunk_data = file_buf.drain(0..size as usize - 1).collect_vec();
|
||||
let decompressed_chunk =
|
||||
Self::decompress_data(compression, chunk_data).map_err(WorldError::Compression)?;
|
||||
|
||||
ChunkData::from_bytes(decompressed_chunk, at)
|
||||
}
|
||||
|
||||
fn decompress_data(
|
||||
compression: Compression,
|
||||
compressed_data: Vec<u8>,
|
||||
) -> Result<Vec<u8>, CompressionError> {
|
||||
match compression {
|
||||
Compression::Gzip => {
|
||||
let mut z = GzDecoder::new(&compressed_data[..]);
|
||||
let mut chunk_data = Vec::with_capacity(compressed_data.len());
|
||||
z.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::GZipError)?;
|
||||
Ok(chunk_data)
|
||||
}
|
||||
Compression::Zlib => {
|
||||
let mut z = ZlibDecoder::new(&compressed_data[..]);
|
||||
let mut chunk_data = Vec::with_capacity(compressed_data.len());
|
||||
z.read_to_end(&mut chunk_data)
|
||||
.map_err(CompressionError::ZlibError)?;
|
||||
Ok(chunk_data)
|
||||
}
|
||||
Compression::None => Ok(compressed_data),
|
||||
Compression::LZ4 => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use noise::Perlin;
|
||||
use pumpkin_core::math::vector2::Vector2;
|
||||
use static_assertions::assert_obj_safe;
|
||||
|
||||
use crate::biome::Biome;
|
||||
use crate::block::block_state::BlockState;
|
||||
@@ -15,7 +14,6 @@ pub trait GeneratorInit {
|
||||
pub trait WorldGenerator: Sync + Send {
|
||||
fn generate_chunk(&self, at: Vector2<i32>) -> ChunkData;
|
||||
}
|
||||
assert_obj_safe! {WorldGenerator}
|
||||
|
||||
pub(crate) trait BiomeGenerator: Sync + Send {
|
||||
fn generate_biome(&self, at: XZBlockCoordinates) -> Biome;
|
||||
|
||||
Reference in New Issue
Block a user