1mod info;
2mod loaders;
3
4use crate::{
5 folder::LoadedFolder,
6 io::{
7 AssetReaderError, AssetSource, AssetSourceEvent, AssetSourceId, AssetSources,
8 AssetWriterError, ErasedAssetReader, MissingAssetSourceError, MissingAssetWriterError,
9 MissingProcessedAssetReaderError, Reader,
10 },
11 loader::{AssetLoader, ErasedAssetLoader, LoadContext, LoadedAsset},
12 meta::{
13 loader_settings_meta_transform, AssetActionMinimal, AssetMetaDyn, AssetMetaMinimal,
14 MetaTransform, Settings,
15 },
16 path::AssetPath,
17 Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets,
18 DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UnapprovedPathMode,
19 UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle,
20};
21use alloc::{borrow::ToOwned, boxed::Box, vec, vec::Vec};
22use alloc::{
23 format,
24 string::{String, ToString},
25 sync::Arc,
26};
27use atomicow::CowArc;
28use bevy_ecs::prelude::*;
29use bevy_platform::collections::HashSet;
30use bevy_tasks::IoTaskPool;
31use core::{any::TypeId, future::Future, panic::AssertUnwindSafe, task::Poll};
32use crossbeam_channel::{Receiver, Sender};
33use either::Either;
34use futures_lite::{FutureExt, StreamExt};
35use info::*;
36use loaders::*;
37use parking_lot::{RwLock, RwLockWriteGuard};
38use std::path::{Path, PathBuf};
39use thiserror::Error;
40use tracing::{error, info};
41
42#[derive(Resource, Clone)]
58pub struct AssetServer {
59 pub(crate) data: Arc<AssetServerData>,
60}
61
62pub(crate) struct AssetServerData {
64 pub(crate) infos: RwLock<AssetInfos>,
65 pub(crate) loaders: Arc<RwLock<AssetLoaders>>,
66 asset_event_sender: Sender<InternalAssetEvent>,
67 asset_event_receiver: Receiver<InternalAssetEvent>,
68 sources: AssetSources,
69 mode: AssetServerMode,
70 meta_check: AssetMetaCheck,
71 unapproved_path_mode: UnapprovedPathMode,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76pub enum AssetServerMode {
77 Unprocessed,
79 Processed,
81}
82
83impl AssetServer {
84 pub fn new(
87 sources: AssetSources,
88 mode: AssetServerMode,
89 watching_for_changes: bool,
90 unapproved_path_mode: UnapprovedPathMode,
91 ) -> Self {
92 Self::new_with_loaders(
93 sources,
94 Default::default(),
95 mode,
96 AssetMetaCheck::Always,
97 watching_for_changes,
98 unapproved_path_mode,
99 )
100 }
101
102 pub fn new_with_meta_check(
105 sources: AssetSources,
106 mode: AssetServerMode,
107 meta_check: AssetMetaCheck,
108 watching_for_changes: bool,
109 unapproved_path_mode: UnapprovedPathMode,
110 ) -> Self {
111 Self::new_with_loaders(
112 sources,
113 Default::default(),
114 mode,
115 meta_check,
116 watching_for_changes,
117 unapproved_path_mode,
118 )
119 }
120
121 pub(crate) fn new_with_loaders(
122 sources: AssetSources,
123 loaders: Arc<RwLock<AssetLoaders>>,
124 mode: AssetServerMode,
125 meta_check: AssetMetaCheck,
126 watching_for_changes: bool,
127 unapproved_path_mode: UnapprovedPathMode,
128 ) -> Self {
129 let (asset_event_sender, asset_event_receiver) = crossbeam_channel::unbounded();
130 let mut infos = AssetInfos::default();
131 infos.watching_for_changes = watching_for_changes;
132 Self {
133 data: Arc::new(AssetServerData {
134 sources,
135 mode,
136 meta_check,
137 asset_event_sender,
138 asset_event_receiver,
139 loaders,
140 infos: RwLock::new(infos),
141 unapproved_path_mode,
142 }),
143 }
144 }
145
146 pub fn get_source<'a>(
148 &self,
149 source: impl Into<AssetSourceId<'a>>,
150 ) -> Result<&AssetSource, MissingAssetSourceError> {
151 self.data.sources.get(source.into())
152 }
153
154 pub fn watching_for_changes(&self) -> bool {
156 self.data.infos.read().watching_for_changes
157 }
158
159 pub fn register_loader<L: AssetLoader>(&self, loader: L) {
161 self.data.loaders.write().push(loader);
162 }
163
164 pub fn register_asset<A: Asset>(&self, assets: &Assets<A>) {
166 self.register_handle_provider(assets.get_handle_provider());
167 fn sender<A: Asset>(world: &mut World, id: UntypedAssetId) {
168 world
169 .resource_mut::<Messages<AssetEvent<A>>>()
170 .write(AssetEvent::LoadedWithDependencies { id: id.typed() });
171 }
172 fn failed_sender<A: Asset>(
173 world: &mut World,
174 id: UntypedAssetId,
175 path: AssetPath<'static>,
176 error: AssetLoadError,
177 ) {
178 world
179 .resource_mut::<Messages<AssetLoadFailedEvent<A>>>()
180 .write(AssetLoadFailedEvent {
181 id: id.typed(),
182 path,
183 error,
184 });
185 }
186
187 let mut infos = self.data.infos.write();
188
189 infos
190 .dependency_loaded_event_sender
191 .insert(TypeId::of::<A>(), sender::<A>);
192
193 infos
194 .dependency_failed_event_sender
195 .insert(TypeId::of::<A>(), failed_sender::<A>);
196 }
197
198 pub(crate) fn register_handle_provider(&self, handle_provider: AssetHandleProvider) {
199 let mut infos = self.data.infos.write();
200 infos
201 .handle_providers
202 .insert(handle_provider.type_id, handle_provider);
203 }
204
205 pub async fn get_asset_loader_with_extension(
207 &self,
208 extension: &str,
209 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError> {
210 let error = || MissingAssetLoaderForExtensionError {
211 extensions: vec![extension.to_string()],
212 };
213
214 let loader = { self.data.loaders.read().get_by_extension(extension) };
215
216 loader.ok_or_else(error)?.get().await.map_err(|_| error())
217 }
218
219 pub async fn get_asset_loader_with_type_name(
221 &self,
222 type_name: &str,
223 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeNameError> {
224 let error = || MissingAssetLoaderForTypeNameError {
225 type_name: type_name.to_string(),
226 };
227
228 let loader = { self.data.loaders.read().get_by_name(type_name) };
229
230 loader.ok_or_else(error)?.get().await.map_err(|_| error())
231 }
232
233 pub async fn get_path_asset_loader<'a>(
235 &self,
236 path: impl Into<AssetPath<'a>>,
237 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForExtensionError> {
238 let path = path.into();
239
240 let error = || {
241 let Some(full_extension) = path.get_full_extension() else {
242 return MissingAssetLoaderForExtensionError {
243 extensions: Vec::new(),
244 };
245 };
246
247 let mut extensions = vec![full_extension.clone()];
248 extensions.extend(
249 AssetPath::iter_secondary_extensions(&full_extension).map(ToString::to_string),
250 );
251
252 MissingAssetLoaderForExtensionError { extensions }
253 };
254
255 let loader = { self.data.loaders.read().get_by_path(&path) };
256
257 loader.ok_or_else(error)?.get().await.map_err(|_| error())
258 }
259
260 pub async fn get_asset_loader_with_asset_type_id(
262 &self,
263 type_id: TypeId,
264 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError> {
265 let error = || MissingAssetLoaderForTypeIdError { type_id };
266
267 let loader = { self.data.loaders.read().get_by_type(type_id) };
268
269 loader.ok_or_else(error)?.get().await.map_err(|_| error())
270 }
271
272 pub async fn get_asset_loader_with_asset_type<A: Asset>(
274 &self,
275 ) -> Result<Arc<dyn ErasedAssetLoader>, MissingAssetLoaderForTypeIdError> {
276 self.get_asset_loader_with_asset_type_id(TypeId::of::<A>())
277 .await
278 }
279
280 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
324 pub fn load<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
325 self.load_with_meta_transform(path, None, (), false)
326 }
327
328 pub fn load_override<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Handle<A> {
334 self.load_with_meta_transform(path, None, (), true)
335 }
336
337 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
353 pub fn load_acquire<'a, A: Asset, G: Send + Sync + 'static>(
354 &self,
355 path: impl Into<AssetPath<'a>>,
356 guard: G,
357 ) -> Handle<A> {
358 self.load_with_meta_transform(path, None, guard, false)
359 }
360
361 pub fn load_acquire_override<'a, A: Asset, G: Send + Sync + 'static>(
367 &self,
368 path: impl Into<AssetPath<'a>>,
369 guard: G,
370 ) -> Handle<A> {
371 self.load_with_meta_transform(path, None, guard, true)
372 }
373
374 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
378 pub fn load_with_settings<'a, A: Asset, S: Settings>(
379 &self,
380 path: impl Into<AssetPath<'a>>,
381 settings: impl Fn(&mut S) + Send + Sync + 'static,
382 ) -> Handle<A> {
383 self.load_with_meta_transform(
384 path,
385 Some(loader_settings_meta_transform(settings)),
386 (),
387 false,
388 )
389 }
390
391 pub fn load_with_settings_override<'a, A: Asset, S: Settings>(
397 &self,
398 path: impl Into<AssetPath<'a>>,
399 settings: impl Fn(&mut S) + Send + Sync + 'static,
400 ) -> Handle<A> {
401 self.load_with_meta_transform(
402 path,
403 Some(loader_settings_meta_transform(settings)),
404 (),
405 true,
406 )
407 }
408
409 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
419 pub fn load_acquire_with_settings<'a, A: Asset, S: Settings, G: Send + Sync + 'static>(
420 &self,
421 path: impl Into<AssetPath<'a>>,
422 settings: impl Fn(&mut S) + Send + Sync + 'static,
423 guard: G,
424 ) -> Handle<A> {
425 self.load_with_meta_transform(
426 path,
427 Some(loader_settings_meta_transform(settings)),
428 guard,
429 false,
430 )
431 }
432
433 pub fn load_acquire_with_settings_override<
439 'a,
440 A: Asset,
441 S: Settings,
442 G: Send + Sync + 'static,
443 >(
444 &self,
445 path: impl Into<AssetPath<'a>>,
446 settings: impl Fn(&mut S) + Send + Sync + 'static,
447 guard: G,
448 ) -> Handle<A> {
449 self.load_with_meta_transform(
450 path,
451 Some(loader_settings_meta_transform(settings)),
452 guard,
453 true,
454 )
455 }
456
457 pub(crate) fn load_with_meta_transform<'a, A: Asset, G: Send + Sync + 'static>(
458 &self,
459 path: impl Into<AssetPath<'a>>,
460 meta_transform: Option<MetaTransform>,
461 guard: G,
462 override_unapproved: bool,
463 ) -> Handle<A> {
464 let path = path.into().into_owned();
465
466 if path.is_unapproved() {
467 match (&self.data.unapproved_path_mode, override_unapproved) {
468 (UnapprovedPathMode::Allow, _) | (UnapprovedPathMode::Deny, true) => {}
469 (UnapprovedPathMode::Deny, false) | (UnapprovedPathMode::Forbid, _) => {
470 error!("Asset path {path} is unapproved. See UnapprovedPathMode for details.");
471 return Handle::default();
472 }
473 }
474 }
475
476 let mut infos = self.data.infos.write();
477 let (handle, should_load) = infos.get_or_create_path_handle::<A>(
478 path.clone(),
479 HandleLoadingMode::Request,
480 meta_transform,
481 );
482
483 if should_load {
484 self.spawn_load_task(handle.clone().untyped(), path, infos, guard);
485 }
486
487 handle
488 }
489
490 pub(crate) fn load_erased_with_meta_transform<'a, G: Send + Sync + 'static>(
491 &self,
492 path: impl Into<AssetPath<'a>>,
493 type_id: TypeId,
494 meta_transform: Option<MetaTransform>,
495 guard: G,
496 ) -> UntypedHandle {
497 let path = path.into().into_owned();
498 let mut infos = self.data.infos.write();
499 let (handle, should_load) = infos.get_or_create_path_handle_erased(
500 path.clone(),
501 type_id,
502 None,
503 HandleLoadingMode::Request,
504 meta_transform,
505 );
506
507 if should_load {
508 self.spawn_load_task(handle.clone(), path, infos, guard);
509 }
510
511 handle
512 }
513
514 pub(crate) fn spawn_load_task<G: Send + Sync + 'static>(
515 &self,
516 handle: UntypedHandle,
517 path: AssetPath<'static>,
518 infos: RwLockWriteGuard<AssetInfos>,
519 guard: G,
520 ) {
521 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
523 drop(infos);
524
525 let owned_handle = handle.clone();
526 let server = self.clone();
527 let task = IoTaskPool::get().spawn(async move {
528 if let Err(err) = server
529 .load_internal(Some(owned_handle), path, false, None)
530 .await
531 {
532 error!("{}", err);
533 }
534 drop(guard);
535 });
536
537 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
538 {
539 let mut infos = infos;
540 infos.pending_tasks.insert(handle.id(), task);
541 }
542
543 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
544 task.detach();
545 }
546
547 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
551 pub async fn load_untyped_async<'a>(
552 &self,
553 path: impl Into<AssetPath<'a>>,
554 ) -> Result<UntypedHandle, AssetLoadError> {
555 let path: AssetPath = path.into();
556 self.load_internal(None, path, false, None)
557 .await
558 .map(|h| h.expect("handle must be returned, since we didn't pass in an input handle"))
559 }
560
561 pub(crate) fn load_unknown_type_with_meta_transform<'a>(
562 &self,
563 path: impl Into<AssetPath<'a>>,
564 meta_transform: Option<MetaTransform>,
565 ) -> Handle<LoadedUntypedAsset> {
566 let path = path.into().into_owned();
567 let untyped_source = AssetSourceId::Name(match path.source() {
568 AssetSourceId::Default => CowArc::Static(UNTYPED_SOURCE_SUFFIX),
569 AssetSourceId::Name(source) => {
570 CowArc::Owned(format!("{source}--{UNTYPED_SOURCE_SUFFIX}").into())
571 }
572 });
573 let mut infos = self.data.infos.write();
574 let (handle, should_load) = infos.get_or_create_path_handle::<LoadedUntypedAsset>(
575 path.clone().with_source(untyped_source),
576 HandleLoadingMode::Request,
577 meta_transform,
578 );
579
580 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
582 drop(infos);
583
584 if !should_load {
585 return handle;
586 }
587 let id = handle.id().untyped();
588
589 let server = self.clone();
590 let task = IoTaskPool::get().spawn(async move {
591 let path_clone = path.clone();
592 match server.load_untyped_async(path).await {
593 Ok(handle) => server.send_asset_event(InternalAssetEvent::Loaded {
594 id,
595 loaded_asset: LoadedAsset::new_with_dependencies(LoadedUntypedAsset { handle })
596 .into(),
597 }),
598 Err(err) => {
599 error!("{err}");
600 server.send_asset_event(InternalAssetEvent::Failed {
601 id,
602 path: path_clone,
603 error: err,
604 });
605 }
606 }
607 });
608
609 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
610 infos.pending_tasks.insert(handle.id().untyped(), task);
611
612 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
613 task.detach();
614
615 handle
616 }
617
618 #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
642 pub fn load_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedUntypedAsset> {
643 self.load_unknown_type_with_meta_transform(path, None)
644 }
645
646 async fn load_internal<'a>(
655 &self,
656 input_handle: Option<UntypedHandle>,
657 path: AssetPath<'a>,
658 force: bool,
659 meta_transform: Option<MetaTransform>,
660 ) -> Result<Option<UntypedHandle>, AssetLoadError> {
661 let input_handle_type_id = input_handle.as_ref().map(UntypedHandle::type_id);
662
663 let path = path.into_owned();
664 let path_clone = path.clone();
665 let (mut meta, loader, mut reader) = self
666 .get_meta_loader_and_reader(&path_clone, input_handle_type_id)
667 .await
668 .inspect_err(|e| {
669 if let Some(handle) = &input_handle {
672 self.send_asset_event(InternalAssetEvent::Failed {
673 id: handle.id(),
674 path: path.clone_owned(),
675 error: e.clone(),
676 });
677 }
678 })?;
679
680 if let Some(meta_transform) = input_handle.as_ref().and_then(|h| h.meta_transform()) {
681 (*meta_transform)(&mut *meta);
682 }
683
684 let asset_id; let fetched_handle; let should_load; if let Some(input_handle) = input_handle {
688 asset_id = Some(input_handle.id());
689 fetched_handle = None;
692 should_load = true;
694 } else {
695 let mut infos = self.data.infos.write();
701 let result = infos.get_or_create_path_handle_internal(
702 path.clone(),
703 path.label().is_none().then(|| loader.asset_type_id()),
704 HandleLoadingMode::Request,
705 meta_transform,
706 );
707 match unwrap_with_context(result, Either::Left(loader.asset_type_name())) {
708 None => {
711 asset_id = None;
714 fetched_handle = None;
715 should_load = true;
718 }
719 Some((handle, result_should_load)) => {
720 asset_id = Some(handle.id());
721 fetched_handle = Some(handle);
722 should_load = result_should_load;
723 }
724 }
725 }
726 if let Some(asset_type_id) = asset_id.map(|id| id.type_id()) {
728 if path.label().is_none() && asset_type_id != loader.asset_type_id() {
731 error!(
732 "Expected {:?}, got {:?}",
733 asset_type_id,
734 loader.asset_type_id()
735 );
736 return Err(AssetLoadError::RequestedHandleTypeMismatch {
737 path: path.into_owned(),
738 requested: asset_type_id,
739 actual_asset_name: loader.asset_type_name(),
740 loader_name: loader.type_name(),
741 });
742 }
743 }
744 if !should_load && !force {
746 return Ok(fetched_handle);
747 }
748
749 let (base_asset_id, _base_handle, base_path) = if path.label().is_some() {
753 let mut infos = self.data.infos.write();
754 let base_path = path.without_label().into_owned();
755 let base_handle = infos
756 .get_or_create_path_handle_erased(
757 base_path.clone(),
758 loader.asset_type_id(),
759 Some(loader.asset_type_name()),
760 HandleLoadingMode::Force,
761 None,
762 )
763 .0;
764 (base_handle.id(), Some(base_handle), base_path)
765 } else {
766 (asset_id.unwrap(), None, path.clone())
767 };
768
769 match self
770 .load_with_meta_loader_and_reader(
771 &base_path,
772 meta.as_ref(),
773 &*loader,
774 &mut *reader,
775 true,
776 false,
777 )
778 .await
779 {
780 Ok(loaded_asset) => {
781 let final_handle = if let Some(label) = path.label_cow() {
782 match loaded_asset.labeled_assets.get(&label) {
783 Some(labeled_asset) => Some(labeled_asset.handle.clone()),
784 None => {
785 let mut all_labels: Vec<String> = loaded_asset
786 .labeled_assets
787 .keys()
788 .map(|s| (**s).to_owned())
789 .collect();
790 all_labels.sort_unstable();
791 return Err(AssetLoadError::MissingLabel {
792 base_path,
793 label: label.to_string(),
794 all_labels,
795 });
796 }
797 }
798 } else {
799 fetched_handle
800 };
801
802 self.send_loaded_asset(base_asset_id, loaded_asset);
803 Ok(final_handle)
804 }
805 Err(err) => {
806 self.send_asset_event(InternalAssetEvent::Failed {
807 id: base_asset_id,
808 error: err.clone(),
809 path: path.into_owned(),
810 });
811 Err(err)
812 }
813 }
814 }
815
816 fn send_loaded_asset(&self, id: UntypedAssetId, mut loaded_asset: ErasedLoadedAsset) {
819 for (_, labeled_asset) in loaded_asset.labeled_assets.drain() {
820 self.send_loaded_asset(labeled_asset.handle.id(), labeled_asset.asset);
821 }
822
823 self.send_asset_event(InternalAssetEvent::Loaded { id, loaded_asset });
824 }
825
826 pub fn reload<'a>(&self, path: impl Into<AssetPath<'a>>) {
828 self.reload_internal(path, false);
829 }
830
831 fn reload_internal<'a>(&self, path: impl Into<AssetPath<'a>>, log: bool) {
832 let server = self.clone();
833 let path = path.into().into_owned();
834 IoTaskPool::get()
835 .spawn(async move {
836 let mut reloaded = false;
837
838 let requests = server
839 .data
840 .infos
841 .read()
842 .get_path_handles(&path)
843 .map(|handle| server.load_internal(Some(handle), path.clone(), true, None))
844 .collect::<Vec<_>>();
845
846 for result in requests {
847 match result.await {
848 Ok(_) => reloaded = true,
849 Err(err) => error!("{}", err),
850 }
851 }
852
853 if !reloaded && server.data.infos.read().should_reload(&path) {
854 match server.load_internal(None, path.clone(), true, None).await {
855 Ok(_) => reloaded = true,
856 Err(err) => error!("{}", err),
857 }
858 }
859
860 if log && reloaded {
861 info!("Reloaded {}", path);
862 }
863 })
864 .detach();
865 }
866
867 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
872 pub fn add<A: Asset>(&self, asset: A) -> Handle<A> {
873 self.load_asset(LoadedAsset::new_with_dependencies(asset))
874 }
875
876 pub(crate) fn load_asset<A: Asset>(&self, asset: impl Into<LoadedAsset<A>>) -> Handle<A> {
877 let loaded_asset: LoadedAsset<A> = asset.into();
878 let erased_loaded_asset: ErasedLoadedAsset = loaded_asset.into();
879 self.load_asset_untyped(None, erased_loaded_asset)
880 .typed_debug_checked()
881 }
882
883 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
884 pub(crate) fn load_asset_untyped(
885 &self,
886 path: Option<AssetPath<'static>>,
887 asset: impl Into<ErasedLoadedAsset>,
888 ) -> UntypedHandle {
889 let loaded_asset = asset.into();
890 let handle = if let Some(path) = path {
891 let (handle, _) = self.data.infos.write().get_or_create_path_handle_erased(
892 path,
893 loaded_asset.asset_type_id(),
894 Some(loaded_asset.asset_type_name()),
895 HandleLoadingMode::NotLoading,
896 None,
897 );
898 handle
899 } else {
900 self.data.infos.write().create_loading_handle_untyped(
901 loaded_asset.asset_type_id(),
902 loaded_asset.asset_type_name(),
903 )
904 };
905 self.send_asset_event(InternalAssetEvent::Loaded {
906 id: handle.id(),
907 loaded_asset,
908 });
909 handle
910 }
911
912 #[must_use = "not using the returned strong handle may result in the unexpected release of the asset"]
917 pub fn add_async<A: Asset, E: core::error::Error + Send + Sync + 'static>(
918 &self,
919 future: impl Future<Output = Result<A, E>> + Send + 'static,
920 ) -> Handle<A> {
921 let mut infos = self.data.infos.write();
922 let handle =
923 infos.create_loading_handle_untyped(TypeId::of::<A>(), core::any::type_name::<A>());
924
925 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
927 drop(infos);
928
929 let id = handle.id();
930
931 let event_sender = self.data.asset_event_sender.clone();
932
933 let task = IoTaskPool::get().spawn(async move {
934 match future.await {
935 Ok(asset) => {
936 let loaded_asset = LoadedAsset::new_with_dependencies(asset).into();
937 event_sender
938 .send(InternalAssetEvent::Loaded { id, loaded_asset })
939 .unwrap();
940 }
941 Err(error) => {
942 let error = AddAsyncError {
943 error: Arc::new(error),
944 };
945 error!("{error}");
946 event_sender
947 .send(InternalAssetEvent::Failed {
948 id,
949 path: Default::default(),
950 error: AssetLoadError::AddAsyncError(error),
951 })
952 .unwrap();
953 }
954 }
955 });
956
957 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
958 infos.pending_tasks.insert(id, task);
959
960 #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]
961 task.detach();
962
963 handle.typed_debug_checked()
964 }
965
966 #[must_use = "not using the returned strong handle may result in the unexpected release of the assets"]
975 pub fn load_folder<'a>(&self, path: impl Into<AssetPath<'a>>) -> Handle<LoadedFolder> {
976 let path = path.into().into_owned();
977 let (handle, should_load) = self
978 .data
979 .infos
980 .write()
981 .get_or_create_path_handle::<LoadedFolder>(
982 path.clone(),
983 HandleLoadingMode::Request,
984 None,
985 );
986 if !should_load {
987 return handle;
988 }
989 let id = handle.id().untyped();
990 self.load_folder_internal(id, path);
991
992 handle
993 }
994
995 pub(crate) fn load_folder_internal(&self, id: UntypedAssetId, path: AssetPath) {
996 async fn load_folder<'a>(
997 source: AssetSourceId<'static>,
998 path: &'a Path,
999 reader: &'a dyn ErasedAssetReader,
1000 server: &'a AssetServer,
1001 handles: &'a mut Vec<UntypedHandle>,
1002 ) -> Result<(), AssetLoadError> {
1003 let is_dir = reader.is_directory(path).await?;
1004 if is_dir {
1005 let mut path_stream = reader.read_directory(path.as_ref()).await?;
1006 while let Some(child_path) = path_stream.next().await {
1007 if reader.is_directory(&child_path).await? {
1008 Box::pin(load_folder(
1009 source.clone(),
1010 &child_path,
1011 reader,
1012 server,
1013 handles,
1014 ))
1015 .await?;
1016 } else {
1017 let path = child_path.to_str().expect("Path should be a valid string.");
1018 let asset_path = AssetPath::parse(path).with_source(source.clone());
1019 match server.load_untyped_async(asset_path).await {
1020 Ok(handle) => handles.push(handle),
1021 Err(
1023 AssetLoadError::MissingAssetLoaderForTypeName(_)
1024 | AssetLoadError::MissingAssetLoaderForExtension(_),
1025 ) => {}
1026 Err(err) => return Err(err),
1027 }
1028 }
1029 }
1030 }
1031 Ok(())
1032 }
1033
1034 let path = path.into_owned();
1035 let server = self.clone();
1036 IoTaskPool::get()
1037 .spawn(async move {
1038 let Ok(source) = server.get_source(path.source()) else {
1039 error!(
1040 "Failed to load {path}. AssetSource {} does not exist",
1041 path.source()
1042 );
1043 return;
1044 };
1045
1046 let asset_reader = match server.data.mode {
1047 AssetServerMode::Unprocessed => source.reader(),
1048 AssetServerMode::Processed => match source.processed_reader() {
1049 Ok(reader) => reader,
1050 Err(_) => {
1051 error!(
1052 "Failed to load {path}. AssetSource {} does not have a processed AssetReader",
1053 path.source()
1054 );
1055 return;
1056 }
1057 },
1058 };
1059
1060 let mut handles = Vec::new();
1061 match load_folder(source.id(), path.path(), asset_reader, &server, &mut handles).await {
1062 Ok(_) => server.send_asset_event(InternalAssetEvent::Loaded {
1063 id,
1064 loaded_asset: LoadedAsset::new_with_dependencies(
1065 LoadedFolder { handles },
1066 )
1067 .into(),
1068 }),
1069 Err(err) => {
1070 error!("Failed to load folder. {err}");
1071 server.send_asset_event(InternalAssetEvent::Failed { id, error: err, path });
1072 },
1073 }
1074 })
1075 .detach();
1076 }
1077
1078 fn send_asset_event(&self, event: InternalAssetEvent) {
1079 self.data.asset_event_sender.send(event).unwrap();
1080 }
1081
1082 pub fn get_load_states(
1084 &self,
1085 id: impl Into<UntypedAssetId>,
1086 ) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)> {
1087 self.data.infos.read().get(id.into()).map(|i| {
1088 (
1089 i.load_state.clone(),
1090 i.dep_load_state.clone(),
1091 i.rec_dep_load_state.clone(),
1092 )
1093 })
1094 }
1095
1096 pub fn get_load_state(&self, id: impl Into<UntypedAssetId>) -> Option<LoadState> {
1102 self.data
1103 .infos
1104 .read()
1105 .get(id.into())
1106 .map(|i| i.load_state.clone())
1107 }
1108
1109 pub fn get_dependency_load_state(
1115 &self,
1116 id: impl Into<UntypedAssetId>,
1117 ) -> Option<DependencyLoadState> {
1118 self.data
1119 .infos
1120 .read()
1121 .get(id.into())
1122 .map(|i| i.dep_load_state.clone())
1123 }
1124
1125 pub fn get_recursive_dependency_load_state(
1131 &self,
1132 id: impl Into<UntypedAssetId>,
1133 ) -> Option<RecursiveDependencyLoadState> {
1134 self.data
1135 .infos
1136 .read()
1137 .get(id.into())
1138 .map(|i| i.rec_dep_load_state.clone())
1139 }
1140
1141 pub fn load_state(&self, id: impl Into<UntypedAssetId>) -> LoadState {
1146 self.get_load_state(id).unwrap_or(LoadState::NotLoaded)
1147 }
1148
1149 pub fn dependency_load_state(&self, id: impl Into<UntypedAssetId>) -> DependencyLoadState {
1154 self.get_dependency_load_state(id)
1155 .unwrap_or(DependencyLoadState::NotLoaded)
1156 }
1157
1158 pub fn recursive_dependency_load_state(
1163 &self,
1164 id: impl Into<UntypedAssetId>,
1165 ) -> RecursiveDependencyLoadState {
1166 self.get_recursive_dependency_load_state(id)
1167 .unwrap_or(RecursiveDependencyLoadState::NotLoaded)
1168 }
1169
1170 pub fn is_loaded(&self, id: impl Into<UntypedAssetId>) -> bool {
1172 matches!(self.load_state(id), LoadState::Loaded)
1173 }
1174
1175 pub fn is_loaded_with_direct_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool {
1177 matches!(
1178 self.get_load_states(id),
1179 Some((LoadState::Loaded, DependencyLoadState::Loaded, _))
1180 )
1181 }
1182
1183 pub fn is_loaded_with_dependencies(&self, id: impl Into<UntypedAssetId>) -> bool {
1186 matches!(
1187 self.get_load_states(id),
1188 Some((
1189 LoadState::Loaded,
1190 DependencyLoadState::Loaded,
1191 RecursiveDependencyLoadState::Loaded
1192 ))
1193 )
1194 }
1195
1196 pub fn get_handle<'a, A: Asset>(&self, path: impl Into<AssetPath<'a>>) -> Option<Handle<A>> {
1199 self.get_path_and_type_id_handle(&path.into(), TypeId::of::<A>())
1200 .map(UntypedHandle::typed_debug_checked)
1201 }
1202
1203 pub fn get_id_handle<A: Asset>(&self, id: AssetId<A>) -> Option<Handle<A>> {
1211 self.get_id_handle_untyped(id.untyped())
1212 .map(UntypedHandle::typed)
1213 }
1214
1215 pub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option<UntypedHandle> {
1218 self.data.infos.read().get_id_handle(id)
1219 }
1220
1221 pub fn is_managed(&self, id: impl Into<UntypedAssetId>) -> bool {
1224 self.data.infos.read().contains_key(id.into())
1225 }
1226
1227 pub fn get_path_id<'a>(&self, path: impl Into<AssetPath<'a>>) -> Option<UntypedAssetId> {
1234 let infos = self.data.infos.read();
1235 let path = path.into();
1236 let mut ids = infos.get_path_ids(&path);
1237 ids.next()
1238 }
1239
1240 pub fn get_path_ids<'a>(&self, path: impl Into<AssetPath<'a>>) -> Vec<UntypedAssetId> {
1244 let infos = self.data.infos.read();
1245 let path = path.into();
1246 infos.get_path_ids(&path).collect()
1247 }
1248
1249 pub fn get_handle_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Option<UntypedHandle> {
1256 let infos = self.data.infos.read();
1257 let path = path.into();
1258 let mut handles = infos.get_path_handles(&path);
1259 handles.next()
1260 }
1261
1262 pub fn get_handles_untyped<'a>(&self, path: impl Into<AssetPath<'a>>) -> Vec<UntypedHandle> {
1266 let infos = self.data.infos.read();
1267 let path = path.into();
1268 infos.get_path_handles(&path).collect()
1269 }
1270
1271 pub fn get_path_and_type_id_handle(
1274 &self,
1275 path: &AssetPath,
1276 type_id: TypeId,
1277 ) -> Option<UntypedHandle> {
1278 let infos = self.data.infos.read();
1279 let path = path.into();
1280 infos.get_path_and_type_id_handle(&path, type_id)
1281 }
1282
1283 pub fn get_path(&self, id: impl Into<UntypedAssetId>) -> Option<AssetPath<'_>> {
1285 let infos = self.data.infos.read();
1286 let info = infos.get(id.into())?;
1287 Some(info.path.as_ref()?.clone())
1288 }
1289
1290 pub fn mode(&self) -> AssetServerMode {
1292 self.data.mode
1293 }
1294
1295 pub fn preregister_loader<L: AssetLoader>(&self, extensions: &[&str]) {
1300 self.data.loaders.write().reserve::<L>(extensions);
1301 }
1302
1303 pub(crate) fn get_or_create_path_handle<'a, A: Asset>(
1305 &self,
1306 path: impl Into<AssetPath<'a>>,
1307 meta_transform: Option<MetaTransform>,
1308 ) -> Handle<A> {
1309 let mut infos = self.data.infos.write();
1310 infos
1311 .get_or_create_path_handle::<A>(
1312 path.into().into_owned(),
1313 HandleLoadingMode::NotLoading,
1314 meta_transform,
1315 )
1316 .0
1317 }
1318
1319 pub(crate) fn get_or_create_path_handle_erased<'a>(
1324 &self,
1325 path: impl Into<AssetPath<'a>>,
1326 type_id: TypeId,
1327 meta_transform: Option<MetaTransform>,
1328 ) -> UntypedHandle {
1329 let mut infos = self.data.infos.write();
1330 infos
1331 .get_or_create_path_handle_erased(
1332 path.into().into_owned(),
1333 type_id,
1334 None,
1335 HandleLoadingMode::NotLoading,
1336 meta_transform,
1337 )
1338 .0
1339 }
1340
1341 pub(crate) async fn get_meta_loader_and_reader<'a>(
1342 &'a self,
1343 asset_path: &'a AssetPath<'_>,
1344 asset_type_id: Option<TypeId>,
1345 ) -> Result<
1346 (
1347 Box<dyn AssetMetaDyn>,
1348 Arc<dyn ErasedAssetLoader>,
1349 Box<dyn Reader + 'a>,
1350 ),
1351 AssetLoadError,
1352 > {
1353 let source = self.get_source(asset_path.source())?;
1354 let asset_reader = match self.data.mode {
1359 AssetServerMode::Unprocessed => source.reader(),
1360 AssetServerMode::Processed => source.processed_reader()?,
1361 };
1362 let reader = asset_reader.read(asset_path.path()).await?;
1363 let read_meta = match &self.data.meta_check {
1364 AssetMetaCheck::Always => true,
1365 AssetMetaCheck::Paths(paths) => paths.contains(asset_path),
1366 AssetMetaCheck::Never => false,
1367 };
1368
1369 if read_meta {
1370 match asset_reader.read_meta_bytes(asset_path.path()).await {
1371 Ok(meta_bytes) => {
1372 let minimal: AssetMetaMinimal =
1374 ron::de::from_bytes(&meta_bytes).map_err(|e| {
1375 AssetLoadError::DeserializeMeta {
1376 path: asset_path.clone_owned(),
1377 error: DeserializeMetaError::DeserializeMinimal(e).into(),
1378 }
1379 })?;
1380 let loader_name = match minimal.asset {
1381 AssetActionMinimal::Load { loader } => loader,
1382 AssetActionMinimal::Process { .. } => {
1383 return Err(AssetLoadError::CannotLoadProcessedAsset {
1384 path: asset_path.clone_owned(),
1385 })
1386 }
1387 AssetActionMinimal::Ignore => {
1388 return Err(AssetLoadError::CannotLoadIgnoredAsset {
1389 path: asset_path.clone_owned(),
1390 })
1391 }
1392 };
1393 let loader = self.get_asset_loader_with_type_name(&loader_name).await?;
1394 let meta = loader.deserialize_meta(&meta_bytes).map_err(|e| {
1395 AssetLoadError::DeserializeMeta {
1396 path: asset_path.clone_owned(),
1397 error: e.into(),
1398 }
1399 })?;
1400
1401 Ok((meta, loader, reader))
1402 }
1403 Err(AssetReaderError::NotFound(_)) => {
1404 let loader = {
1406 self.data
1407 .loaders
1408 .read()
1409 .find(None, asset_type_id, None, Some(asset_path))
1410 };
1411
1412 let error = || AssetLoadError::MissingAssetLoader {
1413 loader_name: None,
1414 asset_type_id,
1415 extension: None,
1416 asset_path: Some(asset_path.to_string()),
1417 };
1418
1419 let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?;
1420
1421 let meta = loader.default_meta();
1422 Ok((meta, loader, reader))
1423 }
1424 Err(err) => Err(err.into()),
1425 }
1426 } else {
1427 let loader = {
1428 self.data
1429 .loaders
1430 .read()
1431 .find(None, asset_type_id, None, Some(asset_path))
1432 };
1433
1434 let error = || AssetLoadError::MissingAssetLoader {
1435 loader_name: None,
1436 asset_type_id,
1437 extension: None,
1438 asset_path: Some(asset_path.to_string()),
1439 };
1440
1441 let loader = loader.ok_or_else(error)?.get().await.map_err(|_| error())?;
1442
1443 let meta = loader.default_meta();
1444 Ok((meta, loader, reader))
1445 }
1446 }
1447
1448 pub(crate) async fn load_with_meta_loader_and_reader(
1449 &self,
1450 asset_path: &AssetPath<'_>,
1451 meta: &dyn AssetMetaDyn,
1452 loader: &dyn ErasedAssetLoader,
1453 reader: &mut dyn Reader,
1454 load_dependencies: bool,
1455 populate_hashes: bool,
1456 ) -> Result<ErasedLoadedAsset, AssetLoadError> {
1457 let asset_path = asset_path.clone_owned();
1459 let load_context =
1460 LoadContext::new(self, asset_path.clone(), load_dependencies, populate_hashes);
1461 AssertUnwindSafe(loader.load(reader, meta, load_context))
1462 .catch_unwind()
1463 .await
1464 .map_err(|_| AssetLoadError::AssetLoaderPanic {
1465 path: asset_path.clone_owned(),
1466 loader_name: loader.type_name(),
1467 })?
1468 .map_err(|e| {
1469 AssetLoadError::AssetLoaderError(AssetLoaderError {
1470 path: asset_path.clone_owned(),
1471 loader_name: loader.type_name(),
1472 error: e.into(),
1473 })
1474 })
1475 }
1476
1477 pub async fn wait_for_asset<A: Asset>(
1485 &self,
1486 handle: &Handle<A>,
1489 ) -> Result<(), WaitForAssetError> {
1490 self.wait_for_asset_id(handle.id().untyped()).await
1491 }
1492
1493 pub async fn wait_for_asset_untyped(
1501 &self,
1502 handle: &UntypedHandle,
1505 ) -> Result<(), WaitForAssetError> {
1506 self.wait_for_asset_id(handle.id()).await
1507 }
1508
1509 pub async fn wait_for_asset_id(
1529 &self,
1530 id: impl Into<UntypedAssetId>,
1531 ) -> Result<(), WaitForAssetError> {
1532 let id = id.into();
1533 core::future::poll_fn(move |cx| self.wait_for_asset_id_poll_fn(cx, id)).await
1534 }
1535
1536 fn wait_for_asset_id_poll_fn(
1538 &self,
1539 cx: &mut core::task::Context<'_>,
1540 id: UntypedAssetId,
1541 ) -> Poll<Result<(), WaitForAssetError>> {
1542 let infos = self.data.infos.read();
1543
1544 let Some(info) = infos.get(id) else {
1545 return Poll::Ready(Err(WaitForAssetError::NotLoaded));
1546 };
1547
1548 match (&info.load_state, &info.rec_dep_load_state) {
1549 (LoadState::Loaded, RecursiveDependencyLoadState::Loaded) => Poll::Ready(Ok(())),
1550 (LoadState::NotLoaded, _) => Poll::Ready(Err(WaitForAssetError::NotLoaded)),
1552 (LoadState::Loading, _)
1554 | (_, RecursiveDependencyLoadState::Loading)
1555 | (LoadState::Loaded, RecursiveDependencyLoadState::NotLoaded) => {
1556 let has_waker = info
1558 .waiting_tasks
1559 .iter()
1560 .any(|waker| waker.will_wake(cx.waker()));
1561
1562 if has_waker {
1563 return Poll::Pending;
1564 }
1565
1566 let mut infos = {
1567 drop(infos);
1569 self.data.infos.write()
1570 };
1571
1572 let Some(info) = infos.get_mut(id) else {
1573 return Poll::Ready(Err(WaitForAssetError::NotLoaded));
1574 };
1575
1576 let is_loading = matches!(
1579 (&info.load_state, &info.rec_dep_load_state),
1580 (LoadState::Loading, _)
1581 | (_, RecursiveDependencyLoadState::Loading)
1582 | (LoadState::Loaded, RecursiveDependencyLoadState::NotLoaded)
1583 );
1584
1585 if !is_loading {
1586 cx.waker().wake_by_ref();
1587 } else {
1588 info.waiting_tasks.push(cx.waker().clone());
1590 }
1591
1592 Poll::Pending
1593 }
1594 (LoadState::Failed(error), _) => {
1595 Poll::Ready(Err(WaitForAssetError::Failed(error.clone())))
1596 }
1597 (_, RecursiveDependencyLoadState::Failed(error)) => {
1598 Poll::Ready(Err(WaitForAssetError::DependencyFailed(error.clone())))
1599 }
1600 }
1601 }
1602
1603 pub async fn write_default_loader_meta_file_for_path(
1614 &self,
1615 path: impl Into<AssetPath<'_>>,
1616 ) -> Result<(), WriteDefaultMetaError> {
1617 let path = path.into();
1618 let loader = self.get_path_asset_loader(&path).await?;
1619
1620 let meta = loader.default_meta();
1621 let serialized_meta = meta.serialize();
1622
1623 let source = self.get_source(path.source())?;
1624
1625 let reader = source.reader();
1626 match reader.read_meta_bytes(path.path()).await {
1627 Ok(_) => return Err(WriteDefaultMetaError::MetaAlreadyExists),
1628 Err(AssetReaderError::NotFound(_)) => {
1629 }
1631 Err(AssetReaderError::Io(err)) => {
1632 return Err(WriteDefaultMetaError::IoErrorFromExistingMetaCheck(err))
1633 }
1634 Err(AssetReaderError::HttpError(err)) => {
1635 return Err(WriteDefaultMetaError::HttpErrorFromExistingMetaCheck(err))
1636 }
1637 }
1638
1639 let writer = source.writer()?;
1640 writer
1641 .write_meta_bytes(path.path(), &serialized_meta)
1642 .await?;
1643
1644 Ok(())
1645 }
1646}
1647
1648pub fn handle_internal_asset_events(world: &mut World) {
1650 world.resource_scope(|world, server: Mut<AssetServer>| {
1651 let mut infos = server.data.infos.write();
1652 let var_name = vec![];
1653 let mut untyped_failures = var_name;
1654 for event in server.data.asset_event_receiver.try_iter() {
1655 match event {
1656 InternalAssetEvent::Loaded { id, loaded_asset } => {
1657 infos.process_asset_load(
1658 id,
1659 loaded_asset,
1660 world,
1661 &server.data.asset_event_sender,
1662 );
1663 }
1664 InternalAssetEvent::LoadedWithDependencies { id } => {
1665 let sender = infos
1666 .dependency_loaded_event_sender
1667 .get(&id.type_id())
1668 .expect("Asset event sender should exist");
1669 sender(world, id);
1670 if let Some(info) = infos.get_mut(id) {
1671 for waker in info.waiting_tasks.drain(..) {
1672 waker.wake();
1673 }
1674 }
1675 }
1676 InternalAssetEvent::Failed { id, path, error } => {
1677 infos.process_asset_fail(id, error.clone());
1678
1679 untyped_failures.push(UntypedAssetLoadFailedEvent {
1681 id,
1682 path: path.clone(),
1683 error: error.clone(),
1684 });
1685
1686 let sender = infos
1688 .dependency_failed_event_sender
1689 .get(&id.type_id())
1690 .expect("Asset failed event sender should exist");
1691 sender(world, id, path, error);
1692 }
1693 }
1694 }
1695
1696 if !untyped_failures.is_empty() {
1697 world.write_message_batch(untyped_failures);
1698 }
1699
1700 fn queue_ancestors(
1701 asset_path: &AssetPath,
1702 infos: &AssetInfos,
1703 paths_to_reload: &mut HashSet<AssetPath<'static>>,
1704 ) {
1705 if let Some(dependents) = infos.loader_dependents.get(asset_path) {
1706 for dependent in dependents {
1707 paths_to_reload.insert(dependent.to_owned());
1708 queue_ancestors(dependent, infos, paths_to_reload);
1709 }
1710 }
1711 }
1712
1713 let reload_parent_folders = |path: &PathBuf, source: &AssetSourceId<'static>| {
1714 for parent in path.ancestors().skip(1) {
1715 let parent_asset_path =
1716 AssetPath::from(parent.to_path_buf()).with_source(source.clone());
1717 for folder_handle in infos.get_path_handles(&parent_asset_path) {
1718 info!("Reloading folder {parent_asset_path} because the content has changed");
1719 server.load_folder_internal(folder_handle.id(), parent_asset_path.clone());
1720 }
1721 }
1722 };
1723
1724 let mut paths_to_reload = <HashSet<_>>::default();
1725 let mut reload_path = |path: PathBuf, source: &AssetSourceId<'static>| {
1726 let path = AssetPath::from(path).with_source(source);
1727 queue_ancestors(&path, &infos, &mut paths_to_reload);
1728 paths_to_reload.insert(path);
1729 };
1730
1731 let mut handle_event = |source: AssetSourceId<'static>, event: AssetSourceEvent| {
1732 match event {
1733 AssetSourceEvent::AddedAsset(path) => {
1734 reload_parent_folders(&path, &source);
1735 reload_path(path, &source);
1736 }
1737 AssetSourceEvent::ModifiedAsset(path) | AssetSourceEvent::ModifiedMeta(path) => {
1740 reload_path(path, &source);
1741 }
1742 AssetSourceEvent::RenamedFolder { old, new } => {
1743 reload_parent_folders(&old, &source);
1744 reload_parent_folders(&new, &source);
1745 }
1746 AssetSourceEvent::RemovedAsset(path)
1747 | AssetSourceEvent::RemovedFolder(path)
1748 | AssetSourceEvent::AddedFolder(path) => {
1749 reload_parent_folders(&path, &source);
1750 }
1751 _ => {}
1752 }
1753 };
1754
1755 for source in server.data.sources.iter() {
1756 match server.data.mode {
1757 AssetServerMode::Unprocessed => {
1758 if let Some(receiver) = source.event_receiver() {
1759 for event in receiver.try_iter() {
1760 handle_event(source.id(), event);
1761 }
1762 }
1763 }
1764 AssetServerMode::Processed => {
1765 if let Some(receiver) = source.processed_event_receiver() {
1766 for event in receiver.try_iter() {
1767 handle_event(source.id(), event);
1768 }
1769 }
1770 }
1771 }
1772 }
1773
1774 for path in paths_to_reload {
1775 server.reload_internal(path, true);
1776 }
1777
1778 #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))]
1779 infos
1780 .pending_tasks
1781 .retain(|_, load_task| !load_task.is_finished());
1782 });
1783}
1784
1785pub(crate) enum InternalAssetEvent {
1787 Loaded {
1788 id: UntypedAssetId,
1789 loaded_asset: ErasedLoadedAsset,
1790 },
1791 LoadedWithDependencies {
1792 id: UntypedAssetId,
1793 },
1794 Failed {
1795 id: UntypedAssetId,
1796 path: AssetPath<'static>,
1797 error: AssetLoadError,
1798 },
1799}
1800
1801#[derive(Component, Clone, Debug)]
1803pub enum LoadState {
1804 NotLoaded,
1806
1807 Loading,
1809
1810 Loaded,
1812
1813 Failed(Arc<AssetLoadError>),
1817}
1818
1819impl LoadState {
1820 pub fn is_loading(&self) -> bool {
1822 matches!(self, Self::Loading)
1823 }
1824
1825 pub fn is_loaded(&self) -> bool {
1827 matches!(self, Self::Loaded)
1828 }
1829
1830 pub fn is_failed(&self) -> bool {
1832 matches!(self, Self::Failed(_))
1833 }
1834}
1835
1836#[derive(Component, Clone, Debug)]
1838pub enum DependencyLoadState {
1839 NotLoaded,
1841
1842 Loading,
1844
1845 Loaded,
1847
1848 Failed(Arc<AssetLoadError>),
1852}
1853
1854impl DependencyLoadState {
1855 pub fn is_loading(&self) -> bool {
1857 matches!(self, Self::Loading)
1858 }
1859
1860 pub fn is_loaded(&self) -> bool {
1862 matches!(self, Self::Loaded)
1863 }
1864
1865 pub fn is_failed(&self) -> bool {
1867 matches!(self, Self::Failed(_))
1868 }
1869}
1870
1871#[derive(Component, Clone, Debug)]
1873pub enum RecursiveDependencyLoadState {
1874 NotLoaded,
1876
1877 Loading,
1879
1880 Loaded,
1882
1883 Failed(Arc<AssetLoadError>),
1888}
1889
1890impl RecursiveDependencyLoadState {
1891 pub fn is_loading(&self) -> bool {
1893 matches!(self, Self::Loading)
1894 }
1895
1896 pub fn is_loaded(&self) -> bool {
1898 matches!(self, Self::Loaded)
1899 }
1900
1901 pub fn is_failed(&self) -> bool {
1903 matches!(self, Self::Failed(_))
1904 }
1905}
1906
1907#[derive(Error, Debug, Clone)]
1909#[expect(
1910 missing_docs,
1911 reason = "Adding docs to the variants would not add information beyond the error message and the names"
1912)]
1913pub enum AssetLoadError {
1914 #[error("Requested handle of type {requested:?} for asset '{path}' does not match actual asset type '{actual_asset_name}', which used loader '{loader_name}'")]
1915 RequestedHandleTypeMismatch {
1916 path: AssetPath<'static>,
1917 requested: TypeId,
1918 actual_asset_name: &'static str,
1919 loader_name: &'static str,
1920 },
1921 #[error("Could not find an asset loader matching: Loader Name: {loader_name:?}; Asset Type: {asset_type_id:?}; Extension: {extension:?}; Path: {asset_path:?};")]
1922 MissingAssetLoader {
1923 loader_name: Option<String>,
1924 asset_type_id: Option<TypeId>,
1925 extension: Option<String>,
1926 asset_path: Option<String>,
1927 },
1928 #[error(transparent)]
1929 MissingAssetLoaderForExtension(#[from] MissingAssetLoaderForExtensionError),
1930 #[error(transparent)]
1931 MissingAssetLoaderForTypeName(#[from] MissingAssetLoaderForTypeNameError),
1932 #[error(transparent)]
1933 MissingAssetLoaderForTypeIdError(#[from] MissingAssetLoaderForTypeIdError),
1934 #[error(transparent)]
1935 AssetReaderError(#[from] AssetReaderError),
1936 #[error(transparent)]
1937 MissingAssetSourceError(#[from] MissingAssetSourceError),
1938 #[error(transparent)]
1939 MissingProcessedAssetReaderError(#[from] MissingProcessedAssetReaderError),
1940 #[error("Encountered an error while reading asset metadata bytes")]
1941 AssetMetaReadError,
1942 #[error("Failed to deserialize meta for asset {path}: {error}")]
1943 DeserializeMeta {
1944 path: AssetPath<'static>,
1945 error: Box<DeserializeMetaError>,
1946 },
1947 #[error("Asset '{path}' is configured to be processed. It cannot be loaded directly.")]
1948 #[from(ignore)]
1949 CannotLoadProcessedAsset { path: AssetPath<'static> },
1950 #[error("Asset '{path}' is configured to be ignored. It cannot be loaded.")]
1951 #[from(ignore)]
1952 CannotLoadIgnoredAsset { path: AssetPath<'static> },
1953 #[error("Failed to load asset '{path}', asset loader '{loader_name}' panicked")]
1954 AssetLoaderPanic {
1955 path: AssetPath<'static>,
1956 loader_name: &'static str,
1957 },
1958 #[error(transparent)]
1959 AssetLoaderError(#[from] AssetLoaderError),
1960 #[error(transparent)]
1961 AddAsyncError(#[from] AddAsyncError),
1962 #[error("The file at '{}' does not contain the labeled asset '{}'; it contains the following {} assets: {}",
1963 base_path,
1964 label,
1965 all_labels.len(),
1966 all_labels.iter().map(|l| format!("'{l}'")).collect::<Vec<_>>().join(", "))]
1967 MissingLabel {
1968 base_path: AssetPath<'static>,
1969 label: String,
1970 all_labels: Vec<String>,
1971 },
1972}
1973
1974#[derive(Error, Debug, Clone)]
1976#[error("Failed to load asset '{path}' with asset loader '{loader_name}': {error}")]
1977pub struct AssetLoaderError {
1978 path: AssetPath<'static>,
1979 loader_name: &'static str,
1980 error: Arc<BevyError>,
1981}
1982
1983impl AssetLoaderError {
1984 pub fn path(&self) -> &AssetPath<'static> {
1986 &self.path
1987 }
1988
1989 pub fn error(&self) -> &BevyError {
1994 &self.error
1995 }
1996}
1997
1998#[derive(Error, Debug, Clone)]
2000#[error("An error occurred while resolving an asset added by `add_async`: {error}")]
2001pub struct AddAsyncError {
2002 error: Arc<dyn core::error::Error + Send + Sync + 'static>,
2003}
2004
2005#[derive(Error, Debug, Clone, PartialEq, Eq)]
2007#[error("no `AssetLoader` found{}", format_missing_asset_ext(extensions))]
2008pub struct MissingAssetLoaderForExtensionError {
2009 extensions: Vec<String>,
2010}
2011
2012#[derive(Error, Debug, Clone, PartialEq, Eq)]
2014#[error("no `AssetLoader` found with the name '{type_name}'")]
2015pub struct MissingAssetLoaderForTypeNameError {
2016 pub type_name: String,
2018}
2019
2020#[derive(Error, Debug, Clone, PartialEq, Eq)]
2022#[error("no `AssetLoader` found with the ID '{type_id:?}'")]
2023pub struct MissingAssetLoaderForTypeIdError {
2024 pub type_id: TypeId,
2026}
2027
2028fn format_missing_asset_ext(exts: &[String]) -> String {
2029 if !exts.is_empty() {
2030 format!(
2031 " for the following extension{}: {}",
2032 if exts.len() > 1 { "s" } else { "" },
2033 exts.join(", ")
2034 )
2035 } else {
2036 " for file with no extension".to_string()
2037 }
2038}
2039
2040impl core::fmt::Debug for AssetServer {
2041 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2042 f.debug_struct("AssetServer")
2043 .field("info", &self.data.infos.read())
2044 .finish()
2045 }
2046}
2047
2048const UNTYPED_SOURCE_SUFFIX: &str = "--untyped";
2051
2052#[derive(Error, Debug, Clone)]
2054pub enum WaitForAssetError {
2055 #[error("tried to wait for an asset that is not being loaded")]
2057 NotLoaded,
2058 #[error(transparent)]
2060 Failed(Arc<AssetLoadError>),
2061 #[error(transparent)]
2063 DependencyFailed(Arc<AssetLoadError>),
2064}
2065
2066#[derive(Error, Debug)]
2067pub enum WriteDefaultMetaError {
2068 #[error(transparent)]
2069 MissingAssetLoader(#[from] MissingAssetLoaderForExtensionError),
2070 #[error(transparent)]
2071 MissingAssetSource(#[from] MissingAssetSourceError),
2072 #[error(transparent)]
2073 MissingAssetWriter(#[from] MissingAssetWriterError),
2074 #[error("failed to write default asset meta file: {0}")]
2075 FailedToWriteMeta(#[from] AssetWriterError),
2076 #[error("asset meta file already exists, so avoiding overwrite")]
2077 MetaAlreadyExists,
2078 #[error("encountered an I/O error while reading the existing meta file: {0}")]
2079 IoErrorFromExistingMetaCheck(Arc<std::io::Error>),
2080 #[error("encountered HTTP status {0} when reading the existing meta file")]
2081 HttpErrorFromExistingMetaCheck(u16),
2082}