add back all changes

This commit is contained in:
Trashtalk 2025-07-10 22:23:13 +00:00
parent 6dbe3600ed
commit 042337f787
10 changed files with 172 additions and 20 deletions

View File

@ -1734,6 +1734,9 @@ pub struct Components {
components: Vec<Option<ComponentInfo>>,
indices: TypeIdMap<ComponentId>,
resource_indices: TypeIdMap<ComponentId>,
/// A lookup for the entities on which resources are stored.
/// It uses ComponentIds instead of TypeIds for untyped APIs
pub(crate) resource_entities: HashMap<ComponentId, Entity>,
// This is kept internal and local to verify that no deadlocks can occor.
queued: bevy_platform::sync::RwLock<QueuedComponents>,
}

View File

@ -207,6 +207,7 @@ mod tests {
use crate::{
prelude::World,
query::{Has, With},
resource::IsResource,
};
use alloc::{vec, vec::Vec};
@ -278,6 +279,9 @@ mod tests {
let mut world = World::new();
world.register_disabling_component::<CustomDisabled>();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.spawn_empty();
world.spawn(Disabled);
world.spawn(CustomDisabled);

View File

@ -332,6 +332,9 @@ mod tests {
#[test]
fn builder_or() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.spawn((A(0), B(0)));
world.spawn(B(0));
world.spawn(C(0));

View File

@ -2659,6 +2659,7 @@ mod tests {
use crate::component::Component;
use crate::entity::Entity;
use crate::prelude::World;
use crate::resource::IsResource;
#[derive(Component, Debug, PartialEq, PartialOrd, Clone, Copy)]
struct A(f32);
@ -2669,6 +2670,9 @@ mod tests {
#[test]
fn query_iter_sorts() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
for i in 0..100 {
world.spawn(A(i as f32));
world.spawn((A(i as f32), Sparse(i)));

View File

@ -1850,6 +1850,9 @@ mod tests {
#[test]
fn can_transmute_empty_tuple() {
let mut world = World::new();
// We don't want to query resources for this test.
world.register_disabling_component::<IsResource>();
world.register_component::<A>();
let entity = world.spawn(A(10)).id();
@ -2207,6 +2210,7 @@ mod tests {
#[test]
fn query_default_filters_updates_is_dense() {
let mut world = World::new();
let num_resources = world.components().num_resources();
world.spawn((Table, Sparse));
world.spawn(Table);
world.spawn(Sparse);
@ -2214,7 +2218,7 @@ mod tests {
let mut query = QueryState::<()>::new(&mut world);
// There are no sparse components involved thus the query is dense
assert!(query.is_dense);
assert_eq!(3, query.iter(&world).count());
assert_eq!(3, query.iter(&world).count() - num_resources);
let mut df = DefaultQueryFilters::empty();
df.register_disabling_component(world.register_component::<Sparse>());

View File

@ -1,5 +1,10 @@
//! Resources are unique, singleton-like data types that can be accessed from systems and stored in the [`World`](crate::world::World).
use crate::prelude::Component;
use crate::prelude::ReflectComponent;
use bevy_reflect::prelude::ReflectDefault;
use bevy_reflect::Reflect;
use core::marker::PhantomData;
// The derive macro for the `Resource` trait
pub use bevy_ecs_macros::Resource;
@ -73,3 +78,89 @@ pub use bevy_ecs_macros::Resource;
note = "consider annotating `{Self}` with `#[derive(Resource)]`"
)]
pub trait Resource: Send + Sync + 'static {}
/// A marker component for the entity that stores the resource of type `T`.
///
/// This component is automatically inserted when a resource of type `T` is inserted into the world,
/// and can be used to find the entity that stores a particular resource.
///
/// By contrast, the [`IsResource`] component is used to find all entities that store resources,
/// regardless of the type of resource they store.
///
/// This component comes with a hook that ensures that at most one entity has this component for any given `R`:
/// adding this component to an entity (or spawning an entity with this component) will despawn any other entity with this component.
#[derive(Component, Debug)]
#[require(IsResource)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Default))]
pub struct ResourceEntity<R: Resource>(#[reflect(ignore)] PhantomData<R>);
impl<R: Resource> Default for ResourceEntity<R> {
fn default() -> Self {
ResourceEntity(PhantomData)
}
}
/// A marker component for entities which store resources.
///
/// By contrast, the [`ResourceEntity<R>`] component is used to find the entity that stores a particular resource.
/// This component is required by the [`ResourceEntity<R>`] component, and will automatically be added.
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Component, Default, Debug)
)]
#[derive(Component, Default, Debug)]
pub struct IsResource;
#[cfg(test)]
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
mod tests {
use crate::change_detection::MaybeLocation;
use crate::ptr::PtrMut;
use crate::resource::Resource;
use crate::world::World;
use bevy_platform::prelude::String;
use core::mem::ManuallyDrop;
#[test]
fn unique_resource_entities() {
#[derive(Default, Resource)]
struct TestResource1;
#[derive(Resource)]
#[expect(dead_code, reason = "field needed for testing")]
struct TestResource2(String);
#[derive(Resource)]
#[expect(dead_code, reason = "field needed for testing")]
struct TestResource3(u8);
let mut world = World::new();
let start = world.entities().len();
world.init_resource::<TestResource1>();
assert_eq!(world.entities().len(), start + 1);
world.insert_resource(TestResource2(String::from("Foo")));
assert_eq!(world.entities().len(), start + 2);
// like component registration, which just makes it known to the world that a component exists,
// registering a resource should not spawn an entity.
let id = world.register_resource::<TestResource3>();
assert_eq!(world.entities().len(), start + 2);
unsafe {
// SAFETY
// *
world.insert_resource_by_id(
id,
PtrMut::from(&mut ManuallyDrop::new(20 as u8)).promote(),
MaybeLocation::caller(),
);
}
assert_eq!(world.entities().len(), start + 3);
assert!(world.remove_resource_by_id(id).is_some());
assert_eq!(world.entities().len(), start + 2);
world.remove_resource::<TestResource1>();
assert_eq!(world.entities().len(), start + 1);
// 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().len(), start + 1);
}
}

View File

@ -216,12 +216,14 @@ impl World {
&mut self.entities
}
/// Retrieves the number of [`Entities`] in the world.
/// Retrieves the number of [`Entities`] in the world. This count does not include resource entities.
///
/// This is helpful as a diagnostic, but it can also be used effectively in tests.
#[inline]
pub fn num_entities(&self) -> u32 {
self.entities.len()
pub fn entity_count(&self) -> u32 {
self.entities
.len()
.saturating_sub(self.components.resource_entities.len() as u32)
}
/// Retrieves this world's [`Archetypes`] collection.

View File

@ -49,7 +49,13 @@ pub mod prelude {
use bevy_app::prelude::*;
#[cfg(feature = "serialize")]
use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
use {
bevy_asset::AssetApp,
bevy_ecs::schedule::IntoScheduleConfigs,
bevy_ecs::{
entity_disabling::DefaultQueryFilters, resource::IsResource, resource::ResourceEntity,
},
};
/// Plugin that provides scene functionality to an [`App`].
#[derive(Default)]
@ -64,6 +70,8 @@ impl Plugin for ScenePlugin {
.init_resource::<SceneSpawner>()
.register_type::<SceneRoot>()
.register_type::<DynamicSceneRoot>()
.register_type::<IsResource>()
.register_type::<ResourceEntity<DefaultQueryFilters>>()
.add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());
// Register component hooks for DynamicSceneRoot

View File

@ -609,7 +609,7 @@ mod tests {
assert_eq!(scene_component_a.y, 4.0);
assert_eq!(
app.world().entity(entity).get::<Children>().unwrap().len(),
1
3 // two resources-as-entities are also counted
);
// let's try to delete the scene

View File

@ -516,9 +516,11 @@ mod tests {
};
use bevy_ecs::{
entity::{Entity, EntityHashMap},
entity_disabling::DefaultQueryFilters,
prelude::{Component, ReflectComponent, ReflectResource, Resource, World},
query::{With, Without},
reflect::AppTypeRegistry,
resource::{IsResource, ResourceEntity},
world::FromWorld,
};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
@ -611,6 +613,8 @@ mod tests {
registry.register::<MyEntityRef>();
registry.register::<Entity>();
registry.register::<MyResource>();
registry.register::<IsResource>();
registry.register::<ResourceEntity<DefaultQueryFilters>>();
}
world.insert_resource(registry);
world
@ -638,20 +642,20 @@ mod tests {
),
},
entities: {
4294967293: (
4294967291: (
components: {
"bevy_scene::serde::tests::Bar": (345),
"bevy_scene::serde::tests::Baz": (789),
"bevy_scene::serde::tests::Foo": (123),
},
),
4294967294: (
4294967292: (
components: {
"bevy_scene::serde::tests::Bar": (345),
"bevy_scene::serde::tests::Foo": (123),
},
),
4294967295: (
4294967293: (
components: {
"bevy_scene::serde::tests::Foo": (123),
},
@ -757,7 +761,7 @@ mod tests {
.write_to_world(&mut dst_world, &mut map)
.unwrap();
assert_eq!(2, deserialized_scene.entities.len());
assert_eq!(4, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
let bar_to_foo = dst_world
@ -785,7 +789,7 @@ mod tests {
let (scene, deserialized_scene) = roundtrip_ron(&world);
assert_eq!(1, deserialized_scene.entities.len());
assert_eq!(3, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
let mut world = create_world();
@ -815,10 +819,19 @@ mod tests {
assert_eq!(
vec![
0, 1, 255, 255, 255, 255, 15, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
0, 3, 253, 255, 255, 255, 15, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
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, 1, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
108, 64, 1, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 254, 255,
255, 255, 15, 1, 30, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115,
111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101, 255,
255, 255, 255, 15, 2, 30, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101,
115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101,
83, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99,
101, 58, 58, 82, 101, 115, 111, 117, 114, 99, 101, 69, 110, 116, 105, 116, 121, 60,
98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 101, 110, 116, 105, 116, 121, 95, 100,
105, 115, 97, 98, 108, 105, 110, 103, 58, 58, 68, 101, 102, 97, 117, 108, 116, 81,
117, 101, 114, 121, 70, 105, 108, 116, 101, 114, 115, 62
],
serialized_scene
);
@ -830,7 +843,7 @@ mod tests {
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
.unwrap();
assert_eq!(1, deserialized_scene.entities.len());
assert_eq!(3, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
}
@ -856,11 +869,21 @@ mod tests {
assert_eq!(
vec![
146, 128, 129, 206, 255, 255, 255, 255, 145, 129, 217, 37, 98, 101, 118, 121, 95,
146, 128, 131, 206, 255, 255, 255, 253, 145, 129, 217, 37, 98, 101, 118, 121, 95,
115, 99, 101, 110, 101, 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, 63, 166, 102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112,
108, 101, 172, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
108, 101, 172, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 206, 255,
255, 255, 254, 145, 129, 190, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114,
101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99,
101, 144, 206, 255, 255, 255, 255, 145, 130, 190, 98, 101, 118, 121, 95, 101, 99,
115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115,
111, 117, 114, 99, 101, 144, 217, 83, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58,
114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 82, 101, 115, 111, 117, 114, 99,
101, 69, 110, 116, 105, 116, 121, 60, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58,
101, 110, 116, 105, 116, 121, 95, 100, 105, 115, 97, 98, 108, 105, 110, 103, 58,
58, 68, 101, 102, 97, 117, 108, 116, 81, 117, 101, 114, 121, 70, 105, 108, 116,
101, 114, 115, 62, 144
],
buf
);
@ -874,7 +897,7 @@ mod tests {
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
.unwrap();
assert_eq!(1, deserialized_scene.entities.len());
assert_eq!(3, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
}
@ -899,13 +922,23 @@ mod tests {
assert_eq!(
vec![
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 253, 255, 255, 255, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 115, 99, 101,
110, 101, 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, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 102, 102, 166, 63, 205, 204, 108, 64, 1,
0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108,
100, 33
100, 33, 254, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0,
0, 0, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114,
99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101, 255, 255, 255, 255,
0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95,
101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82,
101, 115, 111, 117, 114, 99, 101, 83, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95,
101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 82, 101, 115,
111, 117, 114, 99, 101, 69, 110, 116, 105, 116, 121, 60, 98, 101, 118, 121, 95,
101, 99, 115, 58, 58, 101, 110, 116, 105, 116, 121, 95, 100, 105, 115, 97, 98, 108,
105, 110, 103, 58, 58, 68, 101, 102, 97, 117, 108, 116, 81, 117, 101, 114, 121, 70,
105, 108, 116, 101, 114, 115, 62
],
serialized_scene
);
@ -918,7 +951,7 @@ mod tests {
bincode::serde::seed_decode_from_slice(scene_deserializer, &serialized_scene, config)
.unwrap();
assert_eq!(1, deserialized_scene.entities.len());
assert_eq!(3, deserialized_scene.entities.len());
assert_scene_eq(&scene, &deserialized_scene);
}