diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index 6bc72d1a5c..b133d79bca 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -5,7 +5,7 @@ use crate::{ use alloc::sync::Arc; use bevy_ecs::template::{FromTemplate, SpecializeFromTemplate, Template, TemplateContext}; use bevy_platform::{collections::Equivalent, sync::Mutex}; -use bevy_reflect::{Reflect, TypePath}; +use bevy_reflect::{enums::Enum, FromReflect, PartialReflect, Reflect, ReflectRef, TypePath}; use core::{ any::TypeId, hash::{Hash, Hasher}, @@ -130,7 +130,7 @@ impl core::fmt::Debug for StrongHandle { /// /// [`Handle::Strong`], via [`StrongHandle`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists). #[derive(Reflect)] -#[reflect(Debug, Hash, PartialEq, Clone, Handle)] +#[reflect(Debug, Hash, PartialEq, Clone, Handle, from_reflect = false)] pub enum Handle { /// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept /// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata. @@ -140,6 +140,43 @@ pub enum Handle { Uuid(Uuid, #[reflect(ignore, clone)] PhantomData A>), } +// `Handle` needs a custom `FromReflect` to do extra type checking - see the +// `strong_handle.type_id` check below. +// `Handle` needs a custom `FromReflect` to do extra type checking - see the +// `strong_handle.type_id` check below. +impl FromReflect for Handle +where + Handle: Send + Sync, + A: TypePath, +{ + fn from_reflect(reflect_value: &dyn PartialReflect) -> Option { + let ReflectRef::Enum(enum_value) = PartialReflect::reflect_ref(reflect_value) else { + return None; + }; + + match Enum::variant_name(enum_value) { + "Strong" => { + let strong_field = enum_value.field_at(0usize)?; + let strong_handle = Arc::::from_reflect(strong_field)?; + + // This is necessary as otherwise you could construct Handle via Handle + if strong_handle.type_id != TypeId::of::() { + return None; + } + + Some(Handle::Strong(strong_handle)) + } + "Uuid" => { + let uuid_field = enum_value.field_at(0usize)?; + let uuid = Uuid::from_reflect(uuid_field)?; + + Some(Handle::Uuid(uuid, Default::default())) + } + _ => None, + } + } +} + impl Clone for Handle { fn clone(&self) -> Self { match self { @@ -864,4 +901,43 @@ mod tests { _ => panic!("Expected a strong handle"), } } + + #[test] + fn handle_from_reflect_verifies_type_id() { + use crate::{AssetApp, Assets}; + use bevy_reflect::FromReflect; + + #[derive(Reflect, Asset)] + struct A; + #[derive(Reflect, Asset)] + struct B; + + let mut app = create_app().0; + app.init_asset::().init_asset::(); + + let mut assets = app.world_mut().resource_mut::>(); + let handle_a = assets.add(A); + + let dynamic_handle_a = handle_a.to_dynamic(); + let reflected_handle_a = handle_a.as_partial_reflect(); + + let handle_b_from_reflect_dynamic: Option> = + FromReflect::from_reflect(&*dynamic_handle_a); + let handle_b_from_reflect: Option> = + FromReflect::from_reflect(reflected_handle_a); + let handle_a_from_reflect: Option> = + FromReflect::from_reflect(reflected_handle_a); + assert!( + handle_b_from_reflect.is_none(), + "Handle should not be constructible from reflected Handle" + ); + assert!( + handle_b_from_reflect_dynamic.is_none(), + "Handle should not be constructible from dynamic Handle" + ); + assert!( + handle_a_from_reflect.is_some(), + "Handle should be constructible from reflected Handle" + ); + } } diff --git a/crates/bevy_reflect/derive/src/lib.rs b/crates/bevy_reflect/derive/src/lib.rs index ff950c80d3..a19f8834ee 100644 --- a/crates/bevy_reflect/derive/src/lib.rs +++ b/crates/bevy_reflect/derive/src/lib.rs @@ -123,7 +123,8 @@ fn match_reflect_impls(ast: DeriveInput, source: ReflectImplSource) -> TokenStre /// It will automatically generate implementations for `Reflect`, `Typed`, `GetTypeRegistration`, and `FromReflect`. /// And, depending on the item's structure, will either implement `Struct`, `TupleStruct`, or `Enum`. /// -/// See the [`FromReflect`] derive macro for more information on how to customize the `FromReflect` implementation. +/// See the [`FromReflect`] derive macro for more information on how to customize the [`FromReflect`] implementation. +/// To implement [`FromReflect`] manually while deriving [`Reflect`], [opt out](#reflectfrom_reflect--false) of the default implementation. /// /// # Container Attributes ///