veloren_server/
chunk_generator.rs1use crate::metrics::ChunkGenMetrics;
2#[cfg(feature = "worldgen")]
3use crate::rtsim::RtSim;
4#[cfg(not(feature = "worldgen"))]
5use crate::test_world::{IndexOwned, World};
6use common::{
7 calendar::Calendar, generation::ChunkSupplement, resources::TimeOfDay, slowjob::SlowJobPool,
8 terrain::TerrainChunk,
9};
10use hashbrown::{HashMap, hash_map::Entry};
11use rayon::iter::ParallelIterator;
12use specs::Entity as EcsEntity;
13use std::sync::{
14 Arc,
15 atomic::{AtomicBool, Ordering},
16};
17use vek::*;
18#[cfg(feature = "worldgen")]
19use world::{IndexOwned, World};
20
21type ChunkGenResult = (
22 Vec2<i32>,
23 Result<(TerrainChunk, ChunkSupplement), Option<EcsEntity>>,
24);
25
26pub struct ChunkGenerator {
27 chunk_tx: crossbeam_channel::Sender<ChunkGenResult>,
28 chunk_rx: crossbeam_channel::Receiver<ChunkGenResult>,
29 pending_chunks: HashMap<Vec2<i32>, Arc<AtomicBool>>,
30 metrics: Arc<ChunkGenMetrics>,
31}
32impl ChunkGenerator {
33 pub fn new(metrics: ChunkGenMetrics) -> Self {
34 let (chunk_tx, chunk_rx) = crossbeam_channel::unbounded();
35 Self {
36 chunk_tx,
37 chunk_rx,
38 pending_chunks: HashMap::new(),
39 metrics: Arc::new(metrics),
40 }
41 }
42
43 pub fn generate_chunk(
44 &mut self,
45 entity: Option<EcsEntity>,
46 key: Vec2<i32>,
47 slowjob_pool: &SlowJobPool,
48 world: Arc<World>,
49 #[cfg(feature = "worldgen")] rtsim: &RtSim,
50 #[cfg(not(feature = "worldgen"))] _rtsim: &(),
51 index: IndexOwned,
52 time: (TimeOfDay, Calendar),
53 ) {
54 let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
55 v
56 } else {
57 return;
58 };
59 let cancel = Arc::new(AtomicBool::new(false));
60 v.insert(Arc::clone(&cancel));
61 let chunk_tx = self.chunk_tx.clone();
62 self.metrics.chunks_requested.inc();
63
64 #[cfg(feature = "worldgen")]
66 let rtsim_resources = Some(rtsim.get_chunk_resources(key));
67 #[cfg(not(feature = "worldgen"))]
68 let rtsim_resources = None;
69
70 slowjob_pool.spawn("CHUNK_GENERATOR", move || {
71 let index = index.as_index_ref();
72 let payload = world
73 .generate_chunk(index, key, rtsim_resources, || cancel.load(Ordering::Relaxed), Some(time))
74 .map_err(|_| entity);
83 let _ = chunk_tx.send((key, payload));
84 });
85 }
86
87 pub fn recv_new_chunk(&mut self) -> Option<ChunkGenResult> {
88 while let Ok((key, res)) = self.chunk_rx.try_recv() {
91 if self.pending_chunks.remove(&key).is_some() {
92 self.metrics.chunks_served.inc();
93 return Some((key, res));
95 }
96 }
97
98 None
99 }
100
101 pub fn pending_chunks(&self) -> impl Iterator<Item = Vec2<i32>> + '_ {
102 self.pending_chunks.keys().copied()
103 }
104
105 pub fn par_pending_chunks(&self) -> impl rayon::iter::ParallelIterator<Item = Vec2<i32>> + '_ {
106 self.pending_chunks.par_keys().copied()
107 }
108
109 pub fn cancel_if_pending(&mut self, key: Vec2<i32>) {
110 if let Some(cancel) = self.pending_chunks.remove(&key) {
111 cancel.store(true, Ordering::Relaxed);
112 self.metrics.chunks_canceled.inc();
113 }
114 }
115
116 pub fn cancel_all(&mut self) {
117 let metrics = Arc::clone(&self.metrics);
118 self.pending_chunks.drain().for_each(|(_, cancel)| {
119 cancel.store(true, Ordering::Relaxed);
120 metrics.chunks_canceled.inc();
121 });
122 }
123}