diff --git a/_release-content/migration-guides/components-as-entities.md b/_release-content/migration-guides/components-as-entities.md new file mode 100644 index 0000000000000..45f94ee9c09a6 --- /dev/null +++ b/_release-content/migration-guides/components-as-entities.md @@ -0,0 +1,11 @@ +--- +title: "Components as Entities" +pull_requests: [24728] +--- + +- `ComponentId::new` now takes `Entity` as an argument instead of `usize`. For debugging, you can use `ComponentId::from_u32`. +- `ComponentId::index` was removed in favor of implementing `ContainsEntity`, call `ComponentId::entity` to get the underlying entity. +- `ComponentIdSet` is now an `EntityEquivalentHashSet` instead of a `FixedBitSet`. This means that methods like `union_with` no longer work, use `bitor_assign` instead. +- `ComponentIds` has been removed. Instead of `ComponentIds`, `ComponentsRegistrator::new` now takes `EntityAllocator`, while `ComponentsQueuedRegistrator::new` now takes `RemoteAllocator`. +- `Access` and `EcsAccessType` no longer derive `Hash`. +- `ResourceEntities` was removed. The following methods have been removed with it: `World::resource_entities`, `EntityWorldMut::resource_entities`, `UnsafeWorldCell::resource_entities`. If you need the entity linked with a `ComponentId`, simply call `component_id.entity()`. diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs index 4938120ae9a12..dc40bca48f501 100644 --- a/benches/benches/bevy_ecs/empty_archetypes.rs +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -166,7 +166,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(iter); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); @@ -197,7 +196,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(for_each); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); @@ -228,7 +226,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(par_for_each); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index e4b75653aa57b..3aec602c131ee 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -22,7 +22,7 @@ use crate::{ bundle::BundleId, component::{ComponentId, Components, RequiredComponentConstructor, StorageType}, - entity::{Entity, EntityLocation}, + entity::{Entity, EntityEquivalentHashMap, EntityLocation}, event::Event, observer::Observers, query::DebugCheckedUnwrap, @@ -762,7 +762,8 @@ struct ArchetypeComponents { /// Maps a [`ComponentId`] to the list of [`Archetypes`]([`Archetype`]) that contain the [`Component`](crate::component::Component), /// along with an [`ArchetypeRecord`] which contains some metadata about how the component is stored in the archetype. -pub type ComponentIndex = HashMap>; +pub type ComponentIndex = + EntityEquivalentHashMap>; /// The backing store of all [`Archetype`]s within a [`World`]. /// diff --git a/crates/bevy_ecs/src/component/constants.rs b/crates/bevy_ecs/src/component/constants.rs index 3f582545f85b9..69f37f6db9020 100644 --- a/crates/bevy_ecs/src/component/constants.rs +++ b/crates/bevy_ecs/src/component/constants.rs @@ -1,14 +1,14 @@ //! Constant components included in every world. -/// `usize` for the [`Add`](crate::lifecycle::Add) component used in lifecycle observers. -pub const ADD: usize = 0; -/// `usize` for the [`Insert`](crate::lifecycle::Insert) component used in lifecycle observers. -pub const INSERT: usize = 1; -/// `usize` for the [`Discard`](crate::lifecycle::Discard) component used in lifecycle observers. -pub const DISCARD: usize = 2; -/// `usize` for the [`Remove`](crate::lifecycle::Remove) component used in lifecycle observers. -pub const REMOVE: usize = 3; -/// `usize` for [`Despawn`](crate::lifecycle::Despawn) component used in lifecycle observers. -pub const DESPAWN: usize = 4; -/// `usize` of the [`IsResource`](crate::resource::IsResource) component used to mark entities with resources. -pub const IS_RESOURCE: usize = 5; +/// `u32` for the [`Add`](crate::lifecycle::Add) component used in lifecycle observers. +pub const ADD: u32 = 0; +/// `u32` for the [`Insert`](crate::lifecycle::Insert) component used in lifecycle observers. +pub const INSERT: u32 = 1; +/// `u32` for the [`Discard`](crate::lifecycle::Discard) component used in lifecycle observers. +pub const DISCARD: u32 = 2; +/// `u32` for the [`Remove`](crate::lifecycle::Remove) component used in lifecycle observers. +pub const REMOVE: u32 = 3; +/// `u32` for [`Despawn`](crate::lifecycle::Despawn) component used in lifecycle observers. +pub const DESPAWN: u32 = 4; +/// `u32` of the [`IsResource`](crate::resource::IsResource) component used to mark entities with resources. +pub const IS_RESOURCE: u32 = 5; diff --git a/crates/bevy_ecs/src/component/info.rs b/crates/bevy_ecs/src/component/info.rs index 76b39eccd52a2..765d2de31c603 100644 --- a/crates/bevy_ecs/src/component/info.rs +++ b/crates/bevy_ecs/src/component/info.rs @@ -1,4 +1,4 @@ -use alloc::{borrow::Cow, vec::Vec}; +use alloc::borrow::Cow; use bevy_platform::{hash::FixedHasher, sync::PoisonError}; use bevy_ptr::OwningPtr; #[cfg(feature = "bevy_reflect")] @@ -18,6 +18,9 @@ use crate::{ Component, ComponentCloneBehavior, ComponentMutability, QueuedComponents, RequiredComponents, StorageType, }, + entity::{ + ContainsEntity, Entity, EntityEquivalent, EntityEquivalentHashMap, EntityEquivalentHashSet, + }, lifecycle::ComponentHooks, query::DebugCheckedUnwrap as _, relationship::{ @@ -177,37 +180,53 @@ impl ComponentInfo { derive(Reflect), reflect(Debug, Hash, PartialEq, Clone) )] -pub struct ComponentId(pub(super) usize); +pub struct ComponentId(pub(super) Entity); + +impl ContainsEntity for ComponentId { + fn entity(&self) -> Entity { + self.0 + } +} + +// SAFETY: EntityWrapper is a newtype around Entity that derives its comparison traits. +unsafe impl EntityEquivalent for ComponentId {} impl ComponentId { - /// Creates a new [`ComponentId`]. - /// - /// The `index` is a unique value associated with each type of component in a given world. - /// Usually, this value is taken from a counter incremented for each type of component registered with the world. + /// Creates a new [`ComponentId`] from an entity. + /// Usually this entity is created by an [`EntityAllocator`](crate::entity::EntityAllocator). + /// `Entity::PLACEHOLDER` is not valid for `ComponentId`. #[inline] - pub const fn new(index: usize) -> ComponentId { - ComponentId(index) + pub const fn new(entity: Entity) -> ComponentId { + ComponentId(entity) } - /// Returns the index of the current component. + /// Creates a [`ComponentId`] from a u32. + /// This is for debugging purposes, because in a real application you have to ensure the id doesn't conflict with any other entity. + /// Moreover, this function panics when `index` is `u32::MAX`. #[inline] - pub fn index(self) -> usize { - self.0 + pub const fn from_u32(index: u32) -> ComponentId { + ComponentId(Entity::from_raw_u32(index).unwrap()) } } impl SparseSetIndex for ComponentId { #[inline] fn sparse_set_index(&self) -> usize { - self.index() + self.entity().sparse_set_index() } #[inline] fn get_sparse_set_index(value: usize) -> Self { - Self(value) + Self(Entity::get_sparse_set_index(value)) } } +/// A map with [`ComponentId`]s as keys. +pub type ComponentIdMap = EntityEquivalentHashMap; + +/// A set of [`ComponentId`]s. +pub type ComponentIdSet = EntityEquivalentHashSet; + /// A value describing a component or resource, which may or may not correspond to a Rust type. #[derive(Clone)] pub struct ComponentDescriptor { @@ -359,7 +378,7 @@ impl ComponentDescriptor { /// Stores metadata associated with each kind of [`Component`] in a given [`World`](crate::world::World). #[derive(Debug, Default)] pub struct Components { - pub(super) components: Vec>, + pub(super) components: ComponentIdMap, pub(super) indices: TypeIdMap, // This is kept internal and local to verify that no deadlocks can occur. pub(super) queued: bevy_platform::sync::RwLock, @@ -379,15 +398,10 @@ impl Components { ) { descriptor.initialize(id, self); let info = ComponentInfo::new(id, descriptor); - let least_len = id.0 + 1; - if self.components.len() < least_len { - self.components.resize_with(least_len, || None); + // SAFETY: The id has never been registered before. + unsafe { + self.components.insert_unique_unchecked(id, info); } - // SAFETY: We just extended the vec to make this index valid. - let slot = unsafe { self.components.get_mut(id.0).debug_checked_unwrap() }; - // Caller ensures id is unique - debug_assert!(slot.is_none()); - *slot = Some(info); } /// Returns the number of components registered or queued with this instance. @@ -449,7 +463,7 @@ impl Components { /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. #[inline] pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { - self.components.get(id.0).and_then(|info| info.as_ref()) + self.components.get(&id) } /// Gets the [`ComponentDescriptor`] of the component with this [`ComponentId`] if it is present. @@ -461,8 +475,8 @@ impl Components { #[inline] pub fn get_descriptor<'a>(&'a self, id: ComponentId) -> Option> { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| Cow::Borrowed(&info.descriptor))) + .get(&id) + .map(|info| Cow::Borrowed(&info.descriptor)) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -482,8 +496,8 @@ impl Components { #[inline] pub fn get_name<'a>(&'a self, id: ComponentId) -> Option { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| info.descriptor.name())) + .get(&id) + .map(|info| info.descriptor.name()) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -503,27 +517,19 @@ impl Components { #[inline] pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { // SAFETY: The caller ensures `id` is valid. - unsafe { - self.components - .get(id.0) - .debug_checked_unwrap() - .as_ref() - .debug_checked_unwrap() - } + unsafe { self.components.get(&id).debug_checked_unwrap() } } #[inline] pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> { - self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.hooks)) + self.components.get_mut(&id).map(|info| &mut info.hooks) } #[inline] pub(crate) fn get_required_components(&self, id: ComponentId) -> Option<&RequiredComponents> { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| &info.required_components)) + .get(&id) + .map(|info| &info.required_components) } #[inline] @@ -532,8 +538,8 @@ impl Components { id: ComponentId, ) -> Option<&mut RequiredComponents> { self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.required_components)) + .get_mut(&id) + .map(|info| &mut info.required_components) } #[inline] @@ -541,9 +547,7 @@ impl Components { &self, id: ComponentId, ) -> Option<&IndexSet> { - self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| &info.required_by)) + self.components.get(&id).map(|info| &info.required_by) } #[inline] @@ -552,8 +556,8 @@ impl Components { id: ComponentId, ) -> Option<&mut IndexSet> { self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.required_by)) + .get_mut(&id) + .map(|info| &mut info.required_by) } /// Returns true if the [`ComponentId`] is fully registered and valid. @@ -561,7 +565,7 @@ impl Components { /// Those ids are still correct, but they are not usable in every context yet. #[inline] pub fn is_id_valid(&self, id: ComponentId) -> bool { - self.components.get(id.0).is_some_and(Option::is_some) + self.components.get(&id).is_some() } /// Type-erased equivalent of [`Components::valid_component_id()`]. @@ -663,7 +667,12 @@ impl Components { /// Gets an iterator over all components fully registered with this instance. pub fn iter_registered(&self) -> impl Iterator + '_ { - self.components.iter().filter_map(Option::as_ref) + self.components.values() + } + + /// Gets an iterator over all `ComponentId`s fully registered with this instance. + pub fn iter_registered_ids(&self) -> impl Iterator + '_ { + self.components.keys().copied() } pub(crate) fn get_relationship_accessor_mut( @@ -671,10 +680,7 @@ impl Components { component_id: ComponentId, ) -> Option<&mut MaybeRelationshipAccessor> { self.components - .get_mut(component_id.index()) - .and_then(|info| { - info.as_mut() - .map(|info| &mut info.descriptor.relationship_accessor) - }) + .get_mut(&component_id) + .map(|info| &mut info.descriptor.relationship_accessor) } } diff --git a/crates/bevy_ecs/src/component/register.rs b/crates/bevy_ecs/src/component/register.rs index e06eddf021e0a..74fa6213aedb0 100644 --- a/crates/bevy_ecs/src/component/register.rs +++ b/crates/bevy_ecs/src/component/register.rs @@ -5,6 +5,7 @@ use core::any::Any; use core::{any::TypeId, fmt::Debug, ops::Deref}; use crate::component::{enforce_no_required_components_recursion, RequiredComponentsRegistrator}; +use crate::entity::{EntityAllocator, RemoteAllocator}; use crate::lifecycle::ComponentHooks; use crate::{ component::{ @@ -13,57 +14,10 @@ use crate::{ query::DebugCheckedUnwrap as _, }; -/// Generates [`ComponentId`]s. -#[derive(Debug, Default)] -pub struct ComponentIds { - next: bevy_platform::sync::atomic::AtomicUsize, -} - -impl ComponentIds { - /// Peeks the next [`ComponentId`] to be generated without generating it. - pub fn peek(&self) -> ComponentId { - ComponentId( - self.next - .load(bevy_platform::sync::atomic::Ordering::Relaxed), - ) - } - - /// Generates and returns the next [`ComponentId`]. - pub fn next(&self) -> ComponentId { - ComponentId( - self.next - .fetch_add(1, bevy_platform::sync::atomic::Ordering::Relaxed), - ) - } - - /// Peeks the next [`ComponentId`] to be generated without generating it. - pub fn peek_mut(&mut self) -> ComponentId { - ComponentId(*self.next.get_mut()) - } - - /// Generates and returns the next [`ComponentId`]. - pub fn next_mut(&mut self) -> ComponentId { - let id = self.next.get_mut(); - let result = ComponentId(*id); - *id += 1; - result - } - - /// Returns the number of [`ComponentId`]s generated. - pub fn len(&self) -> usize { - self.peek().0 - } - - /// Returns true if and only if no ids have been generated. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - /// A [`Components`] wrapper that enables additional features, like registration. pub struct ComponentsRegistrator<'w> { pub(super) components: &'w mut Components, - pub(super) ids: &'w mut ComponentIds, + pub(super) allocator: &'w mut EntityAllocator, pub(super) recursion_check_stack: Vec, } @@ -80,12 +34,11 @@ impl<'w> ComponentsRegistrator<'w> { /// /// # Safety /// - /// The [`Components`] and [`ComponentIds`] must match. - /// For example, they must be from the same world. - pub unsafe fn new(components: &'w mut Components, ids: &'w mut ComponentIds) -> Self { + /// The [`Components`] and [`EntityAllocator`] must come from the same world. + pub unsafe fn new(components: &'w mut Components, allocator: &'w mut EntityAllocator) -> Self { Self { components, - ids, + allocator, recursion_check_stack: Vec::new(), } } @@ -95,7 +48,12 @@ impl<'w> ComponentsRegistrator<'w> { /// It is generally not a good idea to queue a registration when you can instead register directly on this type. pub fn as_queued(&self) -> ComponentsQueuedRegistrator<'_> { // SAFETY: ensured by the caller that created self. - unsafe { ComponentsQueuedRegistrator::new(self.components, self.ids) } + unsafe { + ComponentsQueuedRegistrator::new( + self.components, + self.allocator.build_remote_allocator(), + ) + } } /// Applies every queued registration. @@ -200,7 +158,7 @@ impl<'w> ComponentsRegistrator<'w> { return registrator.register(self); } - let id = self.ids.next_mut(); + let id = ComponentId::new(self.allocator.alloc()); // SAFETY: The component is not currently registered, and the id is fresh. unsafe { self.register_component_unchecked( @@ -254,9 +212,7 @@ impl<'w> ComponentsRegistrator<'w> { &mut self .components .components - .get_mut(id.0) - .debug_checked_unwrap() - .as_mut() + .get_mut(&id) .debug_checked_unwrap() }; @@ -287,7 +243,7 @@ impl<'w> ComponentsRegistrator<'w> { &mut self, descriptor: ComponentDescriptor, ) -> ComponentId { - let id = self.ids.next_mut(); + let id = ComponentId::new(self.allocator.alloc()); // SAFETY: The id is fresh. unsafe { self.components.register_component_inner(id, descriptor); @@ -336,7 +292,7 @@ impl<'w> ComponentsRegistrator<'w> { return registrator.register(self); } - let id = self.ids.next_mut(); + let id = ComponentId::new(self.allocator.alloc()); // SAFETY: The resource is not currently registered, the id is fresh, and the [`ComponentDescriptor`] matches the [`TypeId`] unsafe { self.components @@ -430,10 +386,9 @@ impl Debug for QueuedComponents { /// /// As a rule of thumb, if you have mutable access to [`ComponentsRegistrator`], prefer to use that instead. /// Use this only if you need to know the id of a component but do not need to modify the contents of the world based on that id. -#[derive(Clone, Copy)] pub struct ComponentsQueuedRegistrator<'w> { components: &'w Components, - ids: &'w ComponentIds, + allocator: RemoteAllocator, } impl Deref for ComponentsQueuedRegistrator<'_> { @@ -449,10 +404,12 @@ impl<'w> ComponentsQueuedRegistrator<'w> { /// /// # Safety /// - /// The [`Components`] and [`ComponentIds`] must match. - /// For example, they must be from the same world. - pub unsafe fn new(components: &'w Components, ids: &'w ComponentIds) -> Self { - Self { components, ids } + /// The [`Components`] and [`RemoteAllocator`] must come from the same world. + pub unsafe fn new(components: &'w Components, allocator: RemoteAllocator) -> Self { + Self { + components, + allocator, + } } /// Queues this function to run as a component registrator if the given @@ -474,8 +431,9 @@ impl<'w> ComponentsQueuedRegistrator<'w> { .components .entry(type_id) .or_insert_with(|| { + let id = ComponentId::new(self.allocator.alloc()); // SAFETY: The id was just generated. - unsafe { QueuedRegistration::new(self.ids.next(), descriptor, func) } + unsafe { QueuedRegistration::new(id, descriptor, func) } }) .id } @@ -486,7 +444,7 @@ impl<'w> ComponentsQueuedRegistrator<'w> { descriptor: ComponentDescriptor, func: fn(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor), ) -> ComponentId { - let id = self.ids.next(); + let id = ComponentId::new(self.allocator.alloc()); self.components .queued .write() diff --git a/crates/bevy_ecs/src/entity/hash_set.rs b/crates/bevy_ecs/src/entity/hash_set.rs index aa0ed49f03023..342f9ed20992b 100644 --- a/crates/bevy_ecs/src/entity/hash_set.rs +++ b/crates/bevy_ecs/src/entity/hash_set.rs @@ -88,6 +88,23 @@ impl EntityEquivalentHashSet { } } +impl EntityEquivalentHashSet { + /// In-place union of two `EntityEquivalentHashSet`s.. + pub fn union_with(&mut self, rhs: &Self) { + self.0.bitor_assign(&rhs.0); + } + + /// In-place intersection of two `EntityEquivalentHashSet`s. + pub fn intersect_with(&mut self, rhs: &Self) { + self.0.bitand_assign(&rhs.0); + } + + /// In-place difference of two `EntityEquivalentHashSet`s. + pub fn difference_with(&mut self, rhs: &Self) { + self.0.sub_assign(&rhs.0); + } +} + impl Deref for EntityEquivalentHashSet { type Target = HashSet; diff --git a/crates/bevy_ecs/src/entity_disabling.rs b/crates/bevy_ecs/src/entity_disabling.rs index 1e751070ac2df..46ff6237ee2ab 100644 --- a/crates/bevy_ecs/src/entity_disabling.rs +++ b/crates/bevy_ecs/src/entity_disabling.rs @@ -253,42 +253,44 @@ mod tests { #[test] fn filters_modify_access() { let mut filters = DefaultQueryFilters::empty(); - filters.register_disabling_component(ComponentId::new(1)); + filters.register_disabling_component(ComponentId::from_u32(1)); // A component access with an unrelated component let mut component_access = FilteredAccess::default(); - component_access.access_mut().add_read(ComponentId::new(2)); + component_access + .access_mut() + .add_read(ComponentId::from_u32(2)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!(0, applied_access.with_filters().count()); assert_eq!( - vec![ComponentId::new(1)], + vec![ComponentId::from_u32(1)], applied_access.without_filters().collect::>() ); // We add a with filter, now we expect to see both filters - component_access.and_with(ComponentId::new(4)); + component_access.and_with(ComponentId::from_u32(4)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(4)], + vec![ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!( - vec![ComponentId::new(1)], + vec![ComponentId::from_u32(1)], applied_access.without_filters().collect::>() ); let copy = component_access.clone(); // We add a rule targeting a default component, that filter should no longer be added - component_access.and_with(ComponentId::new(1)); + component_access.and_with(ComponentId::from_u32(1)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(1), ComponentId::new(4)], + vec![ComponentId::from_u32(1), ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!(0, applied_access.without_filters().count()); @@ -297,12 +299,12 @@ mod tests { component_access = copy.clone(); component_access .access_mut() - .add_archetypal(ComponentId::new(1)); + .add_archetypal(ComponentId::from_u32(1)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(4)], + vec![ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!(0, applied_access.without_filters().count()); diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index 82e73d326c215..cb3638bb4c8d1 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -315,15 +315,15 @@ impl ComponentHooks { } /// [`EventKey`] for [`Add`] -pub const ADD: EventKey = EventKey(ComponentId::new(crate::component::ADD)); +pub const ADD: EventKey = EventKey(ComponentId::from_u32(crate::component::ADD)); /// [`EventKey`] for [`Insert`] -pub const INSERT: EventKey = EventKey(ComponentId::new(crate::component::INSERT)); +pub const INSERT: EventKey = EventKey(ComponentId::from_u32(crate::component::INSERT)); /// [`EventKey`] for [`Discard`] -pub const DISCARD: EventKey = EventKey(ComponentId::new(crate::component::DISCARD)); +pub const DISCARD: EventKey = EventKey(ComponentId::from_u32(crate::component::DISCARD)); /// [`EventKey`] for [`Remove`] -pub const REMOVE: EventKey = EventKey(ComponentId::new(crate::component::REMOVE)); +pub const REMOVE: EventKey = EventKey(ComponentId::from_u32(crate::component::REMOVE)); /// [`EventKey`] for [`Despawn`] -pub const DESPAWN: EventKey = EventKey(ComponentId::new(crate::component::DESPAWN)); +pub const DESPAWN: EventKey = EventKey(ComponentId::from_u32(crate::component::DESPAWN)); /// Trigger emitted when a component is inserted onto an entity that does not already have that /// component. Runs before `Insert`. diff --git a/crates/bevy_ecs/src/name.rs b/crates/bevy_ecs/src/name.rs index bb2b98d85581a..926c5c7a22211 100644 --- a/crates/bevy_ecs/src/name.rs +++ b/crates/bevy_ecs/src/name.rs @@ -299,7 +299,7 @@ mod tests { let mut query = world.query::(); let d1 = query.get(&world, e1).unwrap(); // NameOrEntity Display for entities without a Name should be {index}v{generation} - assert_eq!(d1.to_string(), "1v0"); + assert_eq!(d1.to_string(), "9v0"); let d2 = query.get(&world, e2).unwrap(); // NameOrEntity Display for entities with a Name should be the Name assert_eq!(d2.to_string(), "MyName"); diff --git a/crates/bevy_ecs/src/observer/centralized_storage.rs b/crates/bevy_ecs/src/observer/centralized_storage.rs index 8b09e95a0ec90..23c4e79eb7afe 100644 --- a/crates/bevy_ecs/src/observer/centralized_storage.rs +++ b/crates/bevy_ecs/src/observer/centralized_storage.rs @@ -39,13 +39,18 @@ impl Observers { pub(crate) fn get_observers_mut(&mut self, event_key: EventKey) -> &mut CachedObservers { use crate::lifecycle::*; - match event_key { - ADD => &mut self.add, - INSERT => &mut self.insert, - DISCARD => &mut self.discard, - REMOVE => &mut self.remove, - DESPAWN => &mut self.despawn, - _ => self.cache.entry(event_key).or_default(), + if event_key == ADD { + &mut self.add + } else if event_key == INSERT { + &mut self.insert + } else if event_key == DISCARD { + &mut self.discard + } else if event_key == REMOVE { + &mut self.remove + } else if event_key == DESPAWN { + &mut self.despawn + } else { + self.cache.entry(event_key).or_default() } } @@ -62,26 +67,36 @@ impl Observers { pub fn try_get_observers(&self, event_key: EventKey) -> Option<&CachedObservers> { use crate::lifecycle::*; - match event_key { - ADD => Some(&self.add), - INSERT => Some(&self.insert), - DISCARD => Some(&self.discard), - REMOVE => Some(&self.remove), - DESPAWN => Some(&self.despawn), - _ => self.cache.get(&event_key), + if event_key == ADD { + Some(&self.add) + } else if event_key == INSERT { + Some(&self.insert) + } else if event_key == DISCARD { + Some(&self.discard) + } else if event_key == REMOVE { + Some(&self.remove) + } else if event_key == DESPAWN { + Some(&self.despawn) + } else { + self.cache.get(&event_key) } } pub(crate) fn is_archetype_cached(event_key: EventKey) -> Option { use crate::lifecycle::*; - match event_key { - ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER), - INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER), - DISCARD => Some(ArchetypeFlags::ON_DISCARD_OBSERVER), - REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER), - DESPAWN => Some(ArchetypeFlags::ON_DESPAWN_OBSERVER), - _ => None, + if event_key == ADD { + Some(ArchetypeFlags::ON_ADD_OBSERVER) + } else if event_key == INSERT { + Some(ArchetypeFlags::ON_INSERT_OBSERVER) + } else if event_key == DISCARD { + Some(ArchetypeFlags::ON_DISCARD_OBSERVER) + } else if event_key == REMOVE { + Some(ArchetypeFlags::ON_REMOVE_OBSERVER) + } else if event_key == DESPAWN { + Some(ArchetypeFlags::ON_DESPAWN_OBSERVER) + } else { + None } } diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 2a2189810d074..caf5c38026cf1 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -646,7 +646,7 @@ mod tests { world.add_observer(|_: On, mut res: ResMut| res.observed("add_2")); world.spawn(A).flush(); - assert_eq!(vec!["add_2", "add_1"], world.resource::().0); + assert_eq!(vec!["add_1", "add_2"], world.resource::().0); // we have one A entity and two observers assert_eq!(world.query::<&A>().query(&world).count(), 1); assert_eq!(world.query::<&Observer>().query(&world).count(), 2); diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 3e00230bd5e56..57442bc6b77e6 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -1,17 +1,18 @@ use crate::world::unsafe_world_cell::UnsafeWorldCell; -use crate::{component::ComponentId, resource::IS_RESOURCE}; +use crate::{ + component::{ComponentId, ComponentIdSet}, + resource::IS_RESOURCE, +}; use alloc::{format, string::String, vec, vec::Vec}; -use core::iter::FusedIterator; -use core::{fmt, fmt::Debug}; +use core::fmt::Debug; use derive_more::From; -use fixedbitset::{Difference, FixedBitSet, Intersection, IntoOnes, Ones, Union}; use thiserror::Error; /// Tracks read and write access to specific elements in a collection. /// /// Used internally to ensure soundness during system initialization and execution. /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions. -#[derive(Eq, PartialEq, Default, Hash, Debug)] +#[derive(Eq, PartialEq, Default, Debug)] pub struct Access { /// All accessed components, or forbidden components if /// `Self::component_read_and_writes_inverted` is set. @@ -90,7 +91,7 @@ impl Access { if !self.read_and_writes_inverted { self.read_and_writes.insert(index); } else { - self.read_and_writes.remove(index); + self.read_and_writes.remove(&index); } } @@ -100,7 +101,7 @@ impl Access { if !self.writes_inverted { self.writes.insert(index); } else { - self.writes.remove(index); + self.writes.remove(&index); } } @@ -117,7 +118,7 @@ impl Access { if self.read_and_writes_inverted { self.read_and_writes.insert(index); } else { - self.read_and_writes.remove(index); + self.read_and_writes.remove(&index); } } @@ -133,7 +134,7 @@ impl Access { if self.writes_inverted { self.writes.insert(index); } else { - self.writes.remove(index); + self.writes.remove(&index); } } @@ -152,22 +153,22 @@ impl Access { /// Returns `true` if this can access the component given by `index`. pub fn has_read(&self, index: ComponentId) -> bool { - self.read_and_writes_inverted ^ self.read_and_writes.contains(index) + self.read_and_writes_inverted ^ self.read_and_writes.contains(&index) } /// Returns `true` if this can access any component. pub fn has_any_read(&self) -> bool { - self.read_and_writes_inverted || !self.read_and_writes.is_clear() + self.read_and_writes_inverted || !self.read_and_writes.is_empty() } /// Returns `true` if this can exclusively access the component given by `index`. pub fn has_write(&self, index: ComponentId) -> bool { - self.writes_inverted ^ self.writes.contains(index) + self.writes_inverted ^ self.writes.contains(&index) } /// Returns `true` if this accesses any component mutably. pub fn has_any_write(&self) -> bool { - self.writes_inverted || !self.writes.is_clear() + self.writes_inverted || !self.writes.is_empty() } /// Returns true if this has an archetypal (indirect) access to the component given by `index`. @@ -179,7 +180,7 @@ impl Access { /// /// [`Has`]: crate::query::Has pub fn has_archetypal(&self, index: ComponentId) -> bool { - self.archetypal.contains(index) + self.archetypal.contains(&index) } /// Sets this as having access to all components (i.e. `EntityRef` and `&World`). @@ -200,13 +201,13 @@ impl Access { /// Returns `true` if this has access to all components (i.e. `EntityRef` and `&World`). #[inline] pub fn has_read_all(&self) -> bool { - self.read_and_writes_inverted && self.read_and_writes.is_clear() + self.read_and_writes_inverted && self.read_and_writes.is_empty() } /// Returns `true` if this has write access to all components (i.e. `EntityMut` and `&mut World`). #[inline] pub fn has_write_all(&self) -> bool { - self.writes_inverted && self.writes.is_clear() + self.writes_inverted && self.writes.is_empty() } /// Removes all writes. @@ -385,9 +386,9 @@ impl Access { let temp_conflicts: ComponentIdSet = match (lhs_writes_inverted, rhs_reads_and_writes_inverted) { (true, true) => return AccessConflicts::All, - (false, true) => lhs_writes.difference(rhs_reads_and_writes).collect(), - (true, false) => rhs_reads_and_writes.difference(lhs_writes).collect(), - (false, false) => lhs_writes.intersection(rhs_reads_and_writes).collect(), + (false, true) => lhs_writes - rhs_reads_and_writes, + (true, false) => rhs_reads_and_writes - lhs_writes, + (false, false) => lhs_writes & rhs_reads_and_writes, }; conflicts.union_with(&temp_conflicts); } @@ -446,9 +447,9 @@ impl Access { /// # use bevy_ecs::component::ComponentId; /// let mut access = Access::default(); /// - /// access.add_read(ComponentId::new(1)); - /// access.add_write(ComponentId::new(2)); - /// access.add_archetypal(ComponentId::new(3)); + /// access.add_read(ComponentId::from_u32(1)); + /// access.add_write(ComponentId::from_u32(2)); + /// access.add_archetypal(ComponentId::from_u32(3)); /// /// let result = access /// .try_iter_access() @@ -457,9 +458,9 @@ impl Access { /// assert_eq!( /// result, /// Ok(vec![ - /// ComponentAccessKind::Shared(ComponentId::new(1)), - /// ComponentAccessKind::Exclusive(ComponentId::new(2)), - /// ComponentAccessKind::Archetypal(ComponentId::new(3)), + /// ComponentAccessKind::Exclusive(ComponentId::from_u32(2)), + /// ComponentAccessKind::Shared(ComponentId::from_u32(1)), + /// ComponentAccessKind::Archetypal(ComponentId::from_u32(3)), /// ]), /// ); /// ``` @@ -468,16 +469,16 @@ impl Access { ) -> Result + '_, UnboundedAccessError> { let reads_and_writes = self.try_reads_and_writes()?.iter().map(|index| { if self.writes.contains(index) { - ComponentAccessKind::Exclusive(index) + ComponentAccessKind::Exclusive(*index) } else { - ComponentAccessKind::Shared(index) + ComponentAccessKind::Shared(*index) } }); let archetypal = self .archetypal .difference(&self.read_and_writes) - .map(ComponentAccessKind::Archetypal); + .map(|id| ComponentAccessKind::Archetypal(*id)); Ok(reads_and_writes.chain(archetypal)) } @@ -502,7 +503,7 @@ fn invertible_union_with( (true, false) => self_set.difference_with(other_set), (false, true) => { *self_inverted = true; - self_set.difference_from(other_set); + *self_set = other_set - self_set; } (false, false) => self_set.union_with(other_set), } @@ -647,7 +648,7 @@ impl AccessConflicts { pub fn is_empty(&self) -> bool { match self { Self::All => false, - Self::Individual(set) => set.is_clear(), + Self::Individual(set) => set.is_empty(), } } @@ -659,7 +660,7 @@ impl AccessConflicts { .map(|index| { format!( "{}", - world.components().get_name(index).unwrap().shortname() + world.components().get_name(*index).unwrap().shortname() ) }) .collect::>() @@ -865,12 +866,14 @@ impl FilteredAccess { /// Returns the indices of the elements that this access filters for. pub fn with_filters(&self) -> impl Iterator + '_ { - self.filter_sets.iter().flat_map(|f| f.with.iter()) + self.filter_sets.iter().flat_map(|f| f.with.iter().copied()) } /// Returns the indices of the elements that this access filters out. pub fn without_filters(&self) -> impl Iterator + '_ { - self.filter_sets.iter().flat_map(|f| f.without.iter()) + self.filter_sets + .iter() + .flat_map(|f| f.without.iter().copied()) } /// Returns true if the index is used by this `FilteredAccess` in filters or archetypal access. @@ -881,7 +884,7 @@ impl FilteredAccess { || self .filter_sets .iter() - .any(|f| f.with.contains(index) || f.without.contains(index)) + .any(|f| f.with.contains(&index) || f.without.contains(&index)) } } @@ -1110,238 +1113,25 @@ impl FilteredAccessSet { } } -/// A set of [`ComponentId`]s. -#[derive(Default, Eq, PartialEq, Hash)] -#[repr(transparent)] -pub struct ComponentIdSet(FixedBitSet); - -impl ComponentIdSet { - /// Create a new empty `ComponentIdSet`. - #[inline] - pub const fn new() -> Self { - Self(FixedBitSet::new()) - } - - #[cfg(test)] - pub(crate) fn from_bits(bits: FixedBitSet) -> Self { - Self(bits) - } - - /// Adds a [`ComponentId`] to the set. - #[inline] - pub fn insert(&mut self, index: ComponentId) { - self.0.grow_and_insert(index.index()); - } - - /// Removes a [`ComponentId`] from the set. - #[inline] - pub fn remove(&mut self, index: ComponentId) { - if index.index() < self.0.len() { - self.0.remove(index.index()); - } - } - - /// Removes all [`ComponentId`]s from the set. - #[inline] - pub fn clear(&mut self) { - self.0.clear(); - } - - /// Returns `true` if the [`ComponentId`] is in the set. - #[inline] - pub fn contains(&self, index: ComponentId) -> bool { - self.0.contains(index.index()) - } - - /// Returns `true` if `self` has no elements in common with `other`. This - /// is equivalent to checking for an empty intersection. - #[inline] - pub fn is_disjoint(&self, other: &ComponentIdSet) -> bool { - self.0.is_disjoint(&other.0) - } - - /// Returns `true` if the set is a subset of another, i.e. `other` contains - /// at least all the values in `self`. - #[inline] - pub fn is_subset(&self, other: &ComponentIdSet) -> bool { - self.0.is_subset(&other.0) - } - - /// Returns `true` if the set is empty. - #[inline] - pub fn is_clear(&self) -> bool { - self.0.is_clear() - } - - /// Iterates the [`ComponentId`]s in the set. - #[inline] - pub fn iter(&self) -> ComponentIdIter> { - ComponentIdIter(self.0.ones()) - } - - /// Returns a lazy iterator over the union of two [`ComponentIdSet`]s. - #[inline] - pub fn union<'a>(&'a self, other: &'a ComponentIdSet) -> ComponentIdIter> { - ComponentIdIter(self.0.union(&other.0)) - } - - /// Returns a lazy iterator over the intersection of two [`ComponentIdSet`]s. - #[inline] - pub fn intersection<'a>( - &'a self, - other: &'a ComponentIdSet, - ) -> ComponentIdIter> { - ComponentIdIter(self.0.intersection(&other.0)) - } - - /// Returns a lazy iterator over the difference of two [`ComponentIdSet`]s. - #[inline] - pub fn difference<'a>(&'a self, other: &'a ComponentIdSet) -> ComponentIdIter> { - ComponentIdIter(self.0.difference(&other.0)) - } - - /// In-place union of two [`ComponentIdSet`]s. - #[inline] - pub fn union_with(&mut self, other: &ComponentIdSet) { - self.0.union_with(&other.0); - } - - /// In-place intersection of two [`ComponentIdSet`]s. - #[inline] - pub fn intersect_with(&mut self, other: &ComponentIdSet) { - self.0.intersect_with(&other.0); - } - - /// In-place difference of two [`ComponentIdSet`]s. - #[inline] - pub fn difference_with(&mut self, other: &ComponentIdSet) { - self.0.difference_with(&other.0); - } - - /// In-place reversed difference of two [`ComponentIdSet`]s. - /// This sets `self` to be `other.difference(self)`. - #[inline] - pub fn difference_from(&mut self, other: &ComponentIdSet) { - // Calculate `other - self` as `!self & other` - // We have to grow here because the new bits are going to get flipped to 1. - self.0.grow(other.0.len()); - self.0.toggle_range(..); - self.0.intersect_with(&other.0); - } -} - -impl Debug for ComponentIdSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // `FixedBitSet` normally has a `Debug` output like: - // FixedBitSet { data: [ 160 ], length: 8 } - // Instead, print the list of set values, like: - // [ 5, 7 ] - // Don't wrap in `ComponentId`, since that would just output: - // [ ComponentId(5), ComponentId(7) ] - f.debug_list().entries(self.0.ones()).finish() - } -} - -impl Clone for ComponentIdSet { - #[inline] - fn clone(&self) -> Self { - Self(self.0.clone()) - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - self.0.clone_from(&source.0); - } -} - -impl IntoIterator for ComponentIdSet { - type Item = ComponentId; - - type IntoIter = ComponentIdIter; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - ComponentIdIter(self.0.into_ones()) - } -} - -impl<'a> IntoIterator for &'a ComponentIdSet { - type Item = ComponentId; - - type IntoIter = ComponentIdIter>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl FromIterator for ComponentIdSet { - #[inline] - fn from_iter>(iter: T) -> Self { - Self(FixedBitSet::from_iter( - iter.into_iter().map(ComponentId::index), - )) - } -} - -impl Extend for ComponentIdSet { - #[inline] - fn extend>(&mut self, iter: T) { - self.0.extend(iter.into_iter().map(ComponentId::index)); - } -} - -/// An iterator of [`ComponentId`]s. -/// -/// This is equivalent to `map(ComponentId::new)`, -/// but is a named type to allow it to be used in associated types. -#[repr(transparent)] -pub struct ComponentIdIter(I); - -impl> Iterator for ComponentIdIter { - type Item = ComponentId; - - #[inline] - fn next(&mut self) -> Option { - self.0.next().map(ComponentId::new) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } -} - -impl> DoubleEndedIterator for ComponentIdIter { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back().map(ComponentId::new) - } -} - -impl> FusedIterator for ComponentIdIter {} - #[cfg(test)] mod tests { use super::{invertible_difference_with, invertible_union_with}; use crate::{ - component::ComponentId, + component::{ComponentId, ComponentIdSet}, query::{ - access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, ComponentIdSet, - FilteredAccess, FilteredAccessSet, UnboundedAccessError, + access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, FilteredAccess, + FilteredAccessSet, UnboundedAccessError, }, }; use alloc::{vec, vec::Vec}; - use fixedbitset::FixedBitSet; fn create_sample_access() -> Access { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); - access.add_write(ComponentId::new(3)); - access.add_archetypal(ComponentId::new(5)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); + access.add_write(ComponentId::from_u32(3)); + access.add_archetypal(ComponentId::from_u32(5)); access.read_all(); access @@ -1350,10 +1140,10 @@ mod tests { fn create_sample_filtered_access() -> FilteredAccess { let mut filtered_access = FilteredAccess::default(); - filtered_access.add_write(ComponentId::new(1)); - filtered_access.add_read(ComponentId::new(2)); - filtered_access.add_required(ComponentId::new(3)); - filtered_access.and_with(ComponentId::new(4)); + filtered_access.add_write(ComponentId::from_u32(1)); + filtered_access.add_read(ComponentId::from_u32(2)); + filtered_access.add_required(ComponentId::from_u32(3)); + filtered_access.and_with(ComponentId::from_u32(4)); filtered_access } @@ -1361,8 +1151,8 @@ mod tests { fn create_sample_access_filters() -> AccessFilters { let mut access_filters = AccessFilters::default(); - access_filters.with.insert(ComponentId::new(3)); - access_filters.without.insert(ComponentId::new(5)); + access_filters.with.insert(ComponentId::from_u32(3)); + access_filters.without.insert(ComponentId::from_u32(5)); access_filters } @@ -1370,8 +1160,8 @@ mod tests { fn create_sample_filtered_access_set() -> FilteredAccessSet { let mut filtered_access_set = FilteredAccessSet::default(); - filtered_access_set.add_unfiltered_component_read(ComponentId::new(2)); - filtered_access_set.add_unfiltered_component_write(ComponentId::new(4)); + filtered_access_set.add_unfiltered_component_read(ComponentId::from_u32(2)); + filtered_access_set.add_unfiltered_component_write(ComponentId::from_u32(4)); filtered_access_set.read_all(); filtered_access_set @@ -1390,9 +1180,9 @@ mod tests { let original = create_sample_access(); let mut cloned = Access::default(); - cloned.add_write(ComponentId::new(7)); - cloned.add_read(ComponentId::new(4)); - cloned.add_archetypal(ComponentId::new(8)); + cloned.add_write(ComponentId::from_u32(7)); + cloned.add_read(ComponentId::from_u32(4)); + cloned.add_archetypal(ComponentId::from_u32(8)); cloned.write_all(); cloned.clone_from(&original); @@ -1413,8 +1203,8 @@ mod tests { let original = create_sample_filtered_access(); let mut cloned = FilteredAccess::default(); - cloned.add_write(ComponentId::new(7)); - cloned.add_read(ComponentId::new(4)); + cloned.add_write(ComponentId::from_u32(7)); + cloned.add_read(ComponentId::from_u32(4)); cloned.append_or(&FilteredAccess::default()); cloned.clone_from(&original); @@ -1435,8 +1225,8 @@ mod tests { let original = create_sample_access_filters(); let mut cloned = AccessFilters::default(); - cloned.with.insert(ComponentId::new(1)); - cloned.without.insert(ComponentId::new(2)); + cloned.with.insert(ComponentId::from_u32(1)); + cloned.without.insert(ComponentId::from_u32(2)); cloned.clone_from(&original); @@ -1456,8 +1246,8 @@ mod tests { let original = create_sample_filtered_access_set(); let mut cloned = FilteredAccessSet::default(); - cloned.add_unfiltered_component_read(ComponentId::new(7)); - cloned.add_unfiltered_component_write(ComponentId::new(9)); + cloned.add_unfiltered_component_read(ComponentId::from_u32(7)); + cloned.add_unfiltered_component_write(ComponentId::from_u32(9)); cloned.write_all(); cloned.clone_from(&original); @@ -1469,7 +1259,7 @@ mod tests { fn read_all_access_conflicts() { // read_all / single write let mut access_a = Access::default(); - access_a.add_write(ComponentId::new(0)); + access_a.add_write(ComponentId::from_u32(0)); let mut access_b = Access::default(); access_b.read_all(); @@ -1489,54 +1279,54 @@ mod tests { #[test] fn access_get_conflicts() { let mut access_a = Access::default(); - access_a.add_read(ComponentId::new(0)); - access_a.add_read(ComponentId::new(1)); + access_a.add_read(ComponentId::from_u32(0)); + access_a.add_read(ComponentId::from_u32(1)); let mut access_b = Access::default(); - access_b.add_read(ComponentId::new(0)); - access_b.add_write(ComponentId::new(1)); + access_b.add_read(ComponentId::from_u32(0)); + access_b.add_write(ComponentId::from_u32(1)); assert_eq!( access_a.get_conflicts(&access_b), - vec![ComponentId::new(1)].into() + vec![ComponentId::from_u32(1)].into() ); let mut access_c = Access::default(); - access_c.add_write(ComponentId::new(0)); - access_c.add_write(ComponentId::new(1)); + access_c.add_write(ComponentId::from_u32(0)); + access_c.add_write(ComponentId::from_u32(1)); assert_eq!( access_a.get_conflicts(&access_c), - vec![ComponentId::new(0), ComponentId::new(1)].into() + vec![ComponentId::from_u32(0), ComponentId::from_u32(1)].into() ); assert_eq!( access_b.get_conflicts(&access_c), - vec![ComponentId::new(0), ComponentId::new(1)].into() + vec![ComponentId::from_u32(0), ComponentId::from_u32(1)].into() ); let mut access_d = Access::default(); - access_d.add_read(ComponentId::new(0)); + access_d.add_read(ComponentId::from_u32(0)); assert_eq!(access_d.get_conflicts(&access_a), AccessConflicts::empty()); assert_eq!(access_d.get_conflicts(&access_b), AccessConflicts::empty()); assert_eq!( access_d.get_conflicts(&access_c), - vec![ComponentId::new(0)].into() + vec![ComponentId::from_u32(0)].into() ); } #[test] fn filtered_combined_access() { let mut access_a = FilteredAccessSet::default(); - access_a.add_unfiltered_component_read(ComponentId::new(1)); + access_a.add_unfiltered_component_read(ComponentId::from_u32(1)); let mut filter_b = FilteredAccess::default(); - filter_b.add_write(ComponentId::new(1)); + filter_b.add_write(ComponentId::from_u32(1)); let conflicts = access_a.get_conflicts_single(&filter_b); assert_eq!( &conflicts, - &AccessConflicts::from(vec![ComponentId::new(1)]), + &AccessConflicts::from(vec![ComponentId::from_u32(1)]), "access_a: {access_a:?}, filter_b: {filter_b:?}" ); } @@ -1544,42 +1334,46 @@ mod tests { #[test] fn filtered_access_extend() { let mut access_a = FilteredAccess::default(); - access_a.add_read(ComponentId::new(0)); - access_a.add_read(ComponentId::new(1)); - access_a.and_with(ComponentId::new(2)); + access_a.add_read(ComponentId::from_u32(0)); + access_a.add_read(ComponentId::from_u32(1)); + access_a.and_with(ComponentId::from_u32(2)); let mut access_b = FilteredAccess::default(); - access_b.add_read(ComponentId::new(0)); - access_b.add_write(ComponentId::new(3)); - access_b.and_without(ComponentId::new(4)); + access_b.add_read(ComponentId::from_u32(0)); + access_b.add_write(ComponentId::from_u32(3)); + access_b.and_without(ComponentId::from_u32(4)); access_a.extend(&access_b); let mut expected = FilteredAccess::default(); - expected.add_read(ComponentId::new(0)); - expected.add_read(ComponentId::new(1)); - expected.and_with(ComponentId::new(2)); - expected.add_write(ComponentId::new(3)); - expected.and_without(ComponentId::new(4)); + expected.add_read(ComponentId::from_u32(0)); + expected.add_read(ComponentId::from_u32(1)); + expected.and_with(ComponentId::from_u32(2)); + expected.add_write(ComponentId::from_u32(3)); + expected.and_without(ComponentId::from_u32(4)); assert!(access_a.eq(&expected)); } + fn set_from_u32s(iter: Vec) -> ComponentIdSet { + ComponentIdSet::from_iter(iter.into_iter().map(ComponentId::from_u32)) + } + #[test] fn filtered_access_extend_or() { let mut access_a = FilteredAccess::default(); // Exclusive access to `(&mut A, &mut B)`. - access_a.add_write(ComponentId::new(0)); - access_a.add_write(ComponentId::new(1)); + access_a.add_write(ComponentId::from_u32(0)); + access_a.add_write(ComponentId::from_u32(1)); // Filter by `With`. let mut access_b = FilteredAccess::default(); - access_b.and_with(ComponentId::new(2)); + access_b.and_with(ComponentId::from_u32(2)); // Filter by `(With, Without)`. let mut access_c = FilteredAccess::default(); - access_c.and_with(ComponentId::new(3)); - access_c.and_without(ComponentId::new(4)); + access_c.and_with(ComponentId::from_u32(3)); + access_c.and_without(ComponentId::from_u32(4)); // Turns `access_b` into `Or<(With, (With, Without))>`. access_b.append_or(&access_c); @@ -1591,20 +1385,17 @@ mod tests { // The intention here is to test that exclusive access implied by `add_write` // forms correct normalized access structs when extended with `Or` filters. let mut expected = FilteredAccess::default(); - expected.add_write(ComponentId::new(0)); - expected.add_write(ComponentId::new(1)); + expected.add_write(ComponentId::from_u32(0)); + expected.add_write(ComponentId::from_u32(1)); // The resulted access is expected to represent `Or<((With, With, With), (With, With, With, Without))>`. expected.filter_sets = vec![ AccessFilters { - with: ComponentIdSet::from_bits(FixedBitSet::with_capacity_and_blocks(3, [0b111])), + with: set_from_u32s(vec![0, 1, 2]), without: ComponentIdSet::default(), }, AccessFilters { - with: ComponentIdSet::from_bits(FixedBitSet::with_capacity_and_blocks(4, [0b1011])), - without: ComponentIdSet::from_bits(FixedBitSet::with_capacity_and_blocks( - 5, - [0b10000], - )), + with: set_from_u32s(vec![0, 1, 3]), + without: set_from_u32s(vec![4]), }, ]; @@ -1613,23 +1404,27 @@ mod tests { #[test] fn try_iter_component_access_simple() { + use bevy_platform::collections::HashSet; + let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); - access.add_write(ComponentId::new(3)); - access.add_archetypal(ComponentId::new(5)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); + access.add_write(ComponentId::from_u32(3)); + access.add_archetypal(ComponentId::from_u32(5)); - let result = access.try_iter_access().map(Iterator::collect::>); + let result = access + .try_iter_access() + .map(Iterator::collect::>); assert_eq!( result, - Ok(vec![ - ComponentAccessKind::Shared(ComponentId::new(1)), - ComponentAccessKind::Shared(ComponentId::new(2)), - ComponentAccessKind::Exclusive(ComponentId::new(3)), - ComponentAccessKind::Archetypal(ComponentId::new(5)), - ]), + Ok(HashSet::from_iter([ + ComponentAccessKind::Shared(ComponentId::from_u32(1)), + ComponentAccessKind::Shared(ComponentId::from_u32(2)), + ComponentAccessKind::Exclusive(ComponentId::from_u32(3)), + ComponentAccessKind::Archetypal(ComponentId::from_u32(5)), + ])), ); } @@ -1637,8 +1432,8 @@ mod tests { fn try_iter_component_access_unbounded_write_all() { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); access.write_all(); let result = access.try_iter_access().map(Iterator::collect::>); @@ -1656,8 +1451,8 @@ mod tests { fn try_iter_component_access_unbounded_read_all() { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); access.read_all(); let result = access.try_iter_access().map(Iterator::collect::>); @@ -1671,20 +1466,12 @@ mod tests { ); } - /// Create a `ComponentIdSet` with a given number of total bits and a given list of bits to set. - /// Setting the number of bits is important in tests since the `PartialEq` impl checks that the length matches. - fn bit_set(bits: usize, iter: impl IntoIterator) -> ComponentIdSet { - let mut result = FixedBitSet::with_capacity(bits); - result.extend(iter); - ComponentIdSet::from_bits(result) - } - #[test] fn invertible_union_with_tests() { let invertible_union = |mut self_inverted: bool, other_inverted: bool| { // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); + let mut self_set = set_from_u32s(vec![0, 1]); + let other_set = set_from_u32s(vec![0, 2]); invertible_union_with( &mut self_set, &mut self_inverted, @@ -1697,19 +1484,19 @@ mod tests { // Check each combination of `inverted` flags let (s, i) = invertible_union(false, false); // [0, 1] | [0, 2] = [0, 1, 2] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), false)); + assert_eq!((s, i), (set_from_u32s(vec![0, 1, 2]), false)); let (s, i) = invertible_union(false, true); // [0, 1] | [1, 3, ...] = [0, 1, 3, ...] - assert_eq!((s, i), (bit_set(4, [2]), true)); + assert_eq!((s, i), (set_from_u32s(vec![2]), true)); let (s, i) = invertible_union(true, false); // [2, 3, ...] | [0, 2] = [0, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [1]), true)); + assert_eq!((s, i), (set_from_u32s(vec![1]), true)); let (s, i) = invertible_union(true, true); // [2, 3, ...] | [1, 3, ...] = [1, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [0]), true)); + assert_eq!((s, i), (set_from_u32s(vec![0]), true)); } #[test] @@ -1718,9 +1505,9 @@ mod tests { // make sure we invert the bits beyond the original length. // Failing to call `grow` before `toggle_range` would cause bit 1 to be zero, // which would incorrectly treat it as included in the output set. - let mut self_set = bit_set(1, [0]); + let mut self_set = set_from_u32s(vec![0]); let mut self_inverted = false; - let other_set = bit_set(3, [0, 1]); + let other_set = set_from_u32s(vec![0, 1]); let other_inverted = true; invertible_union_with( &mut self_set, @@ -1730,15 +1517,15 @@ mod tests { ); // [0] | [2, ...] = [0, 2, ...] - assert_eq!((self_set, self_inverted), (bit_set(3, [1]), true)); + assert_eq!((self_set, self_inverted), (set_from_u32s(vec![1]), true)); } #[test] fn invertible_difference_with_tests() { let invertible_difference = |mut self_inverted: bool, other_inverted: bool| { // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); + let mut self_set = set_from_u32s(vec![0, 1]); + let other_set = set_from_u32s(vec![0, 2]); invertible_difference_with( &mut self_set, &mut self_inverted, @@ -1751,66 +1538,66 @@ mod tests { // Check each combination of `inverted` flags let (s, i) = invertible_difference(false, false); // [0, 1] - [0, 2] = [1] - assert_eq!((s, i), (bit_set(4, [1]), false)); + assert_eq!((s, i), (set_from_u32s(vec![1]), false)); let (s, i) = invertible_difference(false, true); // [0, 1] - [1, 3, ...] = [0] - assert_eq!((s, i), (bit_set(4, [0]), false)); + assert_eq!((s, i), (set_from_u32s(vec![0]), false)); let (s, i) = invertible_difference(true, false); // [2, 3, ...] - [0, 2] = [3, ...] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), true)); + assert_eq!((s, i), (set_from_u32s(vec![0, 1, 2]), true)); let (s, i) = invertible_difference(true, true); // [2, 3, ...] - [1, 3, ...] = [2] - assert_eq!((s, i), (bit_set(4, [2]), false)); + assert_eq!((s, i), (set_from_u32s(vec![2]), false)); } #[test] fn component_id_set_insert_remove_clear() { let mut set = ComponentIdSet::new(); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(!set.contains(ComponentId::new(2))); - assert!(set.is_clear()); - set.insert(ComponentId::new(2)); - set.insert(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); - assert!(!set.is_clear()); - set.remove(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); - assert!(!set.is_clear()); - set.insert(ComponentId::new(2)); - set.insert(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); - assert!(!set.is_clear()); + assert!(!set.contains(&ComponentId::from_u32(0))); + assert!(!set.contains(&ComponentId::from_u32(1))); + assert!(!set.contains(&ComponentId::from_u32(2))); + assert!(set.is_empty()); + set.insert(ComponentId::from_u32(2)); + set.insert(ComponentId::from_u32(1)); + assert!(!set.contains(&ComponentId::from_u32(0))); + assert!(set.contains(&ComponentId::from_u32(1))); + assert!(set.contains(&ComponentId::from_u32(2))); + assert!(!set.is_empty()); + set.remove(&ComponentId::from_u32(1)); + assert!(!set.contains(&ComponentId::from_u32(0))); + assert!(!set.contains(&ComponentId::from_u32(1))); + assert!(set.contains(&ComponentId::from_u32(2))); + assert!(!set.is_empty()); + set.insert(ComponentId::from_u32(2)); + set.insert(ComponentId::from_u32(1)); + assert!(!set.contains(&ComponentId::from_u32(0))); + assert!(set.contains(&ComponentId::from_u32(1))); + assert!(set.contains(&ComponentId::from_u32(2))); + assert!(!set.is_empty()); set.clear(); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(!set.contains(ComponentId::new(2))); - assert!(set.is_clear()); + assert!(!set.contains(&ComponentId::from_u32(0))); + assert!(!set.contains(&ComponentId::from_u32(1))); + assert!(!set.contains(&ComponentId::from_u32(2))); + assert!(set.is_empty()); } #[test] fn component_id_set_remove_out_of_range() { let mut set = ComponentIdSet::new(); - set.remove(ComponentId::new(3)); - set.insert(ComponentId::new(1)); - set.remove(ComponentId::new(4)); - assert!(set.iter().eq([1].map(ComponentId::new))); + set.remove(&ComponentId::from_u32(3)); + set.insert(ComponentId::from_u32(1)); + set.remove(&ComponentId::from_u32(4)); + assert!(set.iter().eq([&ComponentId::from_u32(1)])); } #[test] fn component_id_set_is_subset_is_disjoint() { - let set_1234 = ComponentIdSet::from_iter([1, 2, 3, 4].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); - let set_45 = ComponentIdSet::from_iter([4, 5].map(ComponentId::new)); + let set_1234 = set_from_u32s(vec![1, 2, 3, 4]); + let set_23 = set_from_u32s(vec![2, 3]); + let set_45 = set_from_u32s(vec![4, 5]); assert!(set_23.is_subset(&set_1234)); assert!(!set_1234.is_subset(&set_23)); assert!(set_23.is_disjoint(&set_45)); @@ -1821,52 +1608,74 @@ mod tests { #[test] fn component_id_set_union_intersection_difference() { - let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); + let set_13 = set_from_u32s(vec![1, 3]); + let set_23 = set_from_u32s(vec![2, 3]); - assert!(set_13.union(&set_23).eq([1, 3, 2].map(ComponentId::new))); - assert!(set_23.union(&set_13).eq([2, 3, 1].map(ComponentId::new))); - assert!(set_13.intersection(&set_23).eq([3].map(ComponentId::new))); - assert!(set_23.intersection(&set_13).eq([3].map(ComponentId::new))); - assert!(set_13.difference(&set_23).eq([1].map(ComponentId::new))); - assert!(set_23.difference(&set_13).eq([2].map(ComponentId::new))); + assert_eq!( + set_13.union(&set_23).copied().collect::(), + set_from_u32s(vec![1, 2, 3]) + ); + assert_eq!( + set_23.union(&set_13).copied().collect::(), + set_from_u32s(vec![1, 2, 3]) + ); + assert_eq!( + set_13 + .intersection(&set_23) + .copied() + .collect::(), + set_from_u32s(vec![3]) + ); + assert_eq!( + set_23 + .intersection(&set_13) + .copied() + .collect::(), + set_from_u32s(vec![3]) + ); + assert_eq!( + set_13 + .difference(&set_23) + .copied() + .collect::(), + set_from_u32s(vec![1]) + ); + assert_eq!( + set_23 + .difference(&set_13) + .copied() + .collect::(), + set_from_u32s(vec![2]) + ); } #[test] fn component_id_set_union_intersection_difference_with() { - let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); + let set_13 = set_from_u32s(vec![1, 3]); + let set_23 = set_from_u32s(vec![2, 3]); let mut s = set_13.clone(); s.union_with(&set_23); - assert!(s.iter().eq([1, 2, 3].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![1, 2, 3])); let mut s = set_23.clone(); s.union_with(&set_13); - assert!(s.iter().eq([1, 2, 3].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![1, 2, 3])); let mut s = set_13.clone(); s.intersect_with(&set_23); - assert!(s.iter().eq([3].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![3])); let mut s = set_23.clone(); s.intersect_with(&set_13); - assert!(s.iter().eq([3].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![3])); let mut s = set_13.clone(); s.difference_with(&set_23); - assert!(s.iter().eq([1].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![1])); let mut s = set_23.clone(); s.difference_with(&set_13); - assert!(s.iter().eq([2].map(ComponentId::new))); - - let mut s = set_13.clone(); - s.difference_from(&set_23); - assert!(s.iter().eq([2].map(ComponentId::new))); - - let mut s = set_23.clone(); - s.difference_from(&set_13); - assert!(s.iter().eq([1].map(ComponentId::new))); + assert_eq!(s, set_from_u32s(vec![2])); } } diff --git a/crates/bevy_ecs/src/query/access_iter.rs b/crates/bevy_ecs/src/query/access_iter.rs index a33d7d77ae2c9..4a3ab8056ad34 100644 --- a/crates/bevy_ecs/src/query/access_iter.rs +++ b/crates/bevy_ecs/src/query/access_iter.rs @@ -70,7 +70,7 @@ fn has_conflicts_large<'a, Q: QueryData>( let needs_check = match access { EcsAccessType::Component(EcsAccessLevel::Read(component_id)) | EcsAccessType::Component(EcsAccessLevel::Write(component_id)) => { - filter.check_insert(&component_id.index()) + filter.check_insert(&component_id) } EcsAccessType::Component(EcsAccessLevel::ReadAll) | EcsAccessType::Component(EcsAccessLevel::WriteAll) => true, @@ -81,7 +81,7 @@ fn has_conflicts_large<'a, Q: QueryData>( let index = match kind { crate::query::ComponentAccessKind::Shared(id) | crate::query::ComponentAccessKind::Exclusive(id) - | crate::query::ComponentAccessKind::Archetypal(id) => id.index(), + | crate::query::ComponentAccessKind::Archetypal(id) => id, }; if filter.check_insert(&index) { needs_check = true; @@ -110,7 +110,7 @@ fn has_conflicts_large<'a, Q: QueryData>( } /// The data storage type that is being accessed. -#[derive(Copy, Clone, Debug, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum EcsAccessType<'a> { /// Accesses [`Component`](crate::prelude::Component) data Component(EcsAccessLevel), diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 6b97387cd980c..31ae363399a98 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -560,7 +560,7 @@ impl QueryState { self.validate_world(world.id()); D::update_archetypes(&mut self.fetch_state, world); F::update_archetypes(&mut self.filter_state, world); - if self.component_access.required.is_clear() { + if self.component_access.required.is_empty() { let archetypes = world.archetypes(); let old_generation = core::mem::replace(&mut self.archetype_generation, archetypes.generation()); @@ -587,7 +587,7 @@ impl QueryState { world .archetypes() .component_index() - .get(&component_id) + .get(component_id) .map(|index| index.keys()) }) // select the component with the fewest archetypes @@ -666,8 +666,8 @@ impl QueryState { /// Returns `true` if this query matches a set of components. Otherwise, returns `false`. pub fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { self.component_access.filter_sets.iter().any(|set| { - set.with.iter().all(set_contains_id) - && set.without.iter().all(|index| !set_contains_id(index)) + set.with.iter().copied().all(set_contains_id) + && set.without.iter().all(|index| !set_contains_id(*index)) }) } diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index 5189f5e3465f8..5a32063d00b04 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -4,16 +4,14 @@ use log::warn; use crate::{ component::{Component, ComponentId}, - entity::Entity, + entity::ContainsEntity, lifecycle::HookContext, - storage::SparseArray, world::DeferredWorld, }; #[cfg(feature = "bevy_reflect")] use {crate::reflect::ReflectComponent, bevy_reflect::Reflect}; // The derive macro for the `Resource` trait pub use bevy_ecs_macros::Resource; -use bevy_platform::cell::SyncUnsafeCell; /// A type that can be inserted into a [`World`] as a singleton. /// @@ -86,37 +84,6 @@ use bevy_platform::cell::SyncUnsafeCell; )] pub trait Resource: Component {} -/// A cache that links each `ComponentId` from a resource to the corresponding entity. -#[derive(Default)] -pub struct ResourceEntities(SyncUnsafeCell>); - -impl ResourceEntities { - /// Returns an iterator over all registered resource components and their corresponding entity. - /// - /// This must scan the entire array of components to find non-empty values, - /// which may be slow even if there are few resources. - #[inline] - pub fn iter(&self) -> impl Iterator { - self.deref().iter().map(|(id, entity)| (id, *entity)) - } - - /// Returns the entity for the given resource component, or `None` if there is no entity. - #[inline] - pub fn get(&self, id: ComponentId) -> Option { - self.deref().get(id).copied() - } - - #[inline] - fn deref(&self) -> &SparseArray { - // SAFETY: There are no other mutable references to the map. - // The underlying `SyncUnsafeCell` is never exposed outside this module, - // so mutable references are only created by the resource hooks. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &*self.0.get() } - } -} - /// A marker component for entities that have a Resource component. #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))] #[derive(Component, Debug)] @@ -141,43 +108,36 @@ impl IsResource { .unwrap() .resource_component_id(); - if let Some(original_entity) = world.resource_entities.get(resource_component_id) { - if !world.entities().contains(original_entity) { - let name = world - .components() - .get_name(resource_component_id) - .expect("resource is registered"); - panic!( - "Resource entity {} of {} has been despawned, when it's not supposed to be.", - original_entity, name - ); - } + let original_entity = resource_component_id.entity(); + + if !world.entities().contains(original_entity) { + let name = world + .components() + .get_name(resource_component_id) + .expect("resource is registered"); + panic!( + "Resource entity {} of {} has been despawned, when it's not supposed to be.", + original_entity, name + ); + } - if original_entity != context.entity { - // the resource already exists and the new one should be removed - world - .commands() - .entity(context.entity) - .remove_by_id(resource_component_id); - world - .commands() - .entity(context.entity) - .remove_by_id(context.component_id); - let name = world - .components() - .get_name(resource_component_id) - .expect("resource is registered"); - warn!("Tried inserting the resource {} while one already exists. \ - Resources are unique components stored on a single entity. \ - Inserting on a different entity, when one already exists, causes the new value to be removed.", name); - } - } else { - // SAFETY: We have exclusive world access (as long as we don't make structural changes). - let cache = unsafe { world.as_unsafe_world_cell().resource_entities() }; - // SAFETY: There are no shared references to the map. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &mut *cache.0.get() }.insert(resource_component_id, context.entity); + if original_entity != context.entity { + // the resource already exists and the new one should be removed + world + .commands() + .entity(context.entity) + .remove_by_id(resource_component_id); + world + .commands() + .entity(context.entity) + .remove_by_id(context.component_id); + let name = world + .components() + .get_name(resource_component_id) + .expect("resource is registered"); + warn!("Tried inserting the resource {} while one already exists. \ + Resources are unique components stored on a single entity. \ + Inserting on a different entity, when one already exists, causes the new value to be removed.", name); } } @@ -188,16 +148,9 @@ impl IsResource { .unwrap() .resource_component_id(); - if let Some(resource_entity) = world.resource_entities.get(resource_component_id) - && resource_entity == context.entity - { - // SAFETY: We have exclusive world access (as long as we don't make structural changes). - let cache = unsafe { world.as_unsafe_world_cell().resource_entities() }; - // SAFETY: There are no shared references to the map. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &mut *cache.0.get() }.remove(resource_component_id); + let original_entity = resource_component_id.entity(); + if original_entity == context.entity { world .commands() .entity(context.entity) @@ -211,7 +164,7 @@ impl IsResource { } /// [`ComponentId`] of the [`IsResource`] component. -pub const IS_RESOURCE: ComponentId = ComponentId::new(crate::component::IS_RESOURCE); +pub const IS_RESOURCE: ComponentId = ComponentId::from_u32(crate::component::IS_RESOURCE); #[cfg(test)] mod tests { @@ -219,7 +172,7 @@ mod tests { use crate::{ change_detection::MaybeLocation, - entity::Entity, + entity::{ContainsEntity, Entity}, lifecycle::HookContext, ptr::OwningPtr, resource::{IsResource, Resource}, @@ -259,7 +212,7 @@ mod tests { } }); assert_eq!(world.entities().count_spawned(), start + 3); - let e3 = world.resource_entities().get(id3).unwrap(); + let e3 = id3.entity(); assert!(world.remove_resource_by_id(id3)); // the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity assert_eq!(world.entities().count_spawned(), start + 3); @@ -269,13 +222,13 @@ mod tests { world.insert_resource_by_id(id3, ptr, MaybeLocation::caller()); } }); - assert_eq!(e3, world.resource_entities().get(id3).unwrap()); + assert_eq!(e3, id3.entity()); // again, the entity is stable: see previous explanation - let e1 = world.resource_entities().get(id1).unwrap(); + let e1 = id1.entity(); world.remove_resource::(); assert_eq!(world.entities().count_spawned(), start + 3); world.init_resource::(); - assert_eq!(e1, world.resource_entities().get(id1).unwrap()); + assert_eq!(e1, id1.entity()); // make sure that trying to add a resource twice results, doesn't change the entity count world.insert_resource(TestResource2(String::from("Bar"))); assert_eq!(world.entities().count_spawned(), start + 3); @@ -319,9 +272,9 @@ mod tests { entity }; - assert_ne!( + assert_eq!( first_entity, second_entity, - "The first resource entity was invalidated, so the second initialization should be new" + "The resource should always be inserted on the component entity" ); let id = world.spawn(TestResource).id(); diff --git a/crates/bevy_ecs/src/schedule/node.rs b/crates/bevy_ecs/src/schedule/node.rs index b98815dc0b86d..302aca38943f6 100644 --- a/crates/bevy_ecs/src/schedule/node.rs +++ b/crates/bevy_ecs/src/schedule/node.rs @@ -615,6 +615,7 @@ impl Systems { AccessConflicts::Individual(conflicts) => { let conflicts: Box<[_]> = conflicts .iter() + .copied() .filter(|id| !ignored_ambiguities.contains(id)) .collect(); if !conflicts.is_empty() { diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 13460e2defd1e..e0e2a99e62245 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -135,19 +135,6 @@ impl SparseArray { marker: PhantomData, } } - - /// Returns an iterator over the non-empty values in the array. - /// - /// This must scan the entire array to find non-empty values, - /// which may be slow even if the array is sparsely populated. - #[inline] - pub(crate) fn iter(&self) -> impl Iterator { - self.values.iter().enumerate().filter_map(|(index, value)| { - value - .as_ref() - .map(|value| (SparseSetIndex::get_sparse_set_index(index), value)) - }) - } } /// A sparse data structure of [`Component`](crate::component::Component)s. @@ -937,12 +924,12 @@ mod tests { collected_sets.sort(); assert_eq!( collected_sets, - vec![(ComponentId::new(1), 0), (ComponentId::new(2), 0),] + vec![(ComponentId::from_u32(2), 0), (ComponentId::from_u32(1), 0),] ); - fn register_component(sets: &mut SparseSets, id: usize) { + fn register_component(sets: &mut SparseSets, id: u32) { let descriptor = ComponentDescriptor::new::(); - let id = ComponentId::new(id); + let id = ComponentId::from_u32(id); let info = ComponentInfo::new(id, descriptor); sets.get_or_insert(&info); } diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index 9b3a0c86c097d..1debdd3926d08 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -857,8 +857,8 @@ impl Drop for Table { mod tests { use crate::{ change_detection::{MaybeLocation, Tick}, - component::{Component, ComponentIds, Components, ComponentsRegistrator}, - entity::{Entity, EntityIndex}, + component::{Component, Components, ComponentsRegistrator}, + entity::{Entity, EntityAllocator, EntityIndex}, ptr::OwningPtr, storage::{TableBuilder, TableId, TableRow, Tables}, }; @@ -882,10 +882,10 @@ mod tests { #[test] fn table() { let mut components = Components::default(); - let mut componentids = ComponentIds::default(); + let mut allocator = EntityAllocator::default(); // SAFETY: They are both new. let mut registrator = - unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) }; + unsafe { ComponentsRegistrator::new(&mut components, &mut allocator) }; let component_id = registrator.register_component::>(); let columns = &[component_id]; let mut table = TableBuilder::with_capacity(0, columns.len()) diff --git a/crates/bevy_ecs/src/world/entity_access/mod.rs b/crates/bevy_ecs/src/world/entity_access/mod.rs index a4a63e76d2cf3..f11516bca908b 100644 --- a/crates/bevy_ecs/src/world/entity_access/mod.rs +++ b/crates/bevy_ecs/src/world/entity_access/mod.rs @@ -99,7 +99,7 @@ mod tests { #[test] fn entity_ref_get_by_id_invalid_component_id() { - let invalid_component_id = ComponentId::new(usize::MAX); + let invalid_component_id = ComponentId::new(Entity::PLACEHOLDER); let mut world = World::new(); let entity = world.spawn_empty().id(); @@ -109,7 +109,7 @@ mod tests { #[test] fn entity_mut_get_by_id_invalid_component_id() { - let invalid_component_id = ComponentId::new(usize::MAX); + let invalid_component_id = ComponentId::new(Entity::PLACEHOLDER); let mut world = World::new(); let mut entity = world.spawn_empty(); diff --git a/crates/bevy_ecs/src/world/entity_access/world_mut.rs b/crates/bevy_ecs/src/world/entity_access/world_mut.rs index 6ae9d32e255eb..dfc57cf6732a4 100644 --- a/crates/bevy_ecs/src/world/entity_access/world_mut.rs +++ b/crates/bevy_ecs/src/world/entity_access/world_mut.rs @@ -5,7 +5,9 @@ use crate::{ }, change_detection::{ComponentTicks, MaybeLocation, MutUntyped, Tick}, component::{Component, ComponentId, Components, Mutable, StorageType}, - entity::{Entity, EntityCloner, EntityClonerBuilder, EntityLocation, OptIn, OptOut}, + entity::{ + ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityLocation, OptIn, OptOut, + }, event::{EntityComponentsTrigger, EntityEvent}, lifecycle::{Despawn, Discard, Remove, DESPAWN, DISCARD, REMOVE}, observer::IntoEntityObserver, @@ -14,7 +16,7 @@ use crate::{ ReleaseStateQueryData, SingleEntityQueryData, }, relationship::RelationshipHookMode, - resource::{Resource, ResourceEntities}, + resource::Resource, storage::{SparseSets, Table}, system::EntityCommands, template::{SceneEntityReferences, Template, TemplateContext}, @@ -735,19 +737,12 @@ impl<'w> EntityWorldMut<'w> { }) } - /// Retrieves this world's [`ResourceEntities`]. - #[inline] - #[track_caller] - pub fn resource_entities(&self) -> &ResourceEntities { - self.world.resource_entities() - } - /// Retrieves the [`Entity`] associated with the resource of type `R`, if it exists. #[inline] #[track_caller] pub fn resource_entity(&self) -> Option { let component_id = self.world.component_id::()?; - self.world.resource_entities().get(component_id) + Some(component_id.entity()) } /// Retrieves the change ticks for the given component. This can be useful for implementing change diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index bd7c9e33133b0..cd92f9e79563c 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -39,11 +39,13 @@ use crate::{ CheckChangeTicks, ComponentTicks, ComponentTicksMut, MaybeLocation, MutUntyped, Tick, }, component::{ - Component, ComponentDescriptor, ComponentId, ComponentIds, ComponentInfo, Components, + Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable, RequiredComponents, RequiredComponentsError, }, - entity::{Entities, Entity, EntityAllocator, EntityNotSpawnedError, SpawnError}, + entity::{ + ContainsEntity, Entities, Entity, EntityAllocator, EntityNotSpawnedError, SpawnError, + }, entity_disabling::DefaultQueryFilters, error::{ErrorHandler, FallbackErrorHandler}, lifecycle::{ComponentHooks, RemovedComponentMessages, ADD, DESPAWN, DISCARD, INSERT, REMOVE}, @@ -52,7 +54,7 @@ use crate::{ prelude::{Add, Despawn, Discard, Insert, Remove}, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, - resource::{IsResource, Resource, ResourceEntities, IS_RESOURCE}, + resource::{IsResource, Resource, IS_RESOURCE}, schedule::{Schedule, ScheduleLabel, Schedules}, storage::{NonSendData, Storages}, system::Commands, @@ -95,8 +97,6 @@ pub struct World { pub(crate) entities: Entities, pub(crate) entity_allocator: EntityAllocator, pub(crate) components: Components, - pub(crate) component_ids: ComponentIds, - pub(crate) resource_entities: ResourceEntities, pub(crate) archetypes: Archetypes, pub(crate) storages: Storages, pub(crate) bundles: Bundles, @@ -116,7 +116,6 @@ impl Default for World { entities: Entities::new(), entity_allocator: EntityAllocator::default(), components: Default::default(), - resource_entities: Default::default(), archetypes: Archetypes::new(), storages: Default::default(), bundles: Default::default(), @@ -129,7 +128,6 @@ impl Default for World { last_check_tick: Tick::new(0), last_trigger_id: 0, command_queue: RawCommandQueue::new(), - component_ids: ComponentIds::default(), }; world.bootstrap(); world @@ -254,26 +252,25 @@ impl World { &self.components } - /// Retrieves this world's [`ResourceEntities`]. - #[inline] - pub fn resource_entities(&self) -> &ResourceEntities { - &self.resource_entities - } - /// Prepares a [`ComponentsQueuedRegistrator`] for the world. /// **NOTE:** [`ComponentsQueuedRegistrator`] is easily misused. /// See its docs for important notes on when and how it should be used. #[inline] pub fn components_queue(&self) -> ComponentsQueuedRegistrator<'_> { // SAFETY: These are from the same world. - unsafe { ComponentsQueuedRegistrator::new(&self.components, &self.component_ids) } + unsafe { + ComponentsQueuedRegistrator::new( + &self.components, + self.entity_allocator().build_remote_allocator(), + ) + } } /// Prepares a [`ComponentsRegistrator`] for the world. #[inline] pub fn components_registrator(&mut self) -> ComponentsRegistrator<'_> { // SAFETY: These are from the same world. - unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) } + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) } } /// Retrieves this world's [`Storages`] collection. @@ -1485,7 +1482,8 @@ impl World { f: impl FnOnce(&mut R) -> S, ) -> Result, EntityMutableFetchError> { let component_id = self.register_component::(); - if let Some(entity) = self.resource_entities.get(component_id) { + let entity = component_id.entity(); + if self.entities().contains_spawned(entity) { let mut world = DeferredWorld::from(&mut *self); let result = world.modify_component_with_relationship_hook_mode( entity, @@ -1521,7 +1519,8 @@ impl World { component_id: ComponentId, f: impl for<'a> FnOnce(MutUntyped<'a>) -> S, ) -> Result, EntityMutableFetchError> { - if let Some(entity) = self.resource_entities.get(component_id) { + let entity = component_id.entity(); + if self.entities().contains_spawned(entity) { let mut world = DeferredWorld::from(&mut *self); let result = world.modify_component_by_id_with_relationship_hook_mode( @@ -1923,9 +1922,9 @@ impl World { caller: MaybeLocation, ) -> (ComponentId, EntityWorldMut<'_>) { let resource_id = self.register_component::(); + let entity = resource_id.entity(); - if let Some(entity) = self.resource_entities.get(resource_id) { - let entity_ref = self.get_entity(entity).expect("ResourceCache is in sync"); + if let Ok(entity_ref) = self.get_entity(entity) { if !entity_ref.contains_id(resource_id) { let resource = func(self); move_as_ptr!(resource); @@ -1941,7 +1940,9 @@ impl World { let resource = func(self); move_as_ptr!(resource); - let entity_mut = self.spawn_with_caller(resource, caller); // ResourceCache is updated automatically + // cannot panic because self.get_entity(entity) was None (entity hasn't been spawned) + // and self.register_component ensures that the entity is not invalid. + let entity_mut = self.spawn_at_unchecked(entity, resource, caller); (resource_id, entity_mut) } @@ -2047,7 +2048,7 @@ impl World { #[inline] pub fn remove_resource(&mut self) -> Option { let resource_id = self.component_id::()?; - let entity = self.resource_entities.get(resource_id)?; + let entity = resource_id.entity(); let value = self .get_entity_mut(entity) .expect("ResourceCache is in sync") @@ -2085,9 +2086,7 @@ impl World { /// Returns `true` if a resource with provided `component_id` exists. Otherwise returns `false`. #[inline] pub fn contains_resource_by_id(&self, component_id: ComponentId) -> bool { - if let Some(entity) = self.resource_entities.get(component_id) - && let Ok(entity_ref) = self.get_entity(entity) - { + if let Ok(entity_ref) = self.get_entity(component_id.entity()) { return entity_ref.contains_id(component_id); } false @@ -2175,7 +2174,7 @@ impl World { &self, component_id: ComponentId, ) -> Option { - let entity = self.resource_entities.get(component_id)?; + let entity = component_id.entity(); let entity_ref = self.get_entity(entity).ok()?; entity_ref.get_change_ticks_by_id(component_id) } @@ -2803,7 +2802,7 @@ impl World { let change_tick = self.change_tick(); let component_id = self.components.valid_component_id::()?; - let entity = self.resource_entities.get(component_id)?; + let entity = component_id.entity(); let mut entity_mut = self.get_entity_mut(entity).ok()?; let mut ticks = entity_mut.get_change_ticks::()?; @@ -2986,12 +2985,12 @@ impl World { value: OwningPtr<'_>, caller: MaybeLocation, ) { + let entity = component_id.entity(); // if the resource already exists, we replace it on the same entity - let mut entity_mut = if let Some(entity) = self.resource_entities.get(component_id) { - self.get_entity_mut(entity) - .expect("ResourceCache is in sync") + let mut entity_mut = if self.entities().contains_spawned(entity) { + self.entity_mut(entity) } else { - self.spawn_empty() + self.spawn_empty_at_unchecked(entity, caller) }; // SAFETY: pointer valid for this component id per precondition unsafe { @@ -3320,9 +3319,13 @@ impl World { /// This can easily cause systems expecting certain resources to immediately start panicking. /// Use with caution. pub fn clear_resources(&mut self) { - let pairs: Vec<(ComponentId, Entity)> = self.resource_entities().iter().collect(); - for (component_id, entity) in pairs { - self.entity_mut(entity).remove_by_id(component_id); + let ids: Vec = self.components().iter_registered_ids().collect(); + for component_id in ids { + let entity = component_id.entity(); + if self.entities().contains_spawned(entity) { + // only resource entities with a matching component_id should have a component. + self.entity_mut(entity).remove_by_id(component_id); + } } } @@ -3347,7 +3350,7 @@ impl World { 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) }; + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) }; // SAFETY: `registrator`, `self.storages` and `self.bundles` all come from this world. unsafe { @@ -3359,7 +3362,7 @@ impl World { pub(crate) fn register_contributed_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) }; + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) }; // SAFETY: `registrator`, `self.bundles` and `self.storages` are all from this world. unsafe { @@ -3521,10 +3524,11 @@ impl World { /// ``` #[inline] pub fn iter_resources(&self) -> impl Iterator)> { - self.resource_entities - .iter() - .filter_map(|(component_id, entity)| { - let component_info = self.components().get_info(component_id)?; + self.components() + .iter_registered() + .filter_map(|component_info| { + let component_id = component_info.id(); + let entity = component_id.entity(); let entity_cell = self.get_entity(entity).ok()?; let resource = entity_cell.get_by_id(component_id).ok()?; Some((component_info, resource)) @@ -3598,18 +3602,13 @@ impl World { /// ``` pub fn iter_resources_mut(&mut self) -> impl Iterator)> { let unsafe_world = self.as_unsafe_world_cell(); - // SAFETY: exclusive world access to all resources - let resource_entities = unsafe { unsafe_world.resource_entities() }; - let components = unsafe_world.components(); - - resource_entities - .iter() - .filter_map(move |(component_id, entity)| { - // SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with its ID. - let component_info = - unsafe { components.get_info(component_id).debug_checked_unwrap() }; - let entity_cell = unsafe_world.get_entity(entity).ok()?; + unsafe_world + .components() + .iter_registered() + .filter_map(move |component_info| { + let component_id = component_info.id(); + let entity_cell = unsafe_world.get_entity(component_id.entity()).ok()?; // SAFETY: // - We have exclusive world access @@ -3670,8 +3669,8 @@ impl World { /// **You should prefer to use the typed API [`World::remove_resource`] where possible and only /// use this in cases where the actual types are not known at compile time.** pub fn remove_resource_by_id(&mut self, component_id: ComponentId) -> bool { - if let Some(entity) = self.resource_entities.get(component_id) - && let Ok(mut entity_mut) = self.get_entity_mut(entity) + let entity = component_id.entity(); + if let Ok(mut entity_mut) = self.get_entity_mut(entity) && entity_mut.contains_id(component_id) { entity_mut.remove_by_id(component_id); @@ -3949,7 +3948,7 @@ mod tests { prelude::{DetectChanges, Event, Mut, On, Res}, ptr::OwningPtr, resource::Resource, - world::{error::EntityMutableFetchError, DeferredWorld}, + world::{error::EntityMutableFetchError, DeferredWorld, MutUntyped}, }; use alloc::{ borrow::ToOwned, @@ -3960,6 +3959,7 @@ mod tests { }; use bevy_ecs_macros::Component; use bevy_platform::collections::{HashMap, HashSet}; + use bevy_ptr::Ptr; use bevy_utils::prelude::DebugName; use core::{ any::TypeId, @@ -4141,14 +4141,14 @@ mod tests { world.insert_resource(TestResource3); world.remove_resource::(); - let mut iter = world.iter_resources(); + let mut resources = world + .iter_resources() + .collect::)>>(); + resources.sort_by_key(|a| a.0.id()); - let (info, ptr) = iter.next().unwrap(); - assert_eq!(info.name(), DebugName::type_name::()); - // SAFETY: We know that the resource is of type `TestResource` - assert_eq!(unsafe { ptr.deref::().0 }, 42); + assert_eq!(resources.len(), 2); - let (info, ptr) = iter.next().unwrap(); + let (info, ptr) = resources[0]; assert_eq!(info.name(), DebugName::type_name::()); assert_eq!( // SAFETY: We know that the resource is of type `TestResource2` @@ -4156,7 +4156,10 @@ mod tests { &"Hello, world!".to_string() ); - assert!(iter.next().is_none()); + let (info, ptr) = resources[1]; + assert_eq!(info.name(), DebugName::type_name::()); + // SAFETY: We know that the resource is of type `TestResource` + assert_eq!(unsafe { ptr.deref::().0 }, 42); } #[test] @@ -4169,14 +4172,12 @@ mod tests { world.insert_resource(TestResource3); world.remove_resource::(); - let mut iter = world.iter_resources_mut(); + let mut resources = world + .iter_resources_mut() + .collect::)>>(); + resources.sort_by_key(|a| a.0.id()); - let (info, mut mut_untyped) = iter.next().unwrap(); - assert_eq!(info.name(), DebugName::type_name::()); - // SAFETY: We know that the resource is of type `TestResource` - unsafe { - mut_untyped.as_mut().deref_mut::().0 = 43; - }; + let mut iter = resources.into_iter(); let (info, mut mut_untyped) = iter.next().unwrap(); assert_eq!(info.name(), DebugName::type_name::()); @@ -4185,6 +4186,13 @@ mod tests { mut_untyped.as_mut().deref_mut::().0 = "Hello, world?".to_string(); }; + let (info, mut mut_untyped) = iter.next().unwrap(); + assert_eq!(info.name(), DebugName::type_name::()); + // SAFETY: We know that the resource is of type `TestResource` + unsafe { + mut_untyped.as_mut().deref_mut::().0 = 43; + }; + assert!(iter.next().is_none()); drop(iter); diff --git a/crates/bevy_ecs/src/world/reflect.rs b/crates/bevy_ecs/src/world/reflect.rs index 0c7909d5bc4aa..b7c5dfee15b3e 100644 --- a/crates/bevy_ecs/src/world/reflect.rs +++ b/crates/bevy_ecs/src/world/reflect.rs @@ -199,10 +199,13 @@ impl World { resource_id: ComponentId, reflected_resource: Box, ) { - if let Some(entity) = self.resource_entities().get(resource_id) { + let entity = resource_id.entity(); + if self.entities().contains_spawned(entity) { self.entity_mut(entity).insert_reflect(reflected_resource); } else { - self.spawn_empty().insert_reflect(reflected_resource); + self.spawn_empty_at(entity) + .expect("entity isn't already spawned") + .insert_reflect(reflected_resource); } } } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 2ada7b14a831b..6782d26f0e704 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -17,7 +17,7 @@ use crate::{ observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, QueryAccessError, ReleaseStateQueryData, SingleEntityQueryData}, - resource::{Resource, ResourceEntities}, + resource::Resource, storage::{ComponentSparseSet, Storages, Table}, world::RawCommandQueue, }; @@ -292,17 +292,6 @@ impl<'w> UnsafeWorldCell<'w> { &unsafe { self.world_metadata() }.components } - /// Retrieves this world's resource-entity map. - /// - /// # Safety - /// The caller must have exclusive read or write access to the resources that are updated in the cache. - #[inline] - pub unsafe fn resource_entities(self) -> &'w ResourceEntities { - // SAFETY: - // - we only access world metadata - &unsafe { self.world_metadata() }.resource_entities - } - /// Retrieves this world's collection of [removed components](RemovedComponentMessages). pub fn removed_components(self) -> &'w RemovedComponentMessages { // SAFETY: @@ -468,8 +457,7 @@ impl<'w> UnsafeWorldCell<'w> { /// - no mutable reference to the resource exists at the same time #[inline] pub unsafe fn get_resource_by_id(self, component_id: ComponentId) -> Option> { - // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.entity(); let entity_cell = self.get_entity(entity).ok()?; // SAFETY: Exclusive access per preconditions unsafe { entity_cell.get_by_id(component_id) } @@ -554,8 +542,7 @@ impl<'w> UnsafeWorldCell<'w> { component_id: ComponentId, ) -> Option> { self.assert_allows_mutable_access(); - // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.entity(); let entity_cell = self.get_entity(entity).ok()?; // SAFETY: Access permissions and uniqueness per preconditions unsafe { entity_cell.get_mut_by_id(component_id).ok() } @@ -651,8 +638,7 @@ impl<'w> UnsafeWorldCell<'w> { self, component_id: ComponentId, ) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> { - // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.entity(); let storage_type = self.components().get_info(component_id)?.storage_type(); let location = self.get_entity(entity).ok()?.location(); // SAFETY: diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 2f10c3397d6a0..6108ac2f4ec74 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Result as AnyhowResult}; use bevy_dev_tools::schedule_data::serde::ScheduleData; use bevy_ecs::{ component::ComponentId, - entity::Entity, + entity::{ContainsEntity, Entity}, hierarchy::ChildOf, lifecycle::RemovedComponentEntity, message::MessageCursor, @@ -2014,10 +2014,7 @@ fn get_resource_entity_pair( .components() .get_id(type_id) .ok_or(anyhow!("Resource not registered: `{}`", resource_path))?; - let entity = world - .resource_entities() - .get(component_id) - .ok_or(anyhow!("Resource entity does not exist."))?; + let entity = component_id.entity(); Ok((entity, component_id)) } diff --git a/crates/bevy_settings/src/lib.rs b/crates/bevy_settings/src/lib.rs index 40c9cbecadafe..b2c158f6f52c4 100644 --- a/crates/bevy_settings/src/lib.rs +++ b/crates/bevy_settings/src/lib.rs @@ -10,6 +10,7 @@ use std::collections::HashMap; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::{ change_detection::Tick, + entity::ContainsEntity, reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, resource::Resource, system::{Command, Commands, Res, ResMut}, @@ -331,14 +332,15 @@ fn resources_to_toml( continue; }; - let Some(res_entity) = world.resource_entities().get(component_id) else { + let res_entity = component_id.entity(); + if !world.entities().contains_spawned(res_entity) { continue; - }; + } + let res_entity_ref = world.entity(res_entity); let Some(reflect) = cmp.reflect(res_entity_ref) else { continue; }; - let serializer = TypedReflectSerializer::new(reflect.as_partial_reflect(), types); let toml_value = if let Some(settings_key) = settings_key { @@ -460,10 +462,10 @@ fn apply_settings_to_world( let reflect_component = ty.data::().unwrap(); let component_id = world.components().get_id(*tid); - let res_entity = component_id.and_then(|cid| world.resource_entities().get(cid)); - if let Some(res_entity) = res_entity { + if let Some(component_id) = component_id { // Resource already exists, so apply toml properties to it. + let res_entity = component_id.entity(); let res_entity_mut = world.entity_mut(res_entity); let Some(mut reflect) = reflect_component.reflect_mut(res_entity_mut) else { continue; @@ -485,9 +487,13 @@ fn apply_settings_to_world( } } else { // The resource does not exist, so create a default. + let component_id = reflect_component.register_component(world); + let mut res_entity = world + .spawn_empty_at(component_id.entity()) + .expect("entity was just allocated"); + let reflect_default = ty.data::().unwrap(); let mut default_value = reflect_default.default(); - let mut res_entity = world.spawn_empty(); if let Some(toml) = toml && let Some(value) = toml.get(settings_group) diff --git a/crates/bevy_world_serialization/src/dynamic_world.rs b/crates/bevy_world_serialization/src/dynamic_world.rs index 526357c64e1df..44de62d00d19c 100644 --- a/crates/bevy_world_serialization/src/dynamic_world.rs +++ b/crates/bevy_world_serialization/src/dynamic_world.rs @@ -2,7 +2,7 @@ use crate::{DynamicWorldBuilder, WorldAsset, WorldInstanceSpawnError}; use bevy_asset::Asset; use bevy_ecs::reflect::ReflectResource; use bevy_ecs::{ - entity::{Entity, EntityHashMap, SceneEntityMapper}, + entity::{ContainsEntity, Entity, EntityHashMap, SceneEntityMapper}, reflect::{AppTypeRegistry, ReflectComponent}, world::World, }; @@ -181,13 +181,12 @@ impl DynamicWorld { .expect("ReflectComponent is depended on ReflectResource"); let resource_id = reflect_component.register_component(world); + let entity = resource_id.entity(); - // check if the resource already exists, if not spawn it, otherwise override the value - let entity = if let Some(entity) = world.resource_entities().get(resource_id) { - entity - } else { - world.spawn_empty().id() - }; + // check if the resource already exists, if not spawn it + if !world.entities().contains_spawned(entity) { + let _ = world.spawn_empty_at(entity); + } SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { reflect_component.apply_or_insert_mapped( diff --git a/crates/bevy_world_serialization/src/dynamic_world_builder.rs b/crates/bevy_world_serialization/src/dynamic_world_builder.rs index 727d7adc9fecc..d53387289fbd4 100644 --- a/crates/bevy_world_serialization/src/dynamic_world_builder.rs +++ b/crates/bevy_world_serialization/src/dynamic_world_builder.rs @@ -6,6 +6,7 @@ use alloc::collections::BTreeMap; use bevy_ecs::resource::IS_RESOURCE; use bevy_ecs::{ component::{Component, ComponentId}, + entity::ContainsEntity, entity_disabling::DefaultQueryFilters, prelude::Entity, reflect::{ReflectComponent, ReflectResource}, @@ -377,10 +378,17 @@ impl<'w> DynamicWorldBuilder<'w> { .components() .get_valid_id(TypeId::of::()); - for (component_id, entity) in self.original_world.resource_entities().iter() { + for component_id in self.original_world.components().iter_registered_ids() { + let entity = component_id.entity(); + + if !self.original_world.entities().contains_spawned(entity) { + continue; + } + if Some(component_id) == original_world_dqf_id { continue; } + let mut extract_and_push = || { let type_id = self .original_world @@ -501,8 +509,8 @@ mod tests { assert_eq!(dynamic_world.entities.len(), 1); assert_eq!(dynamic_world.entities[0].entity, entity); assert_eq!(dynamic_world.entities[0].components.len(), 2); - assert!(dynamic_world.entities[0].components[0].represents::()); - assert!(dynamic_world.entities[0].components[1].represents::()); + assert!(dynamic_world.entities[0].components[0].represents::()); + assert!(dynamic_world.entities[0].components[1].represents::()); } #[test] diff --git a/crates/bevy_world_serialization/src/serde.rs b/crates/bevy_world_serialization/src/serde.rs index 64505eda4b8ee..ab8cdde410dfb 100644 --- a/crates/bevy_world_serialization/src/serde.rs +++ b/crates/bevy_world_serialization/src/serde.rs @@ -694,25 +694,25 @@ mod tests { ), }, entities: { - 4294967290: ( + 4294967279: ( components: { "bevy_world_serialization::serde::tests::FakeMesh3d": (Uuid("00000000-0000-0000-0000-000000000001")), }, ), - 4294967291: ( + 4294967281: ( components: { "bevy_world_serialization::serde::tests::Bar": (345), "bevy_world_serialization::serde::tests::Baz": (789), "bevy_world_serialization::serde::tests::Foo": (123), }, ), - 4294967292: ( + 4294967283: ( components: { "bevy_world_serialization::serde::tests::Bar": (345), "bevy_world_serialization::serde::tests::Foo": (123), }, ), - 4294967293: ( + 4294967285: ( components: { "bevy_world_serialization::serde::tests::Foo": (123), }, @@ -920,7 +920,7 @@ mod tests { assert_eq!( vec![ - 0, 1, 253, 255, 255, 255, 15, 1, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, + 0, 1, 245, 255, 255, 255, 15, 1, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, 100, 95, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, @@ -963,7 +963,7 @@ mod tests { assert_eq!( vec![ - 146, 128, 129, 206, 255, 255, 255, 253, 145, 129, 217, 51, 98, 101, 118, 121, 95, + 146, 128, 129, 206, 255, 255, 255, 245, 145, 129, 217, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, 100, 95, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, diff --git a/crates/bevy_world_serialization/src/world_asset.rs b/crates/bevy_world_serialization/src/world_asset.rs index 458974e898533..57e03ee393fb6 100644 --- a/crates/bevy_world_serialization/src/world_asset.rs +++ b/crates/bevy_world_serialization/src/world_asset.rs @@ -5,8 +5,8 @@ use crate::{DynamicWorld, WorldInstanceSpawnError}; use bevy_asset::Asset; use bevy_ecs::resource::IS_RESOURCE; use bevy_ecs::{ - component::ComponentCloneBehavior, - entity::{Entity, EntityHashMap, SceneEntityMapper}, + component::{ComponentCloneBehavior, ComponentId}, + entity::{ContainsEntity, Entity, EntityHashMap, SceneEntityMapper}, entity_disabling::DefaultQueryFilters, reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, relationship::RelationshipHookMode, @@ -74,8 +74,14 @@ impl WorldAsset { .components() .get_id(TypeId::of::()); + let ids: Vec = self.world.components().iter_registered_ids().collect(); // Resources archetype - for (component_id, source_entity) in self.world.resource_entities().iter() { + for component_id in ids { + let source_entity = component_id.entity(); + + if !self.world.entities().contains_spawned(source_entity) { + continue; + } if Some(component_id) == self_dqf_id { continue; } @@ -112,21 +118,29 @@ impl WorldAsset { .data::() .expect("ReflectComponent is depended on ReflectResource"); - // check if the resource already exists in the other world, if not spawn it - let destination_entity = - if let Some(entity) = world.resource_entities().get(component_id) { - entity - } else { - world.spawn_empty().id() - }; - - reflect_component.copy( - &self.world, - world, - source_entity, - destination_entity, - &type_registry, - ); + let Some(resource) = reflect_component + .reflect(self.world.entity(source_entity)) + .map(|component| clone_reflect_value(component.as_partial_reflect(), registration)) + else { + continue; + }; + + let destination_component_id = reflect_component.register_component(world); + let destination_entity = destination_component_id.entity(); + + if !world.entities().contains_spawned(destination_entity) { + let _ = world.spawn_empty_at(destination_entity); + } + + SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { + reflect_component.apply_or_insert_mapped( + &mut world.entity_mut(source_entity), + resource.as_partial_reflect(), + &type_registry, + mapper, + RelationshipHookMode::Skip, + ); + }); } // Ensure that all source world entities have been allocated in the destination diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index 73c11ce7b6aaf..018d7d7d5d298 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -126,7 +126,7 @@ fn main() { }; component_names.insert(name.to_string(), id); component_info.insert(id, info.clone()); - println!("Component {} created with id: {}", name, id.index()); + println!("Component {} created with id: {}", name, id.entity()); }); } "s" => { @@ -251,7 +251,7 @@ fn main() { println!( "Event '{name}' registered (key: {}) with a dynamic observer", - event_component_id.index() + event_component_id.entity() ); });