Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions crates/bevy_ecs/src/entity_disabling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ use crate::{
component::{ComponentId, Components, StorageType},
query::FilteredAccess,
};
use bevy_ecs_macros::Resource;
use bevy_ecs_macros::{Component, Resource};

/// A marker component for disabled entities. See [the module docs] for more info.
///
/// [the module docs]: crate::entity_disabling
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update the module docs to mention that the Disabled component is disabled in the default DefaultQueryFilters? Right now they talk about it as a hypothetical.

#[derive(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.
Expand All @@ -37,10 +43,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() {
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1530,6 +1531,7 @@ mod tests {
#[test]
fn filtered_query_access() {
let mut world = World::new();
world.remove_resource::<DefaultQueryFilters>();
let query = world.query_filtered::<&mut A, Changed<B>>();

let mut expected = FilteredAccess::<ComponentId>::default();
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
30 changes: 30 additions & 0 deletions crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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::<Disabled>();
let mut filters = DefaultQueryFilters::default();
filters.set_disabled(disabled);
Copy link
Contributor

@cBournhonesque cBournhonesque Jan 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried that users might do a
app.init_resource::<DefaultQueryFilters>()

and lose the Disabled functionality inadvertently..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

init_resource doesn't overwrite existing resources. You'd have to explicitly call .insert_resource(DefaultQueryFilters::default()) which is probably verbose enough to make it obvious what will happen. Currently it is intended that you can opt-out of entity disabling, but as the feature matures we could consider making it so that it is always there.

self.insert_resource(filters);
}
/// Creates a new empty [`World`].
///
Expand Down Expand Up @@ -3217,6 +3223,15 @@ impl World {
}

impl World {
/// Returns true if the type id is used internally, components and resources used internally
/// most likely should not be affected by things like saving and loading
pub fn is_internal_type(type_id: TypeId) -> bool {
if type_id == TypeId::of::<DefaultQueryFilters>() {
return true;
}
false
}

/// Gets a pointer to the resource with the id [`ComponentId`] if it exists.
/// The returned pointer must not be used to modify the resource, and must not be
/// dereferenced after the immutable borrow of the [`World`] ends.
Expand Down Expand Up @@ -3268,6 +3283,7 @@ impl World {
/// # struct B(u32);
/// #
/// # let mut world = World::new();
/// # world.remove_resource::<bevy_ecs::entity_disabling::DefaultQueryFilters>();
/// # world.insert_resource(A(1));
/// # world.insert_resource(B(2));
/// let mut total = 0;
Expand Down Expand Up @@ -3766,6 +3782,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,
Expand Down Expand Up @@ -3955,6 +3972,7 @@ mod tests {
#[test]
fn iter_resources() {
let mut world = World::new();
world.remove_resource::<DefaultQueryFilters>();
world.insert_resource(TestResource(42));
world.insert_resource(TestResource2("Hello, world!".to_string()));
world.insert_resource(TestResource3);
Expand All @@ -3981,6 +3999,7 @@ mod tests {
#[test]
fn iter_resources_mut() {
let mut world = World::new();
world.remove_resource::<DefaultQueryFilters>();
world.insert_resource(TestResource(42));
world.insert_resource(TestResource2("Hello, world!".to_string()));
world.insert_resource(TestResource3);
Expand Down Expand Up @@ -4447,4 +4466,15 @@ 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());

world.remove_resource::<DefaultQueryFilters>();
assert_eq!(2, world.query::<&Foo>().iter(&world).count());
}
}
10 changes: 10 additions & 0 deletions crates/bevy_scene/src/scene.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{DynamicScene, SceneSpawnError};
use bevy_asset::Asset;
use bevy_ecs::{
component::ComponentInfo,
entity::{hash_map::EntityHashMap, Entity, SceneEntityMapper},
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
world::World,
Expand Down Expand Up @@ -61,6 +62,15 @@ impl Scene {

// Resources archetype
for (component_id, resource_data) in self.world.storages().resources.iter() {
if world
.components()
.get_info(component_id)
.and_then(ComponentInfo::type_id)
.filter(|&type_id| World::is_internal_type(type_id))
.is_some()
{
continue;
}
if !resource_data.is_present() {
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_scene/src/scene_filter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_ecs::world::World;
use bevy_utils::{hashbrown::hash_set::IntoIter, HashSet};
use core::any::{Any, TypeId};

Expand Down Expand Up @@ -154,6 +155,9 @@ impl SceneFilter {
///
/// [`Unset`]: SceneFilter::Unset
pub fn is_allowed_by_id(&self, type_id: TypeId) -> bool {
if World::is_internal_type(type_id) {
return false;
}
match self {
Self::Unset => true,
Self::Allowlist(list) => list.contains(&type_id),
Expand Down
Loading