120 lines
3.7 KiB
Rust
120 lines
3.7 KiB
Rust
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<i32>;
|
|
type ChunkPosPair = (ChunkPos, WorldChunk);
|
|
type ArcRecv<T> = Arc<Mutex<Receiver<T>>>;
|
|
|
|
pub struct ChunkLoader {
|
|
receiver: Receiver<ChunkPosPair>,
|
|
queue: Sender<ChunkPos>
|
|
}
|
|
|
|
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<ChunkPosPair> {
|
|
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<ChunkPos>, tx: Sender<ChunkPosPair>, 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::<i32>{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");
|
|
}
|
|
} |