use std::cmp::{min, max}; use cgmath::Vector3; use std::fs; use std::io::{Read, Write}; use std::thread; use std::sync::{mpsc, mpsc::Receiver, mpsc::Sender}; use std::sync::{Arc, Mutex}; use super::{chunk, chunk::WorldChunk}; use crate::game::world::WorldGen; type ChunkPos = Vector3; type ChunkPosPair = (ChunkPos, WorldChunk); type ArcRecv = Arc>>; pub struct ChunkLoader { receiver: Receiver, queue: Sender } impl ChunkLoader { pub fn new(world_name: String) -> Self { // Create channels let (result_tx, result_rx) = mpsc::channel(); let (input_tx, input_rx) = mpsc::channel(); let input_rx = Arc::new(Mutex::new(input_rx)); // Figure out number of workers let mut n = num_cpus::get_physical(); n = min(max(n / 2, 2), 4); // Spawn n workers for _ in 0..n { let q = Arc::clone(&input_rx); let tx = result_tx.clone(); let name = world_name.clone(); thread::spawn(move || { ChunkLoader::worker(q, tx, name) }); } println!("Spawned {} chunk loader workers", n); // Build struct Self { receiver: result_rx, queue: input_tx } } /* * Receive loaded chunks */ pub fn receive(&mut self) -> Vec { let mut result = Vec::new(); while let Ok((pos, chunk)) = self.receiver.try_recv() { result.push((pos, chunk)); } result } /* * Schedule loading chunks */ pub fn load(&mut self, pos: ChunkPos) { self.queue.send(pos).unwrap(); } /* * Take position from queue and load chunk */ fn worker(queue: ArcRecv, tx: Sender, name: String) { loop { // Wait for job let pos; { // Get lock let rx = queue.lock(); if rx.is_err() { break; } // Receive next pos let ps = rx.unwrap().recv(); if ps.is_err() { break; } pos = ps.unwrap(); // Release lock now (end of scope) } // 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(&name).join(filename); if filepath.is_file() { 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); // send if let Err(_) = tx.send((pos, chunk)) { break; } continue; } // Try generating chunk let mut chunk = WorldChunk::new(); for x in 0..chunk::SIZE as i32 { for z in 0..chunk::SIZE as i32 { let bpos = Vector3::{x,y:0,z}; let block = chunk.get_block_mut(&bpos); WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block); } } // save 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 tx.send((pos, chunk)).unwrap(); } println!("ChunkLoader: worker finished"); } }