ChunkLoader
This commit is contained in:
+27
-128
@@ -1,5 +1,6 @@
|
||||
mod generator;
|
||||
mod data;
|
||||
mod loader;
|
||||
pub mod chunk;
|
||||
pub mod block;
|
||||
use std::thread;
|
||||
@@ -15,10 +16,11 @@ use generator::WorldGen;
|
||||
use chunk::WorldChunk;
|
||||
use block::WorldBlock;
|
||||
use data::WorldData;
|
||||
use loader::ChunkLoader;
|
||||
|
||||
pub struct World {
|
||||
data: WorldData,
|
||||
chunk_channel: (Sender<(Vector3<i32>, WorldChunk)>, Receiver<(Vector3<i32>, WorldChunk)>),
|
||||
loader: ChunkLoader,
|
||||
chunks: HashMap<Vector3<i32>, WorldChunk>,
|
||||
}
|
||||
|
||||
@@ -26,8 +28,8 @@ impl World {
|
||||
|
||||
pub fn init(name: String) -> Self {
|
||||
let mut instance = Self {
|
||||
data: WorldData { name, ..Default::default() },
|
||||
chunk_channel: mpsc::channel(),
|
||||
data: WorldData { name: name.clone(), ..Default::default() },
|
||||
loader: ChunkLoader::new(name.clone()),
|
||||
chunks: HashMap::new(),
|
||||
};
|
||||
instance.load_metadata();
|
||||
@@ -35,10 +37,26 @@ impl World {
|
||||
instance
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives loaded chunks from ChunkLoader
|
||||
*/
|
||||
pub fn update(&mut self) {
|
||||
for (pos, chunk) in self.loader.receive() {
|
||||
self.chunks.insert(pos, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends chunk position to the ChunkLoader
|
||||
*/
|
||||
pub fn load_chunk(&mut self, pos: Vector3<i32>) {
|
||||
self.loader.load(pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads metadata
|
||||
*/
|
||||
fn load_metadata(&mut self) -> bool {
|
||||
fn load_metadata(&mut self, ) -> bool {
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join(self.data.name.clone()).join("world.dat");
|
||||
if let Ok(mut file) = fs::File::open(filepath) {
|
||||
let mut buf = Vec::new();
|
||||
@@ -52,7 +70,7 @@ impl World {
|
||||
}
|
||||
|
||||
/*
|
||||
* Saves metadata and all loaded chunks
|
||||
* Saves metadata and all loaded chunks to files
|
||||
*/
|
||||
pub fn save_all(&self) {
|
||||
// Get directory
|
||||
@@ -73,136 +91,17 @@ impl World {
|
||||
datafile.write_all(datastr.as_bytes()).expect("Failed to write world metadata");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns chunk at given world chunk position
|
||||
*/
|
||||
pub fn get_chunk(&mut self, chunk_wpos: &Vector3<i32>) -> Option<&mut WorldChunk> {
|
||||
self.chunks.get_mut(chunk_wpos)
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads chunk into memory
|
||||
* It will try to load the chunk from file, and if that fails it will generate the chunk and save it.
|
||||
* Returns vec of references to all currently loaded chunks
|
||||
*/
|
||||
pub fn load_chunk(&mut self, pos: Vector3<i32>) -> bool {
|
||||
// Abort if chunk is already loaded
|
||||
if let Some(_) = self.chunks.get(&pos) {
|
||||
println!("Chunk at {:?} already loaded", pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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").join("saves").join(&self.data.name).join(filename);
|
||||
|
||||
if filepath.is_file() {
|
||||
let timer = Instant::now();
|
||||
let mut buff = Vec::new();
|
||||
let mut file = fs::File::open(filepath).expect("Failed to open chunk file");
|
||||
file.read_to_end(&mut buff).expect("Failed to read chunk file");
|
||||
let chunk = WorldChunk::from_bytes(buff);
|
||||
self.chunks.insert(pos, chunk);
|
||||
println!("Chunk at {:?} loaded ({} ms)", pos, timer.elapsed().as_millis());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try generating chunk
|
||||
let timer0 = Instant::now();
|
||||
let mut chunk = WorldChunk::new();
|
||||
for x in 0..chunk::SIZE as i32 {
|
||||
for z in 0..chunk::SIZE as i32 {
|
||||
let bpos = Vector3::<i32>{x,y:0,z};
|
||||
let block = chunk.get_block_mut(&bpos);
|
||||
WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block);
|
||||
}
|
||||
}
|
||||
|
||||
// Save generated chunk to file
|
||||
let timer1 = Instant::now();
|
||||
fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory");
|
||||
let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
|
||||
file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
|
||||
|
||||
// Add to list
|
||||
self.chunks.insert(pos, chunk);
|
||||
|
||||
// Stats
|
||||
let t1 = timer1.elapsed().as_millis();
|
||||
let t0 = timer0.elapsed().as_millis() - t1;
|
||||
println!("Chunk at {:?} generated ({} ms) and saved ({} ms)", pos, t0, t1);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn load_chunk_async(&mut self, pos: Vector3<i32>) {
|
||||
// Abort if chunk is already loaded
|
||||
if let Some(ch) = self.chunks.get_mut(&pos) {
|
||||
println!("Chunk at {:?} already loaded", pos);
|
||||
ch.dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").join("saves").join(&self.data.name).join(filename);
|
||||
|
||||
// Spawn thread
|
||||
let tx = self.chunk_channel.0.clone();
|
||||
let t = thread::spawn(move || {
|
||||
// Try loading chunk from file
|
||||
if filepath.is_file() {
|
||||
let timer = Instant::now();
|
||||
let mut buff = Vec::new();
|
||||
let mut file = fs::File::open(filepath).expect("Failed to open chunk file");
|
||||
file.read_to_end(&mut buff).expect("Failed to read chunk file");
|
||||
let chunk = WorldChunk::from_bytes(buff);
|
||||
tx.send((pos, chunk)).unwrap();
|
||||
println!("Chunk at {:?} loaded ({} ms)", pos, timer.elapsed().as_millis());
|
||||
return;
|
||||
}
|
||||
|
||||
// Try generating chunk
|
||||
let timer0 = Instant::now();
|
||||
let mut chunk = WorldChunk::new();
|
||||
for x in 0..chunk::SIZE as i32 {
|
||||
for z in 0..chunk::SIZE as i32 {
|
||||
let bpos = Vector3::<i32>{x,y:0,z};
|
||||
let block = chunk.get_block_mut(&bpos);
|
||||
WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block);
|
||||
}
|
||||
}
|
||||
// Save generated chunk to file
|
||||
let timer1 = Instant::now();
|
||||
fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory");
|
||||
let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
|
||||
file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
|
||||
// Send it
|
||||
tx.send((pos, chunk)).unwrap();
|
||||
let t1 = timer1.elapsed().as_millis();
|
||||
let t0 = timer0.elapsed().as_millis() - t1;
|
||||
println!("Chunk at {:?} generated ({} ms) and saved ({} ms)", pos, t0, t1);
|
||||
});
|
||||
|
||||
// debug
|
||||
//t.join().unwrap();
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
// Receive all loaded chunks
|
||||
let rx = &self.chunk_channel.1;
|
||||
for (pos, chunk) in rx.try_iter() {
|
||||
self.chunks.insert(pos, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_chunks(&mut self) -> Vec<(&Vector3<i32>, &mut WorldChunk)> {
|
||||
self.chunks.iter_mut().collect()
|
||||
}
|
||||
|
||||
// pub fn get_block(&mut self, block_wpos: &Vector3<i32>) -> Option<&WorldBlock> {
|
||||
// // Split world space to chunk and block space
|
||||
// let ch_pos = block_wpos / chunk::SIZE as i32;
|
||||
// let bl_pos = block_wpos % chunk::SIZE as i32;
|
||||
// // Return block if exists
|
||||
// if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
|
||||
// return Some(chunk.get_block(&bl_pos));
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user