1mod info;
2mod loaders;
3
4use crate::{
5 folder::LoadedFolder,
6 io::{
7 AssetReaderError, AssetSource, AssetSourceEvent, AssetSourceId, AssetSources,
8 ErasedAssetReader, MissingAssetSourceError, MissingProcessedAssetReaderError, Reader,
9 },
10 loader::{AssetLoader, ErasedAssetLoader, LoadContext, LoadedAsset},
11 meta::{
12 loader_settings_meta_transform, AssetActionMinimal, AssetMetaDyn, AssetMetaMinimal,
13 MetaTransform, Settings,
14 },
15 path::AssetPath,
16 Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets,
17 DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UntypedAssetId,
18 UntypedAssetLoadFailedEvent, UntypedHandle,
19};
20use alloc::sync::Arc;
21use atomicow::CowArc;
22use bevy_ecs::prelude::*;
23use bevy_tasks::IoTaskPool;
24use bevy_utils::{
25 tracing::{error, info},
26 HashSet,
27};
28use core::{any::TypeId, future::Future, panic::AssertUnwindSafe, task::Poll};
29use crossbeam_channel::{Receiver, Sender};
30use derive_more::derive::{Display, Error, From};
31use either::Either;
32use futures_lite::{FutureExt, StreamExt};
33use info::*;
34use loaders::*;
35use parking_lot::{RwLock, RwLockWriteGuard};
36use std::path::{Path, PathBuf};
37
38#[derive(Resource, Clone)]
53pub struct AssetServer {
54 pub(crate) data: Arc<AssetServerData>,
55}
56
57pub(crate) struct AssetServerData {
59 pub(crate) infos: RwLock<AssetInfos>,
60 pub(crate) loaders: Arc<RwLock<AssetLoaders>>,
61 asset_event_sender: Sender<InternalAssetEvent>,
62 asset_event_receiver: Receiver<InternalAssetEvent>,
63 sources: AssetSources,
64 mode: AssetServerMode,
65 meta_check: AssetMetaCheck,
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub enum AssetServerMode {
71 Unprocessed,
73 Processed,
75}
76
77impl AssetServer {
78 pub fn new(sources: AssetSources, mode: AssetServerMode, watching_for_changes: bool) -> Self {
81 Self::new_with_loaders(
82 sources,
83 Default::default(),
84 mode,
85 AssetMetaCheck::Always,
86 watching_for_changes,
87 )
88 }
89
90 pub fn new_with_meta_check(
93 sources: AssetSources,
94 mode: AssetServerMode,
95 meta_check: AssetMetaCheck,
96 watching_for_changes: bool,
97 ) -> Self {
98 Self::new_with_loaders(
99 sources,
100 Default::default(),
101 mode,
102 meta_check,
103 watching_for_changes,
104 )
105 }
106
107 pub(crate) fn new_with_loaders(
108 sources: AssetSources,
109 loaders: Arc<RwLock<AssetLoaders>>,
110 mode: AssetServerMode,
111 meta_check: AssetMetaCheck,
112 watching_for_changes: bool,
113 ) -> Self {
114 let (asset_event_sender, asset_event_receiver) = crossbeam_channel::unbounded();
115 let mut infos = AssetInfos::default();
116 infos.watching_for_changes = watching_for_changes;
117 Self {
118 data: Arc::new(AssetServerData {
119 sources,
120 mode,
121 meta_check,
122 asset_event_sender,
123 asset_event_receiver,
124 loaders,
125 infos: RwLock::new(infos),
126 }),
127 }
128 }
129
130 pub fn get_source<'a>(
132 &self,
133 source: impl Into<AssetSourceId<'a>>,
134 ) -> Result<&AssetSource, MissingAssetSourceError> {
135 self.data.sources.get(source.into())
136 }
137
138 pub fn watching_for_changes(&self) -> bool {
140 self.data.infos.read().watching_for_changes
141 }
142
143 pub fn register_loader<L: AssetLoader>(&self, loader: L) {
145 self.data.loaders.write().push(loader);
146 }
147
148 pub fn register_asset<A: Asset>(&self, assets: &Assets<A>) {
150 self.register_handle_provider(assets.get_handle_provider());
151 fn sender<A: Asset>(world: &mut World, id: UntypedAssetId) {
152 world
153 .resource_mut::<Events<AssetEvent<A>>>()
154 .send(AssetEvent::LoadedWithDependencies { id: id.typed() });
155 }
156 fn failed_sender<A: Asset>(
157 world: &mut World,
158 id: UntypedAssetId,
159 path: AssetPath<'static>,
160 error: AssetLoadError,
161 ) {
162 world
163 .resource_mut::<Events<AssetLoadFailedEvent<A>>>()
164 .send(AssetLoadFailedEvent {
165 id: id.typed(),
166 path,
167 error,
168 });
169 }
170
171 let mut infos = self.data.infos.write();
172
173 infos
174 .dependency_loaded_event_sender
175 .insert(TypeId::of::<A>(), sender::<A>);
176
177 infos
178 .dependency_failed_event_sender
179 .insert(TypeId::of::<A>(), failed_sender::<A>);
180 }
181
182 pub(crate) fn register_handle_provider(&self, handle_provider: AssetHandleProvider) {
183 let mut infos = self.data.infos.write();
184 infos
185 .handle_providers
186 .insert(handle_provider.type_id, handle_provider);
187 }
188
189 pub async fn get_asset_loader_with_extension(
191 &self,
192 extension: &str,
193 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError> {
194 let error = || MissingAssetLoaderForExtensionError {
195 extensions: vec![extension.to_string()],
196 };
197
198 let loader = { self.data.loaders.read().get_by_extension(extension) };
199
200 loader.ok_or_else(error)?.get().await.map_err(|_| error())
201 }
202
203 pub async fn get_asset_loader_with_type_name(
205 &self,
206 type_name: &str,
207 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeNameError> {
208 let error = || MissingAssetLoaderForTypeNameError {
209 type_name: type_name.to_string(),
210 };
211
212 let loader = { self.data.loaders.read().get_by_name(type_name) };
213
214 loader.ok_or_else(error)?.get().await.map_err(|_| error())
215 }
216
217 pub async fn get_path_asset_loader<'a>(
219 &self,
220 path: impl Into<AssetPath<'a>>,
221 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError> {
222 let path = path.into();
223
224 let error = || {
225 let Some(full_extension) = path.get_full_extension() else {
226 return MissingAssetLoaderForExtensionError {
227 extensions: Vec::new(),
228 };
229 };
230
231 let mut extensions = vec![full_extension.clone()];
232 extensions.extend(
233 AssetPath::iter_secondary_extensions(&full_extension).map(ToString::to_string),
234 );
235
236 MissingAssetLoaderForExtensionError { extensions }
237 };
238
239 let loader = { self.data.loaders.read().get_by_path(&path) };
240
241 loader.ok_or_else(error)?.get().await.map_err(|_| error())
242 }
243
244 pub async fn get_asset_loader_with_asset_type_id(
246 &self,
247 type_id: TypeId,
248 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError> {
249 let error = || MissingAssetLoaderForTypeIdError { type_id };
250
251 let loader = { self.data.loaders.read().get_by_type(type_id) };
252
253 loader.ok_or_else(error)?.get().await.map_err(|_| error())
254 }
255
256 pub async fn get_asset_loader_with_asset_type<A: Asset>(
258 &self,
259 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError> {
260 self.get_asset_loader_with_asset_type_id(TypeId::of::<A>())
261 .await
262 }
263
264 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
308 pub fn load<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
309 self.load_with_meta_transform(path, None, ())
310 }
311
312 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
328 pub fn load_acquire<'a, A: Asset, G: Send + Sync + 'static>(
329 &self,
330 path: impl Into<AssetPath<'a>>,
331 guard: G,
332 ) -> Handle<A> {
333 self.load_with_meta_transform(path, None, guard)
334 }
335
336 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
340 pub fn load_with_settings<'a, A: Asset, S: Settings>(
341 &self,
342 path: impl Into<AssetPath<'a>>,
343 settings: impl Fn(&mut S) + Send + Sync + 'static,
344 ) -> Handle<A> {
345 self.load_with_meta_transform(path, Some(loader_settings_meta_transform(settings)), ())
346 }
347
348 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
358 pub fn load_acquire_with_settings<'a, A: Asset, S: Settings, G: Send + Sync + 'static>(
359 &self,
360 path: impl Into<AssetPath<'a>>,
361 settings: impl Fn(&mut S) + Send + Sync + 'static,
362 guard: G,
363 ) -> Handle<A> {
364 self.load_with_meta_transform(path, Some(loader_settings_meta_transform(settings)), guard)
365 }
366
367 pub(crate) fn load_with_meta_transform<'a, A: Asset, G: Send + Sync + 'static>(
368 &self,
369 path: impl Into<AssetPath<'a>>,
370 meta_transform: Option<MetaTransform>,
371 guard: G,
372 ) -> Handle<A> {
373 let path = path.into().into_owned();
374 let mut infos = self.data.infos.write();
375 let (handle, should_load) = infos.get_or_create_path_handle::<A>(
376 path.clone(),
377 HandleLoadingMode::Request,
378 meta_transform,
379 );
380
381 if should_load {
382 self.spawn_load_task(handle.clone().untyped(), path, infos, guard);
383 }
384
385 handle
386 }
387
388 pub(crate) fn load_erased_with_meta_transform<'a, G: Send + Sync + 'static>(
389 &self,
390 path: impl Into<AssetPath<'a>>,
391 type_id: TypeId,
392 meta_transform: Option<MetaTransform>,
393 guard: G,
394 ) -> UntypedHandle {
395 let path = path.into().into_owned();
396 let mut infos = self.data.infos.write();
397 let (handle, should_load) = infos.get_or_create_path_handle_erased(
398 path.clone(),
399 type_id,
400 None,
401 HandleLoadingMode::Request,
402 meta_transform,
403 );
404
405 if should_load {
406 self.spawn_load_task(handle.clone(), path, infos, guard);
407 }
408
409 handle
410 }
411
412 pub(crate) fn spawn_load_task<G: Send + Sync + 'static>(
413 &self,
414 handle: UntypedHandle,
415 path: AssetPath<'static>,
416 infos: RwLockWriteGuard<AssetInfos>,
417 guard: G,
418 ) {
419 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
421 drop(infos);
422
423 let owned_handle = handle.clone();
424 let server = self.clone();
425 let task = IoTaskPool::get().spawn(async move {
426 if let Err(err) = server
427 .load_internal(Some(owned_handle), path, false, None)
428 .await
429 {
430 error!("{}", err);
431 }
432 drop(guard);
433 });
434
435 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
436 {
437 let mut infos = infos;
438 infos.pending_tasks.insert(handle.id(), task);
439 }
440
441 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
442 task.detach();
443 }
444
445 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
449 pub async fn load_untyped_async<'a>(
450 &self,
451 path: impl Into<AssetPath<'a>>,
452 ) -> Result<UntypedHandle, AssetLoadError> {
453 let path: AssetPath = path.into();
454 self.load_internal(None, path, false, None).await
455 }
456
457 pub(crate) fn load_unknown_type_with_meta_transform<'a>(
458 &self,
459 path: impl Into<AssetPath<'a>>,
460 meta_transform: Option<MetaTransform>,
461 ) -> Handle<LoadedUntypedAsset> {
462 let path = path.into().into_owned();
463 let untyped_source = AssetSourceId::Name(match path.source() {
464 AssetSourceId::Default => CowArc::Static(UNTYPED_SOURCE_SUFFIX),
465 AssetSourceId::Name(source) => {
466 CowArc::Owned(format!("{source}--{UNTYPED_SOURCE_SUFFIX}").into())
467 }
468 });
469 let mut infos = self.data.infos.write();
470 let (handle, should_load) = infos.get_or_create_path_handle::<LoadedUntypedAsset>(
471 path.clone().with_source(untyped_source),
472 HandleLoadingMode::Request,
473 meta_transform,
474 );
475
476 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
478 drop(infos);
479
480 if !should_load {
481 return handle;
482 }
483 let id = handle.id().untyped();
484
485 let server = self.clone();
486 let task = IoTaskPool::get().spawn(async move {
487 let path_clone = path.clone();
488 match server.load_untyped_async(path).await {
489 Ok(handle) => server.send_asset_event(InternalAssetEvent::Loaded {
490 id,
491 loaded_asset: LoadedAsset::new_with_dependencies(
492 LoadedUntypedAsset { handle },
493 None,
494 )
495 .into(),
496 }),
497 Err(err) => {
498 error!("{err}");
499 server.send_asset_event(InternalAssetEvent::Failed {
500 id,
501 path: path_clone,
502 error: err,
503 });
504 }
505 }
506 });
507
508 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
509 infos.pending_tasks.insert(handle.id().untyped(), task);
510
511 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
512 task.detach();
513
514 handle
515 }
516
517 #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
540 pub fn load_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
541 self.load_unknown_type_with_meta_transform(path, None)
542 }
543
544 async fn load_internal<'a>(
549 &self,
550 mut input_handle: Option<UntypedHandle>,
551 path: AssetPath<'a>,
552 force: bool,
553 meta_transform: Option<MetaTransform>,
554 ) -> Result<UntypedHandle, AssetLoadError> {
555 let asset_type_id = input_handle.as_ref().map(UntypedHandle::type_id);
556
557 let path = path.into_owned();
558 let path_clone = path.clone();
559 let (mut meta, loader, mut reader) = self
560 .get_meta_loader_and_reader(&path_clone, asset_type_id)
561 .await
562 .inspect_err(|e| {
563 if let Some(handle) = &input_handle {
566 self.send_asset_event(InternalAssetEvent::Failed {
567 id: handle.id(),
568 path: path.clone_owned(),
569 error: e.clone(),
570 });
571 }
572 })?;
573
574 if let Some(meta_transform) = input_handle.as_ref().and_then(|h| h.meta_transform()) {
575 (*meta_transform)(&mut *meta);
576 }
577 input_handle = input_handle.map(|h| h.clone_weak());
580
581 let handle_result = match input_handle {
595 Some(handle) => {
596 Some((handle, true))
598 }
599 None => {
600 let mut infos = self.data.infos.write();
601 let result = infos.get_or_create_path_handle_internal(
602 path.clone(),
603 path.label().is_none().then(|| loader.asset_type_id()),
604 HandleLoadingMode::Request,
605 meta_transform,
606 );
607 unwrap_with_context(result, Either::Left(loader.asset_type_name()))
608 }
609 };
610
611 let handle = if let Some((handle, should_load)) = handle_result {
612 if path.label().is_none() && handle.type_id() != loader.asset_type_id() {
613 error!(
614 "Expected {:?}, got {:?}",
615 handle.type_id(),
616 loader.asset_type_id()
617 );
618 return Err(AssetLoadError::RequestedHandleTypeMismatch {
619 path: path.into_owned(),
620 requested: handle.type_id(),
621 actual_asset_name: loader.asset_type_name(),
622 loader_name: loader.type_name(),
623 });
624 }
625 if !should_load && !force {
626 return Ok(handle);
627 }
628 Some(handle)
629 } else {
630 None
631 };
632 let (base_handle, base_path) = if path.label().is_some() {
635 let mut infos = self.data.infos.write();
636 let base_path = path.without_label().into_owned();
637 let (base_handle, _) = infos.get_or_create_path_handle_erased(
638 base_path.clone(),
639 loader.asset_type_id(),
640 Some(loader.asset_type_name()),
641 HandleLoadingMode::Force,
642 None,
643 );
644 (base_handle, base_path)
645 } else {
646 (handle.clone().unwrap(), path.clone())
647 };
648
649 match self
650 .load_with_meta_loader_and_reader(&base_path, meta, &*loader, &mut *reader, true, false)
651 .await
652 {
653 Ok(loaded_asset) => {
654 let final_handle = if let Some(label) = path.label_cow() {
655 match loaded_asset.labeled_assets.get(&label) {
656 Some(labeled_asset) => labeled_asset.handle.clone(),
657 None => {
658 let mut all_labels: Vec<String> = loaded_asset
659 .labeled_assets
660 .keys()
661 .map(|s| (**s).to_owned())
662 .collect();
663 all_labels.sort_unstable();
664 return Err(AssetLoadError::MissingLabel {
665 base_path,
666 label: label.to_string(),
667 all_labels,
668 });
669 }
670 }
671 } else {
672 handle.unwrap()
674 };
675
676 self.send_loaded_asset(base_handle.id(), loaded_asset);
677 Ok(final_handle)
678 }
679 Err(err) => {
680 self.send_asset_event(InternalAssetEvent::Failed {
681 id: base_handle.id(),
682 error: err.clone(),
683 path: path.into_owned(),
684 });
685 Err(err)
686 }
687 }
688 }
689
690 fn send_loaded_asset(&self, id: UntypedAssetId, mut loaded_asset: ErasedLoadedAsset) {
693 for (_, labeled_asset) in loaded_asset.labeled_assets.drain() {
694 self.send_loaded_asset(labeled_asset.handle.id(), labeled_asset.asset);
695 }
696
697 self.send_asset_event(InternalAssetEvent::Loaded { id, loaded_asset });
698 }
699
700 pub fn reload<'a>(&self, path: impl Into<AssetPath<'a>>) {
702 let server = self.clone();
703 let path = path.into().into_owned();
704 IoTaskPool::get()
705 .spawn(async move {
706 let mut reloaded = false;
707
708 let requests = server
709 .data
710 .infos
711 .read()
712 .get_path_handles(&path)
713 .map(|handle| server.load_internal(Some(handle), path.clone(), true, None))
714 .collect::<Vec<_>>();
715
716 for result in requests {
717 match result.await {
718 Ok(_) => reloaded = true,
719 Err(err) => error!("{}", err),
720 }
721 }
722
723 if !reloaded && server.data.infos.read().should_reload(&path) {
724 if let Err(err) = server.load_internal(None, path, true, None).await {
725 error!("{}", err);
726 }
727 }
728 })
729 .detach();
730 }
731
732 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
737 pub fn add<A: Asset>(&self, asset: A) -> Handle<A> {
738 self.load_asset(LoadedAsset::new_with_dependencies(asset, None))
739 }
740
741 pub(crate) fn load_asset<A: Asset>(&self, asset: impl Into<LoadedAsset<A>>) -> Handle<A> {
742 let loaded_asset: LoadedAsset<A> = asset.into();
743 let erased_loaded_asset: ErasedLoadedAsset = loaded_asset.into();
744 self.load_asset_untyped(None, erased_loaded_asset)
745 .typed_debug_checked()
746 }
747
748 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
749 pub(crate) fn load_asset_untyped(
750 &self,
751 path: Option<AssetPath<'static>>,
752 asset: impl Into<ErasedLoadedAsset>,
753 ) -> UntypedHandle {
754 let loaded_asset = asset.into();
755 let handle = if let Some(path) = path {
756 let (handle, _) = self.data.infos.write().get_or_create_path_handle_erased(
757 path,
758 loaded_asset.asset_type_id(),
759 Some(loaded_asset.asset_type_name()),
760 HandleLoadingMode::NotLoading,
761 None,
762 );
763 handle
764 } else {
765 self.data.infos.write().create_loading_handle_untyped(
766 loaded_asset.asset_type_id(),
767 loaded_asset.asset_type_name(),
768 )
769 };
770 self.send_asset_event(InternalAssetEvent::Loaded {
771 id: handle.id(),
772 loaded_asset,
773 });
774 handle
775 }
776
777 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
782 pub fn add_async<A: Asset, E: core::error::Error + Send + Sync + 'static>(
783 &self,
784 future: impl Future<Output = Result<A, E>> + Send + 'static,
785 ) -> Handle<A> {
786 let mut infos = self.data.infos.write();
787 let handle =
788 infos.create_loading_handle_untyped(TypeId::of::<A>(), core::any::type_name::<A>());
789
790 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
792 drop(infos);
793
794 let id = handle.id();
795
796 let event_sender = self.data.asset_event_sender.clone();
797
798 let task = IoTaskPool::get().spawn(async move {
799 match future.await {
800 Ok(asset) => {
801 let loaded_asset = LoadedAsset::new_with_dependencies(asset, None).into();
802 event_sender
803 .send(InternalAssetEvent::Loaded { id, loaded_asset })
804 .unwrap();
805 }
806 Err(error) => {
807 let error = AddAsyncError {
808 error: Arc::new(error),
809 };
810 error!("{error}");
811 event_sender
812 .send(InternalAssetEvent::Failed {
813 id,
814 path: Default::default(),
815 error: AssetLoadError::AddAsyncError(error),
816 })
817 .unwrap();
818 }
819 }
820 });
821
822 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
823 infos.pending_tasks.insert(id, task);
824
825 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
826 task.detach();
827
828 handle.typed_debug_checked()
829 }
830
831 #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
840 pub fn load_folder<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedFolder> {
841 let path = path.into().into_owned();
842 let (handle, should_load) = self
843 .data
844 .infos
845 .write()
846 .get_or_create_path_handle::<LoadedFolder>(
847 path.clone(),
848 HandleLoadingMode::Request,
849 None,
850 );
851 if !should_load {
852 return handle;
853 }
854 let id = handle.id().untyped();
855 self.load_folder_internal(id, path);
856
857 handle
858 }
859
860 pub(crate) fn load_folder_internal(&self, id: UntypedAssetId, path: AssetPath) {
861 async fn load_folder<'a>(
862 source: AssetSourceId<'static>,
863 path: &'a Path,
864 reader: &'a dyn ErasedAssetReader,
865 server: &'a AssetServer,
866 handles: &'a mut Vec<UntypedHandle>,
867 ) -> Result<(), AssetLoadError> {
868 let is_dir = reader.is_directory(path).await?;
869 if is_dir {
870 let mut path_stream = reader.read_directory(path.as_ref()).await?;
871 while let Some(child_path) = path_stream.next().await {
872 if reader.is_directory(&child_path).await? {
873 Box::pin(load_folder(
874 source.clone(),
875 &child_path,
876 reader,
877 server,
878 handles,
879 ))
880 .await?;
881 } else {
882 let path = child_path.to_str().expect("Path should be a valid string.");
883 let asset_path = AssetPath::parse(path).with_source(source.clone());
884 match server.load_untyped_async(asset_path).await {
885 Ok(handle) => handles.push(handle),
886 Err(
888 AssetLoadError::MissingAssetLoaderForTypeName(_)
889 | AssetLoadError::MissingAssetLoaderForExtension(_),
890 ) => {}
891 Err(err) => return Err(err),
892 }
893 }
894 }
895 }
896 Ok(())
897 }
898
899 let path = path.into_owned();
900 let server = self.clone();
901 IoTaskPool::get()
902 .spawn(async move {
903 let Ok(source) = server.get_source(path.source()) else {
904 error!(
905 "Failed to load {path}. AssetSource {:?} does not exist",
906 path.source()
907 );
908 return;
909 };
910
911 let asset_reader = match server.data.mode {
912 AssetServerMode::Unprocessed { .. } => source.reader(),
913 AssetServerMode::Processed { .. } => match source.processed_reader() {
914 Ok(reader) => reader,
915 Err(_) => {
916 error!(
917 "Failed to load {path}. AssetSource {:?} does not have a processed AssetReader",
918 path.source()
919 );
920 return;
921 }
922 },
923 };
924
925 let mut handles = Vec::new();
926 match load_folder(source.id(), path.path(), asset_reader, &server, &mut handles).await {
927 Ok(_) => server.send_asset_event(InternalAssetEvent::Loaded {
928 id,
929 loaded_asset: LoadedAsset::new_with_dependencies(
930 LoadedFolder { handles },
931 None,
932 )
933 .into(),
934 }),
935 Err(err) => {
936 error!("Failed to load folder. {err}");
937 server.send_asset_event(InternalAssetEvent::Failed { id, error: err, path });
938 },
939 }
940 })
941 .detach();
942 }
943
944 fn send_asset_event(&self, event: InternalAssetEvent) {
945 self.data.asset_event_sender.send(event).unwrap();
946 }
947
948 pub fn get_load_states(
950 &self,
951 id: impl Into<UntypedAssetId>,
952 ) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)> {
953 self.data.infos.read().get(id.into()).map(|i| {
954 (
955 i.load_state.clone(),
956 i.dep_load_state.clone(),
957 i.rec_dep_load_state.clone(),
958 )
959 })
960 }
961
962 pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState> {
968 self.data
969 .infos
970 .read()
971 .get(id.into())
972 .map(|i| i.load_state.clone())
973 }
974
975 pub fn get_dependency_load_state(
981 &self,
982 id: impl Into<UntypedAssetId>,
983 ) -> Option<DependencyLoadState> {
984 self.data
985 .infos
986 .read()
987 .get(id.into())
988 .map(|i| i.dep_load_state.clone())
989 }
990
991 pub fn get_recursive_dependency_load_state(
997 &self,
998 id: impl Into<UntypedAssetId>,
999 ) -> Option<RecursiveDependencyLoadState> {
1000 self.data
1001 .infos
1002 .read()
1003 .get(id.into())
1004 .map(|i| i.rec_dep_load_state.clone())
1005 }
1006
1007 pub fn load_state(&self, id: impl Into<UntypedAssetId>) -> LoadState {
1012 self.get_load_state(id).unwrap_or(LoadState::NotLoaded)
1013 }
1014
1015 pub fn dependency_load_state(&self, id: impl Into<UntypedAssetId>) -> DependencyLoadState {
1020 self.get_dependency_load_state(id)
1021 .unwrap_or(DependencyLoadState::NotLoaded)
1022 }
1023
1024 pub fn recursive_dependency_load_state(
1029 &self,
1030 id: impl Into<UntypedAssetId>,
1031 ) -> RecursiveDependencyLoadState {
1032 self.get_recursive_dependency_load_state(id)
1033 .unwrap_or(RecursiveDependencyLoadState::NotLoaded)
1034 }
1035
1036 pub fn is_loaded(&self, id: impl Into<UntypedAssetId>) -> bool {
1038 matches!(self.load_state(id), LoadState::Loaded)
1039 }
1040
1041 pub fn is_loaded_with_direct_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool {
1043 matches!(
1044 self.get_load_states(id),
1045 Some((LoadState::Loaded, DependencyLoadState::Loaded, _))
1046 )
1047 }
1048
1049 pub fn is_loaded_with_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool {
1052 matches!(
1053 self.get_load_states(id),
1054 Some((
1055 LoadState::Loaded,
1056 DependencyLoadState::Loaded,
1057 RecursiveDependencyLoadState::Loaded
1058 ))
1059 )
1060 }
1061
1062 pub fn get_handle<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Option<Handle<A>> {
1065 self.get_path_and_type_id_handle(&path.into(), TypeId::of::<A>())
1066 .map(UntypedHandle::typed_debug_checked)
1067 }
1068
1069 pub fn get_id_handle<A: Asset>(&self, id: AssetId<A>) -> Option<Handle<A>> {
1077 self.get_id_handle_untyped(id.untyped())
1078 .map(UntypedHandle::typed)
1079 }
1080
1081 pub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option<UntypedHandle> {
1084 self.data.infos.read().get_id_handle(id)
1085 }
1086
1087 pub fn is_managed(&self, id: impl Into<UntypedAssetId>) -> bool {
1090 self.data.infos.read().contains_key(id.into())
1091 }
1092
1093 pub fn get_path_id<'a>(&self, path: impl Into<AssetPath<'a>>) -> Option<UntypedAssetId> {
1100 let infos = self.data.infos.read();
1101 let path = path.into();
1102 let mut ids = infos.get_path_ids(&path);
1103 ids.next()
1104 }
1105
1106 pub fn get_path_ids<'a>(&self, path: impl Into<AssetPath<'a>>) -> Vec<UntypedAssetId> {
1110 let infos = self.data.infos.read();
1111 let path = path.into();
1112 infos.get_path_ids(&path).collect()
1113 }
1114
1115 pub fn get_handle_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Option<UntypedHandle> {
1122 let infos = self.data.infos.read();
1123 let path = path.into();
1124 let mut handles = infos.get_path_handles(&path);
1125 handles.next()
1126 }
1127
1128 pub fn get_handles_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Vec<UntypedHandle> {
1132 let infos = self.data.infos.read();
1133 let path = path.into();
1134 infos.get_path_handles(&path).collect()
1135 }
1136
1137 pub fn get_path_and_type_id_handle(
1140 &self,
1141 path: &AssetPath,
1142 type_id: TypeId,
1143 ) -> Option<UntypedHandle> {
1144 let infos = self.data.infos.read();
1145 let path = path.into();
1146 infos.get_path_and_type_id_handle(&path, type_id)
1147 }
1148
1149 pub fn get_path(&self, id: impl Into<UntypedAssetId>) -> Option<AssetPath> {
1151 let infos = self.data.infos.read();
1152 let info = infos.get(id.into())?;
1153 Some(info.path.as_ref()?.clone())
1154 }
1155
1156 pub fn mode(&self) -> AssetServerMode {
1158 self.data.mode
1159 }
1160
1161 pub fn preregister_loader<L: AssetLoader>(&self, extensions: &[&str]) {
1166 self.data.loaders.write().reserve::<L>(extensions);
1167 }
1168
1169 pub(crate) fn get_or_create_path_handle<'a, A: Asset>(
1171 &self,
1172 path: impl Into<AssetPath<'a>>,
1173 meta_transform: Option<MetaTransform>,
1174 ) -> Handle<A> {
1175 let mut infos = self.data.infos.write();
1176 infos
1177 .get_or_create_path_handle::<A>(
1178 path.into().into_owned(),
1179 HandleLoadingMode::NotLoading,
1180 meta_transform,
1181 )
1182 .0
1183 }
1184
1185 pub(crate) fn get_or_create_path_handle_erased<'a>(
1190 &self,
1191 path: impl Into<AssetPath<'a>>,
1192 type_id: TypeId,
1193 meta_transform: Option<MetaTransform>,
1194 ) -> UntypedHandle {
1195 let mut infos = self.data.infos.write();
1196 infos
1197 .get_or_create_path_handle_erased(
1198 path.into().into_owned(),
1199 type_id,
1200 None,
1201 HandleLoadingMode::NotLoading,
1202 meta_transform,
1203 )
1204 .0
1205 }
1206
1207 pub(crate) async fn get_meta_loader_and_reader<'a>(
1208 &'a self,
1209 asset_path: &'a AssetPath<'_>,
1210 asset_type_id: Option<TypeId>,
1211 ) -> Result<
1212 (
1213 Box<dyn AssetMetaDyn>,
1214 Arc<dyn ErasedAssetLoader>,
1215 Box<dyn Reader + 'a>,
1216 ),
1217 AssetLoadError,
1218 > {
1219 let source = self.get_source(asset_path.source())?;
1220 let asset_reader = match self.data.mode {
1225 AssetServerMode::Unprocessed { .. } => source.reader(),
1226 AssetServerMode::Processed { .. } => source.processed_reader()?,
1227 };
1228 let reader = asset_reader.read(asset_path.path()).await?;
1229 let read_meta = match &self.data.meta_check {
1230 AssetMetaCheck::Always => true,
1231 AssetMetaCheck::Paths(paths) => paths.contains(asset_path),
1232 AssetMetaCheck::Never => false,
1233 };
1234
1235 if read_meta {
1236 match asset_reader.read_meta_bytes(asset_path.path()).await {
1237 Ok(meta_bytes) => {
1238 let minimal: AssetMetaMinimal =
1240 ron::de::from_bytes(&meta_bytes).map_err(|e| {
1241 AssetLoadError::DeserializeMeta {
1242 path: asset_path.clone_owned(),
1243 error: DeserializeMetaError::DeserializeMinimal(e).into(),
1244 }
1245 })?;
1246 let loader_name = match minimal.asset {
1247 AssetActionMinimal::Load { loader } => loader,
1248 AssetActionMinimal::Process { .. } => {
1249 return Err(AssetLoadError::CannotLoadProcessedAsset {
1250 path: asset_path.clone_owned(),
1251 })
1252 }
1253 AssetActionMinimal::Ignore => {
1254 return Err(AssetLoadError::CannotLoadIgnoredAsset {
1255 path: asset_path.clone_owned(),
1256 })
1257 }
1258 };
1259 let loader = self.get_asset_loader_with_type_name(&loader_name).await?;
1260 let meta = loader.deserialize_meta(&meta_bytes).map_err(|e| {
1261 AssetLoadError::DeserializeMeta {
1262 path: asset_path.clone_owned(),
1263 error: e.into(),
1264 }
1265 })?;
1266
1267 Ok((meta, loader, reader))
1268 }
1269 Err(AssetReaderError::NotFound(_)) => {
1270 let loader = {
1272 self.data
1273 .loaders
1274 .read()
1275 .find(None, asset_type_id, None, Some(asset_path))
1276 };
1277
1278 let error = || AssetLoadError::MissingAssetLoader {
1279 loader_name: None,
1280 asset_type_id,
1281 extension: None,
1282 asset_path: Some(asset_path.to_string()),
1283 };
1284
1285 let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?;
1286
1287 let meta = loader.default_meta();
1288 Ok((meta, loader, reader))
1289 }
1290 Err(err) => Err(err.into()),
1291 }
1292 } else {
1293 let loader = {
1294 self.data
1295 .loaders
1296 .read()
1297 .find(None, asset_type_id, None, Some(asset_path))
1298 };
1299
1300 let error = || AssetLoadError::MissingAssetLoader {
1301 loader_name: None,
1302 asset_type_id,
1303 extension: None,
1304 asset_path: Some(asset_path.to_string()),
1305 };
1306
1307 let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?;
1308
1309 let meta = loader.default_meta();
1310 Ok((meta, loader, reader))
1311 }
1312 }
1313
1314 pub(crate) async fn load_with_meta_loader_and_reader(
1315 &self,
1316 asset_path: &AssetPath<'_>,
1317 meta: Box<dyn AssetMetaDyn>,
1318 loader: &dyn ErasedAssetLoader,
1319 reader: &mut dyn Reader,
1320 load_dependencies: bool,
1321 populate_hashes: bool,
1322 ) -> Result<ErasedLoadedAsset, AssetLoadError> {
1323 let asset_path = asset_path.clone_owned();
1325 let load_context =
1326 LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes);
1327 AssertUnwindSafe(loader.load(reader, meta, load_context))
1328 .catch_unwind()
1329 .await
1330 .map_err(|_| AssetLoadError::AssetLoaderPanic {
1331 path: asset_path.clone_owned(),
1332 loader_name: loader.type_name(),
1333 })?
1334 .map_err(|e| {
1335 AssetLoadError::AssetLoaderError(AssetLoaderError {
1336 path: asset_path.clone_owned(),
1337 loader_name: loader.type_name(),
1338 error: e.into(),
1339 })
1340 })
1341 }
1342
1343 pub async fn wait_for_asset<A: Asset>(
1351 &self,
1352 handle: &Handle<A>,
1355 ) -> Result<(), WaitForAssetError> {
1356 self.wait_for_asset_id(handle.id().untyped()).await
1357 }
1358
1359 pub async fn wait_for_asset_untyped(
1367 &self,
1368 handle: &UntypedHandle,
1371 ) -> Result<(), WaitForAssetError> {
1372 self.wait_for_asset_id(handle.id()).await
1373 }
1374
1375 pub async fn wait_for_asset_id(
1395 &self,
1396 id: impl Into<UntypedAssetId>,
1397 ) -> Result<(), WaitForAssetError> {
1398 let id = id.into();
1399 core::future::poll_fn(move |cx| self.wait_for_asset_id_poll_fn(cx, id)).await
1400 }
1401
1402 fn wait_for_asset_id_poll_fn(
1404 &self,
1405 cx: &mut core::task::Context<'_>,
1406 id: UntypedAssetId,
1407 ) -> Poll<Result<(), WaitForAssetError>> {
1408 let infos = self.data.infos.read();
1409
1410 let Some(info) = infos.get(id) else {
1411 return Poll::Ready(Err(WaitForAssetError::NotLoaded));
1412 };
1413
1414 match (&info.load_state, &info.rec_dep_load_state) {
1415 (LoadState::Loaded, RecursiveDependencyLoadState::Loaded) => Poll::Ready(Ok(())),
1416 (LoadState::NotLoaded, _) => Poll::Ready(Err(WaitForAssetError::NotLoaded)),
1418 (LoadState::Loading, _)
1420 | (_, RecursiveDependencyLoadState::Loading)
1421 | (LoadState::Loaded, RecursiveDependencyLoadState::NotLoaded) => {
1422 let has_waker = info
1424 .waiting_tasks
1425 .iter()
1426 .any(|waker| waker.will_wake(cx.waker()));
1427
1428 if has_waker {
1429 return Poll::Pending;
1430 }
1431
1432 let mut infos = {
1433 drop(infos);
1435 self.data.infos.write()
1436 };
1437
1438 let Some(info) = infos.get_mut(id) else {
1439 return Poll::Ready(Err(WaitForAssetError::NotLoaded));
1440 };
1441
1442 let is_loading = matches!(
1445 (&info.load_state, &info.rec_dep_load_state),
1446 (LoadState::Loading, _)
1447 | (_, RecursiveDependencyLoadState::Loading)
1448 | (LoadState::Loaded, RecursiveDependencyLoadState::NotLoaded)
1449 );
1450
1451 if !is_loading {
1452 cx.waker().wake_by_ref();
1453 } else {
1454 info.waiting_tasks.push(cx.waker().clone());
1456 }
1457
1458 Poll::Pending
1459 }
1460 (LoadState::Failed(error), _) => {
1461 Poll::Ready(Err(WaitForAssetError::Failed(error.clone())))
1462 }
1463 (_, RecursiveDependencyLoadState::Failed(error)) => {
1464 Poll::Ready(Err(WaitForAssetError::DependencyFailed(error.clone())))
1465 }
1466 }
1467 }
1468}
1469
1470pub fn handle_internal_asset_events(world: &mut World) {
1472 world.resource_scope(|world, server: Mut<AssetServer>| {
1473 let mut infos = server.data.infos.write();
1474 let mut untyped_failures = vec![];
1475 for event in server.data.asset_event_receiver.try_iter() {
1476 match event {
1477 InternalAssetEvent::Loaded { id, loaded_asset } => {
1478 infos.process_asset_load(
1479 id,
1480 loaded_asset,
1481 world,
1482 &server.data.asset_event_sender,
1483 );
1484 }
1485 InternalAssetEvent::LoadedWithDependencies { id } => {
1486 let sender = infos
1487 .dependency_loaded_event_sender
1488 .get(&id.type_id())
1489 .expect("Asset event sender should exist");
1490 sender(world, id);
1491 if let Some(info) = infos.get_mut(id) {
1492 for waker in info.waiting_tasks.drain(..) {
1493 waker.wake();
1494 }
1495 }
1496 }
1497 InternalAssetEvent::Failed { id, path, error } => {
1498 infos.process_asset_fail(id, error.clone());
1499
1500 untyped_failures.push(UntypedAssetLoadFailedEvent {
1502 id,
1503 path: path.clone(),
1504 error: error.clone(),
1505 });
1506
1507 let sender = infos
1509 .dependency_failed_event_sender
1510 .get(&id.type_id())
1511 .expect("Asset failed event sender should exist");
1512 sender(world, id, path, error);
1513 }
1514 }
1515 }
1516
1517 if !untyped_failures.is_empty() {
1518 world.send_event_batch(untyped_failures);
1519 }
1520
1521 fn queue_ancestors(
1522 asset_path: &AssetPath,
1523 infos: &AssetInfos,
1524 paths_to_reload: &mut HashSet<AssetPath<'static>>,
1525 ) {
1526 if let Some(dependents) = infos.loader_dependents.get(asset_path) {
1527 for dependent in dependents {
1528 paths_to_reload.insert(dependent.to_owned());
1529 queue_ancestors(dependent, infos, paths_to_reload);
1530 }
1531 }
1532 }
1533
1534 let reload_parent_folders = |path: PathBuf, source: &AssetSourceId<'static>| {
1535 let mut current_folder = path;
1536 while let Some(parent) = current_folder.parent() {
1537 current_folder = parent.to_path_buf();
1538 let parent_asset_path =
1539 AssetPath::from(current_folder.clone()).with_source(source.clone());
1540 for folder_handle in infos.get_path_handles(&parent_asset_path) {
1541 info!("Reloading folder {parent_asset_path} because the content has changed");
1542 server.load_folder_internal(folder_handle.id(), parent_asset_path.clone());
1543 }
1544 }
1545 };
1546
1547 let mut paths_to_reload = HashSet::new();
1548 let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
1549 match event {
1550 AssetSourceEvent::ModifiedAsset(path) | AssetSourceEvent::ModifiedMeta(path) => {
1553 let path = AssetPath::from(path).with_source(source);
1554 queue_ancestors(&path, &infos, &mut paths_to_reload);
1555 paths_to_reload.insert(path);
1556 }
1557 AssetSourceEvent::RenamedFolder { old, new } => {
1558 reload_parent_folders(old, &source);
1559 reload_parent_folders(new, &source);
1560 }
1561 AssetSourceEvent::AddedAsset(path)
1562 | AssetSourceEvent::RemovedAsset(path)
1563 | AssetSourceEvent::RemovedFolder(path)
1564 | AssetSourceEvent::AddedFolder(path) => {
1565 reload_parent_folders(path, &source);
1566 }
1567 _ => {}
1568 }
1569 };
1570
1571 for source in server.data.sources.iter() {
1572 match server.data.mode {
1573 AssetServerMode::Unprocessed { .. } => {
1574 if let Some(receiver) = source.event_receiver() {
1575 for event in receiver.try_iter() {
1576 handle_event(source.id(), event);
1577 }
1578 }
1579 }
1580 AssetServerMode::Processed { .. } => {
1581 if let Some(receiver) = source.processed_event_receiver() {
1582 for event in receiver.try_iter() {
1583 handle_event(source.id(), event);
1584 }
1585 }
1586 }
1587 }
1588 }
1589
1590 for path in paths_to_reload {
1591 info!("Reloading {path} because it has changed");
1592 server.reload(path);
1593 }
1594
1595 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
1596 infos
1597 .pending_tasks
1598 .retain(|_, load_task| !load_task.is_finished());
1599 });
1600}
1601
1602pub(crate) enum InternalAssetEvent {
1604 Loaded {
1605 id: UntypedAssetId,
1606 loaded_asset: ErasedLoadedAsset,
1607 },
1608 LoadedWithDependencies {
1609 id: UntypedAssetId,
1610 },
1611 Failed {
1612 id: UntypedAssetId,
1613 path: AssetPath<'static>,
1614 error: AssetLoadError,
1615 },
1616}
1617
1618#[derive(Component, Clone, Debug)]
1620pub enum LoadState {
1621 NotLoaded,
1623
1624 Loading,
1626
1627 Loaded,
1629
1630 Failed(Arc<AssetLoadError>),
1634}
1635
1636impl LoadState {
1637 pub fn is_loading(&self) -> bool {
1639 matches!(self, Self::Loading)
1640 }
1641
1642 pub fn is_loaded(&self) -> bool {
1644 matches!(self, Self::Loaded)
1645 }
1646
1647 pub fn is_failed(&self) -> bool {
1649 matches!(self, Self::Failed(_))
1650 }
1651}
1652
1653#[derive(Component, Clone, Debug)]
1655pub enum DependencyLoadState {
1656 NotLoaded,
1658
1659 Loading,
1661
1662 Loaded,
1664
1665 Failed(Arc<AssetLoadError>),
1669}
1670
1671impl DependencyLoadState {
1672 pub fn is_loading(&self) -> bool {
1674 matches!(self, Self::Loading)
1675 }
1676
1677 pub fn is_loaded(&self) -> bool {
1679 matches!(self, Self::Loaded)
1680 }
1681
1682 pub fn is_failed(&self) -> bool {
1684 matches!(self, Self::Failed(_))
1685 }
1686}
1687
1688#[derive(Component, Clone, Debug)]
1690pub enum RecursiveDependencyLoadState {
1691 NotLoaded,
1693
1694 Loading,
1696
1697 Loaded,
1699
1700 Failed(Arc<AssetLoadError>),
1705}
1706
1707impl RecursiveDependencyLoadState {
1708 pub fn is_loading(&self) -> bool {
1710 matches!(self, Self::Loading)
1711 }
1712
1713 pub fn is_loaded(&self) -> bool {
1715 matches!(self, Self::Loaded)
1716 }
1717
1718 pub fn is_failed(&self) -> bool {
1720 matches!(self, Self::Failed(_))
1721 }
1722}
1723
1724#[derive(Error, Display, Debug, Clone, From)]
1726pub enum AssetLoadError {
1727 #[display("Requested handle of type {requested:?} for asset '{path}' does not match actual asset type '{actual_asset_name}', which used loader '{loader_name}'")]
1728 RequestedHandleTypeMismatch {
1729 path: AssetPath<'static>,
1730 requested: TypeId,
1731 actual_asset_name: &'static str,
1732 loader_name: &'static str,
1733 },
1734 #[display("Could not find an asset loader matching: Loader Name: {loader_name:?}; Asset Type: {loader_name:?}; Extension: {extension:?}; Path: {asset_path:?};")]
1735 MissingAssetLoader {
1736 loader_name: Option<String>,
1737 asset_type_id: Option<TypeId>,
1738 extension: Option<String>,
1739 asset_path: Option<String>,
1740 },
1741 MissingAssetLoaderForExtension(MissingAssetLoaderForExtensionError),
1742 MissingAssetLoaderForTypeName(MissingAssetLoaderForTypeNameError),
1743 MissingAssetLoaderForTypeIdError(MissingAssetLoaderForTypeIdError),
1744 AssetReaderError(AssetReaderError),
1745 MissingAssetSourceError(MissingAssetSourceError),
1746 MissingProcessedAssetReaderError(MissingProcessedAssetReaderError),
1747 #[display("Encountered an error while reading asset metadata bytes")]
1748 AssetMetaReadError,
1749 #[display("Failed to deserialize meta for asset {path}: {error}")]
1750 DeserializeMeta {
1751 path: AssetPath<'static>,
1752 error: Box<DeserializeMetaError>,
1753 },
1754 #[display("Asset '{path}' is configured to be processed. It cannot be loaded directly.")]
1755 #[from(ignore)]
1756 CannotLoadProcessedAsset {
1757 path: AssetPath<'static>,
1758 },
1759 #[display("Asset '{path}' is configured to be ignored. It cannot be loaded.")]
1760 #[from(ignore)]
1761 CannotLoadIgnoredAsset {
1762 path: AssetPath<'static>,
1763 },
1764 #[display("Failed to load asset '{path}', asset loader '{loader_name}' panicked")]
1765 AssetLoaderPanic {
1766 path: AssetPath<'static>,
1767 loader_name: &'static str,
1768 },
1769 AssetLoaderError(AssetLoaderError),
1770 AddAsyncError(AddAsyncError),
1771 #[display("The file at '{}' does not contain the labeled asset '{}'; it contains the following {} assets: {}",
1772 base_path,
1773 label,
1774 all_labels.len(),
1775 all_labels.iter().map(|l| format!("'{}'", l)).collect::<Vec<_>>().join(", "))]
1776 MissingLabel {
1777 base_path: AssetPath<'static>,
1778 label: String,
1779 all_labels: Vec<String>,
1780 },
1781}
1782
1783#[derive(Error, Display, Debug, Clone)]
1784#[display("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")]
1785pub struct AssetLoaderError {
1786 path: AssetPath<'static>,
1787 loader_name: &'static str,
1788 error: Arc<dyn core::error::Error + Send + Sync + 'static>,
1789}
1790
1791impl AssetLoaderError {
1792 pub fn path(&self) -> &AssetPath<'static> {
1793 &self.path
1794 }
1795}
1796
1797#[derive(Error, Display, Debug, Clone)]
1798#[display("An error occurred while resolving an asset added by `add_async`: {error}")]
1799pub struct AddAsyncError {
1800 error: Arc<dyn core::error::Error + Send + Sync + 'static>,
1801}
1802
1803#[derive(Error, Display, Debug, Clone, PartialEq, Eq)]
1805#[display("no `AssetLoader` found{}", format_missing_asset_ext(extensions))]
1806pub struct MissingAssetLoaderForExtensionError {
1807 extensions: Vec<String>,
1808}
1809
1810#[derive(Error, Display, Debug, Clone, PartialEq, Eq)]
1812#[display("no `AssetLoader` found with the name '{type_name}'")]
1813pub struct MissingAssetLoaderForTypeNameError {
1814 type_name: String,
1815}
1816
1817#[derive(Error, Display, Debug, Clone, PartialEq, Eq)]
1819#[display("no `AssetLoader` found with the ID '{type_id:?}'")]
1820pub struct MissingAssetLoaderForTypeIdError {
1821 pub type_id: TypeId,
1822}
1823
1824fn format_missing_asset_ext(exts: &[String]) -> String {
1825 if !exts.is_empty() {
1826 format!(
1827 " for the following extension{}: {}",
1828 if exts.len() > 1 { "s" } else { "" },
1829 exts.join(", ")
1830 )
1831 } else {
1832 " for file with no extension".to_string()
1833 }
1834}
1835
1836impl core::fmt::Debug for AssetServer {
1837 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1838 f.debug_struct("AssetServer")
1839 .field("info", &self.data.infos.read())
1840 .finish()
1841 }
1842}
1843
1844const UNTYPED_SOURCE_SUFFIX: &str = "--untyped";
1847
1848#[derive(Error, Debug, Clone, Display)]
1850pub enum WaitForAssetError {
1851 #[display("tried to wait for an asset that is not being loaded")]
1852 NotLoaded,
1853 Failed(Arc<AssetLoadError>),
1854 DependencyFailed(Arc<AssetLoadError>),
1855}