mod generator; pub mod chunk; pub mod block; use std::time::Instant; use linked_hash_map::LinkedHashMap; use std::path::PathBuf; use std::path::Path; use std::io::Write; use std::io::Read; use std::fs; use cgmath::{Point3, InnerSpace}; use std::collections::HashMap; use generator::WorldGen; use chunk::WorldChunk; use block::WorldBlock; use yaml_rust::{Yaml, YamlLoader, YamlEmitter}; pub struct World { name: String, chunks: HashMap, WorldChunk>, } impl World { pub fn new(name: String) -> Self { Self { name, chunks: HashMap::new(), } } pub fn all_blocks(&self) -> Vec<(Point3, &WorldBlock)> { self.chunks.iter().map(|(chunk_pos, chunk)| { let off = chunk_pos * chunk::SIZE as u32; let result: Vec<(Point3, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| { ( Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z}, *block ) }).collect(); result }).flatten().collect() } pub fn save(&self) { // Get directory let mut dir = dirs::data_local_dir().unwrap(); dir.push("Voxelgame"); fs::create_dir_all(&dir).expect("Failed to create directory"); // Save all chunks for (pos, chunk) in self.chunks.iter() { let bytes = chunk.to_bytes(); let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z); let path: PathBuf = [dir.to_str().unwrap(), &filename].iter().collect(); let mut file = fs::File::create(path).expect("Failed to create file"); file.write(&bytes).unwrap(); } // Save metadata let mut data: LinkedHashMap = LinkedHashMap::new(); data.insert(Yaml::String("name".to_string()), Yaml::String(self.name.clone())); let mut data_str = String::new(); let mut emit = YamlEmitter::new(&mut data_str); emit.dump(&Yaml::Hash(data)).expect("Failed to encode yaml"); let data_path: PathBuf = [dir.to_str().unwrap(), "world.yml"].iter().collect(); let mut data_file = fs::File::create(data_path).unwrap(); data_file.write_all(data_str.as_bytes()).expect("Failed to write"); } pub fn load(&mut self) { // Get directory let mut dir = dirs::data_local_dir().unwrap(); dir.push("Voxelgame"); if !dir.is_dir() { println!("Not exists: {:?}", dir); return; } // Load all chunks for entry in fs::read_dir(dir).unwrap() { let path: PathBuf = entry.unwrap().path(); if path.extension().unwrap().to_str() != Some("dat") { continue; } let stem = path.file_stem().unwrap().to_str().unwrap(); if !stem.starts_with("chunk") { continue; } let s: Vec<&str> = stem.split("_").collect(); let pos = Point3{x: s[1].parse::().unwrap(), y: s[2].parse::().unwrap(), z: s[3].parse::().unwrap()}; let mut bytes = Vec::new(); let mut file = fs::File::open(path).expect("Failed to open file"); file.read_to_end(&mut bytes).expect("Failed to read file"); let chunk = WorldChunk::from_bytes(bytes); println!("loaded {:?}", pos); self.chunks.insert(pos, chunk); } // Load metadata } pub fn gen_block(&mut self, block_wpos: &Point3) -> &WorldBlock { // Split world space to chunk and block space let ch_pos = block_wpos / chunk::SIZE as u32; let bl_pos = block_wpos % chunk::SIZE as u32; // Create chunk if does not exist if let None = self.chunks.get(&ch_pos) { self.chunks.insert(ch_pos.clone(), WorldChunk::new()); println!("Alloc chunk [{}, {}, {}]", ch_pos.x, ch_pos.y, ch_pos.z); } // Generate block for chunk let chunk = self.chunks.get_mut(&ch_pos).unwrap(); let block = chunk.get_block(&bl_pos); WorldGen::generate(&block_wpos, block); block } #[allow(unused)] pub fn get_block(&mut self, block_wpos: &Point3) -> Option<&WorldBlock> { // Split world space to chunk and block space let ch_pos = block_wpos / chunk::SIZE as u32; let bl_pos = block_wpos % chunk::SIZE as u32; // Return block if exists if let Some(chunk) = self.chunks.get_mut(&ch_pos) { return Some(chunk.get_block(&bl_pos)); } None } }