Add simple Disabled marker (#17514)
# Objective We have default query filters now, but there is no first-party marker for entity disabling yet Fixes #17458 ## Solution Add the marker, cool recursive features and/or potential hook changes should be follow up work ## Testing Added a unit test to check that the new marker is enabled by default
This commit is contained in:
parent
75e8e8c0f6
commit
62285a47ba
@ -26,7 +26,17 @@ use crate::{
|
|||||||
component::{ComponentId, Components, StorageType},
|
component::{ComponentId, Components, StorageType},
|
||||||
query::FilteredAccess,
|
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.
|
/// 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.
|
/// See the [module docs](crate::entity_disabling) for more info.
|
||||||
@ -37,10 +47,6 @@ pub struct DefaultQueryFilters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DefaultQueryFilters {
|
impl DefaultQueryFilters {
|
||||||
#[cfg_attr(
|
|
||||||
not(test),
|
|
||||||
expect(dead_code, reason = "No Disabled component exist yet")
|
|
||||||
)]
|
|
||||||
/// Set the [`ComponentId`] for the entity disabling marker
|
/// Set the [`ComponentId`] for the entity disabling marker
|
||||||
pub(crate) fn set_disabled(&mut self, component_id: ComponentId) -> Option<()> {
|
pub(crate) fn set_disabled(&mut self, component_id: ComponentId) -> Option<()> {
|
||||||
if self.disabled.is_some() {
|
if self.disabled.is_some() {
|
||||||
|
@ -134,6 +134,7 @@ mod tests {
|
|||||||
change_detection::Ref,
|
change_detection::Ref,
|
||||||
component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError},
|
component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
|
entity_disabling::DefaultQueryFilters,
|
||||||
prelude::Or,
|
prelude::Or,
|
||||||
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
|
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
@ -1530,6 +1531,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn filtered_query_access() {
|
fn filtered_query_access() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
// We remove entity disabling so it doesn't affect our query filters
|
||||||
|
world.remove_resource::<DefaultQueryFilters>();
|
||||||
let query = world.query_filtered::<&mut A, Changed<B>>();
|
let query = world.query_filtered::<&mut A, Changed<B>>();
|
||||||
|
|
||||||
let mut expected = FilteredAccess::<ComponentId>::default();
|
let mut expected = FilteredAccess::<ComponentId>::default();
|
||||||
|
@ -2552,15 +2552,15 @@ mod tests {
|
|||||||
|
|
||||||
fn nothing() {}
|
fn nothing() {}
|
||||||
|
|
||||||
assert!(world.iter_resources().count() == 0);
|
let resources = world.iter_resources().count();
|
||||||
let id = world.register_system_cached(nothing);
|
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());
|
assert!(world.get_entity(id.entity).is_ok());
|
||||||
|
|
||||||
let mut commands = Commands::new(&mut queue, &world);
|
let mut commands = Commands::new(&mut queue, &world);
|
||||||
commands.unregister_system_cached(nothing);
|
commands.unregister_system_cached(nothing);
|
||||||
queue.apply(&mut world);
|
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());
|
assert!(world.get_entity(id.entity).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ use crate::{
|
|||||||
RequiredComponentsError, Tick,
|
RequiredComponentsError, Tick,
|
||||||
},
|
},
|
||||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
||||||
|
entity_disabling::{DefaultQueryFilters, Disabled},
|
||||||
event::{Event, EventId, Events, SendBatchIds},
|
event::{Event, EventId, Events, SendBatchIds},
|
||||||
observer::Observers,
|
observer::Observers,
|
||||||
query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState},
|
query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState},
|
||||||
@ -158,6 +159,11 @@ impl World {
|
|||||||
|
|
||||||
let on_despawn = OnDespawn::register_component_id(self);
|
let on_despawn = OnDespawn::register_component_id(self);
|
||||||
assert_eq!(ON_DESPAWN, on_despawn);
|
assert_eq!(ON_DESPAWN, on_despawn);
|
||||||
|
|
||||||
|
let disabled = self.register_component::<Disabled>();
|
||||||
|
let mut filters = DefaultQueryFilters::default();
|
||||||
|
filters.set_disabled(disabled);
|
||||||
|
self.insert_resource(filters);
|
||||||
}
|
}
|
||||||
/// Creates a new empty [`World`].
|
/// Creates a new empty [`World`].
|
||||||
///
|
///
|
||||||
@ -3267,6 +3273,7 @@ impl World {
|
|||||||
/// # struct B(u32);
|
/// # struct B(u32);
|
||||||
/// #
|
/// #
|
||||||
/// # let mut world = World::new();
|
/// # let mut world = World::new();
|
||||||
|
/// # world.remove_resource::<bevy_ecs::entity_disabling::DefaultQueryFilters>();
|
||||||
/// # world.insert_resource(A(1));
|
/// # world.insert_resource(A(1));
|
||||||
/// # world.insert_resource(B(2));
|
/// # world.insert_resource(B(2));
|
||||||
/// let mut total = 0;
|
/// let mut total = 0;
|
||||||
@ -3765,6 +3772,7 @@ mod tests {
|
|||||||
change_detection::DetectChangesMut,
|
change_detection::DetectChangesMut,
|
||||||
component::{ComponentDescriptor, ComponentInfo, StorageType},
|
component::{ComponentDescriptor, ComponentInfo, StorageType},
|
||||||
entity::hash_set::EntityHashSet,
|
entity::hash_set::EntityHashSet,
|
||||||
|
entity_disabling::{DefaultQueryFilters, Disabled},
|
||||||
ptr::OwningPtr,
|
ptr::OwningPtr,
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
world::error::EntityFetchError,
|
world::error::EntityFetchError,
|
||||||
@ -3954,6 +3962,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn iter_resources() {
|
fn iter_resources() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
// Remove DefaultQueryFilters so it doesn't show up in the iterator
|
||||||
|
world.remove_resource::<DefaultQueryFilters>();
|
||||||
world.insert_resource(TestResource(42));
|
world.insert_resource(TestResource(42));
|
||||||
world.insert_resource(TestResource2("Hello, world!".to_string()));
|
world.insert_resource(TestResource2("Hello, world!".to_string()));
|
||||||
world.insert_resource(TestResource3);
|
world.insert_resource(TestResource3);
|
||||||
@ -3980,6 +3990,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn iter_resources_mut() {
|
fn iter_resources_mut() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
// Remove DefaultQueryFilters so it doesn't show up in the iterator
|
||||||
|
world.remove_resource::<DefaultQueryFilters>();
|
||||||
world.insert_resource(TestResource(42));
|
world.insert_resource(TestResource(42));
|
||||||
world.insert_resource(TestResource2("Hello, world!".to_string()));
|
world.insert_resource(TestResource2("Hello, world!".to_string()));
|
||||||
world.insert_resource(TestResource3);
|
world.insert_resource(TestResource3);
|
||||||
@ -4446,4 +4458,16 @@ mod tests {
|
|||||||
None
|
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::<DefaultQueryFilters>();
|
||||||
|
assert_eq!(2, world.query::<&Foo>().iter(&world).count());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
use core::any::TypeId;
|
||||||
|
|
||||||
use crate::{DynamicEntity, DynamicScene, SceneFilter};
|
use crate::{DynamicEntity, DynamicScene, SceneFilter};
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::{Component, ComponentId},
|
component::{Component, ComponentId},
|
||||||
|
entity_disabling::DefaultQueryFilters,
|
||||||
prelude::Entity,
|
prelude::Entity,
|
||||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
|
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
@ -348,9 +351,17 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||||||
/// [`deny_resource`]: Self::deny_resource
|
/// [`deny_resource`]: Self::deny_resource
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn extract_resources(mut self) -> Self {
|
pub fn extract_resources(mut self) -> Self {
|
||||||
|
let original_world_dqf_id = self
|
||||||
|
.original_world
|
||||||
|
.components()
|
||||||
|
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
|
||||||
|
|
||||||
let type_registry = self.original_world.resource::<AppTypeRegistry>().read();
|
let type_registry = self.original_world.resource::<AppTypeRegistry>().read();
|
||||||
|
|
||||||
for (component_id, _) in self.original_world.storages().resources.iter() {
|
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 mut extract_and_push = || {
|
||||||
let type_id = self
|
let type_id = self
|
||||||
.original_world
|
.original_world
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
use core::any::TypeId;
|
||||||
|
|
||||||
use crate::{DynamicScene, SceneSpawnError};
|
use crate::{DynamicScene, SceneSpawnError};
|
||||||
use bevy_asset::Asset;
|
use bevy_asset::Asset;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
|
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
|
||||||
|
entity_disabling::DefaultQueryFilters,
|
||||||
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
|
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
@ -59,8 +62,16 @@ impl Scene {
|
|||||||
) -> Result<(), SceneSpawnError> {
|
) -> Result<(), SceneSpawnError> {
|
||||||
let type_registry = type_registry.read();
|
let type_registry = type_registry.read();
|
||||||
|
|
||||||
|
let self_dqf_id = self
|
||||||
|
.world
|
||||||
|
.components()
|
||||||
|
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
|
||||||
|
|
||||||
// Resources archetype
|
// Resources archetype
|
||||||
for (component_id, resource_data) in self.world.storages().resources.iter() {
|
for (component_id, resource_data) in self.world.storages().resources.iter() {
|
||||||
|
if Some(component_id) == self_dqf_id {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if !resource_data.is_present() {
|
if !resource_data.is_present() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user