Skip to content

Commit

Permalink
Save level.dat
Browse files Browse the repository at this point in the history
  • Loading branch information
Snowiiii committed Dec 24, 2024
1 parent cc83e09 commit fcf85df
Show file tree
Hide file tree
Showing 13 changed files with 248 additions and 169 deletions.
2 changes: 1 addition & 1 deletion pumpkin-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use gamemode::GameMode;

use serde::{Deserialize, Serialize};

#[derive(PartialEq, Serialize, Deserialize)]
#[derive(PartialEq, Serialize, Deserialize, Clone)]
pub enum Difficulty {
Peaceful,
Easy,
Expand Down
1 change: 1 addition & 0 deletions pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version.workspace = true
edition.workspace = true

[dependencies]
pumpkin-nbt = { path = "../pumpkin-nbt" }
pumpkin-core = { path = "../pumpkin-core" }
pumpkin-config = { path = "../pumpkin-config" }
pumpkin-macros = { path = "../pumpkin-macros" }
Expand Down
8 changes: 4 additions & 4 deletions pumpkin-world/src/chunk/anvil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{

use flate2::bufread::{GzDecoder, ZlibDecoder};

use crate::level::SaveFile;
use crate::level::LevelFolder;

use super::{ChunkData, ChunkReader, ChunkReadingError, CompressionError};

Expand Down Expand Up @@ -87,7 +87,7 @@ impl Compression {
impl ChunkReader for AnvilChunkReader {
fn read_chunk(
&self,
save_file: &SaveFile,
save_file: &LevelFolder,
at: &pumpkin_core::math::vector2::Vector2<i32>,
) -> Result<super::ChunkData, ChunkReadingError> {
let region = (at.x >> 5, at.z >> 5);
Expand Down Expand Up @@ -168,14 +168,14 @@ mod tests {

use crate::{
chunk::{anvil::AnvilChunkReader, ChunkReader, ChunkReadingError},
level::SaveFile,
level::LevelFolder,
};

#[test]
fn not_existing() {
let region_path = PathBuf::from("not_existing");
let result = AnvilChunkReader::new().read_chunk(
&SaveFile {
&LevelFolder {
root_folder: PathBuf::from(""),
region_folder: region_path,
},
Expand Down
4 changes: 2 additions & 2 deletions pumpkin-world/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use thiserror::Error;
use crate::{
block::BlockState,
coordinates::{ChunkRelativeBlockCoordinates, Height},
level::SaveFile,
level::LevelFolder,
WORLD_HEIGHT,
};

Expand All @@ -22,7 +22,7 @@ const CHUNK_VOLUME: usize = CHUNK_AREA * WORLD_HEIGHT;
pub trait ChunkReader: Sync + Send {
fn read_chunk(
&self,
save_file: &SaveFile,
save_file: &LevelFolder,
at: &Vector2<i32>,
) -> Result<ChunkData, ChunkReadingError>;
}
Expand Down
119 changes: 55 additions & 64 deletions pumpkin-world/src/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::{path::PathBuf, sync::Arc};

use dashmap::{DashMap, Entry};
use num_traits::Zero;
use pumpkin_config::BASIC_CONFIG;
use pumpkin_core::math::vector2::Vector2;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use tokio::{
Expand All @@ -15,7 +14,7 @@ use crate::{
anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError,
},
generation::{get_world_gen, Seed, WorldGenerator},
world_info::{anvil::AnvilInfoReader, WorldInfo, WorldInfoReader},
world_info::{anvil::AnvilLevelInfo, LevelData, WorldInfoReader, WorldInfoWriter},
};

/// The `Level` module provides functionality for working with chunks within or outside a Minecraft world.
Expand All @@ -29,69 +28,63 @@ use crate::{
/// For more details on world generation, refer to the `WorldGenerator` module.
pub struct Level {
pub seed: Seed,
pub world_info: Option<WorldInfo>,
save_file: Option<SaveFile>,
pub level_info: LevelData,
world_info_writer: Arc<dyn WorldInfoWriter>,
level_folder: LevelFolder,
loaded_chunks: Arc<DashMap<Vector2<i32>, Arc<RwLock<ChunkData>>>>,
chunk_watchers: Arc<DashMap<Vector2<i32>, usize>>,
chunk_reader: Arc<dyn ChunkReader>,
world_gen: Arc<dyn WorldGenerator>,
}

#[derive(Clone)]
pub struct SaveFile {
pub struct LevelFolder {
pub root_folder: PathBuf,
pub region_folder: PathBuf,
}

fn get_or_create_seed() -> Seed {
// TODO: if there is a seed in the config (!= 0) use it. Otherwise make a random one
Seed::from(BASIC_CONFIG.seed.as_str())
}

impl Level {
pub fn from_root_folder(root_folder: PathBuf) -> Self {
// If we are using an already existing world we want to read the seed from the level.dat, If not we want to check if there is a seed in the config, if not lets create a random one
if root_folder.exists() {
let region_folder = root_folder.join("region");
assert!(
region_folder.exists(),
"World region folder does not exist, despite there being a root folder."
);
let save_file = SaveFile {
root_folder,
region_folder,
};
let region_folder = root_folder.join("region");
if !region_folder.exists() {
std::fs::create_dir_all(&region_folder).expect("Failed to create Region folder");
}
let level_folder = LevelFolder {
root_folder,
region_folder,
};

// TODO: Load info correctly based on world format type
let world_info_reader = AnvilInfoReader::new();
let info = world_info_reader
.read_world_info(&save_file)
.expect("Unable to get world info!"); // TODO: Improve error handling
let seed = Seed(info.seed as u64);
let world_gen = get_world_gen(seed).into(); // TODO Read Seed from config.
// TODO: Load info correctly based on world format type
let level_info = AnvilLevelInfo
.read_world_info(&level_folder)
.unwrap_or_default(); // TODO: Improve error handling
let seed = Seed(level_info.world_gen_settings.seed as u64);
let world_gen = get_world_gen(seed).into();

Self {
seed,
world_gen,
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();
let world_gen = get_world_gen(seed).into();
Self {
seed,
world_gen,
save_file: None,
chunk_reader: Arc::new(AnvilChunkReader::new()),
loaded_chunks: Arc::new(DashMap::new()),
chunk_watchers: Arc::new(DashMap::new()),
world_info: None,
}
Self {
seed,
world_gen,
world_info_writer: Arc::new(AnvilLevelInfo),
level_folder,
chunk_reader: Arc::new(AnvilChunkReader::new()),
loaded_chunks: Arc::new(DashMap::new()),
chunk_watchers: Arc::new(DashMap::new()),
level_info,
}
}

pub async fn save(&self) {
log::info!("Saving level...");
// lets first save all chunks
for chunk in self.loaded_chunks.iter() {
let chunk = chunk.read().await;
self.clean_chunk(&chunk.position);
}
// then lets save the world info
self.world_info_writer
.write_world_info(self.level_info.clone(), &self.level_folder)
.expect("Failed to save world info");
}

pub fn get_block() {}
Expand Down Expand Up @@ -205,10 +198,10 @@ impl Level {

fn load_chunk_from_save(
chunk_reader: Arc<dyn ChunkReader>,
save_file: SaveFile,
save_file: &LevelFolder,
chunk_pos: Vector2<i32>,
) -> Result<Option<Arc<RwLock<ChunkData>>>, ChunkReadingError> {
match chunk_reader.read_chunk(&save_file, &chunk_pos) {
match chunk_reader.read_chunk(save_file, &chunk_pos) {
Ok(data) => Ok(Some(Arc::new(RwLock::new(data)))),
Err(
ChunkReadingError::ChunkNotExist
Expand All @@ -233,28 +226,26 @@ impl Level {
let channel = channel.clone();
let loaded_chunks = self.loaded_chunks.clone();
let chunk_reader = self.chunk_reader.clone();
let save_file = self.save_file.clone();
let level_info = self.level_folder.clone();
let world_gen = self.world_gen.clone();
let chunk_pos = *at;

let chunk = loaded_chunks
.get(&chunk_pos)
.map(|entry| entry.value().clone())
.unwrap_or_else(|| {
let loaded_chunk = save_file
.and_then(|save_file| {
match Self::load_chunk_from_save(chunk_reader, save_file, chunk_pos) {
Ok(chunk) => chunk,
Err(err) => {
log::error!(
"Failed to read chunk (regenerating) {:?}: {:?}",
chunk_pos,
err
);
None
}
let loaded_chunk =
match Self::load_chunk_from_save(chunk_reader, &level_info, chunk_pos) {
Ok(chunk) => chunk,
Err(err) => {
log::error!(
"Failed to read chunk (regenerating) {:?}: {:?}",
chunk_pos,
err
);
None
}
})
}
.unwrap_or_else(|| {
Arc::new(RwLock::new(world_gen.generate_chunk(chunk_pos)))
});
Expand Down
Loading

0 comments on commit fcf85df

Please sign in to comment.