1use crate::{
2 meta::{AssetHash, MetaTransform},
3 Asset, AssetHandleProvider, AssetIndex, AssetLoadError, AssetPath, DependencyLoadState,
4 ErasedAssetIndex, ErasedLoadedAsset, Handle, InternalAssetEvent, LoadState,
5 RecursiveDependencyLoadState, StrongHandle, UntypedHandle,
6};
7use alloc::{
8 borrow::ToOwned,
9 boxed::Box,
10 sync::{Arc, Weak},
11 vec::Vec,
12};
13use bevy_ecs::world::World;
14use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
15use bevy_tasks::Task;
16use bevy_utils::TypeIdMap;
17use core::{any::TypeId, task::Waker};
18use crossbeam_channel::Sender;
19use either::Either;
20use thiserror::Error;
21use tracing::warn;
22
23#[derive(Debug)]
24pub(crate) struct AssetInfo {
25 weak_handle: Weak<StrongHandle>,
26 pub(crate) path: Option<AssetPath<'static>>,
27 pub(crate) load_state: LoadState,
28 pub(crate) dep_load_state: DependencyLoadState,
29 pub(crate) rec_dep_load_state: RecursiveDependencyLoadState,
30 loading_dependencies: HashSet<ErasedAssetIndex>,
31 failed_dependencies: HashSet<ErasedAssetIndex>,
32 loading_rec_dependencies: HashSet<ErasedAssetIndex>,
33 failed_rec_dependencies: HashSet<ErasedAssetIndex>,
34 dependents_waiting_on_load: HashSet<ErasedAssetIndex>,
35 dependents_waiting_on_recursive_dep_load: HashSet<ErasedAssetIndex>,
36 loader_dependencies: HashMap<AssetPath<'static>, AssetHash>,
43 handle_drops_to_skip: usize,
46 pub(crate) waiting_tasks: Vec<Waker>,
48}
49
50impl AssetInfo {
51 fn new(weak_handle: Weak<StrongHandle>, path: Option<AssetPath<'static>>) -> Self {
52 Self {
53 weak_handle,
54 path,
55 load_state: LoadState::NotLoaded,
56 dep_load_state: DependencyLoadState::NotLoaded,
57 rec_dep_load_state: RecursiveDependencyLoadState::NotLoaded,
58 loading_dependencies: HashSet::default(),
59 failed_dependencies: HashSet::default(),
60 loading_rec_dependencies: HashSet::default(),
61 failed_rec_dependencies: HashSet::default(),
62 loader_dependencies: HashMap::default(),
63 dependents_waiting_on_load: HashSet::default(),
64 dependents_waiting_on_recursive_dep_load: HashSet::default(),
65 handle_drops_to_skip: 0,
66 waiting_tasks: Vec::new(),
67 }
68 }
69}
70
71#[derive(Default, Clone, PartialEq, Eq)]
73pub(crate) struct AssetServerStats {
74 pub(crate) started_load_tasks: usize,
76}
77
78#[derive(Default)]
79pub(crate) struct AssetInfos {
80 path_to_index: HashMap<AssetPath<'static>, TypeIdMap<AssetIndex>>,
81 infos: HashMap<ErasedAssetIndex, AssetInfo>,
82 pub(crate) watching_for_changes: bool,
85 pub(crate) loader_dependents: HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
88 pub(crate) living_labeled_assets: HashMap<AssetPath<'static>, HashSet<Box<str>>>,
91 pub(crate) handle_providers: TypeIdMap<AssetHandleProvider>,
92 pub(crate) dependency_loaded_event_sender: TypeIdMap<fn(&mut World, AssetIndex)>,
93 pub(crate) dependency_failed_event_sender:
94 TypeIdMap<fn(&mut World, AssetIndex, AssetPath<'static>, AssetLoadError)>,
95 pub(crate) pending_tasks: HashMap<ErasedAssetIndex, Task<()>>,
96 pub(crate) stats: AssetServerStats,
98}
99
100impl core::fmt::Debug for AssetInfos {
101 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
102 f.debug_struct("AssetInfos")
103 .field("path_to_index", &self.path_to_index)
104 .field("infos", &self.infos)
105 .finish()
106 }
107}
108
109impl AssetInfos {
110 pub(crate) fn create_loading_handle_untyped(
111 &mut self,
112 type_id: TypeId,
113 type_name: &'static str,
114 ) -> UntypedHandle {
115 unwrap_with_context(
116 Self::create_handle_internal(
117 &mut self.infos,
118 &self.handle_providers,
119 &mut self.living_labeled_assets,
120 self.watching_for_changes,
121 type_id,
122 None,
123 None,
124 true,
125 ),
126 Either::Left(type_name),
127 )
128 .unwrap()
129 }
130
131 fn create_handle_internal(
132 infos: &mut HashMap<ErasedAssetIndex, AssetInfo>,
133 handle_providers: &TypeIdMap<AssetHandleProvider>,
134 living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
135 watching_for_changes: bool,
136 type_id: TypeId,
137 path: Option<AssetPath<'static>>,
138 meta_transform: Option<MetaTransform>,
139 loading: bool,
140 ) -> Result<UntypedHandle, GetOrCreateHandleInternalError> {
141 let provider = handle_providers
142 .get(&type_id)
143 .ok_or(MissingHandleProviderError(type_id))?;
144
145 if watching_for_changes && let Some(path) = &path {
146 let mut without_label = path.to_owned();
147 if let Some(label) = without_label.take_label() {
148 let labels = living_labeled_assets.entry(without_label).or_default();
149 labels.insert(label.as_ref().into());
150 }
151 }
152
153 let handle = provider.reserve_handle_internal(true, path.clone(), meta_transform);
154 let mut info = AssetInfo::new(Arc::downgrade(&handle), path);
155 if loading {
156 info.load_state = LoadState::Loading;
157 info.dep_load_state = DependencyLoadState::Loading;
158 info.rec_dep_load_state = RecursiveDependencyLoadState::Loading;
159 }
160 infos.insert(ErasedAssetIndex::new(handle.index, handle.type_id), info);
161
162 Ok(UntypedHandle::Strong(handle))
163 }
164
165 pub(crate) fn get_or_create_path_handle<A: Asset>(
166 &mut self,
167 path: AssetPath<'static>,
168 loading_mode: HandleLoadingMode,
169 meta_transform: Option<MetaTransform>,
170 ) -> (Handle<A>, bool) {
171 let result = self.get_or_create_path_handle_internal(
172 path,
173 Some(TypeId::of::<A>()),
174 loading_mode,
175 meta_transform,
176 );
177 let (handle, should_load) =
179 unwrap_with_context(result, Either::Left(core::any::type_name::<A>())).unwrap();
180 (handle.typed_unchecked(), should_load)
181 }
182
183 pub(crate) fn get_or_create_path_handle_erased(
184 &mut self,
185 path: AssetPath<'static>,
186 type_id: TypeId,
187 type_name: Option<&str>,
188 loading_mode: HandleLoadingMode,
189 meta_transform: Option<MetaTransform>,
190 ) -> (UntypedHandle, bool) {
191 let result = self.get_or_create_path_handle_internal(
192 path,
193 Some(type_id),
194 loading_mode,
195 meta_transform,
196 );
197 let type_info = match type_name {
198 Some(type_name) => Either::Left(type_name),
199 None => Either::Right(type_id),
200 };
201 unwrap_with_context(result, type_info)
202 .expect("type should be correct since the `TypeId` is specified above")
203 }
204
205 pub(crate) fn get_or_create_path_handle_internal(
208 &mut self,
209 path: AssetPath<'static>,
210 type_id: Option<TypeId>,
211 loading_mode: HandleLoadingMode,
212 meta_transform: Option<MetaTransform>,
213 ) -> Result<(UntypedHandle, bool), GetOrCreateHandleInternalError> {
214 let handles = self.path_to_index.entry(path.clone()).or_default();
215
216 let type_id = type_id
217 .or_else(|| {
218 if handles.len() == 1 {
220 Some(*handles.keys().next().unwrap())
221 } else {
222 None
223 }
224 })
225 .ok_or(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified)?;
226
227 match handles.entry(type_id) {
228 Entry::Occupied(entry) => {
229 let index = *entry.get();
230 let info = self
232 .infos
233 .get_mut(&ErasedAssetIndex::new(index, type_id))
234 .unwrap();
235 let mut should_load = false;
236 if loading_mode == HandleLoadingMode::Force
237 || (loading_mode == HandleLoadingMode::Request
238 && matches!(info.load_state, LoadState::NotLoaded | LoadState::Failed(_)))
239 {
240 info.load_state = LoadState::Loading;
241 info.dep_load_state = DependencyLoadState::Loading;
242 info.rec_dep_load_state = RecursiveDependencyLoadState::Loading;
243 should_load = true;
244 }
245
246 if let Some(strong_handle) = info.weak_handle.upgrade() {
247 Ok((UntypedHandle::Strong(strong_handle), should_load))
251 } else {
252 info.handle_drops_to_skip += 1;
260 let provider = self
261 .handle_providers
262 .get(&type_id)
263 .ok_or(MissingHandleProviderError(type_id))?;
264 let handle = provider.get_handle(index, true, Some(path), meta_transform);
265 info.weak_handle = Arc::downgrade(&handle);
266 Ok((UntypedHandle::Strong(handle), should_load))
267 }
268 }
269 Entry::Vacant(entry) => {
271 let should_load = match loading_mode {
272 HandleLoadingMode::NotLoading => false,
273 HandleLoadingMode::Request | HandleLoadingMode::Force => true,
274 };
275 let handle = Self::create_handle_internal(
276 &mut self.infos,
277 &self.handle_providers,
278 &mut self.living_labeled_assets,
279 self.watching_for_changes,
280 type_id,
281 Some(path),
282 meta_transform,
283 should_load,
284 )?;
285 let index = match &handle {
286 UntypedHandle::Strong(handle) => handle.index,
287 UntypedHandle::Uuid { .. } => unreachable!(),
289 };
290 entry.insert(index);
291 Ok((handle, should_load))
292 }
293 }
294 }
295
296 pub(crate) fn get(&self, index: ErasedAssetIndex) -> Option<&AssetInfo> {
297 self.infos.get(&index)
298 }
299
300 pub(crate) fn contains_key(&self, index: ErasedAssetIndex) -> bool {
301 self.infos.contains_key(&index)
302 }
303
304 pub(crate) fn get_mut(&mut self, index: ErasedAssetIndex) -> Option<&mut AssetInfo> {
305 self.infos.get_mut(&index)
306 }
307
308 pub(crate) fn get_path_and_type_id_handle(
309 &self,
310 path: &AssetPath<'_>,
311 type_id: TypeId,
312 ) -> Option<UntypedHandle> {
313 let index = *self.path_to_index.get(path)?.get(&type_id)?;
314 self.get_index_handle(ErasedAssetIndex::new(index, type_id))
315 }
316
317 pub(crate) fn get_path_indices<'a>(
318 &'a self,
319 path: &'a AssetPath<'_>,
320 ) -> impl Iterator<Item = ErasedAssetIndex> + 'a {
321 enum HandlesByPathIterator<T> {
323 None,
324 Some(T),
325 }
326
327 impl<T> Iterator for HandlesByPathIterator<T>
328 where
329 T: Iterator<Item = ErasedAssetIndex>,
330 {
331 type Item = ErasedAssetIndex;
332
333 fn next(&mut self) -> Option<Self::Item> {
334 match self {
335 HandlesByPathIterator::None => None,
336 HandlesByPathIterator::Some(iter) => iter.next(),
337 }
338 }
339 }
340
341 if let Some(type_id_to_id) = self.path_to_index.get(path) {
342 HandlesByPathIterator::Some(
343 type_id_to_id
344 .iter()
345 .map(|(type_id, index)| ErasedAssetIndex::new(*index, *type_id)),
346 )
347 } else {
348 HandlesByPathIterator::None
349 }
350 }
351
352 pub(crate) fn get_path_handles<'a>(
353 &'a self,
354 path: &'a AssetPath<'_>,
355 ) -> impl Iterator<Item = UntypedHandle> + 'a {
356 self.get_path_indices(path)
357 .filter_map(|id| self.get_index_handle(id))
358 }
359
360 pub(crate) fn get_index_handle(&self, index: ErasedAssetIndex) -> Option<UntypedHandle> {
361 let info = self.infos.get(&index)?;
362 let strong_handle = info.weak_handle.upgrade()?;
363 Some(UntypedHandle::Strong(strong_handle))
364 }
365
366 pub(crate) fn is_path_alive<'a>(&self, path: impl Into<AssetPath<'a>>) -> bool {
368 self.get_path_indices(&path.into())
369 .filter_map(|id| self.infos.get(&id))
370 .any(|info| info.weak_handle.strong_count() > 0)
371 }
372
373 pub(crate) fn should_reload(&self, path: &AssetPath) -> bool {
375 if self.is_path_alive(path) {
376 return true;
377 }
378
379 if let Some(living) = self.living_labeled_assets.get(path) {
380 !living.is_empty()
381 } else {
382 false
383 }
384 }
385
386 pub(crate) fn process_handle_drop(&mut self, index: ErasedAssetIndex) -> bool {
388 Self::process_handle_drop_internal(
389 &mut self.infos,
390 &mut self.path_to_index,
391 &mut self.loader_dependents,
392 &mut self.living_labeled_assets,
393 &mut self.pending_tasks,
394 self.watching_for_changes,
395 index,
396 )
397 }
398
399 pub(crate) fn process_asset_load(
401 &mut self,
402 loaded_asset_index: ErasedAssetIndex,
403 loaded_asset: ErasedLoadedAsset,
404 world: &mut World,
405 sender: &Sender<InternalAssetEvent>,
406 ) {
407 for (_, asset) in loaded_asset.labeled_assets {
410 let UntypedHandle::Strong(handle) = &asset.handle else {
411 unreachable!("Labeled assets are always strong handles");
412 };
413 self.process_asset_load(
414 ErasedAssetIndex {
415 index: handle.index,
416 type_id: handle.type_id,
417 },
418 asset.asset,
419 world,
420 sender,
421 );
422 }
423
424 if !self.infos.contains_key(&loaded_asset_index) {
426 return;
427 }
428
429 loaded_asset.value.insert(loaded_asset_index.index, world);
430 let mut loading_deps = loaded_asset.dependencies;
431 let mut failed_deps = <HashSet<_>>::default();
432 let mut dep_error = None;
433 let mut loading_rec_deps = loading_deps.clone();
434 let mut failed_rec_deps = <HashSet<_>>::default();
435 let mut rec_dep_error = None;
436 loading_deps.retain(|dep_id| {
437 if let Some(dep_info) = self.get_mut(*dep_id) {
438 match dep_info.rec_dep_load_state {
439 RecursiveDependencyLoadState::Loading
440 | RecursiveDependencyLoadState::NotLoaded => {
441 dep_info
443 .dependents_waiting_on_recursive_dep_load
444 .insert(loaded_asset_index);
445 }
446 RecursiveDependencyLoadState::Loaded => {
447 loading_rec_deps.remove(dep_id);
449 }
450 RecursiveDependencyLoadState::Failed(ref error) => {
451 if rec_dep_error.is_none() {
452 rec_dep_error = Some(error.clone());
453 }
454 failed_rec_deps.insert(*dep_id);
455 loading_rec_deps.remove(dep_id);
456 }
457 }
458 match dep_info.load_state {
459 LoadState::NotLoaded | LoadState::Loading => {
460 dep_info.dependents_waiting_on_load.insert(loaded_asset_index);
462 true
463 }
464 LoadState::Loaded => {
465 false
467 }
468 LoadState::Failed(ref error) => {
469 if dep_error.is_none() {
470 dep_error = Some(error.clone());
471 }
472 failed_deps.insert(*dep_id);
473 false
474 }
475 }
476 } else {
477 warn!(
479 "Dependency {} from asset {} is unknown. This asset's dependency load status will not switch to 'Loaded' until the unknown dependency is loaded.",
480 dep_id, loaded_asset_index
481 );
482 true
483 }
484 });
485
486 let dep_load_state = match (loading_deps.len(), failed_deps.len()) {
487 (0, 0) => DependencyLoadState::Loaded,
488 (_loading, 0) => DependencyLoadState::Loading,
489 (_loading, _failed) => DependencyLoadState::Failed(dep_error.unwrap()),
490 };
491
492 let rec_dep_load_state = match (loading_rec_deps.len(), failed_rec_deps.len()) {
493 (0, 0) => {
494 sender
495 .send(InternalAssetEvent::LoadedWithDependencies {
496 index: loaded_asset_index,
497 })
498 .unwrap();
499 RecursiveDependencyLoadState::Loaded
500 }
501 (_loading, 0) => RecursiveDependencyLoadState::Loading,
502 (_loading, _failed) => RecursiveDependencyLoadState::Failed(rec_dep_error.unwrap()),
503 };
504
505 let (dependents_waiting_on_load, dependents_waiting_on_rec_load) = {
506 let watching_for_changes = self.watching_for_changes;
507 if watching_for_changes {
509 let info = self
510 .infos
511 .get(&loaded_asset_index)
512 .expect("Asset info should always exist at this point");
513 if let Some(asset_path) = &info.path {
514 for loader_dependency in loaded_asset.loader_dependencies.keys() {
515 let dependents = self
516 .loader_dependents
517 .entry(loader_dependency.clone())
518 .or_default();
519 dependents.insert(asset_path.clone());
520 }
521 }
522 }
523 let info = self
524 .get_mut(loaded_asset_index)
525 .expect("Asset info should always exist at this point");
526 info.loading_dependencies = loading_deps;
527 info.failed_dependencies = failed_deps;
528 info.loading_rec_dependencies = loading_rec_deps;
529 info.failed_rec_dependencies = failed_rec_deps;
530 info.load_state = LoadState::Loaded;
531 info.dep_load_state = dep_load_state;
532 info.rec_dep_load_state = rec_dep_load_state.clone();
533 if watching_for_changes {
534 info.loader_dependencies = loaded_asset.loader_dependencies;
535 }
536
537 let dependents_waiting_on_rec_load =
538 if rec_dep_load_state.is_loaded() || rec_dep_load_state.is_failed() {
539 Some(core::mem::take(
540 &mut info.dependents_waiting_on_recursive_dep_load,
541 ))
542 } else {
543 None
544 };
545
546 (
547 core::mem::take(&mut info.dependents_waiting_on_load),
548 dependents_waiting_on_rec_load,
549 )
550 };
551
552 for id in dependents_waiting_on_load {
553 if let Some(info) = self.get_mut(id) {
554 info.loading_dependencies.remove(&loaded_asset_index);
555 if info.loading_dependencies.is_empty() && !info.dep_load_state.is_failed() {
556 info.dep_load_state = DependencyLoadState::Loaded;
558 }
559 }
560 }
561
562 if let Some(dependents_waiting_on_rec_load) = dependents_waiting_on_rec_load {
563 match rec_dep_load_state {
564 RecursiveDependencyLoadState::Loaded => {
565 for dep_id in dependents_waiting_on_rec_load {
566 Self::propagate_loaded_state(self, loaded_asset_index, dep_id, sender);
567 }
568 }
569 RecursiveDependencyLoadState::Failed(ref error) => {
570 for dep_id in dependents_waiting_on_rec_load {
571 Self::propagate_failed_state(self, loaded_asset_index, dep_id, error);
572 }
573 }
574 RecursiveDependencyLoadState::Loading | RecursiveDependencyLoadState::NotLoaded => {
575 unreachable!("`Loading` and `NotLoaded` state should never be propagated.")
577 }
578 }
579 }
580 }
581
582 fn propagate_loaded_state(
584 infos: &mut AssetInfos,
585 loaded_id: ErasedAssetIndex,
586 waiting_id: ErasedAssetIndex,
587 sender: &Sender<InternalAssetEvent>,
588 ) {
589 let dependents_waiting_on_rec_load = if let Some(info) = infos.get_mut(waiting_id) {
590 info.loading_rec_dependencies.remove(&loaded_id);
591 if info.loading_rec_dependencies.is_empty() && info.failed_rec_dependencies.is_empty() {
592 info.rec_dep_load_state = RecursiveDependencyLoadState::Loaded;
593 if info.load_state.is_loaded() {
594 sender
595 .send(InternalAssetEvent::LoadedWithDependencies { index: waiting_id })
596 .unwrap();
597 }
598 Some(core::mem::take(
599 &mut info.dependents_waiting_on_recursive_dep_load,
600 ))
601 } else {
602 None
603 }
604 } else {
605 None
606 };
607
608 if let Some(dependents_waiting_on_rec_load) = dependents_waiting_on_rec_load {
609 for dep_id in dependents_waiting_on_rec_load {
610 Self::propagate_loaded_state(infos, waiting_id, dep_id, sender);
611 }
612 }
613 }
614
615 fn propagate_failed_state(
617 infos: &mut AssetInfos,
618 failed_id: ErasedAssetIndex,
619 waiting_id: ErasedAssetIndex,
620 error: &Arc<AssetLoadError>,
621 ) {
622 let dependents_waiting_on_rec_load = if let Some(info) = infos.get_mut(waiting_id) {
623 info.loading_rec_dependencies.remove(&failed_id);
624 info.failed_rec_dependencies.insert(failed_id);
625 info.rec_dep_load_state = RecursiveDependencyLoadState::Failed(error.clone());
626 Some(core::mem::take(
627 &mut info.dependents_waiting_on_recursive_dep_load,
628 ))
629 } else {
630 None
631 };
632
633 if let Some(dependents_waiting_on_rec_load) = dependents_waiting_on_rec_load {
634 for dep_id in dependents_waiting_on_rec_load {
635 Self::propagate_failed_state(infos, waiting_id, dep_id, error);
636 }
637 }
638 }
639
640 pub(crate) fn process_asset_fail(
641 &mut self,
642 failed_index: ErasedAssetIndex,
643 error: AssetLoadError,
644 ) {
645 if !self.infos.contains_key(&failed_index) {
647 return;
648 }
649
650 let error = Arc::new(error);
651 let (dependents_waiting_on_load, dependents_waiting_on_rec_load) = {
652 let Some(info) = self.get_mut(failed_index) else {
653 return;
655 };
656 info.load_state = LoadState::Failed(error.clone());
657 info.dep_load_state = DependencyLoadState::Failed(error.clone());
658 info.rec_dep_load_state = RecursiveDependencyLoadState::Failed(error.clone());
659 for waker in info.waiting_tasks.drain(..) {
660 waker.wake();
661 }
662 (
663 core::mem::take(&mut info.dependents_waiting_on_load),
664 core::mem::take(&mut info.dependents_waiting_on_recursive_dep_load),
665 )
666 };
667
668 for waiting_id in dependents_waiting_on_load {
669 if let Some(info) = self.get_mut(waiting_id) {
670 info.loading_dependencies.remove(&failed_index);
671 info.failed_dependencies.insert(failed_index);
672 if !info.dep_load_state.is_failed() {
674 info.dep_load_state = DependencyLoadState::Failed(error.clone());
675 }
676 }
677 }
678
679 for waiting_id in dependents_waiting_on_rec_load {
680 Self::propagate_failed_state(self, failed_index, waiting_id, &error);
681 }
682 }
683
684 fn remove_dependents_and_labels(
685 info: &AssetInfo,
686 loader_dependents: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
687 path: &AssetPath<'static>,
688 living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
689 ) {
690 for loader_dependency in info.loader_dependencies.keys() {
691 if let Some(dependents) = loader_dependents.get_mut(loader_dependency) {
692 dependents.remove(path);
693 }
694 }
695
696 let Some(label) = path.label() else {
697 return;
698 };
699
700 let mut without_label = path.to_owned();
701 without_label.remove_label();
702
703 let Entry::Occupied(mut entry) = living_labeled_assets.entry(without_label) else {
704 return;
705 };
706
707 entry.get_mut().remove(label);
708 if entry.get().is_empty() {
709 entry.remove();
710 }
711 }
712
713 fn process_handle_drop_internal(
714 infos: &mut HashMap<ErasedAssetIndex, AssetInfo>,
715 path_to_id: &mut HashMap<AssetPath<'static>, TypeIdMap<AssetIndex>>,
716 loader_dependents: &mut HashMap<AssetPath<'static>, HashSet<AssetPath<'static>>>,
717 living_labeled_assets: &mut HashMap<AssetPath<'static>, HashSet<Box<str>>>,
718 pending_tasks: &mut HashMap<ErasedAssetIndex, Task<()>>,
719 watching_for_changes: bool,
720 index: ErasedAssetIndex,
721 ) -> bool {
722 let Entry::Occupied(mut entry) = infos.entry(index) else {
723 return false;
726 };
727
728 if entry.get_mut().handle_drops_to_skip > 0 {
729 entry.get_mut().handle_drops_to_skip -= 1;
730 return false;
731 }
732
733 pending_tasks.remove(&index);
734
735 let type_id = entry.key().type_id;
736
737 let info = entry.remove();
738 let Some(path) = &info.path else {
739 return true;
740 };
741
742 if watching_for_changes {
743 Self::remove_dependents_and_labels(
744 &info,
745 loader_dependents,
746 path,
747 living_labeled_assets,
748 );
749 }
750
751 if let Some(map) = path_to_id.get_mut(path) {
752 map.remove(&type_id);
753
754 if map.is_empty() {
755 path_to_id.remove(path);
756 }
757 };
758
759 true
760 }
761
762 pub(crate) fn consume_handle_drop_events(&mut self) {
768 for provider in self.handle_providers.values() {
769 while let Ok(drop_event) = provider.drop_receiver.try_recv() {
770 let id = drop_event.index;
771 if drop_event.asset_server_managed {
772 Self::process_handle_drop_internal(
773 &mut self.infos,
774 &mut self.path_to_index,
775 &mut self.loader_dependents,
776 &mut self.living_labeled_assets,
777 &mut self.pending_tasks,
778 self.watching_for_changes,
779 id,
780 );
781 }
782 }
783 }
784 }
785}
786#[derive(Copy, Clone, PartialEq, Eq)]
788pub(crate) enum HandleLoadingMode {
789 NotLoading,
791 Request,
793 Force,
795}
796
797#[derive(Error, Debug)]
798#[error("Cannot allocate a handle because no handle provider exists for asset type {0:?}")]
799pub struct MissingHandleProviderError(TypeId);
800
801#[derive(Error, Debug)]
803pub(crate) enum GetOrCreateHandleInternalError {
804 #[error(transparent)]
805 MissingHandleProviderError(#[from] MissingHandleProviderError),
806 #[error("Handle does not exist but TypeId was not specified.")]
807 HandleMissingButTypeIdNotSpecified,
808}
809
810pub(crate) fn unwrap_with_context<T>(
811 result: Result<T, GetOrCreateHandleInternalError>,
812 type_info: Either<&str, TypeId>,
813) -> Option<T> {
814 match result {
815 Ok(value) => Some(value),
816 Err(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified) => None,
817 Err(GetOrCreateHandleInternalError::MissingHandleProviderError(_)) => match type_info {
818 Either::Left(type_name) => {
819 panic!("Cannot allocate an Asset Handle of type '{type_name}' because the asset type has not been initialized. \
820 Make sure you have called `app.init_asset::<{type_name}>()`");
821 }
822 Either::Right(type_id) => {
823 panic!("Cannot allocate an AssetHandle of type '{type_id:?}' because the asset type has not been initialized. \
824 Make sure you have called `app.init_asset::<(actual asset type)>()`")
825 }
826 },
827 }
828}