From ea9a3bee7f0bef6ed3c31d9947c998a9922cde90 Mon Sep 17 00:00:00 2001 From: Elliott Pierce Date: Sat, 31 May 2025 11:40:23 -0400 Subject: [PATCH] fixed tests --- crates/bevy_app/src/app.rs | 2 +- .../src/entity_count_diagnostics_plugin.rs | 2 +- crates/bevy_ecs/src/entity/clone_entities.rs | 18 ++++++++----- crates/bevy_ecs/src/entity/mod.rs | 25 +++++++++++++++++-- crates/bevy_ecs/src/lib.rs | 12 ++++----- crates/bevy_ecs/src/observer/mod.rs | 2 +- crates/bevy_ecs/src/system/commands/mod.rs | 2 +- crates/bevy_ecs/src/system/system.rs | 4 +-- crates/bevy_ecs/src/system/system_registry.rs | 4 +-- crates/bevy_ecs/src/world/command_queue.rs | 6 ++--- crates/bevy_ecs/src/world/entity_ref.rs | 1 - crates/bevy_ecs/src/world/mod.rs | 3 ++- 12 files changed, 54 insertions(+), 27 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 61bbad3aed..dcd58e7b77 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1579,7 +1579,7 @@ mod tests { app.add_systems(EnterMainMenu, (foo, bar)); app.world_mut().run_schedule(EnterMainMenu); - assert_eq!(app.world().entities().len(), 2); + assert_eq!(app.world().entities().count_active(), 2); } #[test] diff --git a/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs index 91874a390c..864dc1b489 100644 --- a/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/entity_count_diagnostics_plugin.rs @@ -22,6 +22,6 @@ impl EntityCountDiagnosticsPlugin { pub const ENTITY_COUNT: DiagnosticPath = DiagnosticPath::const_new("entity_count"); pub fn diagnostic_system(mut diagnostics: Diagnostics, entities: &Entities) { - diagnostics.add_measurement(&Self::ENTITY_COUNT, || entities.len() as f64); + diagnostics.add_measurement(&Self::ENTITY_COUNT, || entities.count_active() as f64); } } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index 0f2af6b761..b42b8133fd 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,9 +1,3 @@ -use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec}; -use bevy_platform::collections::{HashMap, HashSet}; -use bevy_ptr::{Ptr, PtrMut}; -use bumpalo::Bump; -use core::any::TypeId; - use crate::{ archetype::Archetype, bundle::Bundle, @@ -13,6 +7,11 @@ use crate::{ relationship::RelationshipHookMode, world::World, }; +use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec}; +use bevy_platform::collections::{HashMap, HashSet}; +use bevy_ptr::{Ptr, PtrMut}; +use bumpalo::Bump; +use core::any::TypeId; use super::EntitiesAllocator; @@ -348,6 +347,8 @@ pub struct EntityCloner { move_components: bool, linked_cloning: bool, default_clone_fn: ComponentCloneFn, + /// Represents a queue of entities to clone. + /// These will have targets in the entity map, which will need to be constructed. clone_queue: VecDeque, deferred_commands: VecDeque>, } @@ -458,6 +459,10 @@ impl EntityCloner { relationship_hook_insert_mode: RelationshipHookMode, ) -> Entity { let target = mapper.get_mapped(source); + // The target may need to be constructed if it hasn't been already. + // If this fails, it either didn't need to be constructed (ok) or doesn't exist (caught better later). + let _ = world.construct_empty(target); + // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. let bundle_scratch_allocator = Bump::new(); let mut bundle_scratch: BundleScratch; @@ -536,6 +541,7 @@ impl EntityCloner { } world.flush(); + world.entity_mut(target); for deferred in self.deferred_commands.drain(..) { (deferred)(world, mapper); diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 4066dd42df..03acd2a1a7 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -662,6 +662,9 @@ impl SparseSetIndex for Entity { #[derive(Default, Debug)] pub struct EntitiesAllocator { free: Vec, + /// This is continually subtracted from. + /// If it wraps to a very large number, it will be outside the bounds of `free`, + /// and a new row will be needed. free_len: AtomicU32, next_row: AtomicU32, } @@ -675,7 +678,12 @@ impl EntitiesAllocator { } pub(crate) fn free(&mut self, freed: Entity) { - self.free.truncate(*self.free_len.get_mut() as usize); + let expected_len = *self.free_len.get_mut() as usize; + if expected_len > self.free.len() { + self.free.clear(); + } else { + self.free.truncate(expected_len); + } self.free.push(freed); *self.free_len.get_mut() = self.free.len() as u32; } @@ -846,7 +854,7 @@ impl Entities { } let index = row.index() as usize; - if self.meta.len() >= index { + if self.meta.len() <= index { // TODO: hint unlikely once stable. expand(&mut self.meta, index + 1); } @@ -985,6 +993,19 @@ impl Entities { pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Counts the number of entities currently participating in the world, those that have locations. + pub fn count_active(&self) -> u32 { + self.meta + .iter() + .filter(|meta| meta.location.is_some()) + .count() as u32 + } + + /// Returns true if there are any entities active in the world, entities that have locations. + pub fn any_active(&self) -> bool { + self.meta.iter().any(|meta| meta.location.is_some()) + } } /// An error that occurs when a specified [`Entity`] can not be constructed. diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 6cd00a5c60..f284aa0f9f 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -358,9 +358,9 @@ mod tests { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); let f = world.spawn((TableStored("def"), A(456))).id(); - assert_eq!(world.entities.len(), 2); + assert_eq!(world.entities.count_active(), 2); assert!(world.despawn(e)); - assert_eq!(world.entities.len(), 1); + assert_eq!(world.entities.count_active(), 1); assert!(world.get::(e).is_none()); assert!(world.get::(e).is_none()); assert_eq!(world.get::(f).unwrap().0, "def"); @@ -373,9 +373,9 @@ mod tests { let e = world.spawn((TableStored("abc"), SparseStored(123))).id(); let f = world.spawn((TableStored("def"), SparseStored(456))).id(); - assert_eq!(world.entities.len(), 2); + assert_eq!(world.entities.count_active(), 2); assert!(world.despawn(e)); - assert_eq!(world.entities.len(), 1); + assert_eq!(world.entities.count_active(), 1); assert!(world.get::(e).is_none()); assert!(world.get::(e).is_none()); assert_eq!(world.get::(f).unwrap().0, "def"); @@ -1610,7 +1610,7 @@ mod tests { assert_eq!(q1.iter(&world).len(), 1); assert_eq!(q2.iter(&world).len(), 1); - assert_eq!(world.entities().len(), 2); + assert_eq!(world.entities().count_active(), 2); world.clear_entities(); @@ -1625,7 +1625,7 @@ mod tests { "world should not contain sparse set components" ); assert_eq!( - world.entities().len(), + world.entities().count_active(), 0, "world should not have any entities" ); diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 767dc7ec95..26bddad656 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -1079,7 +1079,7 @@ mod tests { world.spawn(A).flush(); assert_eq!(vec!["add_2", "add_1"], world.resource::().0); // Our A entity plus our two observers - assert_eq!(world.entities().len(), 3); + assert_eq!(world.entities().count_active(), 3); } #[test] diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 57b88bc4f4..61276f5c1a 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2321,7 +2321,7 @@ mod tests { .spawn((W(1u32), W(2u64))) .id(); command_queue.apply(&mut world); - assert_eq!(world.entities().len(), 1); + assert_eq!(world.entities().count_active(), 1); let results = world .query::<(&W, &W)>() .iter(&world) diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index be650588bd..8b57e272f1 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -423,9 +423,9 @@ mod tests { #[test] fn command_processing() { let mut world = World::new(); - assert_eq!(world.entities.len(), 0); + assert_eq!(world.entities.count_active(), 0); world.run_system_once(spawn_entity).unwrap(); - assert_eq!(world.entities.len(), 1); + assert_eq!(world.entities.count_active(), 1); } #[test] diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index cf53b35be5..80ee952282 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -657,9 +657,9 @@ mod tests { let exclusive_system_id = world.register_system(|world: &mut World| { world.spawn_empty(); }); - let entity_count = world.entities.len(); + let entity_count = world.entities.count_active(); let _ = world.run_system(exclusive_system_id); - assert_eq!(world.entities.len(), entity_count + 1); + assert_eq!(world.entities.count_active(), entity_count + 1); } #[test] diff --git a/crates/bevy_ecs/src/world/command_queue.rs b/crates/bevy_ecs/src/world/command_queue.rs index ef98e47008..3d922036b2 100644 --- a/crates/bevy_ecs/src/world/command_queue.rs +++ b/crates/bevy_ecs/src/world/command_queue.rs @@ -420,12 +420,12 @@ mod test { let mut world = World::new(); queue.apply(&mut world); - assert_eq!(world.entities().len(), 2); + assert_eq!(world.entities().count_active(), 2); // The previous call to `apply` cleared the queue. // This call should do nothing. queue.apply(&mut world); - assert_eq!(world.entities().len(), 2); + assert_eq!(world.entities().count_active(), 2); } #[expect( @@ -459,7 +459,7 @@ mod test { queue.push(SpawnCommand); queue.push(SpawnCommand); queue.apply(&mut world); - assert_eq!(world.entities().len(), 3); + assert_eq!(world.entities().count_active(), 3); } #[test] diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 5a61535d52..564c7b02cc 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -5818,7 +5818,6 @@ mod tests { ); world.commands().queue(count_flush); let entity = world.spawn_empty().id(); - assert_eq!(world.resource::().0, 1); world.commands().queue(count_flush); let mut a = world.entity_mut(entity); a.trigger(TestEvent); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 2dfd24d704..7ad931743d 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3645,7 +3645,7 @@ impl fmt::Debug for World { // Accessing any data stored in the world would be unsound. f.debug_struct("World") .field("id", &self.id) - .field("entity_count", &self.entities.len()) + .field("entity_count", &self.entities.count_active()) .field("archetype_count", &self.archetypes.len()) .field("component_count", &self.components.len()) .field("resource_count", &self.storages.resources.len()) @@ -4349,6 +4349,7 @@ mod tests { .is_ok()); world.entity_mut(e1).despawn(); + assert!(world.get_entity_mut(e2).is_ok()); assert!(matches!( world.get_entity_mut(e1).map(|_| {}),