WIP generating chunks at player position

This commit is contained in:
Piotrek
2021-05-12 16:17:38 +02:00
parent 4acf8ea816
commit ab9f4c83d0
10 changed files with 186 additions and 147 deletions
+69 -70
View File
@@ -1,7 +1,6 @@
mod generator;
pub mod chunk;
pub mod block;
use linked_hash_map::LinkedHashMap;
use std::path::PathBuf;
use std::io::Write;
use std::io::Read;
@@ -11,29 +10,37 @@ use std::collections::HashMap;
use generator::WorldGen;
use chunk::WorldChunk;
use block::WorldBlock;
use yaml_rust::{Yaml, YamlEmitter};
#[derive(serde::Serialize, serde::Deserialize, Debug)]
struct WorldData {
#[serde(default)]
pub name: String
}
pub struct World {
name: String,
chunks: HashMap<Point3<u32>, WorldChunk>,
data: WorldData,
chunks: HashMap<Point3<i32>, WorldChunk>,
}
fn point_abs(p: &Point3<i32>) -> Point3<u32> {
Point3 {x: p.x.abs() as u32, y: p.y.abs() as u32, z: p.z.abs() as u32 }
}
impl World {
pub fn new(name: String) -> Self {
Self {
name,
data: WorldData { name },
chunks: HashMap::new(),
}
}
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
pub fn all_blocks(&self) -> Vec<(Point3<i32>, &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)| {
let off = chunk_pos * chunk::SIZE as i32;
let result: Vec<(Point3<i32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
(
Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z},
Point3{x: off.x+pos.x as i32, y: off.y+pos.y as i32, z: off.z+pos.z as i32},
*block
)
}).collect();
@@ -58,77 +65,69 @@ impl World {
}
// 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 data_str = toml::to_string(&self.data).unwrap();
let data_path: PathBuf = [dir.to_str().unwrap(), "world.dat"].iter().collect();
let mut data_file = fs::File::create(data_path).unwrap();
data_file.write_all(data_str.as_bytes()).expect("Failed to write");
data_file.write_all(data_str.as_bytes()).expect("Failed to write world metadata");
}
/*
* Loads chunk and displays it in the world.
* It will try to load the chunk from file, and if that fails it will generate the chunk and save it.
*/
pub fn load_chunk(&mut self, pos: Point3<i32>) -> Option<&WorldChunk> {
// Abort if chunk is already loaded
if let Some(_) = self.chunks.get(&pos) {
return None;
}
// Try loading chunk from file
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name(filename);
if filepath.is_file() {
let mut buff = Vec::new();
let mut file = fs::File::open(filepath).expect("Failed to open file");
file.read_to_end(&mut buff).expect("Failed to read file");
let chunk = WorldChunk::from_bytes(buff);
self.chunks.insert(pos, chunk);
println!("Chunk at {:?} loaded", pos);
return Some(&chunk);
}
// Try generating chunk
let mut chunk = WorldChunk::new();
for x in 0..32 {
for z in 0..32 {
let bpos = &Point3::<u32>{x,y:0,z};
let block = chunk.get_block_mut(&bpos);
let wpos = &Point3::<i32>{ x: pos.x+bpos.x as i32, y: pos.y+bpos.y as i32, z: pos.z+bpos.z as i32};
WorldGen::generate(wpos, block);
}
}
self.chunks.insert(pos, chunk);
println!("Chunk at {:?} generated", pos);
Some(&chunk)
}
pub fn load(&mut self) -> bool {
// Get directory
let mut dir = dirs::data_local_dir().unwrap();
dir.push("Voxelgame");
if !dir.is_dir() {
println!("Not exists: {:?}", dir);
return false;
}
// Load all chunks
let mut count = 0;
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!("Load: Chunk {}, {}, {}", pos.x, pos.y, pos.z);
self.chunks.insert(pos, chunk);
count += 1;
}
// Load metadata
return count > 0;
}
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);
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name("world.dat");
if let Ok(mut file) = fs::File::open(filepath) {
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
if let Ok(data) = toml::from_slice::<WorldData>(&buf[..]) {
self.data = data;
return true;
}
}
// 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
return false;
}
#[allow(unused)]
pub fn get_block(&mut self, block_wpos: &Point3<u32>) -> Option<&WorldBlock> {
pub fn get_block(&mut self, block_wpos: &Point3<i32>) -> 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;
let ch_pos = block_wpos / chunk::SIZE as i32;
let bl_pos = point_abs(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));