From dd36013f5e31f3d24276e6e3c4c2f5859d8e00ec Mon Sep 17 00:00:00 2001 From: "neelesh.poli2006@gmail.com" <72574589+neeleshpoli@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:10:59 -0600 Subject: [PATCH] Implement level.dat reading and use the seed in the world for chunk generation --- pumpkin-world/src/chunk/anvil.rs | 55 ++++++++++++++++++++++++++++++-- pumpkin-world/src/chunk/mod.rs | 6 ++++ pumpkin-world/src/level.rs | 21 ++++++++---- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 86389be6..af24f70b 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -4,10 +4,11 @@ use std::{ }; use flate2::bufread::{GzDecoder, ZlibDecoder}; +use serde::Deserialize; -use crate::level::SaveFile; +use crate::{chunk::ChunkParsingError, level::SaveFile}; -use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError}; +use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError, WorldInfo}; #[derive(Clone)] pub struct AnvilChunkReader {} @@ -158,6 +159,56 @@ impl ChunkReader for AnvilChunkReader { ChunkData::from_bytes(&decompressed_chunk, *at).map_err(ChunkReadingError::ParsingError) } + + fn read_world_info(&self, save_file: &SaveFile) -> Result { + let path = save_file.root_folder.join("level.dat"); + + let mut world_info_file = OpenOptions::new() + .read(true) + .open(path) + .map_err(|e| ChunkReadingError::IoError(e.kind()))?; + + let mut buffer = Vec::new(); + world_info_file + .read_to_end(&mut buffer) + .map_err(|e| ChunkReadingError::IoError(e.kind()))?; + + let decompressed_data = Compression::GZip + .decompress_data(buffer) + .map_err(ChunkReadingError::Compression)?; + let info = fastnbt::from_bytes::(&decompressed_data).map_err(|e| { + ChunkReadingError::ParsingError(ChunkParsingError::ErrorDeserializingChunk( + e.to_string(), + )) + })?; + + Ok(WorldInfo { + seed: info.data.world_gen_settings.seed, + }) + } +} + +#[derive(Deserialize)] +pub struct LevelDat { + // No idea why its formatted like this + #[serde(rename = "Data")] + pub data: WorldData, +} + +#[derive(Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct WorldData { + pub world_gen_settings: WorldGenSettings, + // TODO: Implement the rest of the fields + // Fields below this comment are being deserialized, but are not being used + pub spawn_x: i32, + pub spawn_y: i32, + pub spawn_z: i32, +} + +#[derive(Deserialize)] +pub struct WorldGenSettings { + pub seed: i64, } #[cfg(test)] diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index fcbb53f1..5e69e8f6 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -25,6 +25,7 @@ pub trait ChunkReader: Sync + Send { save_file: &SaveFile, at: &Vector2, ) -> Result; + fn read_world_info(&self, save_file: &SaveFile) -> Result; } #[derive(Error, Debug)] @@ -326,3 +327,8 @@ pub enum ChunkParsingError { #[error("Error deserializing chunk: {0}")] ErrorDeserializingChunk(String), } + +pub struct WorldInfo { + pub seed: i64, + // TODO: Implement all fields +} diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 2a6a9571..eb107b55 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -13,6 +13,7 @@ use tokio::{ use crate::{ chunk::{ anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, + WorldInfo, }, world_gen::{get_world_gen, Seed, WorldGenerator}, }; @@ -28,6 +29,7 @@ use crate::{ /// For more details on world generation, refer to the `WorldGenerator` module. pub struct Level { pub seed: Seed, + pub world_info: Option, save_file: Option, loaded_chunks: Arc, Arc>>>, chunk_watchers: Arc, usize>>, @@ -55,20 +57,26 @@ impl Level { region_folder.exists(), "World region folder does not exist, despite there being a root folder." ); - // TODO: read seed from level.dat - let seed = get_or_create_seed(); + let save_file = SaveFile { + root_folder, + region_folder, + }; + + let reader = AnvilChunkReader::new(); + let info = reader + .read_world_info(&save_file) + .expect("Unable to get world info!"); + let seed = Seed(info.seed as u64); let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config. Self { seed, world_gen, - save_file: Some(SaveFile { - root_folder, - region_folder, - }), + save_file: Some(save_file), chunk_reader: Arc::new(AnvilChunkReader::new()), loaded_chunks: Arc::new(DashMap::new()), chunk_watchers: Arc::new(DashMap::new()), + world_info: Some(info), } } else { let seed = get_or_create_seed(); @@ -80,6 +88,7 @@ impl Level { chunk_reader: Arc::new(AnvilChunkReader::new()), loaded_chunks: Arc::new(DashMap::new()), chunk_watchers: Arc::new(DashMap::new()), + world_info: None, } } }