@@ -10,6 +10,7 @@ use crate::{
1010 error:: InteropError , reflection_extensions:: PartialReflectExt , with_access_read,
1111 with_access_write,
1212} ;
13+ use bevy_asset:: { ReflectAsset , UntypedHandle } ;
1314use bevy_ecs:: { component:: Component , ptr:: Ptr , resource:: Resource } ;
1415use bevy_mod_scripting_derive:: DebugWithTypeInfo ;
1516use bevy_mod_scripting_display:: {
@@ -252,6 +253,61 @@ impl ReflectReference {
252253 }
253254 }
254255
256+ /// Create a new reference to an asset by untyped handle.
257+ /// If the type id is incorrect, you will get runtime errors when trying to access the value.
258+ pub fn new_asset_ref (
259+ handle : UntypedHandle ,
260+ asset_type_id : TypeId ,
261+ world : WorldGuard ,
262+ ) -> Result < Self , InteropError > {
263+ Ok ( Self {
264+ base : ReflectBaseType :: new_asset_base ( handle, asset_type_id, world) ?,
265+ reflect_path : ParsedPath ( Vec :: default ( ) ) ,
266+ } )
267+ }
268+
269+ /// Tries get an untyped asset handle from this reference.
270+ pub fn try_untyped_asset_handle (
271+ & self ,
272+ world : WorldGuard ,
273+ ) -> Result < UntypedHandle , InteropError > {
274+ let handle_type_id = self . tail_type_id ( world. clone ( ) ) ?. ok_or_else ( || {
275+ InteropError :: invariant ( "Cannot determine handle type ID from reflection" )
276+ . with_context ( "Asset handle reflection failed - handle may be invalid or corrupted" )
277+ } ) ?;
278+
279+ let type_registry = world. type_registry ( ) ;
280+ let type_registry = type_registry. read ( ) ;
281+ let reflect_handle = type_registry
282+ . get_type_data :: < bevy_asset:: ReflectHandle > ( handle_type_id)
283+ . ok_or_else ( || {
284+ InteropError :: missing_type_data (
285+ handle_type_id,
286+ "ReflectHandle" . to_string ( ) ,
287+ )
288+ . with_context ( "Handle type is not registered for asset operations - ensure the asset type is properly registered with ReflectHandle type data" )
289+ } ) ?;
290+
291+ let untyped_handle = self . with_reflect ( world. clone ( ) , |reflect| {
292+ let reflect_any = reflect. try_as_reflect ( ) . ok_or_else ( || {
293+ InteropError :: type_mismatch (
294+ std:: any:: TypeId :: of :: < dyn bevy_reflect:: Reflect > ( ) ,
295+ Some ( handle_type_id) ,
296+ )
297+ . with_context ( "Handle must implement Reflect trait for asset operations" )
298+ } ) ?;
299+
300+ reflect_handle
301+ . downcast_handle_untyped ( reflect_any. as_any ( ) )
302+ . ok_or_else ( || {
303+ InteropError :: invariant ( "Failed to get UntypedHandle" )
304+ . with_context ( "Handle downcast failed - handle may be of wrong type or corrupted" )
305+ } )
306+ } ) ??;
307+
308+ Ok ( untyped_handle)
309+ }
310+
255311 /// Indexes into the reflect path inside this reference.
256312 /// You can use [`Self::reflect`] and [`Self::reflect_mut`] to get the actual value.
257313 pub fn index_path < T : Into < ParsedPath > > ( & mut self , index : T ) {
@@ -399,6 +455,11 @@ impl ReflectReference {
399455 return self . walk_path ( unsafe { & * arc. get_ptr ( ) } ) ;
400456 }
401457
458+ if let ReflectBase :: Asset ( handle, _) = & self . base . base_id {
459+ let asset = unsafe { self . load_asset_mut ( handle, world. clone ( ) ) ? } ;
460+ return self . walk_path ( asset. as_partial_reflect ( ) ) ;
461+ }
462+
402463 let type_registry = world. type_registry ( ) ;
403464 let type_registry = type_registry. read ( ) ;
404465
@@ -454,6 +515,11 @@ impl ReflectReference {
454515 return self . walk_path_mut ( unsafe { & mut * arc. get_ptr ( ) } ) ;
455516 } ;
456517
518+ if let ReflectBase :: Asset ( handle, _) = & self . base . base_id {
519+ let asset = unsafe { self . load_asset_mut ( handle, world. clone ( ) ) ? } ;
520+ return self . walk_path_mut ( asset. as_partial_reflect_mut ( ) ) ;
521+ } ;
522+
457523 let type_registry = world. type_registry ( ) ;
458524 let type_registry = type_registry. read ( ) ;
459525
@@ -486,6 +552,32 @@ impl ReflectReference {
486552 self . walk_path_mut ( base. as_partial_reflect_mut ( ) )
487553 }
488554
555+ /// Get asset from world and return a mutable reference to it
556+ unsafe fn load_asset_mut < ' w > (
557+ & self ,
558+ handle : & UntypedHandle ,
559+ world : WorldGuard < ' w > ,
560+ ) -> Result < & ' w mut dyn Reflect , InteropError > {
561+ let type_registry = world. type_registry ( ) ;
562+ let type_registry = type_registry. read ( ) ;
563+
564+ let reflect_asset: & ReflectAsset = type_registry
565+ . get_type_data ( self . base . type_id )
566+ . ok_or_else ( || InteropError :: unregistered_base ( self . base . clone ( ) ) ) ?;
567+
568+ let world_cell = world. as_unsafe_world_cell ( ) ?;
569+ let asset = unsafe { reflect_asset. get_unchecked_mut ( world_cell, handle. clone ( ) ) }
570+ . ok_or_else ( || {
571+ InteropError :: unsupported_operation (
572+ Some ( self . base . type_id ) ,
573+ None ,
574+ "Asset not loaded or handle is invalid" ,
575+ )
576+ } ) ?;
577+
578+ Ok ( asset)
579+ }
580+
489581 fn walk_path < ' a > (
490582 & self ,
491583 root : & ' a dyn PartialReflect ,
@@ -589,6 +681,47 @@ impl ReflectBaseType {
589681 ) ) ,
590682 }
591683 }
684+
685+ /// Create a new reflection base pointing to an asset with untyped handle
686+ pub fn new_asset_base (
687+ handle : UntypedHandle ,
688+ asset_type_id : TypeId ,
689+ world : WorldGuard ,
690+ ) -> Result < Self , InteropError > {
691+ // We need to get the Assets<T> resource ComponentId by type registry lookup
692+ let type_registry = world. type_registry ( ) ;
693+ let type_registry = type_registry. read ( ) ;
694+
695+ // Get the ReflectAsset data to find the Assets<T> resource type ID
696+ let reflect_asset: & ReflectAsset =
697+ type_registry. get_type_data ( asset_type_id) . ok_or_else ( || {
698+ InteropError :: unsupported_operation (
699+ Some ( asset_type_id) ,
700+ None ,
701+ "Asset type is not registered with ReflectAsset type data" ,
702+ )
703+ } ) ?;
704+
705+ let assets_resource_type_id = reflect_asset. assets_resource_type_id ( ) ;
706+
707+ // Convert the TypeId to ComponentId via unsafe world cell
708+ let world_cell = world. as_unsafe_world_cell ( ) ?;
709+ let components = world_cell. components ( ) ;
710+ let assets_resource_id = components
711+ . get_resource_id ( assets_resource_type_id)
712+ . ok_or_else ( || {
713+ InteropError :: unsupported_operation (
714+ Some ( assets_resource_type_id) ,
715+ None ,
716+ "Assets<T> resource is not registered in the world" ,
717+ )
718+ } ) ?;
719+
720+ Ok ( Self {
721+ type_id : asset_type_id,
722+ base_id : ReflectBase :: Asset ( handle, assets_resource_id) ,
723+ } )
724+ }
592725}
593726
594727/// The Id of the kind of reflection base being pointed to
@@ -599,8 +732,10 @@ pub enum ReflectBase {
599732 Component ( Entity , ComponentId ) ,
600733 /// A resource
601734 Resource ( ComponentId ) ,
602- /// an allocation
735+ /// An allocation
603736 Owned ( ReflectAllocationId ) ,
737+ /// An asset accessed by handle
738+ Asset ( UntypedHandle , ComponentId ) ,
604739}
605740
606741impl DisplayWithTypeInfo for ReflectBase {
@@ -655,6 +790,13 @@ impl DisplayWithTypeInfo for ReflectBase {
655790 WithTypeInfo :: new_with_opt_info ( id, type_info_provider)
656791 . display_with_type_info ( f, type_info_provider)
657792 }
793+ ReflectBase :: Asset ( handle, assets_resource_id) => {
794+ f. write_str ( "asset with handle: " ) ?;
795+ write ! ( f, "{:?}" , handle) ?;
796+ f. write_str ( ", in Assets resource: " ) ?;
797+ WithTypeInfo :: new_with_opt_info ( assets_resource_id, type_info_provider)
798+ . display_with_type_info ( f, type_info_provider)
799+ }
658800 }
659801 }
660802}
0 commit comments