Skip to content

Commit 042337f

Browse files
committed
add back all changes
1 parent 6dbe360 commit 042337f

File tree

10 files changed

+172
-20
lines changed

10 files changed

+172
-20
lines changed

crates/bevy_ecs/src/component.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,9 @@ pub struct Components {
17341734
components: Vec<Option<ComponentInfo>>,
17351735
indices: TypeIdMap<ComponentId>,
17361736
resource_indices: TypeIdMap<ComponentId>,
1737+
/// A lookup for the entities on which resources are stored.
1738+
/// It uses ComponentIds instead of TypeIds for untyped APIs
1739+
pub(crate) resource_entities: HashMap<ComponentId, Entity>,
17371740
// This is kept internal and local to verify that no deadlocks can occor.
17381741
queued: bevy_platform::sync::RwLock<QueuedComponents>,
17391742
}

crates/bevy_ecs/src/entity_disabling.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ mod tests {
207207
use crate::{
208208
prelude::World,
209209
query::{Has, With},
210+
resource::IsResource,
210211
};
211212
use alloc::{vec, vec::Vec};
212213

@@ -278,6 +279,9 @@ mod tests {
278279
let mut world = World::new();
279280
world.register_disabling_component::<CustomDisabled>();
280281

282+
// We don't want to query resources for this test.
283+
world.register_disabling_component::<IsResource>();
284+
281285
world.spawn_empty();
282286
world.spawn(Disabled);
283287
world.spawn(CustomDisabled);

crates/bevy_ecs/src/query/builder.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ mod tests {
332332
#[test]
333333
fn builder_or() {
334334
let mut world = World::new();
335+
// We don't want to query resources for this test.
336+
world.register_disabling_component::<IsResource>();
337+
335338
world.spawn((A(0), B(0)));
336339
world.spawn(B(0));
337340
world.spawn(C(0));

crates/bevy_ecs/src/query/iter.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,7 @@ mod tests {
26592659
use crate::component::Component;
26602660
use crate::entity::Entity;
26612661
use crate::prelude::World;
2662+
use crate::resource::IsResource;
26622663

26632664
#[derive(Component, Debug, PartialEq, PartialOrd, Clone, Copy)]
26642665
struct A(f32);
@@ -2669,6 +2670,9 @@ mod tests {
26692670
#[test]
26702671
fn query_iter_sorts() {
26712672
let mut world = World::new();
2673+
// We don't want to query resources for this test.
2674+
world.register_disabling_component::<IsResource>();
2675+
26722676
for i in 0..100 {
26732677
world.spawn(A(i as f32));
26742678
world.spawn((A(i as f32), Sparse(i)));

crates/bevy_ecs/src/query/state.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,9 @@ mod tests {
18501850
#[test]
18511851
fn can_transmute_empty_tuple() {
18521852
let mut world = World::new();
1853+
// We don't want to query resources for this test.
1854+
world.register_disabling_component::<IsResource>();
1855+
18531856
world.register_component::<A>();
18541857
let entity = world.spawn(A(10)).id();
18551858

@@ -2207,14 +2210,15 @@ mod tests {
22072210
#[test]
22082211
fn query_default_filters_updates_is_dense() {
22092212
let mut world = World::new();
2213+
let num_resources = world.components().num_resources();
22102214
world.spawn((Table, Sparse));
22112215
world.spawn(Table);
22122216
world.spawn(Sparse);
22132217

22142218
let mut query = QueryState::<()>::new(&mut world);
22152219
// There are no sparse components involved thus the query is dense
22162220
assert!(query.is_dense);
2217-
assert_eq!(3, query.iter(&world).count());
2221+
assert_eq!(3, query.iter(&world).count() - num_resources);
22182222

22192223
let mut df = DefaultQueryFilters::empty();
22202224
df.register_disabling_component(world.register_component::<Sparse>());

crates/bevy_ecs/src/resource.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
//! Resources are unique, singleton-like data types that can be accessed from systems and stored in the [`World`](crate::world::World).
22
3+
use crate::prelude::Component;
4+
use crate::prelude::ReflectComponent;
5+
use bevy_reflect::prelude::ReflectDefault;
6+
use bevy_reflect::Reflect;
7+
use core::marker::PhantomData;
38
// The derive macro for the `Resource` trait
49
pub use bevy_ecs_macros::Resource;
510

@@ -73,3 +78,89 @@ pub use bevy_ecs_macros::Resource;
7378
note = "consider annotating `{Self}` with `#[derive(Resource)]`"
7479
)]
7580
pub trait Resource: Send + Sync + 'static {}
81+
82+
/// A marker component for the entity that stores the resource of type `T`.
83+
///
84+
/// This component is automatically inserted when a resource of type `T` is inserted into the world,
85+
/// and can be used to find the entity that stores a particular resource.
86+
///
87+
/// By contrast, the [`IsResource`] component is used to find all entities that store resources,
88+
/// regardless of the type of resource they store.
89+
///
90+
/// This component comes with a hook that ensures that at most one entity has this component for any given `R`:
91+
/// adding this component to an entity (or spawning an entity with this component) will despawn any other entity with this component.
92+
#[derive(Component, Debug)]
93+
#[require(IsResource)]
94+
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Default))]
95+
pub struct ResourceEntity<R: Resource>(#[reflect(ignore)] PhantomData<R>);
96+
97+
impl<R: Resource> Default for ResourceEntity<R> {
98+
fn default() -> Self {
99+
ResourceEntity(PhantomData)
100+
}
101+
}
102+
103+
/// A marker component for entities which store resources.
104+
///
105+
/// By contrast, the [`ResourceEntity<R>`] component is used to find the entity that stores a particular resource.
106+
/// This component is required by the [`ResourceEntity<R>`] component, and will automatically be added.
107+
#[cfg_attr(
108+
feature = "bevy_reflect",
109+
derive(Reflect),
110+
reflect(Component, Default, Debug)
111+
)]
112+
#[derive(Component, Default, Debug)]
113+
pub struct IsResource;
114+
115+
#[cfg(test)]
116+
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
117+
mod tests {
118+
use crate::change_detection::MaybeLocation;
119+
use crate::ptr::PtrMut;
120+
use crate::resource::Resource;
121+
use crate::world::World;
122+
use bevy_platform::prelude::String;
123+
use core::mem::ManuallyDrop;
124+
125+
#[test]
126+
fn unique_resource_entities() {
127+
#[derive(Default, Resource)]
128+
struct TestResource1;
129+
130+
#[derive(Resource)]
131+
#[expect(dead_code, reason = "field needed for testing")]
132+
struct TestResource2(String);
133+
134+
#[derive(Resource)]
135+
#[expect(dead_code, reason = "field needed for testing")]
136+
struct TestResource3(u8);
137+
138+
let mut world = World::new();
139+
let start = world.entities().len();
140+
world.init_resource::<TestResource1>();
141+
assert_eq!(world.entities().len(), start + 1);
142+
world.insert_resource(TestResource2(String::from("Foo")));
143+
assert_eq!(world.entities().len(), start + 2);
144+
// like component registration, which just makes it known to the world that a component exists,
145+
// registering a resource should not spawn an entity.
146+
let id = world.register_resource::<TestResource3>();
147+
assert_eq!(world.entities().len(), start + 2);
148+
unsafe {
149+
// SAFETY
150+
// *
151+
world.insert_resource_by_id(
152+
id,
153+
PtrMut::from(&mut ManuallyDrop::new(20 as u8)).promote(),
154+
MaybeLocation::caller(),
155+
);
156+
}
157+
assert_eq!(world.entities().len(), start + 3);
158+
assert!(world.remove_resource_by_id(id).is_some());
159+
assert_eq!(world.entities().len(), start + 2);
160+
world.remove_resource::<TestResource1>();
161+
assert_eq!(world.entities().len(), start + 1);
162+
// make sure that trying to add a resource twice results, doesn't change the entity count
163+
world.insert_resource(TestResource2(String::from("Bar")));
164+
assert_eq!(world.entities().len(), start + 1);
165+
}
166+
}

crates/bevy_ecs/src/world/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,14 @@ impl World {
216216
&mut self.entities
217217
}
218218

219-
/// Retrieves the number of [`Entities`] in the world.
219+
/// Retrieves the number of [`Entities`] in the world. This count does not include resource entities.
220220
///
221221
/// This is helpful as a diagnostic, but it can also be used effectively in tests.
222222
#[inline]
223-
pub fn num_entities(&self) -> u32 {
224-
self.entities.len()
223+
pub fn entity_count(&self) -> u32 {
224+
self.entities
225+
.len()
226+
.saturating_sub(self.components.resource_entities.len() as u32)
225227
}
226228

227229
/// Retrieves this world's [`Archetypes`] collection.

crates/bevy_scene/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ pub mod prelude {
4949
use bevy_app::prelude::*;
5050

5151
#[cfg(feature = "serialize")]
52-
use {bevy_asset::AssetApp, bevy_ecs::schedule::IntoScheduleConfigs};
52+
use {
53+
bevy_asset::AssetApp,
54+
bevy_ecs::schedule::IntoScheduleConfigs,
55+
bevy_ecs::{
56+
entity_disabling::DefaultQueryFilters, resource::IsResource, resource::ResourceEntity,
57+
},
58+
};
5359

5460
/// Plugin that provides scene functionality to an [`App`].
5561
#[derive(Default)]
@@ -64,6 +70,8 @@ impl Plugin for ScenePlugin {
6470
.init_resource::<SceneSpawner>()
6571
.register_type::<SceneRoot>()
6672
.register_type::<DynamicSceneRoot>()
73+
.register_type::<IsResource>()
74+
.register_type::<ResourceEntity<DefaultQueryFilters>>()
6775
.add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());
6876

6977
// Register component hooks for DynamicSceneRoot

crates/bevy_scene/src/scene_spawner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ mod tests {
609609
assert_eq!(scene_component_a.y, 4.0);
610610
assert_eq!(
611611
app.world().entity(entity).get::<Children>().unwrap().len(),
612-
1
612+
3 // two resources-as-entities are also counted
613613
);
614614

615615
// let's try to delete the scene

crates/bevy_scene/src/serde.rs

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,11 @@ mod tests {
516516
};
517517
use bevy_ecs::{
518518
entity::{Entity, EntityHashMap},
519+
entity_disabling::DefaultQueryFilters,
519520
prelude::{Component, ReflectComponent, ReflectResource, Resource, World},
520521
query::{With, Without},
521522
reflect::AppTypeRegistry,
523+
resource::{IsResource, ResourceEntity},
522524
world::FromWorld,
523525
};
524526
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
@@ -611,6 +613,8 @@ mod tests {
611613
registry.register::<MyEntityRef>();
612614
registry.register::<Entity>();
613615
registry.register::<MyResource>();
616+
registry.register::<IsResource>();
617+
registry.register::<ResourceEntity<DefaultQueryFilters>>();
614618
}
615619
world.insert_resource(registry);
616620
world
@@ -638,20 +642,20 @@ mod tests {
638642
),
639643
},
640644
entities: {
641-
4294967293: (
645+
4294967291: (
642646
components: {
643647
"bevy_scene::serde::tests::Bar": (345),
644648
"bevy_scene::serde::tests::Baz": (789),
645649
"bevy_scene::serde::tests::Foo": (123),
646650
},
647651
),
648-
4294967294: (
652+
4294967292: (
649653
components: {
650654
"bevy_scene::serde::tests::Bar": (345),
651655
"bevy_scene::serde::tests::Foo": (123),
652656
},
653657
),
654-
4294967295: (
658+
4294967293: (
655659
components: {
656660
"bevy_scene::serde::tests::Foo": (123),
657661
},
@@ -757,7 +761,7 @@ mod tests {
757761
.write_to_world(&mut dst_world, &mut map)
758762
.unwrap();
759763

760-
assert_eq!(2, deserialized_scene.entities.len());
764+
assert_eq!(4, deserialized_scene.entities.len());
761765
assert_scene_eq(&scene, &deserialized_scene);
762766

763767
let bar_to_foo = dst_world
@@ -785,7 +789,7 @@ mod tests {
785789

786790
let (scene, deserialized_scene) = roundtrip_ron(&world);
787791

788-
assert_eq!(1, deserialized_scene.entities.len());
792+
assert_eq!(3, deserialized_scene.entities.len());
789793
assert_scene_eq(&scene, &deserialized_scene);
790794

791795
let mut world = create_world();
@@ -815,10 +819,19 @@ mod tests {
815819

816820
assert_eq!(
817821
vec![
818-
0, 1, 255, 255, 255, 255, 15, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
822+
0, 3, 253, 255, 255, 255, 15, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
819823
58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121,
820824
67, 111, 109, 112, 111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204,
821-
108, 64, 1, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
825+
108, 64, 1, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 254, 255,
826+
255, 255, 15, 1, 30, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115,
827+
111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101, 255,
828+
255, 255, 255, 15, 2, 30, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101,
829+
115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101,
830+
83, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99,
831+
101, 58, 58, 82, 101, 115, 111, 117, 114, 99, 101, 69, 110, 116, 105, 116, 121, 60,
832+
98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 101, 110, 116, 105, 116, 121, 95, 100,
833+
105, 115, 97, 98, 108, 105, 110, 103, 58, 58, 68, 101, 102, 97, 117, 108, 116, 81,
834+
117, 101, 114, 121, 70, 105, 108, 116, 101, 114, 115, 62
822835
],
823836
serialized_scene
824837
);
@@ -830,7 +843,7 @@ mod tests {
830843
.deserialize(&mut postcard::Deserializer::from_bytes(&serialized_scene))
831844
.unwrap();
832845

833-
assert_eq!(1, deserialized_scene.entities.len());
846+
assert_eq!(3, deserialized_scene.entities.len());
834847
assert_scene_eq(&scene, &deserialized_scene);
835848
}
836849

@@ -856,11 +869,21 @@ mod tests {
856869

857870
assert_eq!(
858871
vec![
859-
146, 128, 129, 206, 255, 255, 255, 255, 145, 129, 217, 37, 98, 101, 118, 121, 95,
872+
146, 128, 131, 206, 255, 255, 255, 253, 145, 129, 217, 37, 98, 101, 118, 121, 95,
860873
115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115,
861874
116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1,
862875
2, 3, 146, 202, 63, 166, 102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112,
863-
108, 101, 172, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
876+
108, 101, 172, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 206, 255,
877+
255, 255, 254, 145, 129, 190, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114,
878+
101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99,
879+
101, 144, 206, 255, 255, 255, 255, 145, 130, 190, 98, 101, 118, 121, 95, 101, 99,
880+
115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82, 101, 115,
881+
111, 117, 114, 99, 101, 144, 217, 83, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58,
882+
114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 82, 101, 115, 111, 117, 114, 99,
883+
101, 69, 110, 116, 105, 116, 121, 60, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58,
884+
101, 110, 116, 105, 116, 121, 95, 100, 105, 115, 97, 98, 108, 105, 110, 103, 58,
885+
58, 68, 101, 102, 97, 117, 108, 116, 81, 117, 101, 114, 121, 70, 105, 108, 116,
886+
101, 114, 115, 62, 144
864887
],
865888
buf
866889
);
@@ -874,7 +897,7 @@ mod tests {
874897
.deserialize(&mut rmp_serde::Deserializer::new(&mut reader))
875898
.unwrap();
876899

877-
assert_eq!(1, deserialized_scene.entities.len());
900+
assert_eq!(3, deserialized_scene.entities.len());
878901
assert_scene_eq(&scene, &deserialized_scene);
879902
}
880903

@@ -899,13 +922,23 @@ mod tests {
899922

900923
assert_eq!(
901924
vec![
902-
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,
925+
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,
903926
0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 115, 99, 101,
904927
110, 101, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58,
905928
77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0,
906929
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 102, 102, 166, 63, 205, 204, 108, 64, 1,
907930
0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108,
908-
100, 33
931+
100, 33, 254, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0,
932+
0, 0, 98, 101, 118, 121, 95, 101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114,
933+
99, 101, 58, 58, 73, 115, 82, 101, 115, 111, 117, 114, 99, 101, 255, 255, 255, 255,
934+
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,
935+
101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 73, 115, 82,
936+
101, 115, 111, 117, 114, 99, 101, 83, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95,
937+
101, 99, 115, 58, 58, 114, 101, 115, 111, 117, 114, 99, 101, 58, 58, 82, 101, 115,
938+
111, 117, 114, 99, 101, 69, 110, 116, 105, 116, 121, 60, 98, 101, 118, 121, 95,
939+
101, 99, 115, 58, 58, 101, 110, 116, 105, 116, 121, 95, 100, 105, 115, 97, 98, 108,
940+
105, 110, 103, 58, 58, 68, 101, 102, 97, 117, 108, 116, 81, 117, 101, 114, 121, 70,
941+
105, 108, 116, 101, 114, 115, 62
909942
],
910943
serialized_scene
911944
);
@@ -918,7 +951,7 @@ mod tests {
918951
bincode::serde::seed_decode_from_slice(scene_deserializer, &serialized_scene, config)
919952
.unwrap();
920953

921-
assert_eq!(1, deserialized_scene.entities.len());
954+
assert_eq!(3, deserialized_scene.entities.len());
922955
assert_scene_eq(&scene, &deserialized_scene);
923956
}
924957

0 commit comments

Comments
 (0)