1use std::marker::PhantomData;
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6use bevy_ecs::prelude::*;
7use bevy_ecs::system::SystemParam;
8use bevy_state::state::FreelyMutableState;
9use bevy_platform::collections::HashMap;
10use parking_lot::Mutex;
11
12use crate::prelude::*;
13
14static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub struct ProgressEntryId(usize);
31
32impl ProgressEntryId {
33 pub fn new() -> ProgressEntryId {
35 let next_id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
36 ProgressEntryId(next_id)
37 }
38}
39
40#[derive(Resource)]
54pub struct ProgressTracker<S: FreelyMutableState> {
55 inner: Mutex<GlobalProgressTrackerInner>,
56 #[cfg(feature = "async")]
57 pub(crate) chan: Option<(Sender, Receiver)>,
58 _pd: PhantomData<S>,
59}
60
61impl<S: FreelyMutableState> Default for ProgressTracker<S> {
62 fn default() -> Self {
63 Self {
64 inner: Default::default(),
65 #[cfg(feature = "async")]
66 chan: None,
67 _pd: PhantomData,
68 }
69 }
70}
71
72#[derive(Default)]
73struct GlobalProgressTrackerInner {
74 entries: HashMap<ProgressEntryId, (Progress, HiddenProgress)>,
75 sum_entities: (Progress, HiddenProgress),
76 sum_entries: (Progress, HiddenProgress),
77}
78
79impl<S: FreelyMutableState> ProgressTracker<S> {
80 pub fn clear(&mut self) {
82 self.inner = Default::default();
83 #[cfg(feature = "async")]
84 {
85 self.chan = None;
86 }
87 }
88
89 #[cfg(feature = "async")]
94 pub fn new_async_entry(&mut self) -> ProgressSender {
95 if let Some((tx, _)) = &self.chan {
96 ProgressSender {
97 id: ProgressEntryId::new(),
98 sender: tx.clone(),
99 }
100 } else {
101 let chan = crossbeam_channel::unbounded();
102 let r = ProgressSender {
103 id: ProgressEntryId::new(),
104 sender: chan.0.clone(),
105 };
106 self.chan = Some(chan);
107 r
108 }
109 }
110
111 pub fn foreach_entry(
116 &self,
117 mut f: impl FnMut(ProgressEntryId, &mut Progress, &mut HiddenProgress),
118 ) {
119 let mut inner = self.inner.lock();
120 for (k, v) in inner.entries.iter_mut() {
121 f(*k, &mut v.0, &mut v.1);
122 }
123 }
124
125 pub fn contains_id(&self, id: ProgressEntryId) -> bool {
127 self.inner.lock().entries.contains_key(&id)
128 }
129
130 pub fn is_ready(&self) -> bool {
134 self.get_global_combined_progress().is_ready()
135 }
136
137 pub fn is_id_ready(&self, id: ProgressEntryId) -> bool {
141 let inner = self.inner.lock();
142 inner
143 .entries
144 .get(&id)
145 .map(|x| (x.0 + x.1 .0).is_ready())
146 .unwrap_or_default()
147 }
148
149 pub(crate) fn set_sum_entities(&self, v: Progress, h: HiddenProgress) {
150 let mut inner = self.inner.lock();
151 inner.sum_entities.0 = v;
152 inner.sum_entities.1 = h;
153 }
154
155 pub fn get_global_progress(&self) -> Progress {
160 let inner = self.inner.lock();
161 inner.sum_entries.0 + inner.sum_entities.0
162 }
163
164 pub fn get_global_hidden_progress(&self) -> HiddenProgress {
166 let inner = self.inner.lock();
167 inner.sum_entries.1 + inner.sum_entities.1
168 }
169
170 pub fn get_global_combined_progress(&self) -> Progress {
174 let inner = self.inner.lock();
175 inner.sum_entries.0 + inner.sum_entries.1 .0 +
176 inner.sum_entities.0 + inner.sum_entities.1 .0
177 }
178
179 pub fn get_progress(&self, id: ProgressEntryId) -> Progress {
181 let inner = self.inner.lock();
182 inner.entries.get(&id).copied().unwrap_or_default().0
183 }
184
185 pub fn get_hidden_progress(&self, id: ProgressEntryId) -> HiddenProgress {
187 let inner = self.inner.lock();
188 inner.entries.get(&id).copied().unwrap_or_default().1
189 }
190
191 pub fn get_combined_progress(&self, id: ProgressEntryId) -> Progress {
193 let inner = self.inner.lock();
194 inner
195 .entries
196 .get(&id)
197 .map(|x| x.0 + x.1 .0)
198 .unwrap_or_default()
199 }
200
201 pub fn get_total(&self, id: ProgressEntryId) -> u32 {
203 let inner = self.inner.lock();
204 inner.entries.get(&id).copied().unwrap_or_default().0.total
205 }
206
207 pub fn get_done(&self, id: ProgressEntryId) -> u32 {
209 let inner = self.inner.lock();
210 inner.entries.get(&id).copied().unwrap_or_default().0.done
211 }
212
213 pub fn get_hidden_total(&self, id: ProgressEntryId) -> u32 {
215 let inner = self.inner.lock();
216 inner.entries.get(&id).copied().unwrap_or_default().1.total
217 }
218
219 pub fn get_hidden_done(&self, id: ProgressEntryId) -> u32 {
221 let inner = self.inner.lock();
222 inner.entries.get(&id).copied().unwrap_or_default().1.done
223 }
224
225 pub fn set_progress(&self, id: ProgressEntryId, done: u32, total: u32) {
229 let inner = &mut *self.inner.lock();
230 if let Some(p) = inner.entries.get_mut(&id) {
231 if p.0.total < total {
232 let diff = total - p.0.total;
233 inner.sum_entries.0.total += diff;
234 }
235 if p.0.total > total {
236 let diff = p.0.total - total;
237 inner.sum_entries.0.total -= diff;
238 }
239 if p.0.done < done {
240 let diff = done - p.0.done;
241 inner.sum_entries.0.done += diff;
242 }
243 if p.0.done > done {
244 let diff = p.0.done - done;
245 inner.sum_entries.0.done -= diff;
246 }
247 p.0 = Progress { done, total };
248 } else {
249 inner.entries.insert(
250 id,
251 (Progress { done, total }, HiddenProgress::default()),
252 );
253 inner.sum_entries.0.total += total;
254 inner.sum_entries.0.done += done;
255 }
256 }
257
258 pub fn set_hidden_progress(
262 &self,
263 id: ProgressEntryId,
264 done: u32,
265 total: u32,
266 ) {
267 let inner = &mut *self.inner.lock();
268 if let Some(p) = inner.entries.get_mut(&id) {
269 if p.1.total < total {
270 let diff = total - p.1.total;
271 inner.sum_entries.1.total += diff;
272 }
273 if p.1.total > total {
274 let diff = p.1.total - total;
275 inner.sum_entries.1.total -= diff;
276 }
277 if p.1.done < done {
278 let diff = done - p.1.done;
279 inner.sum_entries.1.done += diff;
280 }
281 if p.1.done > done {
282 let diff = p.1.done - done;
283 inner.sum_entries.1.done -= diff;
284 }
285 p.1 = Progress { done, total }.into();
286 } else {
287 inner.entries.insert(
288 id,
289 (Progress::default(), Progress { done, total }.into()),
290 );
291 inner.sum_entries.1.total += total;
292 inner.sum_entries.1.done += done;
293 }
294 }
295
296 pub fn set_total(&self, id: ProgressEntryId, total: u32) {
298 let inner = &mut *self.inner.lock();
299 if let Some(p) = inner.entries.get_mut(&id) {
300 if p.0.total < total {
301 let diff = total - p.0.total;
302 inner.sum_entries.0.total += diff;
303 }
304 if p.0.total > total {
305 let diff = p.0.total - total;
306 inner.sum_entries.0.total -= diff;
307 }
308 p.0.total = total;
309 } else {
310 inner.entries.insert(
311 id,
312 (Progress { done: 0, total }, HiddenProgress::default()),
313 );
314 inner.sum_entries.0.total += total;
315 }
316 }
317
318 pub fn set_done(&self, id: ProgressEntryId, done: u32) {
320 let inner = &mut *self.inner.lock();
321 if let Some(p) = inner.entries.get_mut(&id) {
322 if p.0.done < done {
323 let diff = done - p.0.done;
324 inner.sum_entries.0.done += diff;
325 }
326 if p.0.done > done {
327 let diff = p.0.done - done;
328 inner.sum_entries.0.done -= diff;
329 }
330 p.0.done = done;
331 } else {
332 inner.entries.insert(
333 id,
334 (Progress { done, total: 0 }, HiddenProgress::default()),
335 );
336 inner.sum_entries.0.done += done;
337 }
338 }
339
340 pub fn set_hidden_total(&self, id: ProgressEntryId, total: u32) {
342 let inner = &mut *self.inner.lock();
343 if let Some(p) = inner.entries.get_mut(&id) {
344 if p.1.total < total {
345 let diff = total - p.1.total;
346 inner.sum_entries.1.total += diff;
347 }
348 if p.1.total > total {
349 let diff = p.1.total - total;
350 inner.sum_entries.1.total -= diff;
351 }
352 p.1.total = total;
353 } else {
354 inner.entries.insert(
355 id,
356 (Progress::default(), Progress { done: 0, total }.into()),
357 );
358 inner.sum_entries.1.total += total;
359 }
360 }
361
362 pub fn set_hidden_done(&self, id: ProgressEntryId, done: u32) {
364 let inner = &mut *self.inner.lock();
365 if let Some(p) = inner.entries.get_mut(&id) {
366 if p.1.done < done {
367 let diff = done - p.1.done;
368 inner.sum_entries.1.done += diff;
369 }
370 if p.1.done > done {
371 let diff = p.1.done - done;
372 inner.sum_entries.1.done -= diff;
373 }
374 p.1.done = done;
375 } else {
376 inner.entries.insert(
377 id,
378 (Progress::default(), Progress { done, total: 0 }.into()),
379 );
380 inner.sum_entries.1.done += done;
381 }
382 }
383
384 pub fn add_progress(&self, id: ProgressEntryId, done: u32, total: u32) {
389 let inner = &mut *self.inner.lock();
390 if let Some(p) = inner.entries.get_mut(&id) {
391 p.0.done += done;
392 p.0.total += total;
393 } else {
394 inner.entries.insert(
395 id,
396 (Progress { done, total }, HiddenProgress::default()),
397 );
398 }
399 inner.sum_entries.0.total += total;
400 inner.sum_entries.0.done += done;
401 }
402
403 pub fn add_total(&self, id: ProgressEntryId, total: u32) {
406 let inner = &mut *self.inner.lock();
407 if let Some(p) = inner.entries.get_mut(&id) {
408 p.0.total += total;
409 } else {
410 inner.entries.insert(
411 id,
412 (Progress { done: 0, total }, HiddenProgress::default()),
413 );
414 }
415 inner.sum_entries.0.total += total;
416 }
417
418 pub fn add_done(&self, id: ProgressEntryId, done: u32) {
421 let inner = &mut *self.inner.lock();
422 if let Some(p) = inner.entries.get_mut(&id) {
423 p.0.done += done;
424 } else {
425 inner.entries.insert(
426 id,
427 (Progress { done, total: 0 }, HiddenProgress::default()),
428 );
429 }
430 inner.sum_entries.0.done += done;
431 }
432
433 pub fn add_hidden_progress(
438 &self,
439 id: ProgressEntryId,
440 done: u32,
441 total: u32,
442 ) {
443 let inner = &mut *self.inner.lock();
444 if let Some(p) = inner.entries.get_mut(&id) {
445 p.1.done += done;
446 p.1.total += total;
447 } else {
448 inner.entries.insert(
449 id,
450 (Progress::default(), Progress { done, total }.into()),
451 );
452 }
453 inner.sum_entries.1.total += total;
454 inner.sum_entries.1.done += done;
455 }
456
457 pub fn add_hidden_total(&self, id: ProgressEntryId, total: u32) {
460 let inner = &mut *self.inner.lock();
461 if let Some(p) = inner.entries.get_mut(&id) {
462 p.1.total += total;
463 } else {
464 inner.entries.insert(
465 id,
466 (Progress::default(), Progress { done: 0, total }.into()),
467 );
468 }
469 inner.sum_entries.1.total += total;
470 }
471
472 pub fn add_hidden_done(&self, id: ProgressEntryId, done: u32) {
475 let inner = &mut *self.inner.lock();
476 if let Some(p) = inner.entries.get_mut(&id) {
477 p.1.done += done;
478 } else {
479 inner.entries.insert(
480 id,
481 (Progress::default(), Progress { done, total: 0 }.into()),
482 );
483 }
484 inner.sum_entries.1.done += done;
485 }
486}
487
488struct ProgressEntryIdWrapper(ProgressEntryId);
491
492impl Default for ProgressEntryIdWrapper {
493 fn default() -> Self {
494 Self(ProgressEntryId::new())
495 }
496}
497
498#[derive(SystemParam)]
506pub struct ProgressEntry<'w, 's, S: FreelyMutableState> {
507 global: Res<'w, ProgressTracker<S>>,
508 my_id: Local<'s, ProgressEntryIdWrapper>,
509}
510
511impl<S: FreelyMutableState> ProgressEntry<'_, '_, S> {
512 pub fn id(&self) -> ProgressEntryId {
514 self.my_id.0
515 }
516
517 pub fn get_global_progress(&self) -> Progress {
522 self.global.get_global_progress()
523 }
524
525 pub fn get_global_hidden_progress(&self) -> HiddenProgress {
527 self.global.get_global_hidden_progress()
528 }
529
530 pub fn get_global_combined_progress(&self) -> Progress {
534 self.global.get_global_combined_progress()
535 }
536
537 pub fn is_global_ready(&self) -> bool {
539 self.global.is_ready()
540 }
541
542 pub fn is_ready(&self) -> bool {
544 self.global.is_id_ready(self.my_id.0)
545 }
546
547 pub fn get_combined_progress(&self) -> Progress {
549 self.global.get_combined_progress(self.my_id.0)
550 }
551
552 pub fn get_progress(&self) -> Progress {
554 self.global.get_progress(self.my_id.0)
555 }
556
557 pub fn get_total(&self) -> u32 {
559 self.global.get_total(self.my_id.0)
560 }
561
562 pub fn get_done(&self) -> u32 {
565 self.global.get_done(self.my_id.0)
566 }
567
568 pub fn set_progress(&self, done: u32, total: u32) {
572 self.global.set_progress(self.my_id.0, done, total)
573 }
574
575 pub fn set_total(&self, total: u32) {
578 self.global.set_total(self.my_id.0, total)
579 }
580
581 pub fn set_done(&self, done: u32) {
584 self.global.set_done(self.my_id.0, done)
585 }
586
587 pub fn add_progress(&self, done: u32, total: u32) {
591 self.global.add_progress(self.my_id.0, done, total)
592 }
593
594 pub fn add_total(&self, total: u32) {
597 self.global.add_total(self.my_id.0, total)
598 }
599
600 pub fn add_done(&self, done: u32) {
603 self.global.add_done(self.my_id.0, done)
604 }
605
606 pub fn get_hidden_progress(&self) -> HiddenProgress {
608 self.global.get_hidden_progress(self.my_id.0)
609 }
610
611 pub fn get_hidden_total(&self) -> u32 {
613 self.global.get_hidden_total(self.my_id.0)
614 }
615
616 pub fn get_hidden_done(&self) -> u32 {
618 self.global.get_hidden_done(self.my_id.0)
619 }
620
621 pub fn set_hidden_progress(&self, done: u32, total: u32) {
625 self.global.set_hidden_progress(self.my_id.0, done, total)
626 }
627
628 pub fn set_hidden_total(&self, total: u32) {
631 self.global.set_hidden_total(self.my_id.0, total)
632 }
633
634 pub fn set_hidden_done(&self, done: u32) {
637 self.global.set_hidden_done(self.my_id.0, done)
638 }
639
640 pub fn add_hidden_progress(&self, done: u32, total: u32) {
644 self.global.add_hidden_progress(self.my_id.0, done, total)
645 }
646
647 pub fn add_hidden_total(&self, total: u32) {
649 self.global.add_hidden_total(self.my_id.0, total)
650 }
651
652 pub fn add_hidden_done(&self, done: u32) {
655 self.global.add_hidden_done(self.my_id.0, done)
656 }
657}
658
659pub(crate) trait ApplyProgress: Sized {
660 fn apply_progress<S: FreelyMutableState>(
661 self,
662 tracker: &ProgressTracker<S>,
663 id: ProgressEntryId,
664 );
665}
666
667impl ApplyProgress for Progress {
668 fn apply_progress<S: FreelyMutableState>(
669 self,
670 tracker: &ProgressTracker<S>,
671 id: ProgressEntryId,
672 ) {
673 tracker.set_progress(id, self.done, self.total);
674 }
675}
676
677impl ApplyProgress for HiddenProgress {
678 fn apply_progress<S: FreelyMutableState>(
679 self,
680 tracker: &ProgressTracker<S>,
681 id: ProgressEntryId,
682 ) {
683 tracker.set_hidden_progress(id, self.0.done, self.0.total);
684 }
685}
686
687impl<T1: ApplyProgress, T2: ApplyProgress> ApplyProgress for (T1, T2) {
688 fn apply_progress<S: FreelyMutableState>(
689 self,
690 tracker: &ProgressTracker<S>,
691 id: ProgressEntryId,
692 ) {
693 self.0.apply_progress(tracker, id);
694 self.1.apply_progress(tracker, id);
695 }
696}