bevy_ecs/reflect/
bundle.rs

1//! Definitions for [`Bundle`] reflection.
2//! This allows inserting, updating and/or removing bundles whose type is only known at runtime.
3//!
4//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
5//!
6//! Same as [`super::component`], but for bundles.
7use alloc::boxed::Box;
8use core::any::{Any, TypeId};
9
10use crate::{
11    bundle::BundleFromComponents,
12    entity::EntityMapper,
13    prelude::Bundle,
14    relationship::RelationshipHookMode,
15    world::{EntityMut, EntityWorldMut},
16};
17use bevy_reflect::{
18    FromReflect, FromType, PartialReflect, Reflect, ReflectRef, TypePath, TypeRegistry,
19};
20
21use super::{from_reflect_with_fallback, ReflectComponent};
22
23/// A struct used to operate on reflected [`Bundle`] trait of a type.
24///
25/// A [`ReflectBundle`] for type `T` can be obtained via
26/// [`bevy_reflect::TypeRegistration::data`].
27#[derive(Clone)]
28pub struct ReflectBundle(ReflectBundleFns);
29
30/// The raw function pointers needed to make up a [`ReflectBundle`].
31///
32/// The also [`super::component::ReflectComponentFns`].
33#[derive(Clone)]
34pub struct ReflectBundleFns {
35    /// Function pointer implementing [`ReflectBundle::insert`].
36    pub insert: fn(&mut EntityWorldMut, &dyn PartialReflect, &TypeRegistry),
37    /// Function pointer implementing [`ReflectBundle::apply`].
38    pub apply: fn(EntityMut, &dyn PartialReflect, &TypeRegistry),
39    /// Function pointer implementing [`ReflectBundle::apply_or_insert_mapped`].
40    pub apply_or_insert_mapped: fn(
41        &mut EntityWorldMut,
42        &dyn PartialReflect,
43        &TypeRegistry,
44        &mut dyn EntityMapper,
45        RelationshipHookMode,
46    ),
47    /// Function pointer implementing [`ReflectBundle::remove`].
48    pub remove: fn(&mut EntityWorldMut),
49    /// Function pointer implementing [`ReflectBundle::take`].
50    pub take: fn(&mut EntityWorldMut) -> Option<Box<dyn Reflect>>,
51}
52
53impl ReflectBundleFns {
54    /// Get the default set of [`ReflectBundleFns`] for a specific bundle type using its
55    /// [`FromType`] implementation.
56    ///
57    /// This is useful if you want to start with the default implementation before overriding some
58    /// of the functions to create a custom implementation.
59    pub fn new<T: Bundle + FromReflect + TypePath + BundleFromComponents>() -> Self {
60        <ReflectBundle as FromType<T>>::from_type().0
61    }
62}
63
64impl ReflectBundle {
65    /// Insert a reflected [`Bundle`] into the entity like [`insert()`](EntityWorldMut::insert).
66    pub fn insert(
67        &self,
68        entity: &mut EntityWorldMut,
69        bundle: &dyn PartialReflect,
70        registry: &TypeRegistry,
71    ) {
72        (self.0.insert)(entity, bundle, registry);
73    }
74
75    /// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value.
76    ///
77    /// # Panics
78    ///
79    /// Panics if there is no [`Bundle`] of the given type.
80    pub fn apply<'a>(
81        &self,
82        entity: impl Into<EntityMut<'a>>,
83        bundle: &dyn PartialReflect,
84        registry: &TypeRegistry,
85    ) {
86        (self.0.apply)(entity.into(), bundle, registry);
87    }
88
89    /// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value or insert a new one if it does not exist.
90    pub fn apply_or_insert_mapped(
91        &self,
92        entity: &mut EntityWorldMut,
93        bundle: &dyn PartialReflect,
94        registry: &TypeRegistry,
95        mapper: &mut dyn EntityMapper,
96        relationship_hook_mode: RelationshipHookMode,
97    ) {
98        (self.0.apply_or_insert_mapped)(entity, bundle, registry, mapper, relationship_hook_mode);
99    }
100
101    /// Removes this [`Bundle`] type from the entity. Does nothing if it doesn't exist.
102    pub fn remove(&self, entity: &mut EntityWorldMut) -> &ReflectBundle {
103        (self.0.remove)(entity);
104        self
105    }
106
107    /// Removes all components in the [`Bundle`] from the entity and returns their previous values.
108    ///
109    /// **Note:** If the entity does not have every component in the bundle, this method will not remove any of them.
110    #[must_use]
111    pub fn take(&self, entity: &mut EntityWorldMut) -> Option<Box<dyn Reflect>> {
112        (self.0.take)(entity)
113    }
114
115    /// Create a custom implementation of [`ReflectBundle`].
116    ///
117    /// This is an advanced feature,
118    /// useful for scripting implementations,
119    /// that should not be used by most users
120    /// unless you know what you are doing.
121    ///
122    /// Usually you should derive [`Reflect`] and add the `#[reflect(Bundle)]` bundle
123    /// to generate a [`ReflectBundle`] implementation automatically.
124    ///
125    /// See [`ReflectBundleFns`] for more information.
126    pub fn new(fns: ReflectBundleFns) -> Self {
127        Self(fns)
128    }
129
130    /// The underlying function pointers implementing methods on `ReflectBundle`.
131    ///
132    /// This is useful when you want to keep track locally of an individual
133    /// function pointer.
134    ///
135    /// Calling [`TypeRegistry::get`] followed by
136    /// [`TypeRegistration::data::<ReflectBundle>`] can be costly if done several
137    /// times per frame. Consider cloning [`ReflectBundle`] and keeping it
138    /// between frames, cloning a `ReflectBundle` is very cheap.
139    ///
140    /// If you only need a subset of the methods on `ReflectBundle`,
141    /// use `fn_pointers` to get the underlying [`ReflectBundleFns`]
142    /// and copy the subset of function pointers you care about.
143    ///
144    /// [`TypeRegistration::data::<ReflectBundle>`]: bevy_reflect::TypeRegistration::data
145    pub fn fn_pointers(&self) -> &ReflectBundleFns {
146        &self.0
147    }
148}
149
150impl<B: Bundle + Reflect + TypePath + BundleFromComponents> FromType<B> for ReflectBundle {
151    fn from_type() -> Self {
152        ReflectBundle(ReflectBundleFns {
153            insert: |entity, reflected_bundle, registry| {
154                let bundle = entity.world_scope(|world| {
155                    from_reflect_with_fallback::<B>(reflected_bundle, world, registry)
156                });
157                entity.insert(bundle);
158            },
159            apply: |mut entity, reflected_bundle, registry| {
160                if let Some(reflect_component) =
161                    registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
162                {
163                    reflect_component.apply(entity, reflected_bundle);
164                } else {
165                    match reflected_bundle.reflect_ref() {
166                        ReflectRef::Struct(bundle) => bundle
167                            .iter_fields()
168                            .for_each(|field| apply_field(&mut entity, field, registry)),
169                        ReflectRef::Tuple(bundle) => bundle
170                            .iter_fields()
171                            .for_each(|field| apply_field(&mut entity, field, registry)),
172                        _ => panic!(
173                            "expected bundle `{}` to be named struct or tuple",
174                            // FIXME: once we have unique reflect, use `TypePath`.
175                            core::any::type_name::<B>(),
176                        ),
177                    }
178                }
179            },
180            apply_or_insert_mapped: |entity,
181                                     reflected_bundle,
182                                     registry,
183                                     mapper,
184                                     relationship_hook_mode| {
185                if let Some(reflect_component) =
186                    registry.get_type_data::<ReflectComponent>(TypeId::of::<B>())
187                {
188                    reflect_component.apply_or_insert_mapped(
189                        entity,
190                        reflected_bundle,
191                        registry,
192                        mapper,
193                        relationship_hook_mode,
194                    );
195                } else {
196                    match reflected_bundle.reflect_ref() {
197                        ReflectRef::Struct(bundle) => bundle.iter_fields().for_each(|field| {
198                            apply_or_insert_field_mapped(
199                                entity,
200                                field,
201                                registry,
202                                mapper,
203                                relationship_hook_mode,
204                            );
205                        }),
206                        ReflectRef::Tuple(bundle) => bundle.iter_fields().for_each(|field| {
207                            apply_or_insert_field_mapped(
208                                entity,
209                                field,
210                                registry,
211                                mapper,
212                                relationship_hook_mode,
213                            );
214                        }),
215                        _ => panic!(
216                            "expected bundle `{}` to be a named struct or tuple",
217                            // FIXME: once we have unique reflect, use `TypePath`.
218                            core::any::type_name::<B>(),
219                        ),
220                    }
221                }
222            },
223            remove: |entity| {
224                entity.remove::<B>();
225            },
226            take: |entity| {
227                entity
228                    .take::<B>()
229                    .map(|bundle| Box::new(bundle).into_reflect())
230            },
231        })
232    }
233}
234
235fn apply_field(entity: &mut EntityMut, field: &dyn PartialReflect, registry: &TypeRegistry) {
236    let Some(type_id) = field.try_as_reflect().map(Any::type_id) else {
237        panic!(
238            "`{}` did not implement `Reflect`",
239            field.reflect_type_path()
240        );
241    };
242    if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(type_id) {
243        reflect_component.apply(entity.reborrow(), field);
244    } else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(type_id) {
245        reflect_bundle.apply(entity.reborrow(), field, registry);
246    } else {
247        panic!(
248            "no `ReflectComponent` nor `ReflectBundle` registration found for `{}`",
249            field.reflect_type_path()
250        );
251    }
252}
253
254fn apply_or_insert_field_mapped(
255    entity: &mut EntityWorldMut,
256    field: &dyn PartialReflect,
257    registry: &TypeRegistry,
258    mapper: &mut dyn EntityMapper,
259    relationship_hook_mode: RelationshipHookMode,
260) {
261    let Some(type_id) = field.try_as_reflect().map(Any::type_id) else {
262        panic!(
263            "`{}` did not implement `Reflect`",
264            field.reflect_type_path()
265        );
266    };
267
268    if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(type_id) {
269        reflect_component.apply_or_insert_mapped(
270            entity,
271            field,
272            registry,
273            mapper,
274            relationship_hook_mode,
275        );
276    } else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(type_id) {
277        reflect_bundle.apply_or_insert_mapped(
278            entity,
279            field,
280            registry,
281            mapper,
282            relationship_hook_mode,
283        );
284    } else {
285        let is_component = entity.world().components().get_id(type_id).is_some();
286
287        if is_component {
288            panic!(
289                "no `ReflectComponent` registration found for `{}`",
290                field.reflect_type_path(),
291            );
292        } else {
293            panic!(
294                "no `ReflectBundle` registration found for `{}`",
295                field.reflect_type_path(),
296            )
297        }
298    }
299}