Skip to content
Open
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
2 changes: 1 addition & 1 deletion crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,7 +1414,7 @@ impl App {
/// }
/// });
/// ```
pub fn add_observer<E: Event, B: Bundle, M>(
pub fn add_observer<E: Event, B: Bundle + 'static, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev", default-fea
"alloc",
] }

typeid = { version = "1" }
bitflags = { version = "2.3", default-features = false }
fixedbitset = { version = "0.5", default-features = false }
serde = { version = "1", default-features = false, features = [
Expand Down
37 changes: 37 additions & 0 deletions crates/bevy_ecs/src/bundle/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@ use crate::{
world::EntityWorldMut,
};

// SAFETY: `MovingPtr` forwards its implementation of `Bundle` to another `Bundle`, so it is correct if that impl is correct
unsafe impl<B: Bundle> Bundle for MovingPtr<'_, B> {
fn component_ids(components: &mut ComponentsRegistrator, ids: &mut impl FnMut(ComponentId)) {
B::component_ids(components, ids);
}

fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)) {
B::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);
}

// SAFETY: `MovingPtr` forwards its implementation of `apply_effect` to another
// `DynamicBundle`, so it is correct if that impl is correct
unsafe fn apply_effect(ptr: MovingPtr<'_, MaybeUninit<Self>>, entity: &mut EntityWorldMut) {
// SAFETY: the `MovingPtr` is still init, but it's inner value may no
// longer be fully init
let this = unsafe {
core::mem::transmute::<MaybeUninit<MovingPtr<'_, T>>, MovingPtr<'_, MaybeUninit<T>>>(
ptr.read(),
)
};
T::apply_effect(this, entity);
}
}

