forked from feather-rs/feather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathworker.rs
More file actions
115 lines (106 loc) · 3.75 KB
/
worker.rs
File metadata and controls
115 lines (106 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::{path::PathBuf, sync::Arc};
use anyhow::bail;
use base::{
anvil::{block_entity::BlockEntityData, entity::EntityData},
Chunk, ChunkHandle, ChunkPosition,
};
use flume::{Receiver, Sender};
use worldgen::WorldGenerator;
use crate::region_worker::RegionWorker;
#[derive(Debug)]
pub struct LoadRequest {
pub pos: ChunkPosition,
}
#[derive(Debug)]
pub struct LoadedChunk {
pub pos: ChunkPosition,
pub chunk: Chunk,
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum ChunkLoadResult {
/// The chunk does not exist in this source.
Missing(ChunkPosition),
/// An error occurred while loading the chunk.
Error(anyhow::Error),
/// Successfully loaded the chunk.
Loaded(LoadedChunk),
}
#[derive(Debug)]
pub struct SaveRequest {
pub pos: ChunkPosition,
pub chunk: ChunkHandle,
pub entities: Vec<EntityData>,
pub block_entities: Vec<BlockEntityData>,
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum WorkerRequest {
Load(LoadRequest),
Save(SaveRequest),
}
pub struct ChunkWorker {
generator: Arc<dyn WorldGenerator>,
send_req: Sender<WorkerRequest>,
send_gen: Sender<LoadedChunk>,
recv_gen: Receiver<LoadedChunk>, // Chunk generation should be infallible.
recv_load: Receiver<ChunkLoadResult>,
}
impl ChunkWorker {
pub fn new(world_dir: impl Into<PathBuf>, generator: Arc<dyn WorldGenerator>) -> Self {
let (send_req, recv_req) = flume::unbounded();
let (send_gen, recv_gen) = flume::unbounded();
let (region_worker, recv_load) = RegionWorker::new(world_dir.into(), recv_req);
region_worker.start();
Self {
generator,
send_req,
send_gen,
recv_gen,
recv_load,
}
}
pub fn queue_load(&mut self, request: LoadRequest) {
self.send_req.send(WorkerRequest::Load(request)).unwrap()
}
/// Helper function for poll_loaded_chunk. Attemts to receive a freshly generated chunk.
/// Function signature identical to that of poll_loaded_chunk for ease of use.
fn try_recv_gen(&mut self) -> Result<Option<LoadedChunk>, anyhow::Error> {
match self.recv_gen.try_recv() {
Ok(l) => Ok(Some(l)),
Err(e) => match e {
flume::TryRecvError::Empty => Ok(None),
flume::TryRecvError::Disconnected => bail!("chunkgen channel died"),
},
}
}
pub fn poll_loaded_chunk(&mut self) -> Result<Option<LoadedChunk>, anyhow::Error> {
match self.recv_load.try_recv() {
Ok(answer) => {
match answer {
// RegionWorker answered
ChunkLoadResult::Missing(pos) => {
// chunk does not exist, queue it for generation
let send_gen = self.send_gen.clone();
let gen = self.generator.clone();
rayon::spawn(move || {
// spawn task to generate chunk
let chunk = gen.generate_chunk(pos);
send_gen.send(LoadedChunk { pos, chunk }).unwrap()
});
self.try_recv_gen() // check for generated chunks
}
ChunkLoadResult::Error(e) => Err(e),
ChunkLoadResult::Loaded(l) => Ok(Some(l)),
}
}
Err(e) => match e {
flume::TryRecvError::Empty => self.try_recv_gen(), // check for generated chunks
flume::TryRecvError::Disconnected => bail!("RegionWorker died"),
},
}
}
pub fn queue_chunk_save(&mut self, req: SaveRequest) {
self.send_req.send(WorkerRequest::Save(req)).unwrap()
}
}