diff --git a/crates/bevy_ecs/src/entity_disabling.rs b/crates/bevy_ecs/src/entity_disabling.rs index 449ae3d7177c8..f96c78c1632c9 100644 --- a/crates/bevy_ecs/src/entity_disabling.rs +++ b/crates/bevy_ecs/src/entity_disabling.rs @@ -26,7 +26,17 @@ use crate::{ component::{ComponentId, Components, StorageType}, query::FilteredAccess, }; -use bevy_ecs_macros::Resource; +use bevy_ecs_macros::{Component, Resource}; + +#[cfg(feature = "bevy_reflect")] +use {crate::reflect::ReflectComponent, bevy_reflect::Reflect}; + +/// A marker component for disabled entities. See [the module docs] for more info. +/// +/// [the module docs]: crate::entity_disabling +#[derive(Component)] +#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))] +pub struct Disabled; /// The default filters for all queries, these are used to globally exclude entities from queries. /// See the [module docs](crate::entity_disabling) for more info. @@ -37,10 +47,6 @@ pub struct DefaultQueryFilters { } impl DefaultQueryFilters { - #[cfg_attr( - not(test), - expect(dead_code, reason = "No Disabled component exist yet") - )] /// Set the [`ComponentId`] for the entity disabling marker pub(crate) fn set_disabled(&mut self, component_id: ComponentId) -> Option<()> { if self.disabled.is_some() { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index b79ac90f468ef..da7707d77d34a 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -134,6 +134,7 @@ mod tests { change_detection::Ref, component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError}, entity::Entity, + entity_disabling::DefaultQueryFilters, prelude::Or, query::{Added, Changed, FilteredAccess, QueryFilter, With, Without}, resource::Resource, @@ -1530,6 +1531,8 @@ mod tests { #[test] fn filtered_query_access() { let mut world = World::new(); + // We remove entity disabling so it doesn't affect our query filters + world.remove_resource::(); let query = world.query_filtered::<&mut A, Changed>(); let mut expected = FilteredAccess::::default(); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 3748c5c1fb4a3..e6845a97a4358 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2516,15 +2516,15 @@ mod tests { fn nothing() {} - assert!(world.iter_resources().count() == 0); + let resources = world.iter_resources().count(); let id = world.register_system_cached(nothing); - assert!(world.iter_resources().count() == 1); + assert_eq!(world.iter_resources().count(), resources + 1); assert!(world.get_entity(id.entity).is_ok()); let mut commands = Commands::new(&mut queue, &world); commands.unregister_system_cached(nothing); queue.apply(&mut world); - assert!(world.iter_resources().count() == 0); + assert_eq!(world.iter_resources().count(), resources); assert!(world.get_entity(id.entity).is_err()); } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 22520ac71a268..941dd0b07a548 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -40,6 +40,7 @@ use crate::{ RequiredComponentsError, Tick, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, + entity_disabling::{DefaultQueryFilters, Disabled}, event::{Event, EventId, Events, SendBatchIds}, observer::Observers, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, @@ -159,6 +160,11 @@ impl World { let on_despawn = OnDespawn::register_component_id(self); assert_eq!(ON_DESPAWN, on_despawn); + + let disabled = self.register_component::(); + let mut filters = DefaultQueryFilters::default(); + filters.set_disabled(disabled); + self.insert_resource(filters); } /// Creates a new empty [`World`]. /// @@ -3268,6 +3274,7 @@ impl World { /// # struct B(u32); /// # /// # let mut world = World::new(); + /// # world.remove_resource::(); /// # world.insert_resource(A(1)); /// # world.insert_resource(B(2)); /// let mut total = 0; @@ -3766,6 +3773,7 @@ mod tests { change_detection::DetectChangesMut, component::{ComponentDescriptor, ComponentInfo, StorageType}, entity::hash_set::EntityHashSet, + entity_disabling::{DefaultQueryFilters, Disabled}, ptr::OwningPtr, resource::Resource, world::error::EntityFetchError, @@ -3955,6 +3963,8 @@ mod tests { #[test] fn iter_resources() { let mut world = World::new(); + // Remove DefaultQueryFilters so it doesn't show up in the iterator + world.remove_resource::(); world.insert_resource(TestResource(42)); world.insert_resource(TestResource2("Hello, world!".to_string())); world.insert_resource(TestResource3); @@ -3981,6 +3991,8 @@ mod tests { #[test] fn iter_resources_mut() { let mut world = World::new(); + // Remove DefaultQueryFilters so it doesn't show up in the iterator + world.remove_resource::(); world.insert_resource(TestResource(42)); world.insert_resource(TestResource2("Hello, world!".to_string())); world.insert_resource(TestResource3); @@ -4447,4 +4459,16 @@ mod tests { None ); } + + #[test] + fn new_world_has_disabling() { + let mut world = World::new(); + world.spawn(Foo); + world.spawn((Foo, Disabled)); + assert_eq!(1, world.query::<&Foo>().iter(&world).count()); + + // If we explicitly remove the resource, no entities should be filtered anymore + world.remove_resource::(); + assert_eq!(2, world.query::<&Foo>().iter(&world).count()); + } } diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 842b985a48954..e2981ed973048 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,7 +1,10 @@ +use core::any::TypeId; + use crate::{DynamicEntity, DynamicScene, SceneFilter}; use alloc::collections::BTreeMap; use bevy_ecs::{ component::{Component, ComponentId}, + entity_disabling::DefaultQueryFilters, prelude::Entity, reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, resource::Resource, @@ -348,9 +351,17 @@ impl<'w> DynamicSceneBuilder<'w> { /// [`deny_resource`]: Self::deny_resource #[must_use] pub fn extract_resources(mut self) -> Self { + let original_world_dqf_id = self + .original_world + .components() + .get_resource_id(TypeId::of::()); + let type_registry = self.original_world.resource::().read(); for (component_id, _) in self.original_world.storages().resources.iter() { + if Some(component_id) == original_world_dqf_id { + continue; + } let mut extract_and_push = || { let type_id = self .original_world diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 232acf345685e..7d74ce8cb25e3 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,7 +1,10 @@ +use core::any::TypeId; + use crate::{DynamicScene, SceneSpawnError}; use bevy_asset::Asset; use bevy_ecs::{ entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper}, + entity_disabling::DefaultQueryFilters, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; @@ -59,8 +62,16 @@ impl Scene { ) -> Result<(), SceneSpawnError> { let type_registry = type_registry.read(); + let self_dqf_id = self + .world + .components() + .get_resource_id(TypeId::of::()); + // Resources archetype for (component_id, resource_data) in self.world.storages().resources.iter() { + if Some(component_id) == self_dqf_id { + continue; + } if !resource_data.is_present() { continue; }