// 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.
Expand Down
12 changes: 8 additions & 4 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 Down Expand Up @@ -435,7 +435,7 @@ impl Bundles {
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 +465,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 +489,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
19 changes: 17 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,7 @@ 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 Bundle: DynamicBundle + Send + Sync {
/// 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 +206,21 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
}

/// Retrieves the `TypeId` of the bundle type. Used for registering bundles.
///
/// See also [`bundle_id_of_val`] for retrieving the bundle id without naming the type.
pub fn bundle_id_of<T: Bundle>() -> TypeId {
typeid::of::<T>()
}

/// Retrieves the `TypeId` of a bundle when the type may not be easily named. Used for registering bundles.
///
/// See also [`bundle_id_of`] for retrieving the bundle id without an instance of the bundle.
pub fn bundle_id_of_val<T: Bundle>(val: T) -> TypeId {
_ = val;
typeid::of::<T>()
}

/// Creates a [`Bundle`] by taking it from internal storage.
///
/// # Safety
Expand Down
12 changes: 12 additions & 0 deletions crates/bevy_ecs/src/bundle/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use bevy_ptr::move_as_ptr;

use crate::{
archetype::ArchetypeCreated, lifecycle::HookContext, prelude::*, world::DeferredWorld,
};
Expand Down Expand Up @@ -263,3 +265,13 @@ struct Ignore {
#[bundle(ignore)]
bar: i32,
}

#[test]
fn moving_ptr_bundle() {
let mut world = World::new();
let v = V("yogurt");
move_as_ptr!(v);

let e1 = world.spawn((v, B)).id();
assert_eq!(world.entity(e1).get::<V>(), Some(&V("yogurt")));
}
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ impl<'a> EntityCommands<'a> {
/// For efficient spawning of multiple children, use [`with_children`].
///
/// [`with_children`]: EntityCommands::with_children
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn with_child(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
self.with_related::<ChildOf>(bundle);
self
}
Expand Down
6 changes: 4 additions & 2 deletions crates/bevy_ecs/src/observer/distributed_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ impl Observer {
/// # Panics
///
/// Panics if the given system is an exclusive system.
pub fn new<E: Event, B: Bundle, M, I: IntoObserverSystem<E, B, M>>(system: I) -> Self {
pub fn new<E: Event, B: Bundle + 'static, M, I: IntoObserverSystem<E, B, M>>(
system: I,
) -> Self {
let system = Box::new(IntoObserverSystem::into_system(system));
assert!(
!system.is_exclusive(),
Expand Down Expand Up @@ -430,7 +432,7 @@ impl ObserverDescriptor {
/// The type parameters of this function _must_ match those used to create the [`Observer`].
/// As such, it is recommended to only use this function within the [`Observer::new`] method to
/// ensure type parameters match.
fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
fn hook_on_add<E: Event, B: Bundle + 'static, S: ObserverSystem<E, B>>(
mut world: DeferredWorld<'_>,
HookContext { entity, .. }: HookContext,
) {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl World {
/// # Panics
///
/// Panics if the given system is an exclusive system.
pub fn add_observer<E: Event, B: Bundle, M>(
pub fn add_observer<E: Event, B: Bundle + 'static, M>(
&mut self,
system: impl IntoObserverSystem<E, B, M>,
) -> EntityWorldMut<'_> {
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_ecs/src/observer/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ pub type ObserverRunner =
// NOTE: The way `Trigger` and `On` interact in this implementation is _subtle_ and _easily invalidated_
// from a soundness perspective. Please read and understand the safety comments before making any changes,
// either here or in `On`.
pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
pub(super) unsafe fn observer_system_runner<
E: Event,
B: Bundle + 'static,
S: ObserverSystem<E, B>,
>(
mut world: DeferredWorld,
observer: Entity,
trigger_context: &TriggerContext,
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/relationship/related_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ impl<'w> EntityWorldMut<'w> {

impl<'a> EntityCommands<'a> {
/// Spawns a entity related to this entity (with the `R` relationship) by taking a bundle
pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn with_related<R: Relationship>(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
let parent = self.id();
self.commands.spawn((bundle, R::from(parent)));
self
Expand Down Expand Up @@ -539,7 +539,7 @@ impl<'a> EntityCommands<'a> {
/// Any cycles will cause this method to loop infinitely.
pub fn insert_recursive<S: RelationshipTarget>(
&mut self,
bundle: impl Bundle + Clone,
bundle: impl Bundle + Clone + 'static,
) -> &mut Self {
self.queue(move |mut entity: EntityWorldMut| {
entity.insert_recursive::<S>(bundle);
Expand Down Expand Up @@ -626,7 +626,7 @@ impl<'w, R: Relationship> RelatedSpawnerCommands<'w, R> {

/// Spawns an entity with the given `bundle` and an `R` relationship targeting the `target`
/// entity this spawner was initialized with.
pub fn spawn(&mut self, bundle: impl Bundle) -> EntityCommands<'_> {
pub fn spawn(&mut self, bundle: impl Bundle + 'static) -> EntityCommands<'_> {
self.commands.spawn((R::from(self.target), bundle))
}

Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/commands/entity_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ where

/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
#[track_caller]
pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
pub fn insert(bundle: impl Bundle + 'static, mode: InsertMode) -> impl EntityCommand {
let caller = MaybeLocation::caller();
move |mut entity: EntityWorldMut| {
move_as_ptr!(bundle);
Expand Down Expand Up @@ -248,7 +248,7 @@ pub fn despawn() -> impl EntityCommand {
/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]
/// targets this entity.
#[track_caller]
pub fn observe<E: EntityEvent, B: Bundle, M>(
pub fn observe<E: EntityEvent, B: Bundle + 'static, M>(
observer: impl IntoObserverSystem<E, B, M>,
) -> impl EntityCommand {
let caller = MaybeLocation::caller();
Expand Down
26 changes: 15 additions & 11 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl<'w, 's> Commands<'w, 's> {
/// - [`spawn_batch`](Self::spawn_batch) to spawn many entities
/// with the same combination of components.
#[track_caller]
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands<'_> {
pub fn spawn<T: Bundle + 'static>(&mut self, bundle: T) -> EntityCommands<'_> {
let entity = self.entities.reserve_entity();
let mut entity_commands = EntityCommands {
entity,
Expand Down Expand Up @@ -1131,7 +1131,7 @@ impl<'w, 's> Commands<'w, 's> {
/// Panics if the given system is an exclusive system.
///
/// [`On`]: crate::observer::On
pub fn add_observer<E: Event, B: Bundle, M>(
pub fn add_observer<E: Event, B: Bundle + 'static, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> EntityCommands<'_> {
Expand Down Expand Up @@ -1387,7 +1387,7 @@ impl<'a> EntityCommands<'a> {
/// # bevy_ecs::system::assert_is_system(add_combat_stats_system);
/// ```
#[track_caller]
pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn insert(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
self.queue(entity_command::insert(bundle, InsertMode::Replace))
}

Expand Down Expand Up @@ -1416,7 +1416,7 @@ impl<'a> EntityCommands<'a> {
/// # bevy_ecs::system::assert_is_system(add_health_system);
/// ```
#[track_caller]
pub fn insert_if<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
pub fn insert_if<F>(&mut self, bundle: impl Bundle + 'static, condition: F) -> &mut Self
where
F: FnOnce() -> bool,
{
Expand All @@ -1435,7 +1435,7 @@ impl<'a> EntityCommands<'a> {
/// See also [`entry`](Self::entry), which lets you modify a [`Component`] if it's present,
/// as well as initialize it with a default value.
#[track_caller]
pub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn insert_if_new(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
self.queue(entity_command::insert(bundle, InsertMode::Keep))
}

Expand All @@ -1445,7 +1445,7 @@ impl<'a> EntityCommands<'a> {
/// This is the same as [`EntityCommands::insert_if`], but in case of duplicate
/// components will leave the old values instead of replacing them with new ones.
#[track_caller]
pub fn insert_if_new_and<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
pub fn insert_if_new_and<F>(&mut self, bundle: impl Bundle + 'static, condition: F) -> &mut Self
where
F: FnOnce() -> bool,
{
Expand Down Expand Up @@ -1556,7 +1556,7 @@ impl<'a> EntityCommands<'a> {
/// # bevy_ecs::system::assert_is_system(add_combat_stats_system);
/// ```
#[track_caller]
pub fn try_insert(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn try_insert(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
self.queue_silenced(entity_command::insert(bundle, InsertMode::Replace))
}

Expand All @@ -1569,7 +1569,7 @@ impl<'a> EntityCommands<'a> {
/// If the entity does not exist when this command is executed,
/// the resulting error will be ignored.
#[track_caller]
pub fn try_insert_if<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
pub fn try_insert_if<F>(&mut self, bundle: impl Bundle + 'static, condition: F) -> &mut Self
where
F: FnOnce() -> bool,
{
Expand All @@ -1591,7 +1591,11 @@ impl<'a> EntityCommands<'a> {
/// If the entity does not exist when this command is executed,
/// the resulting error will be ignored.
#[track_caller]
pub fn try_insert_if_new_and<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
pub fn try_insert_if_new_and<F>(
&mut self,
bundle: impl Bundle + 'static,
condition: F,
) -> &mut Self
where
F: FnOnce() -> bool,
{
Expand All @@ -1612,7 +1616,7 @@ impl<'a> EntityCommands<'a> {
/// If the entity does not exist when this command is executed,
/// the resulting error will be ignored.
#[track_caller]
pub fn try_insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
pub fn try_insert_if_new(&mut self, bundle: impl Bundle + 'static) -> &mut Self {
self.queue_silenced(entity_command::insert(bundle, InsertMode::Keep))
}

Expand Down Expand Up @@ -2007,7 +2011,7 @@ impl<'a> EntityCommands<'a> {

/// Creates an [`Observer`] watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]
/// targets this entity.
pub fn observe<E: EntityEvent, B: Bundle, M>(
pub fn observe<E: EntityEvent, B: Bundle + 'static, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self {
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2209,7 +2209,7 @@ impl<'w> EntityWorldMut<'w> {
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
#[must_use]
#[track_caller]
pub fn take<T: Bundle + BundleFromComponents>(&mut self) -> Option<T> {
pub fn take<T: Bundle + BundleFromComponents + 'static>(&mut self) -> Option<T> {
let location = self.location();
let entity = self.entity;

Expand Down Expand Up @@ -2857,14 +2857,14 @@ impl<'w> EntityWorldMut<'w> {
///
/// Panics if the given system is an exclusive system.
#[track_caller]
pub fn observe<E: EntityEvent, B: Bundle, M>(
pub fn observe<E: EntityEvent, B: Bundle + 'static, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self {
self.observe_with_caller(observer, MaybeLocation::caller())
}

pub(crate) fn observe_with_caller<E: EntityEvent, B: Bundle, M>(
pub(crate) fn observe_with_caller<E: EntityEvent, B: Bundle + 'static, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
caller: MaybeLocation,
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_ptr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,12 @@ impl<T, A: IsAligned> Drop for MovingPtr<'_, T, A> {
}
}

// These traits aren't auto-implemented as `MovingPtr` contains a raw pointer:
// SAFETY: MovingPtr<T> owns the value it points to and so it is `Send` if `T` is `Send`.
unsafe impl<T: Send, A: IsAligned> Send for MovingPtr<'_, T, A> {}
// SAFETY: MovingPtr<T> owns the value it points to and so it is `Sync` if `T` is `Sync`.
unsafe impl<T: Sync, A: IsAligned> Sync for MovingPtr<'_, T, A> {}

impl<'a, A: IsAligned> Ptr<'a, A> {
/// Creates a new instance from a raw pointer.
///
Expand Down
Loading
Loading