bevy_asset/
reflect.rs

1use alloc::boxed::Box;
2use core::any::{Any, TypeId};
3
4use bevy_ecs::world::{unsafe_world_cell::UnsafeWorldCell, World};
5use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect};
6
7use crate::{
8    Asset, AssetId, Assets, Handle, InvalidGenerationError, UntypedAssetId, UntypedHandle,
9};
10
11/// Type data for the [`TypeRegistry`](bevy_reflect::TypeRegistry) used to operate on reflected [`Asset`]s.
12///
13/// This type provides similar methods to [`Assets<T>`] like [`get`](ReflectAsset::get),
14/// [`add`](ReflectAsset::add) and [`remove`](ReflectAsset::remove), but can be used in situations where you don't know which asset type `T` you want
15/// until runtime.
16///
17/// [`ReflectAsset`] can be obtained via [`TypeRegistration::data`](bevy_reflect::TypeRegistration::data) if the asset was registered using [`register_asset_reflect`](crate::AssetApp::register_asset_reflect).
18#[derive(Clone)]
19pub struct ReflectAsset {
20    handle_type_id: TypeId,
21    assets_resource_type_id: TypeId,
22
23    get: fn(&World, UntypedAssetId) -> Option<&dyn Reflect>,
24    // SAFETY:
25    // - may only be called with an [`UnsafeWorldCell`] which can be used to access the corresponding `Assets<T>` resource mutably
26    // - may only be used to access **at most one** access at once
27    get_unchecked_mut: unsafe fn(UnsafeWorldCell<'_>, UntypedAssetId) -> Option<&mut dyn Reflect>,
28    add: fn(&mut World, &dyn PartialReflect) -> UntypedHandle,
29    insert:
30        fn(&mut World, UntypedAssetId, &dyn PartialReflect) -> Result<(), InvalidGenerationError>,
31    len: fn(&World) -> usize,
32    ids: for<'w> fn(&'w World) -> Box<dyn Iterator<Item = UntypedAssetId> + 'w>,
33    remove: fn(&mut World, UntypedAssetId) -> Option<Box<dyn Reflect>>,
34}
35
36impl ReflectAsset {
37    /// The [`TypeId`] of the [`Handle<T>`] for this asset
38    pub fn handle_type_id(&self) -> TypeId {
39        self.handle_type_id
40    }
41
42    /// The [`TypeId`] of the [`Assets<T>`] resource
43    pub fn assets_resource_type_id(&self) -> TypeId {
44        self.assets_resource_type_id
45    }
46
47    /// Equivalent of [`Assets::get`]
48    pub fn get<'w>(
49        &self,
50        world: &'w World,
51        asset_id: impl Into<UntypedAssetId>,
52    ) -> Option<&'w dyn Reflect> {
53        (self.get)(world, asset_id.into())
54    }
55
56    /// Equivalent of [`Assets::get_mut`]
57    pub fn get_mut<'w>(
58        &self,
59        world: &'w mut World,
60        asset_id: impl Into<UntypedAssetId>,
61    ) -> Option<&'w mut dyn Reflect> {
62        // SAFETY: unique world access
63        #[expect(
64            unsafe_code,
65            reason = "Use of unsafe `Self::get_unchecked_mut()` function."
66        )]
67        unsafe {
68            (self.get_unchecked_mut)(world.as_unsafe_world_cell(), asset_id.into())
69        }
70    }
71
72    /// Equivalent of [`Assets::get_mut`], but works with an [`UnsafeWorldCell`].
73    ///
74    /// Only use this method when you have ensured that you are the *only* one with access to the [`Assets`] resource of the asset type.
75    /// Furthermore, this does *not* allow you to have look up two distinct handles,
76    /// you can only have at most one alive at the same time.
77    /// This means that this is *not allowed*:
78    /// ```no_run
79    /// # use bevy_asset::{ReflectAsset, UntypedHandle};
80    /// # use bevy_ecs::prelude::World;
81    /// # let reflect_asset: ReflectAsset = unimplemented!();
82    /// # let mut world: World = unimplemented!();
83    /// # let handle_1: UntypedHandle = unimplemented!();
84    /// # let handle_2: UntypedHandle = unimplemented!();
85    /// let unsafe_world_cell = world.as_unsafe_world_cell();
86    /// let a = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, &handle_1).unwrap() };
87    /// let b = unsafe { reflect_asset.get_unchecked_mut(unsafe_world_cell, &handle_2).unwrap() };
88    /// // ^ not allowed, two mutable references through the same asset resource, even though the
89    /// // handles are distinct
90    ///
91    /// println!("a = {a:?}, b = {b:?}");
92    /// ```
93    ///
94    /// # Safety
95    /// This method does not prevent you from having two mutable pointers to the same data,
96    /// violating Rust's aliasing rules. To avoid this:
97    /// * Only call this method if you know that the [`UnsafeWorldCell`] may be used to access the corresponding `Assets<T>`
98    /// * Don't call this method more than once in the same scope.
99    #[expect(
100        unsafe_code,
101        reason = "This function calls unsafe code and has safety requirements."
102    )]
103    pub unsafe fn get_unchecked_mut<'w>(
104        &self,
105        world: UnsafeWorldCell<'w>,
106        asset_id: impl Into<UntypedAssetId>,
107    ) -> Option<&'w mut dyn Reflect> {
108        // SAFETY: requirements are deferred to the caller
109        unsafe { (self.get_unchecked_mut)(world, asset_id.into()) }
110    }
111
112    /// Equivalent of [`Assets::add`]
113    pub fn add(&self, world: &mut World, value: &dyn PartialReflect) -> UntypedHandle {
114        (self.add)(world, value)
115    }
116    /// Equivalent of [`Assets::insert`]
117    pub fn insert(
118        &self,
119        world: &mut World,
120        asset_id: impl Into<UntypedAssetId>,
121        value: &dyn PartialReflect,
122    ) -> Result<(), InvalidGenerationError> {
123        (self.insert)(world, asset_id.into(), value)
124    }
125
126    /// Equivalent of [`Assets::remove`]
127    pub fn remove(
128        &self,
129        world: &mut World,
130        asset_id: impl Into<UntypedAssetId>,
131    ) -> Option<Box<dyn Reflect>> {
132        (self.remove)(world, asset_id.into())
133    }
134
135    /// Equivalent of [`Assets::len`]
136    pub fn len(&self, world: &World) -> usize {
137        (self.len)(world)
138    }
139
140    /// Equivalent of [`Assets::is_empty`]
141    pub fn is_empty(&self, world: &World) -> bool {
142        self.len(world) == 0
143    }
144
145    /// Equivalent of [`Assets::ids`]
146    pub fn ids<'w>(&self, world: &'w World) -> impl Iterator<Item = UntypedAssetId> + 'w {
147        (self.ids)(world)
148    }
149}
150
151impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
152    fn from_type() -> Self {
153        ReflectAsset {
154            handle_type_id: TypeId::of::<Handle<A>>(),
155            assets_resource_type_id: TypeId::of::<Assets<A>>(),
156            get: |world, asset_id| {
157                let assets = world.resource::<Assets<A>>();
158                let asset = assets.get(asset_id.typed_debug_checked());
159                asset.map(|asset| asset as &dyn Reflect)
160            },
161            get_unchecked_mut: |world, asset_id| {
162                // SAFETY: `get_unchecked_mut` must be called with `UnsafeWorldCell` having access to `Assets<A>`,
163                // and must ensure to only have at most one reference to it live at all times.
164                #[expect(unsafe_code, reason = "Uses `UnsafeWorldCell::get_resource_mut()`.")]
165                let assets = unsafe { world.get_resource_mut::<Assets<A>>().unwrap().into_inner() };
166                let asset = assets.get_mut(asset_id.typed_debug_checked());
167                asset.map(|asset| asset as &mut dyn Reflect)
168            },
169            add: |world, value| {
170                let mut assets = world.resource_mut::<Assets<A>>();
171                let value: A = FromReflect::from_reflect(value)
172                    .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::add`");
173                assets.add(value).untyped()
174            },
175            insert: |world, asset_id, value| {
176                let mut assets = world.resource_mut::<Assets<A>>();
177                let value: A = FromReflect::from_reflect(value)
178                    .expect("could not call `FromReflect::from_reflect` in `ReflectAsset::set`");
179                assets.insert(asset_id.typed_debug_checked(), value)
180            },
181            len: |world| {
182                let assets = world.resource::<Assets<A>>();
183                assets.len()
184            },
185            ids: |world| {
186                let assets = world.resource::<Assets<A>>();
187                Box::new(assets.ids().map(AssetId::untyped))
188            },
189            remove: |world, asset_id| {
190                let mut assets = world.resource_mut::<Assets<A>>();
191                let value = assets.remove(asset_id.typed_debug_checked());
192                value.map(|value| Box::new(value) as Box<dyn Reflect>)
193            },
194        }
195    }
196}
197
198/// Reflect type data struct relating a [`Handle<T>`] back to the `T` asset type.
199///
200/// Say you want to look up the asset values of a list of handles when you have access to their `&dyn Reflect` form.
201/// Assets can be looked up in the world using [`ReflectAsset`], but how do you determine which [`ReflectAsset`] to use when
202/// only looking at the handle? [`ReflectHandle`] is stored in the type registry on each `Handle<T>` type, so you can use [`ReflectHandle::asset_type_id`] to look up
203/// the [`ReflectAsset`] type data on the corresponding `T` asset type:
204///
205///
206/// ```no_run
207/// # use bevy_reflect::{TypeRegistry, prelude::*};
208/// # use bevy_ecs::prelude::*;
209/// use bevy_asset::{ReflectHandle, ReflectAsset};
210///
211/// # let world: &World = unimplemented!();
212/// # let type_registry: TypeRegistry = unimplemented!();
213/// let handles: Vec<&dyn Reflect> = unimplemented!();
214/// for handle in handles {
215///     let reflect_handle = type_registry.get_type_data::<ReflectHandle>(handle.type_id()).unwrap();
216///     let reflect_asset = type_registry.get_type_data::<ReflectAsset>(reflect_handle.asset_type_id()).unwrap();
217///
218///     let handle = reflect_handle.downcast_handle_untyped(handle.as_any()).unwrap();
219///     let value = reflect_asset.get(world, &handle).unwrap();
220///     println!("{value:?}");
221/// }
222/// ```
223#[derive(Clone)]
224pub struct ReflectHandle {
225    asset_type_id: TypeId,
226    downcast_handle_untyped: fn(&dyn Any) -> Option<UntypedHandle>,
227    typed: fn(UntypedHandle) -> Box<dyn Reflect>,
228}
229
230impl ReflectHandle {
231    /// The [`TypeId`] of the asset
232    pub fn asset_type_id(&self) -> TypeId {
233        self.asset_type_id
234    }
235
236    /// A way to go from a [`Handle<T>`] in a `dyn Any` to a [`UntypedHandle`]
237    pub fn downcast_handle_untyped(&self, handle: &dyn Any) -> Option<UntypedHandle> {
238        (self.downcast_handle_untyped)(handle)
239    }
240
241    /// A way to go from a [`UntypedHandle`] to a [`Handle<T>`] in a `Box<dyn Reflect>`.
242    /// Equivalent of [`UntypedHandle::typed`].
243    pub fn typed(&self, handle: UntypedHandle) -> Box<dyn Reflect> {
244        (self.typed)(handle)
245    }
246}
247
248impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
249    fn from_type() -> Self {
250        ReflectHandle {
251            asset_type_id: TypeId::of::<A>(),
252            downcast_handle_untyped: |handle: &dyn Any| {
253                handle
254                    .downcast_ref::<Handle<A>>()
255                    .map(|h| h.clone().untyped())
256            },
257            typed: |handle: UntypedHandle| Box::new(handle.typed_debug_checked::<A>()),
258        }
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use alloc::{string::String, vec::Vec};
265    use core::any::TypeId;
266
267    use crate::{Asset, AssetApp, AssetPlugin, ReflectAsset};
268    use bevy_app::App;
269    use bevy_ecs::reflect::AppTypeRegistry;
270    use bevy_reflect::Reflect;
271
272    #[derive(Asset, Reflect)]
273    struct AssetType {
274        field: String,
275    }
276
277    #[test]
278    fn test_reflect_asset_operations() {
279        let mut app = App::new();
280        app.add_plugins(AssetPlugin::default())
281            .init_asset::<AssetType>()
282            .register_asset_reflect::<AssetType>();
283
284        let reflect_asset = {
285            let type_registry = app.world().resource::<AppTypeRegistry>();
286            let type_registry = type_registry.read();
287
288            type_registry
289                .get_type_data::<ReflectAsset>(TypeId::of::<AssetType>())
290                .unwrap()
291                .clone()
292        };
293
294        let value = AssetType {
295            field: "test".into(),
296        };
297
298        let handle = reflect_asset.add(app.world_mut(), &value);
299        // struct is a reserved keyword, so we can't use it here
300        let strukt = reflect_asset
301            .get_mut(app.world_mut(), &handle)
302            .unwrap()
303            .reflect_mut()
304            .as_struct()
305            .unwrap();
306        strukt
307            .field_mut("field")
308            .unwrap()
309            .apply(&String::from("edited"));
310
311        assert_eq!(reflect_asset.len(app.world()), 1);
312        let ids: Vec<_> = reflect_asset.ids(app.world()).collect();
313        assert_eq!(ids.len(), 1);
314        let id = ids[0];
315
316        let asset = reflect_asset.get(app.world(), id).unwrap();
317        assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
318
319        reflect_asset.remove(app.world_mut(), id).unwrap();
320        assert_eq!(reflect_asset.len(app.world()), 0);
321    }
322}