Files
first-voxels/src/game/world/loader.rs
T
2021-05-16 17:19:23 +02:00

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");
}
}