/* wearechat - generic multiplayer game with voip Copyright (C) 2025 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::network::Network; use anyhow::Result; use egui::{Grid, Widget}; use log::debug; use std::{collections::HashSet, marker::PhantomData, sync::RwLock}; use weareshared::{ helper::ReadWrite, packets::{Packet, Resource}, store::{ResourceStore, resource_hash}, }; pub struct Downloader { inner: RwLock, } struct DownloaderState { have: HashSet, need: HashSet, pending: HashSet, store: ResourceStore, } impl Downloader { pub fn new(store: ResourceStore) -> Self { Self { inner: DownloaderState { have: HashSet::new(), need: HashSet::new(), pending: HashSet::new(), store, } .into(), } } pub fn try_get(&self, hash: Resource) -> Result> { self.try_get_raw(Resource(hash.0, PhantomData))? .map(|x| T::read(&mut x.as_slice())) .transpose() } pub fn try_get_raw(&self, hash: Resource) -> Result>> { let mut state = self.inner.write().unwrap(); if state.have.contains(&hash) { state.store.get_raw(hash) } else if state.need.contains(&hash) { Ok(None) } else if let Some(res) = state.store.get_raw(hash)? { state.have.insert(hash); Ok(Some(res)) } else { state.need.insert(hash); Ok(None) } } pub fn packet(&self, p: &Packet) -> Result<()> { let mut state = self.inner.write().unwrap(); if let Packet::RespondResource(_, d) = p { let key = Resource(resource_hash(&d.0), PhantomData); state.store.set_raw(&d.0)?; state.need.remove(&key); state.pending.remove(&key); if state.have.insert(key) { debug!("have {key}"); } } Ok(()) } pub fn update(&self, network: &Network) -> Result<()> { let mut state = self.inner.write().unwrap(); let mut new_pending = Vec::new(); for n in state .need .difference(&state.pending) .take(32 - state.pending.len()) { network .packet_send .send(Packet::RequestResource(*n)) .unwrap(); debug!("need {n}"); new_pending.push(*n); } state.pending.extend(new_pending); Ok(()) } } impl Widget for &Downloader { fn ui(self, ui: &mut egui::Ui) -> egui::Response { let state = self.inner.read().unwrap(); Grid::new("dl") .num_columns(2) .show(ui, |ui| { ui.label("Need"); ui.label(state.need.len().to_string()); ui.end_row(); ui.label("Pending"); ui.label(state.pending.len().to_string()); ui.end_row(); ui.label("Have"); ui.label(state.have.len().to_string()); ui.end_row(); }) .response } }