use hashbrown::{HashMap, HashSet};
use specs::{Join, ReadExpect, ReadStorage, System, WriteExpect, WriteStorage};
use crate::{
ChunkInterests, ChunkProtocol, ChunkRequestsComp, ChunkStatus, Chunks, ClientFilter, IDComp,
Mesher, Message, MessageQueue, MessageType, Pipeline, Vec2, WorldConfig,
};
pub struct ChunkRequestsSystem;
impl<'a> System<'a> for ChunkRequestsSystem {
type SystemData = (
ReadExpect<'a, Chunks>,
ReadExpect<'a, WorldConfig>,
WriteExpect<'a, ChunkInterests>,
WriteExpect<'a, Pipeline>,
WriteExpect<'a, Mesher>,
WriteExpect<'a, MessageQueue>,
ReadStorage<'a, IDComp>,
WriteStorage<'a, ChunkRequestsComp>,
);
fn run(&mut self, data: Self::SystemData) {
let (chunks, config, mut interests, mut pipeline, mut mesher, mut queue, ids, mut requests) =
data;
let max_response_per_tick = config.max_response_per_tick;
let mut to_send: HashMap<String, HashSet<Vec2<i32>>> = HashMap::new();
for (id, requests) in (&ids, &mut requests).join() {
let mut to_add_back_to_requested = HashSet::new();
for coords in requests.requests.drain(..) {
if chunks.is_chunk_ready(&coords) {
let mut clients_to_send = to_send.remove(&id.0).unwrap_or_default();
if clients_to_send.len() >= max_response_per_tick {
to_send.insert(id.0.clone(), clients_to_send);
to_add_back_to_requested.insert(coords);
continue;
}
clients_to_send.insert(coords.clone());
to_send.insert(id.0.clone(), clients_to_send);
interests.add(&id.0, &coords);
continue;
}
if !interests.has_interests(&coords) {
chunks
.light_traversed_chunks(&coords)
.into_iter()
.for_each(|coords| {
if chunks.raw(&coords).is_none()
|| matches!(
chunks.raw(&coords).unwrap().status,
ChunkStatus::Generating(_)
)
{
pipeline.add_chunk(&coords, false);
}
else if let Some(chunk) = chunks.raw(&coords) {
if matches!(chunk.status, ChunkStatus::Meshing) {
mesher.add_chunk(&coords, false);
}
}
});
}
interests.add(&id.0, &coords);
}
requests.requests.extend(to_add_back_to_requested);
}
to_send.into_iter().for_each(|(id, coords)| {
let chunks: Vec<ChunkProtocol> = coords
.into_iter()
.map(|coords| {
let chunk = chunks.get(&coords).unwrap();
chunk.to_model(true, true, 0..config.sub_chunks as u32)
})
.collect();
let message = Message::new(&MessageType::Load).chunks(&chunks).build();
queue.push((message, ClientFilter::Direct(id)))
})
}
}