use std::marker::PhantomData;
use std::sync::atomic::{AtomicUsize, Ordering};
use bevy_ecs::prelude::*;
use bevy_ecs::system::SystemParam;
use bevy_state::state::FreelyMutableState;
use bevy_platform::collections::HashMap;
use parking_lot::Mutex;
use crate::prelude::*;
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ProgressEntryId(usize);
impl ProgressEntryId {
pub fn new() -> ProgressEntryId {
let next_id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
ProgressEntryId(next_id)
}
}
#[derive(Resource)]
pub struct ProgressTracker<S: FreelyMutableState> {
inner: Mutex<GlobalProgressTrackerInner>,
#[cfg(feature = "async")]
pub(crate) chan: Option<(Sender, Receiver)>,
_pd: PhantomData<S>,
}
impl<S: FreelyMutableState> Default for ProgressTracker<S> {
fn default() -> Self {
Self {
inner: Default::default(),
#[cfg(feature = "async")]
chan: None,
_pd: PhantomData,
}
}
}
#[derive(Default)]
struct GlobalProgressTrackerInner {
entries: HashMap<ProgressEntryId, (Progress, HiddenProgress)>,
sum_entities: (Progress, HiddenProgress),
sum_entries: (Progress, HiddenProgress),
}
impl<S: FreelyMutableState> ProgressTracker<S> {
pub fn clear(&mut self) {
self.inner = Default::default();
#[cfg(feature = "async")]
{
self.chan = None;
}
}
#[cfg(feature = "async")]
pub fn new_async_entry(&mut self) -> ProgressSender {
if let Some((tx, _)) = &self.chan {
ProgressSender {
id: ProgressEntryId::new(),
sender: tx.clone(),
}
} else {
let chan = crossbeam_channel::unbounded();
let r = ProgressSender {
id: ProgressEntryId::new(),
sender: chan.0.clone(),
};
self.chan = Some(chan);
r
}
}
pub fn foreach_entry(
&self,
mut f: impl FnMut(ProgressEntryId, &mut Progress, &mut HiddenProgress),
) {
let mut inner = self.inner.lock();
for (k, v) in inner.entries.iter_mut() {
f(*k, &mut v.0, &mut v.1);
}
}
pub fn contains_id(&self, id: ProgressEntryId) -> bool {
self.inner.lock().entries.contains_key(&id)
}
pub fn is_ready(&self) -> bool {
self.get_global_combined_progress().is_ready()
}
pub fn is_id_ready(&self, id: ProgressEntryId) -> bool {
let inner = self.inner.lock();
inner
.entries
.get(&id)
.map(|x| (x.0 + x.1 .0).is_ready())
.unwrap_or_default()
}
pub(crate) fn set_sum_entities(&self, v: Progress, h: HiddenProgress) {
let mut inner = self.inner.lock();
inner.sum_entities.0 = v;
inner.sum_entities.1 = h;
}
pub fn get_global_progress(&self) -> Progress {
let inner = self.inner.lock();
inner.sum_entries.0 + inner.sum_entities.0
}
pub fn get_global_hidden_progress(&self) -> HiddenProgress {
let inner = self.inner.lock();
inner.sum_entries.1 + inner.sum_entities.1
}
pub fn get_global_combined_progress(&self) -> Progress {
let inner = self.inner.lock();
inner.sum_entries.0 + inner.sum_entries.1 .0 +
inner.sum_entities.0 + inner.sum_entities.1 .0
}
pub fn get_progress(&self, id: ProgressEntryId) -> Progress {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().0
}
pub fn get_hidden_progress(&self, id: ProgressEntryId) -> HiddenProgress {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().1
}
pub fn get_combined_progress(&self, id: ProgressEntryId) -> Progress {
let inner = self.inner.lock();
inner
.entries
.get(&id)
.map(|x| x.0 + x.1 .0)
.unwrap_or_default()
}
pub fn get_total(&self, id: ProgressEntryId) -> u32 {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().0.total
}
pub fn get_done(&self, id: ProgressEntryId) -> u32 {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().0.done
}
pub fn get_hidden_total(&self, id: ProgressEntryId) -> u32 {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().1.total
}
pub fn get_hidden_done(&self, id: ProgressEntryId) -> u32 {
let inner = self.inner.lock();
inner.entries.get(&id).copied().unwrap_or_default().1.done
}
pub fn set_progress(&self, id: ProgressEntryId, done: u32, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.0.total < total {
let diff = total - p.0.total;
inner.sum_entries.0.total += diff;
}
if p.0.total > total {
let diff = p.0.total - total;
inner.sum_entries.0.total -= diff;
}
if p.0.done < done {
let diff = done - p.0.done;
inner.sum_entries.0.done += diff;
}
if p.0.done > done {
let diff = p.0.done - done;
inner.sum_entries.0.done -= diff;
}
p.0 = Progress { done, total };
} else {
inner.entries.insert(
id,
(Progress { done, total }, HiddenProgress::default()),
);
inner.sum_entries.0.total += total;
inner.sum_entries.0.done += done;
}
}
pub fn set_hidden_progress(
&self,
id: ProgressEntryId,
done: u32,
total: u32,
) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.1.total < total {
let diff = total - p.1.total;
inner.sum_entries.1.total += diff;
}
if p.1.total > total {
let diff = p.1.total - total;
inner.sum_entries.1.total -= diff;
}
if p.1.done < done {
let diff = done - p.1.done;
inner.sum_entries.1.done += diff;
}
if p.1.done > done {
let diff = p.1.done - done;
inner.sum_entries.1.done -= diff;
}
p.1 = Progress { done, total }.into();
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done, total }.into()),
);
inner.sum_entries.1.total += total;
inner.sum_entries.1.done += done;
}
}
pub fn set_total(&self, id: ProgressEntryId, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.0.total < total {
let diff = total - p.0.total;
inner.sum_entries.0.total += diff;
}
if p.0.total > total {
let diff = p.0.total - total;
inner.sum_entries.0.total -= diff;
}
p.0.total = total;
} else {
inner.entries.insert(
id,
(Progress { done: 0, total }, HiddenProgress::default()),
);
inner.sum_entries.0.total += total;
}
}
pub fn set_done(&self, id: ProgressEntryId, done: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.0.done < done {
let diff = done - p.0.done;
inner.sum_entries.0.done += diff;
}
if p.0.done > done {
let diff = p.0.done - done;
inner.sum_entries.0.done -= diff;
}
p.0.done = done;
} else {
inner.entries.insert(
id,
(Progress { done, total: 0 }, HiddenProgress::default()),
);
inner.sum_entries.0.done += done;
}
}
pub fn set_hidden_total(&self, id: ProgressEntryId, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.1.total < total {
let diff = total - p.1.total;
inner.sum_entries.1.total += diff;
}
if p.1.total > total {
let diff = p.1.total - total;
inner.sum_entries.1.total -= diff;
}
p.1.total = total;
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done: 0, total }.into()),
);
inner.sum_entries.1.total += total;
}
}
pub fn set_hidden_done(&self, id: ProgressEntryId, done: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
if p.1.done < done {
let diff = done - p.1.done;
inner.sum_entries.1.done += diff;
}
if p.1.done > done {
let diff = p.1.done - done;
inner.sum_entries.1.done -= diff;
}
p.1.done = done;
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done, total: 0 }.into()),
);
inner.sum_entries.1.done += done;
}
}
pub fn add_progress(&self, id: ProgressEntryId, done: u32, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.0.done += done;
p.0.total += total;
} else {
inner.entries.insert(
id,
(Progress { done, total }, HiddenProgress::default()),
);
}
inner.sum_entries.0.total += total;
inner.sum_entries.0.done += done;
}
pub fn add_total(&self, id: ProgressEntryId, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.0.total += total;
} else {
inner.entries.insert(
id,
(Progress { done: 0, total }, HiddenProgress::default()),
);
}
inner.sum_entries.0.total += total;
}
pub fn add_done(&self, id: ProgressEntryId, done: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.0.done += done;
} else {
inner.entries.insert(
id,
(Progress { done, total: 0 }, HiddenProgress::default()),
);
}
inner.sum_entries.0.done += done;
}
pub fn add_hidden_progress(
&self,
id: ProgressEntryId,
done: u32,
total: u32,
) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.1.done += done;
p.1.total += total;
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done, total }.into()),
);
}
inner.sum_entries.1.total += total;
inner.sum_entries.1.done += done;
}
pub fn add_hidden_total(&self, id: ProgressEntryId, total: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.1.total += total;
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done: 0, total }.into()),
);
}
inner.sum_entries.1.total += total;
}
pub fn add_hidden_done(&self, id: ProgressEntryId, done: u32) {
let inner = &mut *self.inner.lock();
if let Some(p) = inner.entries.get_mut(&id) {
p.1.done += done;
} else {
inner.entries.insert(
id,
(Progress::default(), Progress { done, total: 0 }.into()),
);
}
inner.sum_entries.1.done += done;
}
}
struct ProgressEntryIdWrapper(ProgressEntryId);
impl Default for ProgressEntryIdWrapper {
fn default() -> Self {
Self(ProgressEntryId::new())
}
}
#[derive(SystemParam)]
pub struct ProgressEntry<'w, 's, S: FreelyMutableState> {
global: Res<'w, ProgressTracker<S>>,
my_id: Local<'s, ProgressEntryIdWrapper>,
}
impl<S: FreelyMutableState> ProgressEntry<'_, '_, S> {
pub fn id(&self) -> ProgressEntryId {
self.my_id.0
}
pub fn get_global_progress(&self) -> Progress {
self.global.get_global_progress()
}
pub fn get_global_hidden_progress(&self) -> HiddenProgress {
self.global.get_global_hidden_progress()
}
pub fn get_global_combined_progress(&self) -> Progress {
self.global.get_global_combined_progress()
}
pub fn is_global_ready(&self) -> bool {
self.global.is_ready()
}
pub fn is_ready(&self) -> bool {
self.global.is_id_ready(self.my_id.0)
}
pub fn get_combined_progress(&self) -> Progress {
self.global.get_combined_progress(self.my_id.0)
}
pub fn get_progress(&self) -> Progress {
self.global.get_progress(self.my_id.0)
}
pub fn get_total(&self) -> u32 {
self.global.get_total(self.my_id.0)
}
pub fn get_done(&self) -> u32 {
self.global.get_done(self.my_id.0)
}
pub fn set_progress(&self, done: u32, total: u32) {
self.global.set_progress(self.my_id.0, done, total)
}
pub fn set_total(&self, total: u32) {
self.global.set_total(self.my_id.0, total)
}
pub fn set_done(&self, done: u32) {
self.global.set_done(self.my_id.0, done)
}
pub fn add_progress(&self, done: u32, total: u32) {
self.global.add_progress(self.my_id.0, done, total)
}
pub fn add_total(&self, total: u32) {
self.global.add_total(self.my_id.0, total)
}
pub fn add_done(&self, done: u32) {
self.global.add_done(self.my_id.0, done)
}
pub fn get_hidden_progress(&self) -> HiddenProgress {
self.global.get_hidden_progress(self.my_id.0)
}
pub fn get_hidden_total(&self) -> u32 {
self.global.get_hidden_total(self.my_id.0)
}
pub fn get_hidden_done(&self) -> u32 {
self.global.get_hidden_done(self.my_id.0)
}
pub fn set_hidden_progress(&self, done: u32, total: u32) {
self.global.set_hidden_progress(self.my_id.0, done, total)
}
pub fn set_hidden_total(&self, total: u32) {
self.global.set_hidden_total(self.my_id.0, total)
}
pub fn set_hidden_done(&self, done: u32) {
self.global.set_hidden_done(self.my_id.0, done)
}
pub fn add_hidden_progress(&self, done: u32, total: u32) {
self.global.add_hidden_progress(self.my_id.0, done, total)
}
pub fn add_hidden_total(&self, total: u32) {
self.global.add_hidden_total(self.my_id.0, total)
}
pub fn add_hidden_done(&self, done: u32) {
self.global.add_hidden_done(self.my_id.0, done)
}
}
pub(crate) trait ApplyProgress: Sized {
fn apply_progress<S: FreelyMutableState>(
self,
tracker: &ProgressTracker<S>,
id: ProgressEntryId,
);
}
impl ApplyProgress for Progress {
fn apply_progress<S: FreelyMutableState>(
self,
tracker: &ProgressTracker<S>,
id: ProgressEntryId,
) {
tracker.set_progress(id, self.done, self.total);
}
}
impl ApplyProgress for HiddenProgress {
fn apply_progress<S: FreelyMutableState>(
self,
tracker: &ProgressTracker<S>,
id: ProgressEntryId,
) {
tracker.set_hidden_progress(id, self.0.done, self.0.total);
}
}
impl<T1: ApplyProgress, T2: ApplyProgress> ApplyProgress for (T1, T2) {
fn apply_progress<S: FreelyMutableState>(
self,
tracker: &ProgressTracker<S>,
id: ProgressEntryId,
) {
self.0.apply_progress(tracker, id);
self.1.apply_progress(tracker, id);
}
}