Skip to content

Commit 62285a4

Browse files
authored
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
1 parent 75e8e8c commit 62285a4

File tree

6 files changed

+63
-8
lines changed

6 files changed

+63
-8
lines changed

crates/bevy_ecs/src/entity_disabling.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,17 @@ use crate::{
2626
component::{ComponentId, Components, StorageType},
2727
query::FilteredAccess,
2828
};
29-
use bevy_ecs_macros::Resource;
29+
use bevy_ecs_macros::{Component, Resource};
30+
31+
#[cfg(feature = "bevy_reflect")]
32+
use {crate::reflect::ReflectComponent, bevy_reflect::Reflect};
33+
34+
/// A marker component for disabled entities. See [the module docs] for more info.
35+
///
36+
/// [the module docs]: crate::entity_disabling
37+
#[derive(Component)]
38+
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
39+
pub struct Disabled;
3040

3141
/// The default filters for all queries, these are used to globally exclude entities from queries.
3242
/// See the [module docs](crate::entity_disabling) for more info.
@@ -37,10 +47,6 @@ pub struct DefaultQueryFilters {
3747
}
3848

3949
impl DefaultQueryFilters {
40-
#[cfg_attr(
41-
not(test),
42-
expect(dead_code, reason = "No Disabled component exist yet")
43-
)]
4450
/// Set the [`ComponentId`] for the entity disabling marker
4551
pub(crate) fn set_disabled(&mut self, component_id: ComponentId) -> Option<()> {
4652
if self.disabled.is_some() {

crates/bevy_ecs/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ mod tests {
134134
change_detection::Ref,
135135
component::{require, Component, ComponentId, RequiredComponents, RequiredComponentsError},
136136
entity::Entity,
137+
entity_disabling::DefaultQueryFilters,
137138
prelude::Or,
138139
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
139140
resource::Resource,
@@ -1530,6 +1531,8 @@ mod tests {
15301531
#[test]
15311532
fn filtered_query_access() {
15321533
let mut world = World::new();
1534+
// We remove entity disabling so it doesn't affect our query filters
1535+
world.remove_resource::<DefaultQueryFilters>();
15331536
let query = world.query_filtered::<&mut A, Changed<B>>();
15341537

15351538
let mut expected = FilteredAccess::<ComponentId>::default();

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2552,15 +2552,15 @@ mod tests {
25522552

25532553
fn nothing() {}
25542554

2555-
assert!(world.iter_resources().count() == 0);
2555+
let resources = world.iter_resources().count();
25562556
let id = world.register_system_cached(nothing);
2557-
assert!(world.iter_resources().count() == 1);
2557+
assert_eq!(world.iter_resources().count(), resources + 1);
25582558
assert!(world.get_entity(id.entity).is_ok());
25592559

25602560
let mut commands = Commands::new(&mut queue, &world);
25612561
commands.unregister_system_cached(nothing);
25622562
queue.apply(&mut world);
2563-
assert!(world.iter_resources().count() == 0);
2563+
assert_eq!(world.iter_resources().count(), resources);
25642564
assert!(world.get_entity(id.entity).is_err());
25652565
}
25662566

crates/bevy_ecs/src/world/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::{
4040
RequiredComponentsError, Tick,
4141
},
4242
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
43+
entity_disabling::{DefaultQueryFilters, Disabled},
4344
event::{Event, EventId, Events, SendBatchIds},
4445
observer::Observers,
4546
query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState},
@@ -158,6 +159,11 @@ impl World {
158159

159160
let on_despawn = OnDespawn::register_component_id(self);
160161
assert_eq!(ON_DESPAWN, on_despawn);
162+
163+
let disabled = self.register_component::<Disabled>();
164+
let mut filters = DefaultQueryFilters::default();
165+
filters.set_disabled(disabled);
166+
self.insert_resource(filters);
161167
}
162168
/// Creates a new empty [`World`].
163169
///
@@ -3267,6 +3273,7 @@ impl World {
32673273
/// # struct B(u32);
32683274
/// #
32693275
/// # let mut world = World::new();
3276+
/// # world.remove_resource::<bevy_ecs::entity_disabling::DefaultQueryFilters>();
32703277
/// # world.insert_resource(A(1));
32713278
/// # world.insert_resource(B(2));
32723279
/// let mut total = 0;
@@ -3765,6 +3772,7 @@ mod tests {
37653772
change_detection::DetectChangesMut,
37663773
component::{ComponentDescriptor, ComponentInfo, StorageType},
37673774
entity::hash_set::EntityHashSet,
3775+
entity_disabling::{DefaultQueryFilters, Disabled},
37683776
ptr::OwningPtr,
37693777
resource::Resource,
37703778
world::error::EntityFetchError,
@@ -3954,6 +3962,8 @@ mod tests {
39543962
#[test]
39553963
fn iter_resources() {
39563964
let mut world = World::new();
3965+
// Remove DefaultQueryFilters so it doesn't show up in the iterator
3966+
world.remove_resource::<DefaultQueryFilters>();
39573967
world.insert_resource(TestResource(42));
39583968
world.insert_resource(TestResource2("Hello, world!".to_string()));
39593969
world.insert_resource(TestResource3);
@@ -3980,6 +3990,8 @@ mod tests {
39803990
#[test]
39813991
fn iter_resources_mut() {
39823992
let mut world = World::new();
3993+
// Remove DefaultQueryFilters so it doesn't show up in the iterator
3994+
world.remove_resource::<DefaultQueryFilters>();
39833995
world.insert_resource(TestResource(42));
39843996
world.insert_resource(TestResource2("Hello, world!".to_string()));
39853997
world.insert_resource(TestResource3);
@@ -4446,4 +4458,16 @@ mod tests {
44464458
None
44474459
);
44484460
}
4461+
4462+
#[test]
4463+
fn new_world_has_disabling() {
4464+
let mut world = World::new();
4465+
world.spawn(Foo);
4466+
world.spawn((Foo, Disabled));
4467+
assert_eq!(1, world.query::<&Foo>().iter(&world).count());
4468+
4469+
// If we explicitly remove the resource, no entities should be filtered anymore
4470+
world.remove_resource::<DefaultQueryFilters>();
4471+
assert_eq!(2, world.query::<&Foo>().iter(&world).count());
4472+
}
44494473
}

crates/bevy_scene/src/dynamic_scene_builder.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use core::any::TypeId;
2+
13
use crate::{DynamicEntity, DynamicScene, SceneFilter};
24
use alloc::collections::BTreeMap;
35
use bevy_ecs::{
46
component::{Component, ComponentId},
7+
entity_disabling::DefaultQueryFilters,
58
prelude::Entity,
69
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
710
resource::Resource,
@@ -348,9 +351,17 @@ impl<'w> DynamicSceneBuilder<'w> {
348351
/// [`deny_resource`]: Self::deny_resource
349352
#[must_use]
350353
pub fn extract_resources(mut self) -> Self {
354+
let original_world_dqf_id = self
355+
.original_world
356+
.components()
357+
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
358+
351359
let type_registry = self.original_world.resource::<AppTypeRegistry>().read();
352360

353361
for (component_id, _) in self.original_world.storages().resources.iter() {
362+
if Some(component_id) == original_world_dqf_id {
363+
continue;
364+
}
354365
let mut extract_and_push = || {
355366
let type_id = self
356367
.original_world

crates/bevy_scene/src/scene.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use core::any::TypeId;
2+
13
use crate::{DynamicScene, SceneSpawnError};
24
use bevy_asset::Asset;
35
use bevy_ecs::{
46
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
7+
entity_disabling::DefaultQueryFilters,
58
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
69
world::World,
710
};
@@ -59,8 +62,16 @@ impl Scene {
5962
) -> Result<(), SceneSpawnError> {
6063
let type_registry = type_registry.read();
6164

65+
let self_dqf_id = self
66+
.world
67+
.components()
68+
.get_resource_id(TypeId::of::<DefaultQueryFilters>());
69+
6270
// Resources archetype
6371
for (component_id, resource_data) in self.world.storages().resources.iter() {
72+
if Some(component_id) == self_dqf_id {
73+
continue;
74+
}
6475
if !resource_data.is_present() {
6576
continue;
6677
}

0 commit comments

Comments
 (0)