diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index c465e052d284b..87677eb5f2c0f 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -150,19 +150,20 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { // - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass // the correct `StorageType` into the callback. #[allow(deprecated)] - unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause { + unsafe impl #impl_generics #ecs_path::bundle::BundleImpl for #struct_name #ty_generics #where_clause { + type Name = (#(<#active_field_types as #ecs_path::bundle::BundleImpl>::Name,)*); fn component_ids( components: &mut #ecs_path::component::ComponentsRegistrator, ids: &mut impl FnMut(#ecs_path::component::ComponentId) ) { - #(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components, ids);)* + #(<#active_field_types as #ecs_path::bundle::BundleImpl>::component_ids(components, ids);)* } fn get_component_ids( components: &#ecs_path::component::Components, ids: &mut impl FnMut(Option<#ecs_path::component::ComponentId>) ) { - #(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);)* + #(<#active_field_types as #ecs_path::bundle::BundleImpl>::get_component_ids(components, &mut *ids);)* } } }; diff --git a/crates/bevy_ecs/src/bundle/impls.rs b/crates/bevy_ecs/src/bundle/impls.rs index 7cf74a86e324f..2a6fbdd4bb9d6 100644 --- a/crates/bevy_ecs/src/bundle/impls.rs +++ b/crates/bevy_ecs/src/bundle/impls.rs @@ -5,16 +5,21 @@ use core::mem::MaybeUninit; use variadics_please::all_tuples_enumerated; use crate::{ - bundle::{Bundle, BundleFromComponents, DynamicBundle, NoBundleEffect}, + bundle::{BundleFromComponents, DynamicBundle, NoBundleEffect}, component::{Component, ComponentId, Components, ComponentsRegistrator, StorageType}, query::DebugCheckedUnwrap, world::EntityWorldMut, }; +use super::BundleImpl; + +// note: `Component: 'static`, so `C: Bundle`. // SAFETY: // - `Bundle::component_ids` calls `ids` for C's component id (and nothing else) // - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant. -unsafe impl Bundle for C { +unsafe impl BundleImpl for C { + type Name = C; + fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) { ids(components.register_component::()); } @@ -53,6 +58,40 @@ impl DynamicBundle for C { unsafe fn apply_effect(_ptr: MovingPtr<'_, MaybeUninit>, _entity: &mut EntityWorldMut) {} } +unsafe impl BundleImpl for MovingPtr<'_, T> { + type Name = ::Name; + + fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) { + T::component_ids(components, ids); + } + + fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option)) { + T::get_component_ids(components, ids); + } +} + +impl DynamicBundle for MovingPtr<'_, T> { + type Effect = T::Effect; + + unsafe fn get_components( + ptr: MovingPtr<'_, Self>, + func: &mut impl FnMut(StorageType, OwningPtr<'_>), + ) { + let this = ptr.read(); + + T::get_components(this, func); + } + + unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit>, entity: &mut EntityWorldMut) { + let this = unsafe { + core::mem::transmute::>, MovingPtr<'_, MaybeUninit>>( + ptr.read(), + ) + }; + T::apply_effect(this, entity); + } +} + macro_rules! tuple_impl { ($(#[$meta:meta])* $(($index:tt, $name: ident, $alias: ident)),*) => { #[expect( @@ -71,13 +110,14 @@ macro_rules! tuple_impl { // - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`. // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct // `StorageType` into the callback. - unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) { + unsafe impl<$($name: BundleImpl),*> BundleImpl for ($($name,)*) { + type Name = ($(<$name as BundleImpl>::Name,)*); fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)){ - $(<$name as Bundle>::component_ids(components, ids);)* + $(<$name as BundleImpl>::component_ids(components, ids);)* } fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option)){ - $(<$name as Bundle>::get_component_ids(components, ids);)* + $(<$name as BundleImpl>::get_component_ids(components, ids);)* } } @@ -126,7 +166,7 @@ macro_rules! tuple_impl { reason = "Zero-length tuples won't use any of the parameters." )] $(#[$meta])* - impl<$($name: Bundle),*> DynamicBundle for ($($name,)*) { + impl<$($name: DynamicBundle),*> DynamicBundle for ($($name,)*) { type Effect = ($($name::Effect,)*); #[allow( clippy::unused_unit, diff --git a/crates/bevy_ecs/src/bundle/info.rs b/crates/bevy_ecs/src/bundle/info.rs index 589ec0b7c65e6..ad2589a90f8bf 100644 --- a/crates/bevy_ecs/src/bundle/info.rs +++ b/crates/bevy_ecs/src/bundle/info.rs @@ -10,7 +10,7 @@ use indexmap::{IndexMap, IndexSet}; use crate::{ archetype::{Archetype, BundleComponentStatus, ComponentStatus}, - bundle::{Bundle, DynamicBundle}, + bundle::{bundle_id_of, Bundle, DynamicBundle}, change_detection::MaybeLocation, component::{ ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, StorageType, @@ -21,6 +21,8 @@ use crate::{ storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, }; +use super::BundleImpl; + /// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`]. /// /// [`World`]: crate::world::World @@ -411,9 +413,10 @@ impl Bundles { self.bundle_infos.get(bundle_id.index()) } - /// Gets the value identifying a specific type of bundle. + /// Gets the value identifying a specific bundle. + /// You can use [`bundle_id_of`] or [`bundle_id_of_val`](super::bundle_id_of_val) to easily get a bundle's `TypeId`. /// Returns `None` if the bundle does not exist in the world, - /// or if `type_id` does not correspond to a type of bundle. + /// or if `type_id` does not correspond to a bundle. #[inline] pub fn get_id(&self, type_id: TypeId) -> Option { self.bundle_ids.get(&type_id).cloned() @@ -429,13 +432,13 @@ impl Bundles { /// /// [`World`]: crate::world::World #[deny(unsafe_op_in_unsafe_fn)] - pub(crate) unsafe fn register_info( + pub(crate) unsafe fn register_info( &mut self, components: &mut ComponentsRegistrator, storages: &mut Storages, ) -> BundleId { let bundle_infos = &mut self.bundle_infos; - *self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { + *self.bundle_ids.entry(bundle_id_of::()).or_insert_with(|| { let mut component_ids= Vec::new(); T::component_ids(components, &mut |id| component_ids.push(id)); let id = BundleId(bundle_infos.len()); @@ -465,7 +468,11 @@ impl Bundles { components: &mut ComponentsRegistrator, storages: &mut Storages, ) -> BundleId { - if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::()).cloned() { + if let Some(id) = self + .contributed_bundle_ids + .get(&bundle_id_of::()) + .cloned() + { id } else { // SAFETY: as per the guarantees of this function, components and @@ -485,7 +492,7 @@ impl Bundles { // part of init_dynamic_info. No mutable references will be created and the allocation will remain valid. self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len)) }; - self.contributed_bundle_ids.insert(TypeId::of::(), id); + self.contributed_bundle_ids.insert(bundle_id_of::(), id); id } } diff --git a/crates/bevy_ecs/src/bundle/mod.rs b/crates/bevy_ecs/src/bundle/mod.rs index 17d894d40b660..a5c96bf671ec4 100644 --- a/crates/bevy_ecs/src/bundle/mod.rs +++ b/crates/bevy_ecs/src/bundle/mod.rs @@ -15,7 +15,7 @@ pub(crate) use remove::BundleRemover; pub(crate) use spawner::BundleSpawner; use bevy_ptr::MovingPtr; -use core::mem::MaybeUninit; +use core::{any::TypeId, mem::MaybeUninit}; pub use info::*; /// Derive the [`Bundle`] trait @@ -197,7 +197,8 @@ use bevy_ptr::OwningPtr; label = "invalid `Bundle`", note = "consider annotating `{Self}` with `#[derive(Component)]` or `#[derive(Bundle)]`" )] -pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static { +pub unsafe trait BundleImpl: DynamicBundle + Send + Sync { + type Name: 'static; /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s #[doc(hidden)] fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)); @@ -206,6 +207,16 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static { fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option)); } +pub const fn bundle_id_of() -> TypeId { + TypeId::of::() +} +pub const fn bundle_id_of_val(_: &T) -> TypeId { + TypeId::of::() +} + +pub trait Bundle: BundleImpl + 'static {} +impl Bundle for T {} + /// Creates a [`Bundle`] by taking it from internal storage. /// /// # Safety diff --git a/crates/bevy_ecs/src/bundle/spawner.rs b/crates/bevy_ecs/src/bundle/spawner.rs index b4f32147aecdf..45e9a24298805 100644 --- a/crates/bevy_ecs/src/bundle/spawner.rs +++ b/crates/bevy_ecs/src/bundle/spawner.rs @@ -15,6 +15,8 @@ use crate::{ world::{unsafe_world_cell::UnsafeWorldCell, World}, }; +use super::BundleImpl; + // SAFETY: We have exclusive world access so our pointers can't be invalidated externally pub(crate) struct BundleSpawner<'w> { world: UnsafeWorldCell<'w>, @@ -26,7 +28,7 @@ pub(crate) struct BundleSpawner<'w> { impl<'w> BundleSpawner<'w> { #[inline] - pub fn new(world: &'w mut World, change_tick: Tick) -> Self { + pub fn new(world: &'w mut World, change_tick: Tick) -> Self { let bundle_id = world.register_bundle_info::(); // SAFETY: we initialized this bundle_id in `init_info` diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 0d148b346895f..718505ea023c1 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -158,7 +158,7 @@ pub struct HotPatchChanges; #[cfg(test)] mod tests { use crate::{ - bundle::Bundle, + bundle::{Bundle, BundleImpl}, change_detection::Ref, component::Component, entity::{Entity, EntityMapper}, @@ -250,7 +250,7 @@ mod tests { y: SparseStored, } let mut ids = Vec::new(); - ::component_ids(&mut world.components_registrator(), &mut |id| { + ::component_ids(&mut world.components_registrator(), &mut |id| { ids.push(id); }); @@ -300,9 +300,12 @@ mod tests { } let mut ids = Vec::new(); - ::component_ids(&mut world.components_registrator(), &mut |id| { - ids.push(id); - }); + ::component_ids( + &mut world.components_registrator(), + &mut |id| { + ids.push(id); + }, + ); assert_eq!( ids, @@ -352,7 +355,7 @@ mod tests { } let mut ids = Vec::new(); - ::component_ids( + ::component_ids( &mut world.components_registrator(), &mut |id| { ids.push(id); diff --git a/crates/bevy_ecs/src/spawn.rs b/crates/bevy_ecs/src/spawn.rs index e4b7340c9ccfc..47d4f10dc9605 100644 --- a/crates/bevy_ecs/src/spawn.rs +++ b/crates/bevy_ecs/src/spawn.rs @@ -2,7 +2,7 @@ //! for the best entry points into these APIs and examples of how to use them. use crate::{ - bundle::{Bundle, DynamicBundle, InsertMode, NoBundleEffect}, + bundle::{Bundle, BundleImpl, DynamicBundle, InsertMode, NoBundleEffect}, change_detection::MaybeLocation, entity::Entity, query::DebugCheckedUnwrap, @@ -80,23 +80,23 @@ impl SpawnableList for Spawn { // SAFETY: // - `Spawn` has one field at index 0. // - if `this` is aligned, then its inner bundle must be as well. - let bundle = unsafe { + let bundle: MovingPtr<'_, B, _> = unsafe { bevy_ptr::deconstruct_moving_ptr!(this => ( 0 => bundle, )); bundle.try_into().debug_checked_unwrap() }; - let r = R::from(entity); + let r = (R::from(entity), bundle); move_as_ptr!(r); - let mut entity = world.spawn_with_caller(r, caller); - - entity.insert_with_caller( - bundle, - InsertMode::Replace, - caller, - RelationshipHookMode::Run, - ); + world.spawn_with_caller(r, caller); + + // entity.insert_with_caller( + // bundle, + // InsertMode::Replace, + // caller, + // RelationshipHookMode::Run, + // ); } spawn::(this, world, entity); @@ -306,21 +306,22 @@ pub struct SpawnRelatedBundle> { } // SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound. -unsafe impl + Send + Sync + 'static> Bundle +unsafe impl + Send + Sync + 'static> BundleImpl for SpawnRelatedBundle { + type Name = ::Name; fn component_ids( components: &mut crate::component::ComponentsRegistrator, ids: &mut impl FnMut(crate::component::ComponentId), ) { - ::component_ids(components, ids); + ::component_ids(components, ids); } fn get_component_ids( components: &crate::component::Components, ids: &mut impl FnMut(Option), ) { - ::get_component_ids(components, ids); + ::get_component_ids(components, ids); } } @@ -401,19 +402,20 @@ impl DynamicBundle for SpawnOneRelated { } // SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound. -unsafe impl Bundle for SpawnOneRelated { +unsafe impl BundleImpl for SpawnOneRelated { + type Name = ::Name; fn component_ids( components: &mut crate::component::ComponentsRegistrator, ids: &mut impl FnMut(crate::component::ComponentId), ) { - ::component_ids(components, ids); + ::component_ids(components, ids); } fn get_component_ids( components: &crate::component::Components, ids: &mut impl FnMut(Option), ) { - ::get_component_ids(components, ids); + ::get_component_ids(components, ids); } } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index b5738733d7f95..360028445c43f 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -403,7 +403,7 @@ mod tests { use crate::{ archetype::Archetypes, - bundle::Bundles, + bundle::{bundle_id_of, Bundles}, change_detection::DetectChanges, component::{Component, Components}, entity::{Entities, Entity}, @@ -1129,7 +1129,7 @@ mod tests { let archetype = archetypes.get(location.archetype_id).unwrap(); let archetype_components = archetype.components(); let bundle_id = bundles - .get_id(TypeId::of::<(W, W)>()) + .get_id(bundle_id_of::<(W, W)>()) .expect("Bundle used to spawn entity should exist"); let bundle_info = bundles.get(bundle_id).unwrap(); let mut bundle_components = bundle_info.contributed_components().to_vec(); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 4c6d3c5455d51..3886d576f8018 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -32,8 +32,8 @@ pub use spawn_batch::*; use crate::{ archetype::{ArchetypeId, Archetypes}, bundle::{ - Bundle, BundleId, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode, - NoBundleEffect, + Bundle, BundleId, BundleImpl, BundleInfo, BundleInserter, BundleSpawner, Bundles, + InsertMode, NoBundleEffect, }, change_detection::{MaybeLocation, MutUntyped, TicksMut}, component::{ @@ -1156,7 +1156,7 @@ impl World { self.spawn_with_caller(bundle, MaybeLocation::caller()) } - pub(crate) fn spawn_with_caller( + pub(crate) fn spawn_with_caller( &mut self, bundle: MovingPtr<'_, B>, caller: MaybeLocation, @@ -3085,14 +3085,14 @@ impl World { /// This is largely equivalent to calling [`register_component`](Self::register_component) on each /// component in the bundle. #[inline] - pub fn register_bundle(&mut self) -> &BundleInfo { + pub fn register_bundle(&mut self) -> &BundleInfo { let id = self.register_bundle_info::(); // SAFETY: We just initialized the bundle so its id should definitely be valid. unsafe { self.bundles.get(id).debug_checked_unwrap() } } - pub(crate) fn register_bundle_info(&mut self) -> BundleId { + pub(crate) fn register_bundle_info(&mut self) -> BundleId { // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. let mut registrator = unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; diff --git a/crates/bevy_ptr/src/lib.rs b/crates/bevy_ptr/src/lib.rs index 1d6e43fcf1f9b..c41ec896c1c71 100644 --- a/crates/bevy_ptr/src/lib.rs +++ b/crates/bevy_ptr/src/lib.rs @@ -805,6 +805,9 @@ impl Drop for MovingPtr<'_, T, A> { } } +unsafe impl Send for MovingPtr<'_, T, A> {} +unsafe impl Sync for MovingPtr<'_, T, A> {} + impl<'a, A: IsAligned> Ptr<'a, A> { /// Creates a new instance from a raw pointer. ///