Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);)*
}
}
};
Expand Down
52 changes: 46 additions & 6 deletions crates/bevy_ecs/src/bundle/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<C: Component> Bundle for C {
unsafe impl<C: Component> BundleImpl for C {
type Name = C;

fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
ids(components.register_component::<C>());
}
Expand Down Expand Up @@ -53,6 +58,40 @@ impl<C: Component> DynamicBundle for C {
unsafe fn apply_effect(_ptr: MovingPtr<'_, MaybeUninit<Self>>, _entity: &mut EntityWorldMut) {}
}

unsafe impl<T: BundleImpl> BundleImpl for MovingPtr<'_, T> {
type Name = <T as BundleImpl>::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<ComponentId>)) {
T::get_component_ids(components, ids);
}
}

impl<T: DynamicBundle> 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<Self>>, entity: &mut EntityWorldMut) {
let this = unsafe {
core::mem::transmute::<MaybeUninit<MovingPtr<'_, T>>, MovingPtr<'_, MaybeUninit<T>>>(
ptr.read(),
)
};
T::apply_effect(this, entity);
}
}

macro_rules! tuple_impl {
($(#[$meta:meta])* $(($index:tt, $name: ident, $alias: ident)),*) => {
#[expect(
Expand All @@ -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<ComponentId>)){
$(<$name as Bundle>::get_component_ids(components, ids);)*
$(<$name as BundleImpl>::get_component_ids(components, ids);)*
}
}

Expand Down Expand Up @@ -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,
Expand Down
21 changes: 14 additions & 7 deletions crates/bevy_ecs/src/bundle/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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<BundleId> {
self.bundle_ids.get(&type_id).cloned()
Expand All @@ -429,13 +432,13 @@ impl Bundles {
///
/// [`World`]: crate::world::World
#[deny(unsafe_op_in_unsafe_fn)]
pub(crate) unsafe fn register_info<T: Bundle>(
pub(crate) unsafe fn register_info<T: BundleImpl>(
&mut self,
components: &mut ComponentsRegistrator,
storages: &mut Storages,
) -> BundleId {
let bundle_infos = &mut self.bundle_infos;
*self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
*self.bundle_ids.entry(bundle_id_of::<T>()).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());
Expand Down Expand Up @@ -465,7 +468,11 @@ impl Bundles {
components: &mut ComponentsRegistrator,
storages: &mut Storages,
) -> BundleId {
if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::<T>()).cloned() {
if let Some(id) = self
.contributed_bundle_ids
.get(&bundle_id_of::<T>())
.cloned()
{
id
} else {
// SAFETY: as per the guarantees of this function, components and
Expand All @@ -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::<T>(), id);
self.contributed_bundle_ids.insert(bundle_id_of::<T>(), id);
id
}
}
Expand Down
15 changes: 13 additions & 2 deletions crates/bevy_ecs/src/bundle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand All @@ -206,6 +207,16 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
}

pub const fn bundle_id_of<T: BundleImpl>() -> TypeId {
TypeId::of::<T::Name>()
}
pub const fn bundle_id_of_val<T: BundleImpl>(_: &T) -> TypeId {
TypeId::of::<T::Name>()
}

pub trait Bundle: BundleImpl + 'static {}
impl<T: BundleImpl + 'static> Bundle for T {}

/// Creates a [`Bundle`] by taking it from internal storage.
///
/// # Safety
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/bundle/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -26,7 +28,7 @@ pub(crate) struct BundleSpawner<'w> {

impl<'w> BundleSpawner<'w> {
#[inline]
pub fn new<T: Bundle>(world: &'w mut World, change_tick: Tick) -> Self {
pub fn new<T: BundleImpl>(world: &'w mut World, change_tick: Tick) -> Self {
let bundle_id = world.register_bundle_info::<T>();

// SAFETY: we initialized this bundle_id in `init_info`
Expand Down
15 changes: 9 additions & 6 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -250,7 +250,7 @@ mod tests {
y: SparseStored,
}
let mut ids = Vec::new();
<FooBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
<FooBundle as BundleImpl>::component_ids(&mut world.components_registrator(), &mut |id| {
ids.push(id);
});

Expand Down Expand Up @@ -300,9 +300,12 @@ mod tests {
}

let mut ids = Vec::new();
<NestedBundle as Bundle>::component_ids(&mut world.components_registrator(), &mut |id| {
ids.push(id);
});
<NestedBundle as BundleImpl>::component_ids(
&mut world.components_registrator(),
&mut |id| {
ids.push(id);
},
);

assert_eq!(
ids,
Expand Down Expand Up @@ -352,7 +355,7 @@ mod tests {
}

let mut ids = Vec::new();
<BundleWithIgnored as Bundle>::component_ids(
<BundleWithIgnored as BundleImpl>::component_ids(
&mut world.components_registrator(),
&mut |id| {
ids.push(id);
Expand Down
36 changes: 19 additions & 17 deletions crates/bevy_ecs/src/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -80,23 +80,23 @@ impl<R: Relationship, B: Bundle> SpawnableList<R> for Spawn<B> {
// SAFETY:
// - `Spawn<B>` 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::<B, R>(this, world, entity);
Expand Down Expand Up @@ -306,21 +306,22 @@ pub struct SpawnRelatedBundle<R: Relationship, L: SpawnableList<R>> {
}

// SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound.
unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> Bundle
unsafe impl<R: Relationship, L: SpawnableList<R> + Send + Sync + 'static> BundleImpl
for SpawnRelatedBundle<R, L>
{
type Name = <R::RelationshipTarget as BundleImpl>::Name;
fn component_ids(
components: &mut crate::component::ComponentsRegistrator,
ids: &mut impl FnMut(crate::component::ComponentId),
) {
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
<R::RelationshipTarget as BundleImpl>::component_ids(components, ids);
}

fn get_component_ids(
components: &crate::component::Components,
ids: &mut impl FnMut(Option<crate::component::ComponentId>),
) {
<R::RelationshipTarget as Bundle>::get_component_ids(components, ids);
<R::RelationshipTarget as BundleImpl>::get_component_ids(components, ids);
}
}

Expand Down Expand Up @@ -401,19 +402,20 @@ impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
}

// SAFETY: This internally relies on the RelationshipTarget's Bundle implementation, which is sound.
unsafe impl<R: Relationship, B: Bundle> Bundle for SpawnOneRelated<R, B> {
unsafe impl<R: Relationship, B: Bundle> BundleImpl for SpawnOneRelated<R, B> {
type Name = <R::RelationshipTarget as BundleImpl>::Name;
fn component_ids(
components: &mut crate::component::ComponentsRegistrator,
ids: &mut impl FnMut(crate::component::ComponentId),
) {
<R::RelationshipTarget as Bundle>::component_ids(components, ids);
<R::RelationshipTarget as BundleImpl>::component_ids(components, ids);
}

fn get_component_ids(
components: &crate::component::Components,
ids: &mut impl FnMut(Option<crate::component::ComponentId>),
) {
<R::RelationshipTarget as Bundle>::get_component_ids(components, ids);
<R::RelationshipTarget as BundleImpl>::get_component_ids(components, ids);
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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<i32>, W<bool>)>())
.get_id(bundle_id_of::<(W<i32>, W<bool>)>())
.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();
Expand Down
Loading
Loading