From b00f549ac592d85b7752459e0712965c17ee5b83 Mon Sep 17 00:00:00 2001 From: Chris Russell <8494645+chescock@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:19:03 -0400 Subject: [PATCH 1/2] Don't remove entry from `ResourceEntities` when removing a resource by ID. --- crates/bevy_ecs/src/resource.rs | 19 +++++++++++++++---- crates/bevy_ecs/src/world/mod.rs | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index a94fa13277efc..b044b4da1a81a 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -233,27 +233,38 @@ mod tests { let mut world = World::new(); let start = world.entities().count_spawned(); - world.init_resource::(); + let id1 = world.init_resource::(); assert_eq!(world.entities().count_spawned(), start + 1); world.insert_resource(TestResource2(String::from("Foo"))); assert_eq!(world.entities().count_spawned(), 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::(); + let id3 = world.register_resource::(); assert_eq!(world.entities().count_spawned(), start + 2); OwningPtr::make(20_u8, |ptr| { // SAFETY: id was just initialized and corresponds to a resource. unsafe { - world.insert_resource_by_id(id, ptr, MaybeLocation::caller()); + world.insert_resource_by_id(id3, ptr, MaybeLocation::caller()); } }); assert_eq!(world.entities().count_spawned(), start + 3); - assert!(world.remove_resource_by_id(id)); + let e3 = *world.resource_entities().get(id3).unwrap(); + assert!(world.remove_resource_by_id(id3)); // the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity assert_eq!(world.entities().count_spawned(), start + 3); + OwningPtr::make(20_u8, |ptr| { + // SAFETY: id was just initialized and corresponds to a resource. + unsafe { + world.insert_resource_by_id(id3, ptr, MaybeLocation::caller()); + } + }); + assert_eq!(e3, *world.resource_entities().get(id3).unwrap()); // again, the entity is stable: see previous explanation + let e1 = *world.resource_entities().get(id1).unwrap(); world.remove_resource::(); assert_eq!(world.entities().count_spawned(), start + 3); + world.init_resource::(); + assert_eq!(e1, *world.resource_entities().get(id1).unwrap()); // 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().count_spawned(), start + 3); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 23cb6a48395c5..2d19a4ddf544e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3658,7 +3658,7 @@ impl World { /// **You should prefer to use the typed API [`World::remove_resource`] where possible and only /// use this in cases where the actual types are not known at compile time.** pub fn remove_resource_by_id(&mut self, component_id: ComponentId) -> bool { - if let Some(entity) = self.resource_entities.remove(component_id) + if let Some(&entity) = self.resource_entities.get(component_id) && let Ok(mut entity_mut) = self.get_entity_mut(entity) && entity_mut.contains_id(component_id) { From c22a9b283fe0a1b6b889d578fe797442dbd52da4 Mon Sep 17 00:00:00 2001 From: Chris Russell <8494645+chescock@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:08:17 -0400 Subject: [PATCH 2/2] Fix merge conflicts. Remove unused `remove` method and remove dereferences from new `get` calls. --- crates/bevy_ecs/src/resource.rs | 15 ++++----------- crates/bevy_ecs/src/world/mod.rs | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index 95b7eb9170ab9..3e8099f53b057 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -106,13 +106,6 @@ impl ResourceEntities { self.deref().get(id).copied() } - /// Removes the entry for the given resource component. - /// Returns the entity that was removed, or `None` if there was no entity. - #[inline] - pub(crate) fn remove(&mut self, id: ComponentId) -> Option { - self.0.get_mut().remove(id) - } - #[inline] fn deref(&self) -> &SparseArray { // SAFETY: There are no other mutable references to the map. @@ -262,7 +255,7 @@ mod tests { } }); assert_eq!(world.entities().count_spawned(), start + 3); - let e3 = *world.resource_entities().get(id3).unwrap(); + let e3 = world.resource_entities().get(id3).unwrap(); assert!(world.remove_resource_by_id(id3)); // the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity assert_eq!(world.entities().count_spawned(), start + 3); @@ -272,13 +265,13 @@ mod tests { world.insert_resource_by_id(id3, ptr, MaybeLocation::caller()); } }); - assert_eq!(e3, *world.resource_entities().get(id3).unwrap()); + assert_eq!(e3, world.resource_entities().get(id3).unwrap()); // again, the entity is stable: see previous explanation - let e1 = *world.resource_entities().get(id1).unwrap(); + let e1 = world.resource_entities().get(id1).unwrap(); world.remove_resource::(); assert_eq!(world.entities().count_spawned(), start + 3); world.init_resource::(); - assert_eq!(e1, *world.resource_entities().get(id1).unwrap()); + assert_eq!(e1, world.resource_entities().get(id1).unwrap()); // 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().count_spawned(), start + 3); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 67452c72fc64f..8ee75de95f147 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3653,7 +3653,7 @@ impl World { /// **You should prefer to use the typed API [`World::remove_resource`] where possible and only /// use this in cases where the actual types are not known at compile time.** pub fn remove_resource_by_id(&mut self, component_id: ComponentId) -> bool { - if let Some(&entity) = self.resource_entities.get(component_id) + if let Some(entity) = self.resource_entities.get(component_id) && let Ok(mut entity_mut) = self.get_entity_mut(entity) && entity_mut.contains_id(component_id) {