diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 6e5b488ee0446..084f184c882b6 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -291,7 +291,7 @@ pub struct Assets { queued_events: Vec>, /// Assets managed by the `Assets` struct with live strong `Handle`s /// originating from `get_strong_handle`. - duplicate_handles: HashMap, u16>, + duplicate_handles: HashMap, } impl Default for Assets { @@ -387,10 +387,7 @@ impl Assets { pub fn add(&mut self, asset: impl Into) -> Handle { let index = self.dense_storage.allocator.reserve(); self.insert_with_index(index, asset.into()).unwrap(); - Handle::Strong( - self.handle_provider - .get_handle(index.into(), false, None, None), - ) + Handle::Strong(self.handle_provider.get_handle(index, false, None, None)) } /// Upgrade an `AssetId` into a strong `Handle` that will prevent asset drop. @@ -402,11 +399,12 @@ impl Assets { if !self.contains(id) { return None; } - *self.duplicate_handles.entry(id).or_insert(0) += 1; let index = match id { - AssetId::Index { index, .. } => index.into(), - AssetId::Uuid { uuid } => uuid.into(), + AssetId::Index { index, .. } => index, + // We don't support strong handles for Uuid assets. + AssetId::Uuid { .. } => return None, }; + *self.duplicate_handles.entry(index).or_insert(0) += 1; Some(Handle::Strong( self.handle_provider.get_handle(index, false, None, None), )) @@ -466,19 +464,21 @@ impl Assets { /// This is the same as [`Assets::remove`] except it doesn't emit [`AssetEvent::Removed`]. pub fn remove_untracked(&mut self, id: impl Into>) -> Option { let id: AssetId = id.into(); - self.duplicate_handles.remove(&id); match id { - AssetId::Index { index, .. } => self.dense_storage.remove_still_alive(index), + AssetId::Index { index, .. } => { + self.duplicate_handles.remove(&index); + self.dense_storage.remove_still_alive(index) + } AssetId::Uuid { uuid } => self.hash_map.remove(&uuid), } } /// Removes the [`Asset`] with the given `id`. - pub(crate) fn remove_dropped(&mut self, id: AssetId) { - match self.duplicate_handles.get_mut(&id) { + pub(crate) fn remove_dropped(&mut self, index: AssetIndex) { + match self.duplicate_handles.get_mut(&index) { None => {} Some(0) => { - self.duplicate_handles.remove(&id); + self.duplicate_handles.remove(&index); } Some(value) => { *value -= 1; @@ -486,14 +486,13 @@ impl Assets { } } - let existed = match id { - AssetId::Index { index, .. } => self.dense_storage.remove_dropped(index).is_some(), - AssetId::Uuid { uuid } => self.hash_map.remove(&uuid).is_some(), - }; + let existed = self.dense_storage.remove_dropped(index).is_some(); - self.queued_events.push(AssetEvent::Unused { id }); + self.queued_events + .push(AssetEvent::Unused { id: index.into() }); if existed { - self.queued_events.push(AssetEvent::Removed { id }); + self.queued_events + .push(AssetEvent::Removed { id: index.into() }); } } @@ -561,19 +560,15 @@ impl Assets { // to other asset info operations let mut infos = asset_server.data.infos.write(); while let Ok(drop_event) = assets.handle_provider.drop_receiver.try_recv() { - let id = drop_event.id.typed(); - if drop_event.asset_server_managed { - let untyped_id = id.untyped(); - // the process_handle_drop call checks whether new handles have been created since the drop event was fired, before removing the asset - if !infos.process_handle_drop(untyped_id) { + if !infos.process_handle_drop(drop_event.index) { // a new handle has been created, or the asset doesn't exist continue; } } - assets.remove_dropped(id); + assets.remove_dropped(drop_event.index.index); } } diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index b9a20a7af8d9d..f59d4398c6b07 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -1,6 +1,6 @@ use crate::{ - meta::MetaTransform, Asset, AssetId, AssetIndexAllocator, AssetPath, InternalAssetId, - UntypedAssetId, + meta::MetaTransform, Asset, AssetId, AssetIndex, AssetIndexAllocator, AssetPath, + ErasedAssetIndex, UntypedAssetId, }; use alloc::sync::Arc; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; @@ -26,7 +26,7 @@ pub struct AssetHandleProvider { #[derive(Debug)] pub(crate) struct DropEvent { - pub(crate) id: InternalAssetId, + pub(crate) index: ErasedAssetIndex, pub(crate) asset_server_managed: bool, } @@ -45,18 +45,19 @@ impl AssetHandleProvider { /// [`UntypedHandle`] will match the [`Asset`] [`TypeId`] assigned to this [`AssetHandleProvider`]. pub fn reserve_handle(&self) -> UntypedHandle { let index = self.allocator.reserve(); - UntypedHandle::Strong(self.get_handle(InternalAssetId::Index(index), false, None, None)) + UntypedHandle::Strong(self.get_handle(index, false, None, None)) } pub(crate) fn get_handle( &self, - id: InternalAssetId, + index: AssetIndex, asset_server_managed: bool, path: Option>, meta_transform: Option, ) -> Arc { Arc::new(StrongHandle { - id: id.untyped(self.type_id), + index, + type_id: self.type_id, drop_sender: self.drop_sender.clone(), meta_transform, path, @@ -71,12 +72,7 @@ impl AssetHandleProvider { meta_transform: Option, ) -> Arc { let index = self.allocator.reserve(); - self.get_handle( - InternalAssetId::Index(index), - asset_server_managed, - path, - meta_transform, - ) + self.get_handle(index, asset_server_managed, path, meta_transform) } } @@ -84,7 +80,8 @@ impl AssetHandleProvider { /// the [`Asset`] will be freed. It also stores some asset metadata for easy access from handles. #[derive(TypePath)] pub struct StrongHandle { - pub(crate) id: UntypedAssetId, + pub(crate) index: AssetIndex, + pub(crate) type_id: TypeId, pub(crate) asset_server_managed: bool, pub(crate) path: Option>, /// Modifies asset meta. This is stored on the handle because it is: @@ -97,7 +94,7 @@ pub struct StrongHandle { impl Drop for StrongHandle { fn drop(&mut self) { let _ = self.drop_sender.send(DropEvent { - id: self.id.internal(), + index: ErasedAssetIndex::new(self.index, self.type_id), asset_server_managed: self.asset_server_managed, }); } @@ -106,7 +103,8 @@ impl Drop for StrongHandle { impl core::fmt::Debug for StrongHandle { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("StrongHandle") - .field("id", &self.id) + .field("index", &self.index) + .field("type_id", &self.type_id) .field("asset_server_managed", &self.asset_server_managed) .field("path", &self.path) .field("drop_sender", &self.drop_sender) @@ -154,7 +152,10 @@ impl Handle { #[inline] pub fn id(&self) -> AssetId { match self { - Handle::Strong(handle) => handle.id.typed_unchecked(), + Handle::Strong(handle) => AssetId::Index { + index: handle.index, + marker: PhantomData, + }, Handle::Uuid(uuid, ..) => AssetId::Uuid { uuid: *uuid }, } } @@ -202,9 +203,8 @@ impl core::fmt::Debug for Handle { Handle::Strong(handle) => { write!( f, - "StrongHandle<{name}>{{ id: {:?}, path: {:?} }}", - handle.id.internal(), - handle.path + "StrongHandle<{name}>{{ index: {:?}, type_id: {:?}, path: {:?} }}", + handle.index, handle.type_id, handle.path ) } Handle::Uuid(uuid, ..) => write!(f, "UuidHandle<{name}>({uuid:?})"), @@ -291,7 +291,10 @@ impl UntypedHandle { #[inline] pub fn id(&self) -> UntypedAssetId { match self { - UntypedHandle::Strong(handle) => handle.id, + UntypedHandle::Strong(handle) => UntypedAssetId::Index { + type_id: handle.type_id, + index: handle.index, + }, UntypedHandle::Uuid { type_id, uuid } => UntypedAssetId::Uuid { uuid: *uuid, type_id: *type_id, @@ -312,7 +315,7 @@ impl UntypedHandle { #[inline] pub fn type_id(&self) -> TypeId { match self { - UntypedHandle::Strong(handle) => handle.id.type_id(), + UntypedHandle::Strong(handle) => handle.type_id, UntypedHandle::Uuid { type_id, .. } => *type_id, } } @@ -393,9 +396,7 @@ impl core::fmt::Debug for UntypedHandle { write!( f, "StrongHandle{{ type_id: {:?}, id: {:?}, path: {:?} }}", - handle.id.type_id(), - handle.id.internal(), - handle.path + handle.type_id, handle.index, handle.path ) } UntypedHandle::Uuid { type_id, uuid } => { diff --git a/crates/bevy_asset/src/id.rs b/crates/bevy_asset/src/id.rs index 78686e31c2678..98fbb1090d774 100644 --- a/crates/bevy_asset/src/id.rs +++ b/crates/bevy_asset/src/id.rs @@ -1,4 +1,4 @@ -use crate::{Asset, AssetIndex}; +use crate::{Asset, AssetIndex, Handle, UntypedHandle}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -68,7 +68,7 @@ impl AssetId { } #[inline] - pub(crate) fn internal(self) -> InternalAssetId { + fn internal(self) -> InternalAssetId { match self { AssetId::Index { index, .. } => InternalAssetId::Index(index), AssetId::Uuid { uuid } => InternalAssetId::Uuid(uuid), @@ -255,7 +255,7 @@ impl UntypedAssetId { } #[inline] - pub(crate) fn internal(self) -> InternalAssetId { + fn internal(self) -> InternalAssetId { match self { UntypedAssetId::Index { index, .. } => InternalAssetId::Index(index), UntypedAssetId::Uuid { uuid, .. } => InternalAssetId::Uuid(uuid), @@ -314,36 +314,33 @@ impl PartialOrd for UntypedAssetId { /// An asset id without static or dynamic types associated with it. /// -/// This exist to support efficient type erased id drop tracking. We -/// could use [`UntypedAssetId`] for this, but the [`TypeId`] is unnecessary. -/// -/// Do not _ever_ use this across asset types for comparison. -/// [`InternalAssetId`] contains no type information and will happily collide -/// with indices across types. +/// This is provided to make implementing traits easier for the many different asset ID types. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, From)] -pub(crate) enum InternalAssetId { +enum InternalAssetId { Index(AssetIndex), Uuid(Uuid), } -impl InternalAssetId { - #[inline] - pub(crate) fn typed(self) -> AssetId { - match self { - InternalAssetId::Index(index) => AssetId::Index { - index, - marker: PhantomData, - }, - InternalAssetId::Uuid(uuid) => AssetId::Uuid { uuid }, - } +/// An asset index bundled with its (dynamic) type. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub(crate) struct ErasedAssetIndex { + pub(crate) index: AssetIndex, + pub(crate) type_id: TypeId, +} + +impl ErasedAssetIndex { + pub(crate) fn new(index: AssetIndex, type_id: TypeId) -> Self { + Self { index, type_id } } +} - #[inline] - pub(crate) fn untyped(self, type_id: TypeId) -> UntypedAssetId { - match self { - InternalAssetId::Index(index) => UntypedAssetId::Index { index, type_id }, - InternalAssetId::Uuid(uuid) => UntypedAssetId::Uuid { uuid, type_id }, - } +impl Display for ErasedAssetIndex { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ErasedAssetIndex") + .field("type_id", &self.type_id) + .field("index", &self.index.index) + .field("generation", &self.index.generation) + .finish() } } @@ -414,6 +411,52 @@ impl TryFrom for AssetId { } } +impl TryFrom for ErasedAssetIndex { + type Error = UuidNotSupportedError; + + fn try_from(asset_id: UntypedAssetId) -> Result { + match asset_id { + UntypedAssetId::Index { type_id, index } => Ok(ErasedAssetIndex { index, type_id }), + UntypedAssetId::Uuid { .. } => Err(UuidNotSupportedError), + } + } +} + +impl TryFrom<&Handle> for ErasedAssetIndex { + type Error = UuidNotSupportedError; + + fn try_from(handle: &Handle) -> Result { + match handle { + Handle::Strong(handle) => Ok(Self::new(handle.index, handle.type_id)), + Handle::Uuid(..) => Err(UuidNotSupportedError), + } + } +} + +impl TryFrom<&UntypedHandle> for ErasedAssetIndex { + type Error = UuidNotSupportedError; + + fn try_from(handle: &UntypedHandle) -> Result { + match handle { + UntypedHandle::Strong(handle) => Ok(Self::new(handle.index, handle.type_id)), + UntypedHandle::Uuid { .. } => Err(UuidNotSupportedError), + } + } +} + +impl From for UntypedAssetId { + fn from(value: ErasedAssetIndex) -> Self { + Self::Index { + type_id: value.type_id, + index: value.index, + } + } +} + +#[derive(Error, Debug)] +#[error("Attempted to create a TypedAssetIndex from a Uuid")] +pub(crate) struct UuidNotSupportedError; + /// Errors preventing the conversion of to/from an [`UntypedAssetId`] and an [`AssetId`]. #[derive(Error, Debug, PartialEq, Clone)] #[non_exhaustive] diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 24405f0657d5e..58c02aeedec72 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -3,8 +3,8 @@ use crate::{ loader_builders::{Deferred, NestedLoader, StaticTyped}, meta::{AssetHash, AssetMeta, AssetMetaDyn, ProcessedInfoMinimal, Settings}, path::AssetPath, - Asset, AssetLoadError, AssetServer, AssetServerMode, Assets, Handle, UntypedAssetId, - UntypedHandle, + Asset, AssetIndex, AssetLoadError, AssetServer, AssetServerMode, Assets, ErasedAssetIndex, + Handle, UntypedHandle, }; use alloc::{ boxed::Box, @@ -144,7 +144,7 @@ pub(crate) struct LabeledAsset { /// * Loader dependencies: dependencies whose actual asset values are used during the load process pub struct LoadedAsset { pub(crate) value: A, - pub(crate) dependencies: HashSet, + pub(crate) dependencies: HashSet, pub(crate) loader_dependencies: HashMap, AssetHash>, pub(crate) labeled_assets: HashMap, LabeledAsset>, } @@ -154,7 +154,10 @@ impl LoadedAsset { pub fn new_with_dependencies(value: A) -> Self { let mut dependencies = >::default(); value.visit_dependencies(&mut |id| { - dependencies.insert(id); + let Ok(asset_index) = id.try_into() else { + return; + }; + dependencies.insert(asset_index); }); LoadedAsset { value, @@ -197,7 +200,7 @@ impl From for LoadedAsset { /// A "type erased / boxed" counterpart to [`LoadedAsset`]. This is used in places where the loaded type is not statically known. pub struct ErasedLoadedAsset { pub(crate) value: Box, - pub(crate) dependencies: HashSet, + pub(crate) dependencies: HashSet, pub(crate) loader_dependencies: HashMap, AssetHash>, pub(crate) labeled_assets: HashMap, LabeledAsset>, } @@ -267,16 +270,16 @@ impl ErasedLoadedAsset { } /// A type erased container for an [`Asset`] value that is capable of inserting the [`Asset`] into a [`World`]'s [`Assets`] collection. -pub trait AssetContainer: Downcast + Any + Send + Sync + 'static { - fn insert(self: Box, id: UntypedAssetId, world: &mut World); +pub(crate) trait AssetContainer: Downcast + Any + Send + Sync + 'static { + fn insert(self: Box, id: AssetIndex, world: &mut World); fn asset_type_name(&self) -> &'static str; } impl_downcast!(AssetContainer); impl AssetContainer for A { - fn insert(self: Box, id: UntypedAssetId, world: &mut World) { - world.resource_mut::>().insert(id.typed(), *self); + fn insert(self: Box, index: AssetIndex, world: &mut World) { + world.resource_mut::>().insert(index, *self); } fn asset_type_name(&self) -> &'static str { @@ -317,7 +320,7 @@ pub struct LoadContext<'a> { pub(crate) should_load_dependencies: bool, populate_hashes: bool, asset_path: AssetPath<'static>, - pub(crate) dependencies: HashSet, + pub(crate) dependencies: HashSet, /// Direct dependencies used by this loader. pub(crate) loader_dependencies: HashMap, AssetHash>, pub(crate) labeled_assets: HashMap, LabeledAsset>, @@ -514,7 +517,9 @@ impl<'a> LoadContext<'a> { ) -> Handle { let path = self.asset_path.clone().with_label(label); let handle = self.asset_server.get_or_create_path_handle::(path, None); - self.dependencies.insert(handle.id().untyped()); + // `get_or_create_path_handle` always returns a Strong variant, so we are safe to unwrap. + let index = (&handle).try_into().unwrap(); + self.dependencies.insert(index); handle } diff --git a/crates/bevy_asset/src/loader_builders.rs b/crates/bevy_asset/src/loader_builders.rs index 13bea2b71dc2a..2d1948978fc1c 100644 --- a/crates/bevy_asset/src/loader_builders.rs +++ b/crates/bevy_asset/src/loader_builders.rs @@ -316,7 +316,10 @@ impl NestedLoader<'_, '_, StaticTyped, Deferred> { .asset_server .get_or_create_path_handle(path, None) }; - self.load_context.dependencies.insert(handle.id().untyped()); + // `load_with_meta_transform` and `get_or_create_path_handle` always returns a Strong + // variant, so we are safe to unwrap. + let index = (&handle).try_into().unwrap(); + self.load_context.dependencies.insert(index); handle } } @@ -349,7 +352,10 @@ impl NestedLoader<'_, '_, DynamicTyped, Deferred> { self.meta_transform, ) }; - self.load_context.dependencies.insert(handle.id()); + // `load_erased_with_meta_transform` and `get_or_create_path_handle_erased` always returns a + // Strong variant, so we are safe to unwrap. + let index = (&handle).try_into().unwrap(); + self.load_context.dependencies.insert(index); handle } } @@ -370,7 +376,10 @@ impl NestedLoader<'_, '_, UnknownTyped, Deferred> { .asset_server .get_or_create_path_handle(path, self.meta_transform) }; - self.load_context.dependencies.insert(handle.id().untyped()); + // `load_unknown_type_with_meta_transform` and `get_or_create_path_handle` always returns a + // Strong variant, so we are safe to unwrap. + let index = (&handle).try_into().unwrap(); + self.load_context.dependencies.insert(index); handle } } diff --git a/crates/bevy_asset/src/server/info.rs b/crates/bevy_asset/src/server/info.rs index 1b3bb3cb65d68..1c061bfb5c958 100644 --- a/crates/bevy_asset/src/server/info.rs +++ b/crates/bevy_asset/src/server/info.rs @@ -1,8 +1,8 @@ use crate::{ meta::{AssetHash, MetaTransform}, - Asset, AssetHandleProvider, AssetLoadError, AssetPath, DependencyLoadState, ErasedLoadedAsset, - Handle, InternalAssetEvent, LoadState, RecursiveDependencyLoadState, StrongHandle, - UntypedAssetId, UntypedHandle, + Asset, AssetHandleProvider, AssetIndex, AssetLoadError, AssetPath, DependencyLoadState, + ErasedAssetIndex, ErasedLoadedAsset, Handle, InternalAssetEvent, LoadState, + RecursiveDependencyLoadState, StrongHandle, UntypedHandle, }; use alloc::{ borrow::ToOwned, @@ -27,12 +27,12 @@ pub(crate) struct AssetInfo { pub(crate) load_state: LoadState, pub(crate) dep_load_state: DependencyLoadState, pub(crate) rec_dep_load_state: RecursiveDependencyLoadState, - loading_dependencies: HashSet, - failed_dependencies: HashSet, - loading_rec_dependencies: HashSet, - failed_rec_dependencies: HashSet, - dependents_waiting_on_load: HashSet, - dependents_waiting_on_recursive_dep_load: HashSet, + loading_dependencies: HashSet, + failed_dependencies: HashSet, + loading_rec_dependencies: HashSet, + failed_rec_dependencies: HashSet, + dependents_waiting_on_load: HashSet, + dependents_waiting_on_recursive_dep_load: HashSet, /// The asset paths required to load this asset. Hashes will only be set for processed assets. /// This is set using the value from [`LoadedAsset`]. /// This will only be populated if [`AssetInfos::watching_for_changes`] is set to `true` to @@ -70,8 +70,8 @@ impl AssetInfo { #[derive(Default)] pub(crate) struct AssetInfos { - path_to_id: HashMap, TypeIdMap>, - infos: HashMap, + path_to_index: HashMap, TypeIdMap>, + infos: HashMap, /// If set to `true`, this informs [`AssetInfos`] to track data relevant to watching for changes (such as `load_dependents`) /// This should only be set at startup. pub(crate) watching_for_changes: bool, @@ -82,16 +82,16 @@ pub(crate) struct AssetInfos { /// This should only be set when watching for changes to avoid unnecessary work. pub(crate) living_labeled_assets: HashMap, HashSet>>, pub(crate) handle_providers: TypeIdMap, - pub(crate) dependency_loaded_event_sender: TypeIdMap, + pub(crate) dependency_loaded_event_sender: TypeIdMap, pub(crate) dependency_failed_event_sender: - TypeIdMap, AssetLoadError)>, - pub(crate) pending_tasks: HashMap>, + TypeIdMap, AssetLoadError)>, + pub(crate) pending_tasks: HashMap>, } impl core::fmt::Debug for AssetInfos { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("AssetInfos") - .field("path_to_id", &self.path_to_id) + .field("path_to_index", &self.path_to_index) .field("infos", &self.infos) .finish() } @@ -120,7 +120,7 @@ impl AssetInfos { } fn create_handle_internal( - infos: &mut HashMap, + infos: &mut HashMap, handle_providers: &TypeIdMap, living_labeled_assets: &mut HashMap, HashSet>>, watching_for_changes: bool, @@ -150,7 +150,7 @@ impl AssetInfos { info.dep_load_state = DependencyLoadState::Loading; info.rec_dep_load_state = RecursiveDependencyLoadState::Loading; } - infos.insert(handle.id, info); + infos.insert(ErasedAssetIndex::new(handle.index, handle.type_id), info); Ok(UntypedHandle::Strong(handle)) } @@ -204,7 +204,7 @@ impl AssetInfos { loading_mode: HandleLoadingMode, meta_transform: Option, ) -> Result<(UntypedHandle, bool), GetOrCreateHandleInternalError> { - let handles = self.path_to_id.entry(path.clone()).or_default(); + let handles = self.path_to_index.entry(path.clone()).or_default(); let type_id = type_id .or_else(|| { @@ -219,9 +219,12 @@ impl AssetInfos { match handles.entry(type_id) { Entry::Occupied(entry) => { - let id = *entry.get(); + let index = *entry.get(); // if there is a path_to_id entry, info always exists - let info = self.infos.get_mut(&id).unwrap(); + let info = self + .infos + .get_mut(&ErasedAssetIndex::new(index, type_id)) + .unwrap(); let mut should_load = false; if loading_mode == HandleLoadingMode::Force || (loading_mode == HandleLoadingMode::Request @@ -251,8 +254,7 @@ impl AssetInfos { .handle_providers .get(&type_id) .ok_or(MissingHandleProviderError(type_id))?; - let handle = - provider.get_handle(id.internal(), true, Some(path), meta_transform); + let handle = provider.get_handle(index, true, Some(path), meta_transform); info.weak_handle = Arc::downgrade(&handle); Ok((UntypedHandle::Strong(handle), should_load)) } @@ -273,22 +275,27 @@ impl AssetInfos { meta_transform, should_load, )?; - entry.insert(handle.id()); + let index = match &handle { + UntypedHandle::Strong(handle) => handle.index, + // `create_handle_internal` always returns Strong variant. + UntypedHandle::Uuid { .. } => unreachable!(), + }; + entry.insert(index); Ok((handle, should_load)) } } } - pub(crate) fn get(&self, id: UntypedAssetId) -> Option<&AssetInfo> { - self.infos.get(&id) + pub(crate) fn get(&self, index: ErasedAssetIndex) -> Option<&AssetInfo> { + self.infos.get(&index) } - pub(crate) fn contains_key(&self, id: UntypedAssetId) -> bool { - self.infos.contains_key(&id) + pub(crate) fn contains_key(&self, index: ErasedAssetIndex) -> bool { + self.infos.contains_key(&index) } - pub(crate) fn get_mut(&mut self, id: UntypedAssetId) -> Option<&mut AssetInfo> { - self.infos.get_mut(&id) + pub(crate) fn get_mut(&mut self, index: ErasedAssetIndex) -> Option<&mut AssetInfo> { + self.infos.get_mut(&index) } pub(crate) fn get_path_and_type_id_handle( @@ -296,14 +303,14 @@ impl AssetInfos { path: &AssetPath<'_>, type_id: TypeId, ) -> Option { - let id = self.path_to_id.get(path)?.get(&type_id)?; - self.get_id_handle(*id) + let index = *self.path_to_index.get(path)?.get(&type_id)?; + self.get_index_handle(ErasedAssetIndex::new(index, type_id)) } - pub(crate) fn get_path_ids<'a>( + pub(crate) fn get_path_indices<'a>( &'a self, path: &'a AssetPath<'_>, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { /// Concrete type to allow returning an `impl Iterator` even if `self.path_to_id.get(&path)` is `None` enum HandlesByPathIterator { None, @@ -312,9 +319,9 @@ impl AssetInfos { impl Iterator for HandlesByPathIterator where - T: Iterator, + T: Iterator, { - type Item = UntypedAssetId; + type Item = ErasedAssetIndex; fn next(&mut self) -> Option { match self { @@ -324,8 +331,12 @@ impl AssetInfos { } } - if let Some(type_id_to_id) = self.path_to_id.get(path) { - HandlesByPathIterator::Some(type_id_to_id.values().copied()) + if let Some(type_id_to_id) = self.path_to_index.get(path) { + HandlesByPathIterator::Some( + type_id_to_id + .iter() + .map(|(type_id, index)| ErasedAssetIndex::new(*index, *type_id)), + ) } else { HandlesByPathIterator::None } @@ -335,19 +346,19 @@ impl AssetInfos { &'a self, path: &'a AssetPath<'_>, ) -> impl Iterator + 'a { - self.get_path_ids(path) - .filter_map(|id| self.get_id_handle(id)) + self.get_path_indices(path) + .filter_map(|id| self.get_index_handle(id)) } - pub(crate) fn get_id_handle(&self, id: UntypedAssetId) -> Option { - let info = self.infos.get(&id)?; + pub(crate) fn get_index_handle(&self, index: ErasedAssetIndex) -> Option { + let info = self.infos.get(&index)?; let strong_handle = info.weak_handle.upgrade()?; Some(UntypedHandle::Strong(strong_handle)) } /// Returns `true` if the asset this path points to is still alive pub(crate) fn is_path_alive<'a>(&self, path: impl Into>) -> bool { - self.get_path_ids(&path.into()) + self.get_path_indices(&path.into()) .filter_map(|id| self.infos.get(&id)) .any(|info| info.weak_handle.strong_count() > 0) } @@ -366,32 +377,32 @@ impl AssetInfos { } /// Returns `true` if the asset should be removed from the collection. - pub(crate) fn process_handle_drop(&mut self, id: UntypedAssetId) -> bool { + pub(crate) fn process_handle_drop(&mut self, index: ErasedAssetIndex) -> bool { Self::process_handle_drop_internal( &mut self.infos, - &mut self.path_to_id, + &mut self.path_to_index, &mut self.loader_dependents, &mut self.living_labeled_assets, &mut self.pending_tasks, self.watching_for_changes, - id, + index, ) } /// Updates [`AssetInfo`] / load state for an asset that has finished loading (and relevant dependencies / dependents). pub(crate) fn process_asset_load( &mut self, - loaded_asset_id: UntypedAssetId, + loaded_asset_index: ErasedAssetIndex, loaded_asset: ErasedLoadedAsset, world: &mut World, sender: &Sender, ) { // Check whether the handle has been dropped since the asset was loaded. - if !self.infos.contains_key(&loaded_asset_id) { + if !self.infos.contains_key(&loaded_asset_index) { return; } - loaded_asset.value.insert(loaded_asset_id, world); + loaded_asset.value.insert(loaded_asset_index.index, world); let mut loading_deps = loaded_asset.dependencies; let mut failed_deps = >::default(); let mut dep_error = None; @@ -406,7 +417,7 @@ impl AssetInfos { // If dependency is loading, wait for it. dep_info .dependents_waiting_on_recursive_dep_load - .insert(loaded_asset_id); + .insert(loaded_asset_index); } RecursiveDependencyLoadState::Loaded => { // If dependency is loaded, reduce our count by one @@ -423,7 +434,7 @@ impl AssetInfos { match dep_info.load_state { LoadState::NotLoaded | LoadState::Loading => { // If dependency is loading, wait for it. - dep_info.dependents_waiting_on_load.insert(loaded_asset_id); + dep_info.dependents_waiting_on_load.insert(loaded_asset_index); true } LoadState::Loaded => { @@ -442,7 +453,7 @@ impl AssetInfos { // the dependency id does not exist, which implies it was manually removed or never existed in the first place warn!( "Dependency {} from asset {} is unknown. This asset's dependency load status will not switch to 'Loaded' until the unknown dependency is loaded.", - dep_id, loaded_asset_id + dep_id, loaded_asset_index ); true } @@ -458,7 +469,7 @@ impl AssetInfos { (0, 0) => { sender .send(InternalAssetEvent::LoadedWithDependencies { - id: loaded_asset_id, + index: loaded_asset_index, }) .unwrap(); RecursiveDependencyLoadState::Loaded @@ -473,7 +484,7 @@ impl AssetInfos { if watching_for_changes { let info = self .infos - .get(&loaded_asset_id) + .get(&loaded_asset_index) .expect("Asset info should always exist at this point"); if let Some(asset_path) = &info.path { for loader_dependency in loaded_asset.loader_dependencies.keys() { @@ -486,7 +497,7 @@ impl AssetInfos { } } let info = self - .get_mut(loaded_asset_id) + .get_mut(loaded_asset_index) .expect("Asset info should always exist at this point"); info.loading_dependencies = loading_deps; info.failed_dependencies = failed_deps; @@ -516,7 +527,7 @@ impl AssetInfos { for id in dependents_waiting_on_load { if let Some(info) = self.get_mut(id) { - info.loading_dependencies.remove(&loaded_asset_id); + info.loading_dependencies.remove(&loaded_asset_index); if info.loading_dependencies.is_empty() && !info.dep_load_state.is_failed() { // send dependencies loaded event info.dep_load_state = DependencyLoadState::Loaded; @@ -528,12 +539,12 @@ impl AssetInfos { match rec_dep_load_state { RecursiveDependencyLoadState::Loaded => { for dep_id in dependents_waiting_on_rec_load { - Self::propagate_loaded_state(self, loaded_asset_id, dep_id, sender); + Self::propagate_loaded_state(self, loaded_asset_index, dep_id, sender); } } RecursiveDependencyLoadState::Failed(ref error) => { for dep_id in dependents_waiting_on_rec_load { - Self::propagate_failed_state(self, loaded_asset_id, dep_id, error); + Self::propagate_failed_state(self, loaded_asset_index, dep_id, error); } } RecursiveDependencyLoadState::Loading | RecursiveDependencyLoadState::NotLoaded => { @@ -547,8 +558,8 @@ impl AssetInfos { /// Recursively propagates loaded state up the dependency tree. fn propagate_loaded_state( infos: &mut AssetInfos, - loaded_id: UntypedAssetId, - waiting_id: UntypedAssetId, + loaded_id: ErasedAssetIndex, + waiting_id: ErasedAssetIndex, sender: &Sender, ) { let dependents_waiting_on_rec_load = if let Some(info) = infos.get_mut(waiting_id) { @@ -557,7 +568,7 @@ impl AssetInfos { info.rec_dep_load_state = RecursiveDependencyLoadState::Loaded; if info.load_state.is_loaded() { sender - .send(InternalAssetEvent::LoadedWithDependencies { id: waiting_id }) + .send(InternalAssetEvent::LoadedWithDependencies { index: waiting_id }) .unwrap(); } Some(core::mem::take( @@ -580,8 +591,8 @@ impl AssetInfos { /// Recursively propagates failed state up the dependency tree fn propagate_failed_state( infos: &mut AssetInfos, - failed_id: UntypedAssetId, - waiting_id: UntypedAssetId, + failed_id: ErasedAssetIndex, + waiting_id: ErasedAssetIndex, error: &Arc, ) { let dependents_waiting_on_rec_load = if let Some(info) = infos.get_mut(waiting_id) { @@ -602,15 +613,19 @@ impl AssetInfos { } } - pub(crate) fn process_asset_fail(&mut self, failed_id: UntypedAssetId, error: AssetLoadError) { + pub(crate) fn process_asset_fail( + &mut self, + failed_index: ErasedAssetIndex, + error: AssetLoadError, + ) { // Check whether the handle has been dropped since the asset was loaded. - if !self.infos.contains_key(&failed_id) { + if !self.infos.contains_key(&failed_index) { return; } let error = Arc::new(error); let (dependents_waiting_on_load, dependents_waiting_on_rec_load) = { - let Some(info) = self.get_mut(failed_id) else { + let Some(info) = self.get_mut(failed_index) else { // The asset was already dropped. return; }; @@ -628,8 +643,8 @@ impl AssetInfos { for waiting_id in dependents_waiting_on_load { if let Some(info) = self.get_mut(waiting_id) { - info.loading_dependencies.remove(&failed_id); - info.failed_dependencies.insert(failed_id); + info.loading_dependencies.remove(&failed_index); + info.failed_dependencies.insert(failed_index); // don't overwrite DependencyLoadState if already failed to preserve first error if !info.dep_load_state.is_failed() { info.dep_load_state = DependencyLoadState::Failed(error.clone()); @@ -638,7 +653,7 @@ impl AssetInfos { } for waiting_id in dependents_waiting_on_rec_load { - Self::propagate_failed_state(self, failed_id, waiting_id, &error); + Self::propagate_failed_state(self, failed_index, waiting_id, &error); } } @@ -672,15 +687,15 @@ impl AssetInfos { } fn process_handle_drop_internal( - infos: &mut HashMap, - path_to_id: &mut HashMap, TypeIdMap>, + infos: &mut HashMap, + path_to_id: &mut HashMap, TypeIdMap>, loader_dependents: &mut HashMap, HashSet>>, living_labeled_assets: &mut HashMap, HashSet>>, - pending_tasks: &mut HashMap>, + pending_tasks: &mut HashMap>, watching_for_changes: bool, - id: UntypedAssetId, + index: ErasedAssetIndex, ) -> bool { - let Entry::Occupied(mut entry) = infos.entry(id) else { + let Entry::Occupied(mut entry) = infos.entry(index) else { // Either the asset was already dropped, it doesn't exist, or it isn't managed by the asset server // None of these cases should result in a removal from the Assets collection return false; @@ -691,9 +706,9 @@ impl AssetInfos { return false; } - pending_tasks.remove(&id); + pending_tasks.remove(&index); - let type_id = entry.key().type_id(); + let type_id = entry.key().type_id; let info = entry.remove(); let Some(path) = &info.path else { @@ -728,16 +743,16 @@ impl AssetInfos { pub(crate) fn consume_handle_drop_events(&mut self) { for provider in self.handle_providers.values() { while let Ok(drop_event) = provider.drop_receiver.try_recv() { - let id = drop_event.id; + let id = drop_event.index; if drop_event.asset_server_managed { Self::process_handle_drop_internal( &mut self.infos, - &mut self.path_to_id, + &mut self.path_to_index, &mut self.loader_dependents, &mut self.living_labeled_assets, &mut self.pending_tasks, self.watching_for_changes, - id.untyped(provider.type_id), + id, ); } } diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 0b64fe74e0afb..0ebc9350efc87 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -14,9 +14,10 @@ use crate::{ MetaTransform, Settings, }, path::AssetPath, - Asset, AssetEvent, AssetHandleProvider, AssetId, AssetLoadFailedEvent, AssetMetaCheck, Assets, - DeserializeMetaError, ErasedLoadedAsset, Handle, LoadedUntypedAsset, UnapprovedPathMode, - UntypedAssetId, UntypedAssetLoadFailedEvent, UntypedHandle, + Asset, AssetEvent, AssetHandleProvider, AssetId, AssetIndex, AssetLoadFailedEvent, + AssetMetaCheck, Assets, DeserializeMetaError, ErasedAssetIndex, ErasedLoadedAsset, Handle, + LoadedUntypedAsset, UnapprovedPathMode, UntypedAssetId, UntypedAssetLoadFailedEvent, + UntypedHandle, }; use alloc::{borrow::ToOwned, boxed::Box, vec, vec::Vec}; use alloc::{ @@ -164,21 +165,21 @@ impl AssetServer { /// Registers a new [`Asset`] type. [`Asset`] types must be registered before assets of that type can be loaded. pub fn register_asset(&self, assets: &Assets) { self.register_handle_provider(assets.get_handle_provider()); - fn sender(world: &mut World, id: UntypedAssetId) { + fn sender(world: &mut World, index: AssetIndex) { world .resource_mut::>>() - .send(AssetEvent::LoadedWithDependencies { id: id.typed() }); + .send(AssetEvent::LoadedWithDependencies { id: index.into() }); } fn failed_sender( world: &mut World, - id: UntypedAssetId, + index: AssetIndex, path: AssetPath<'static>, error: AssetLoadError, ) { world .resource_mut::>>() .send(AssetLoadFailedEvent { - id: id.typed(), + id: index.into(), path, error, }); @@ -537,7 +538,9 @@ impl AssetServer { #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))] { let mut infos = infos; - infos.pending_tasks.insert(handle.id(), task); + infos + .pending_tasks + .insert((&handle).try_into().unwrap(), task); } #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))] @@ -584,21 +587,21 @@ impl AssetServer { if !should_load { return handle; } - let id = handle.id().untyped(); + let index = (&handle).try_into().unwrap(); let server = self.clone(); let task = IoTaskPool::get().spawn(async move { let path_clone = path.clone(); match server.load_untyped_async(path).await { Ok(handle) => server.send_asset_event(InternalAssetEvent::Loaded { - id, + index, loaded_asset: LoadedAsset::new_with_dependencies(LoadedUntypedAsset { handle }) .into(), }), Err(err) => { error!("{err}"); server.send_asset_event(InternalAssetEvent::Failed { - id, + index, path: path_clone, error: err, }); @@ -607,7 +610,7 @@ impl AssetServer { }); #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))] - infos.pending_tasks.insert(handle.id().untyped(), task); + infos.pending_tasks.insert(index, task); #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))] task.detach(); @@ -670,7 +673,7 @@ impl AssetServer { // we cannot find the meta and loader if let Some(handle) = &input_handle { self.send_asset_event(InternalAssetEvent::Failed { - id: handle.id(), + index: handle.try_into().unwrap(), path: path.clone_owned(), error: e.clone(), }); @@ -681,11 +684,13 @@ impl AssetServer { (*meta_transform)(&mut *meta); } - let asset_id; // The asset ID of the asset we are trying to load. + let asset_id: Option; // The asset ID of the asset we are trying to load. let fetched_handle; // The handle if one was looked up/created. let should_load; // Whether we need to load the asset. if let Some(input_handle) = input_handle { - asset_id = Some(input_handle.id()); + // This must have been created with `get_or_create_path_handle_internal` at some point, + // which only produces Strong variant handles, so this is safe. + asset_id = Some((&input_handle).try_into().unwrap()); // In this case, we intentionally drop the input handle so we can cancel loading the // asset if the handle gets dropped (externally) before it finishes loading. fetched_handle = None; @@ -717,14 +722,16 @@ impl AssetServer { should_load = true; } Some((handle, result_should_load)) => { - asset_id = Some(handle.id()); + // `get_or_create_path_handle_internal` always returns Strong variant, so this + // is safe. + asset_id = Some((&handle).try_into().unwrap()); fetched_handle = Some(handle); should_load = result_should_load; } } } // Verify that the expected type matches the loader's type. - if let Some(asset_type_id) = asset_id.map(|id| id.type_id()) { + if let Some(asset_type_id) = asset_id.map(|id| id.type_id) { // If we are loading a subasset, then the subasset's type almost certainly doesn't match // the loader's type - and that's ok. if path.label().is_none() && asset_type_id != loader.asset_type_id() { @@ -761,7 +768,13 @@ impl AssetServer { None, ) .0; - (base_handle.id(), Some(base_handle), base_path) + ( + // `get_or_create_path_handle_erased` always returns Strong variant, so this is + // safe. + (&base_handle).try_into().unwrap(), + Some(base_handle), + base_path, + ) } else { (asset_id.unwrap(), None, path.clone()) }; @@ -804,7 +817,7 @@ impl AssetServer { } Err(err) => { self.send_asset_event(InternalAssetEvent::Failed { - id: base_asset_id, + index: base_asset_id, error: err.clone(), path: path.into_owned(), }); @@ -815,12 +828,19 @@ impl AssetServer { /// Sends a load event for the given `loaded_asset` and does the same recursively for all /// labeled assets. - fn send_loaded_asset(&self, id: UntypedAssetId, mut loaded_asset: ErasedLoadedAsset) { + fn send_loaded_asset(&self, index: ErasedAssetIndex, mut loaded_asset: ErasedLoadedAsset) { for (_, labeled_asset) in loaded_asset.labeled_assets.drain() { - self.send_loaded_asset(labeled_asset.handle.id(), labeled_asset.asset); + self.send_loaded_asset( + // Labeled assets are always Strong variant, so this is safe. + (&labeled_asset.handle).try_into().unwrap(), + labeled_asset.asset, + ); } - self.send_asset_event(InternalAssetEvent::Loaded { id, loaded_asset }); + self.send_asset_event(InternalAssetEvent::Loaded { + index, + loaded_asset, + }); } /// Kicks off a reload of the asset stored at the given path. This will only reload the asset if it currently loaded. @@ -894,7 +914,8 @@ impl AssetServer { ) }; self.send_asset_event(InternalAssetEvent::Loaded { - id: handle.id(), + // `get_or_create_path_handle_erased` always returns Strong variant, so this is safe. + index: (&handle).try_into().unwrap(), loaded_asset, }); handle @@ -917,7 +938,8 @@ impl AssetServer { #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))] drop(infos); - let id = handle.id(); + // `create_loading_handle_untyped` always returns a Strong variant, so this is safe. + let index = (&handle).try_into().unwrap(); let event_sender = self.data.asset_event_sender.clone(); @@ -926,7 +948,10 @@ impl AssetServer { Ok(asset) => { let loaded_asset = LoadedAsset::new_with_dependencies(asset).into(); event_sender - .send(InternalAssetEvent::Loaded { id, loaded_asset }) + .send(InternalAssetEvent::Loaded { + index, + loaded_asset, + }) .unwrap(); } Err(error) => { @@ -936,7 +961,7 @@ impl AssetServer { error!("{error}"); event_sender .send(InternalAssetEvent::Failed { - id, + index, path: Default::default(), error: AssetLoadError::AddAsyncError(error), }) @@ -946,7 +971,7 @@ impl AssetServer { }); #[cfg(not(any(target_arch = "wasm32", not(feature = "multi_threaded"))))] - infos.pending_tasks.insert(id, task); + infos.pending_tasks.insert(index, task); #[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))] task.detach(); @@ -977,13 +1002,14 @@ impl AssetServer { if !should_load { return handle; } - let id = handle.id().untyped(); - self.load_folder_internal(id, path); + // `get_or_create_path_handle` always returns a Strong variant, so this is safe. + let index = (&handle).try_into().unwrap(); + self.load_folder_internal(index, path); handle } - pub(crate) fn load_folder_internal(&self, id: UntypedAssetId, path: AssetPath) { + pub(crate) fn load_folder_internal(&self, index: ErasedAssetIndex, path: AssetPath) { async fn load_folder<'a>( source: AssetSourceId<'static>, path: &'a Path, @@ -1051,7 +1077,7 @@ impl AssetServer { let mut handles = Vec::new(); match load_folder(source.id(), path.path(), asset_reader, &server, &mut handles).await { Ok(_) => server.send_asset_event(InternalAssetEvent::Loaded { - id, + index, loaded_asset: LoadedAsset::new_with_dependencies( LoadedFolder { handles }, ) @@ -1059,7 +1085,7 @@ impl AssetServer { }), Err(err) => { error!("Failed to load folder. {err}"); - server.send_asset_event(InternalAssetEvent::Failed { id, error: err, path }); + server.send_asset_event(InternalAssetEvent::Failed { index, error: err, path }); }, } }) @@ -1075,7 +1101,11 @@ impl AssetServer { &self, id: impl Into, ) -> Option<(LoadState, DependencyLoadState, RecursiveDependencyLoadState)> { - self.data.infos.read().get(id.into()).map(|i| { + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return None; + }; + self.data.infos.read().get(index).map(|i| { ( i.load_state.clone(), i.dep_load_state.clone(), @@ -1090,10 +1120,14 @@ impl AssetServer { /// its dependencies or recursive dependencies, see [`AssetServer::get_dependency_load_state`] /// and [`AssetServer::get_recursive_dependency_load_state`] respectively. pub fn get_load_state(&self, id: impl Into) -> Option { + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return None; + }; self.data .infos .read() - .get(id.into()) + .get(index) .map(|i| i.load_state.clone()) } @@ -1106,10 +1140,14 @@ impl AssetServer { &self, id: impl Into, ) -> Option { + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return None; + }; self.data .infos .read() - .get(id.into()) + .get(index) .map(|i| i.dep_load_state.clone()) } @@ -1122,10 +1160,14 @@ impl AssetServer { &self, id: impl Into, ) -> Option { + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return None; + }; self.data .infos .read() - .get(id.into()) + .get(index) .map(|i| i.rec_dep_load_state.clone()) } @@ -1206,13 +1248,21 @@ impl AssetServer { /// Get an `UntypedHandle` from an `UntypedAssetId`. /// See [`AssetServer::get_id_handle`] for details. pub fn get_id_handle_untyped(&self, id: UntypedAssetId) -> Option { - self.data.infos.read().get_id_handle(id) + let Ok(index) = id.try_into() else { + // Always say we don't have Uuid assets. + return None; + }; + self.data.infos.read().get_index_handle(index) } /// Returns `true` if the given `id` corresponds to an asset that is managed by this [`AssetServer`]. /// Otherwise, returns `false`. pub fn is_managed(&self, id: impl Into) -> bool { - self.data.infos.read().contains_key(id.into()) + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return false; + }; + self.data.infos.read().contains_key(index) } /// Returns an active untyped asset id for the given path, if the asset at the given path has already started loading, @@ -1224,8 +1274,8 @@ impl AssetServer { pub fn get_path_id<'a>(&self, path: impl Into>) -> Option { let infos = self.data.infos.read(); let path = path.into(); - let mut ids = infos.get_path_ids(&path); - ids.next() + let mut ids = infos.get_path_indices(&path); + ids.next().map(Into::into) } /// Returns all active untyped asset IDs for the given path, if the assets at the given path have already started loading, @@ -1234,7 +1284,7 @@ impl AssetServer { pub fn get_path_ids<'a>(&self, path: impl Into>) -> Vec { let infos = self.data.infos.read(); let path = path.into(); - infos.get_path_ids(&path).collect() + infos.get_path_indices(&path).map(Into::into).collect() } /// Returns an active untyped handle for the given path, if the asset at the given path has already started loading, @@ -1273,8 +1323,12 @@ impl AssetServer { /// Returns the path for the given `id`, if it has one. pub fn get_path(&self, id: impl Into) -> Option { + let Ok(index) = id.into().try_into() else { + // Always say we don't have Uuid assets. + return None; + }; let infos = self.data.infos.read(); - let info = infos.get(id.into())?; + let info = infos.get(index)?; Some(info.path.as_ref()?.clone()) } @@ -1520,19 +1574,22 @@ impl AssetServer { &self, id: impl Into, ) -> Result<(), WaitForAssetError> { - let id = id.into(); - core::future::poll_fn(move |cx| self.wait_for_asset_id_poll_fn(cx, id)).await + let Ok(index) = id.into().try_into() else { + // Always say we aren't loading Uuid assets. + return Err(WaitForAssetError::NotLoaded); + }; + core::future::poll_fn(move |cx| self.wait_for_asset_id_poll_fn(cx, index)).await } /// Used by [`wait_for_asset_id`](AssetServer::wait_for_asset_id) in [`poll_fn`](core::future::poll_fn). fn wait_for_asset_id_poll_fn( &self, cx: &mut core::task::Context<'_>, - id: UntypedAssetId, + index: ErasedAssetIndex, ) -> Poll> { let infos = self.data.infos.read(); - let Some(info) = infos.get(id) else { + let Some(info) = infos.get(index) else { return Poll::Ready(Err(WaitForAssetError::NotLoaded)); }; @@ -1560,7 +1617,7 @@ impl AssetServer { self.data.infos.write() }; - let Some(info) = infos.get_mut(id) else { + let Some(info) = infos.get_mut(index) else { return Poll::Ready(Err(WaitForAssetError::NotLoaded)); }; @@ -1644,32 +1701,35 @@ pub fn handle_internal_asset_events(world: &mut World) { let mut untyped_failures = var_name; for event in server.data.asset_event_receiver.try_iter() { match event { - InternalAssetEvent::Loaded { id, loaded_asset } => { + InternalAssetEvent::Loaded { + index, + loaded_asset, + } => { infos.process_asset_load( - id, + index, loaded_asset, world, &server.data.asset_event_sender, ); } - InternalAssetEvent::LoadedWithDependencies { id } => { + InternalAssetEvent::LoadedWithDependencies { index } => { let sender = infos .dependency_loaded_event_sender - .get(&id.type_id()) + .get(&index.type_id) .expect("Asset event sender should exist"); - sender(world, id); - if let Some(info) = infos.get_mut(id) { + sender(world, index.index); + if let Some(info) = infos.get_mut(index) { for waker in info.waiting_tasks.drain(..) { waker.wake(); } } } - InternalAssetEvent::Failed { id, path, error } => { - infos.process_asset_fail(id, error.clone()); + InternalAssetEvent::Failed { index, path, error } => { + infos.process_asset_fail(index, error.clone()); // Send untyped failure event untyped_failures.push(UntypedAssetLoadFailedEvent { - id, + id: index.into(), path: path.clone(), error: error.clone(), }); @@ -1677,9 +1737,9 @@ pub fn handle_internal_asset_events(world: &mut World) { // Send typed failure event let sender = infos .dependency_failed_event_sender - .get(&id.type_id()) + .get(&index.type_id) .expect("Asset failed event sender should exist"); - sender(world, id, path, error); + sender(world, index.index, path, error); } } } @@ -1709,7 +1769,9 @@ pub fn handle_internal_asset_events(world: &mut World) { AssetPath::from(current_folder.clone()).with_source(source.clone()); for folder_handle in infos.get_path_handles(&parent_asset_path) { info!("Reloading folder {parent_asset_path} because the content has changed"); - server.load_folder_internal(folder_handle.id(), parent_asset_path.clone()); + // `get_path_handles` only returns Strong variants, so this is safe. + let index = (&folder_handle).try_into().unwrap(); + server.load_folder_internal(index, parent_asset_path.clone()); } } }; @@ -1772,14 +1834,14 @@ pub fn handle_internal_asset_events(world: &mut World) { /// Internal events for asset load results pub(crate) enum InternalAssetEvent { Loaded { - id: UntypedAssetId, + index: ErasedAssetIndex, loaded_asset: ErasedLoadedAsset, }, LoadedWithDependencies { - id: UntypedAssetId, + index: ErasedAssetIndex, }, Failed { - id: UntypedAssetId, + index: ErasedAssetIndex, path: AssetPath<'static>, error: AssetLoadError, },