138 lines
4.6 KiB
Rust
138 lines
4.6 KiB
Rust
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<Point3<u32>, WorldChunk>,
|
|
}
|
|
|
|
impl World {
|
|
|
|
pub fn new(name: String) -> Self {
|
|
Self {
|
|
name,
|
|
chunks: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
|
|
self.chunks.iter().map(|(chunk_pos, chunk)| {
|
|
let off = chunk_pos * chunk::SIZE as u32;
|
|
let result: Vec<(Point3<u32>, &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<Yaml, Yaml> = 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::<u32>().unwrap(), y: s[2].parse::<u32>().unwrap(), z: s[3].parse::<u32>().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<u32>) -> &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<u32>) -> 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
|
|
}
|
|
} |