mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Add dynamic slice based variants of get_many_entities methods (#13584)
# Objective Add slice based variants of existing `get_many_entities` functions on `World`. This allows for a collection of entries to be looked up mutably or immutably instead of requiring a compile time constant number. ## Solution We just take slices and return Vectors. the following functions have been added: - `get_many_entities_dynamic` - `get_many_entities_dynamic_mut` - `get_many_entities_from_set_mut` ## Testing - Doc tests, which pass when run through Miri
This commit is contained in:
parent
39609f1708
commit
2b6bfdb5ea
1 changed files with 167 additions and 9 deletions
|
@ -8,8 +8,8 @@ mod spawn_batch;
|
|||
pub mod unsafe_world_cell;
|
||||
|
||||
pub use crate::change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD};
|
||||
use crate::component::ComponentInitializer;
|
||||
pub use crate::world::command_queue::CommandQueue;
|
||||
use crate::{component::ComponentInitializer, entity::EntityHashSet};
|
||||
pub use deferred_world::DeferredWorld;
|
||||
pub use entity_ref::{
|
||||
EntityMut, EntityRef, EntityWorldMut, Entry, FilteredEntityMut, FilteredEntityRef,
|
||||
|
@ -582,6 +582,40 @@ impl World {
|
|||
Ok(refs)
|
||||
}
|
||||
|
||||
/// Gets an [`EntityRef`] for multiple entities at once, whose number is determined at runtime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If any entity does not exist in the world.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # let mut world = World::new();
|
||||
/// # let id1 = world.spawn_empty().id();
|
||||
/// # let id2 = world.spawn_empty().id();
|
||||
/// // Getting multiple entities.
|
||||
/// let entities = world.get_many_entities_dynamic(&[id1, id2]).unwrap();
|
||||
/// let entity1 = entities.get(0).unwrap();
|
||||
/// let entity2 = entities.get(1).unwrap();
|
||||
///
|
||||
/// // Trying to get a despawned entity will fail.
|
||||
/// world.despawn(id2);
|
||||
/// assert!(world.get_many_entities_dynamic(&[id1, id2]).is_err());
|
||||
/// ```
|
||||
pub fn get_many_entities_dynamic<'w>(
|
||||
&'w self,
|
||||
entities: &[Entity],
|
||||
) -> Result<Vec<EntityRef<'w>>, Entity> {
|
||||
let mut borrows = Vec::with_capacity(entities.len());
|
||||
for &id in entities {
|
||||
borrows.push(self.get_entity(id).ok_or(id)?);
|
||||
}
|
||||
|
||||
Ok(borrows)
|
||||
}
|
||||
|
||||
/// Returns an [`Entity`] iterator of current entities.
|
||||
///
|
||||
/// This is useful in contexts where you only have read-only access to the [`World`].
|
||||
|
@ -665,6 +699,23 @@ impl World {
|
|||
Some(unsafe { EntityWorldMut::new(self, entity, location) })
|
||||
}
|
||||
|
||||
/// Verify that no duplicate entities are present in the given slice.
|
||||
/// Does NOT check if the entities actually exist in the world.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If any entities are duplicated.
|
||||
fn verify_unique_entities(entities: &[Entity]) -> Result<(), QueryEntityError> {
|
||||
for i in 0..entities.len() {
|
||||
for j in 0..i {
|
||||
if entities[i] == entities[j] {
|
||||
return Err(QueryEntityError::AliasedMutability(entities[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets mutable access to multiple entities.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -689,14 +740,7 @@ impl World {
|
|||
&mut self,
|
||||
entities: [Entity; N],
|
||||
) -> Result<[EntityMut<'_>; N], QueryEntityError> {
|
||||
// Ensure each entity is unique.
|
||||
for i in 0..N {
|
||||
for j in 0..i {
|
||||
if entities[i] == entities[j] {
|
||||
return Err(QueryEntityError::AliasedMutability(entities[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::verify_unique_entities(&entities)?;
|
||||
|
||||
// SAFETY: Each entity is unique.
|
||||
unsafe { self.get_entities_mut_unchecked(entities) }
|
||||
|
@ -730,6 +774,101 @@ impl World {
|
|||
Ok(borrows)
|
||||
}
|
||||
|
||||
/// Gets mutable access to multiple entities, whose number is determined at runtime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If any entities do not exist in the world,
|
||||
/// or if the same entity is specified multiple times.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # let mut world = World::new();
|
||||
/// # let id1 = world.spawn_empty().id();
|
||||
/// # let id2 = world.spawn_empty().id();
|
||||
/// // Disjoint mutable access.
|
||||
/// let mut entities = world.get_many_entities_dynamic_mut(&[id1, id2]).unwrap();
|
||||
/// let entity1 = entities.get_mut(0).unwrap();
|
||||
///
|
||||
/// // Trying to access the same entity multiple times will fail.
|
||||
/// assert!(world.get_many_entities_dynamic_mut(&[id1, id1]).is_err());
|
||||
/// ```
|
||||
pub fn get_many_entities_dynamic_mut<'w>(
|
||||
&'w mut self,
|
||||
entities: &[Entity],
|
||||
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
||||
Self::verify_unique_entities(entities)?;
|
||||
|
||||
// SAFETY: Each entity is unique.
|
||||
unsafe { self.get_entities_dynamic_mut_unchecked(entities.iter().copied()) }
|
||||
}
|
||||
|
||||
/// Gets mutable access to multiple entities, contained in a [`HashSet`].
|
||||
/// The uniqueness of items in a [`HashSet`] allows us to avoid checking for duplicates.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If any entities do not exist in the world.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # use bevy_ecs::entity::EntityHash;
|
||||
/// # use bevy_ecs::entity::EntityHashSet;
|
||||
/// # use bevy_utils::hashbrown::HashSet;
|
||||
/// # use bevy_utils::hashbrown::hash_map::DefaultHashBuilder;
|
||||
/// # let mut world = World::new();
|
||||
/// # let id1 = world.spawn_empty().id();
|
||||
/// # let id2 = world.spawn_empty().id();
|
||||
/// let s = EntityHash::default();
|
||||
/// let mut set = EntityHashSet::with_hasher(s);
|
||||
/// set.insert(id1);
|
||||
/// set.insert(id2);
|
||||
///
|
||||
/// // Disjoint mutable access.
|
||||
/// let mut entities = world.get_many_entities_from_set_mut(&set).unwrap();
|
||||
/// let entity1 = entities.get_mut(0).unwrap();
|
||||
/// ```
|
||||
pub fn get_many_entities_from_set_mut<'w>(
|
||||
&'w mut self,
|
||||
entities: &EntityHashSet,
|
||||
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
||||
// SAFETY: Each entity is unique.
|
||||
unsafe { self.get_entities_dynamic_mut_unchecked(entities.iter().copied()) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `entities` must produce no duplicate [`Entity`] IDs.
|
||||
unsafe fn get_entities_dynamic_mut_unchecked(
|
||||
&mut self,
|
||||
entities: impl ExactSizeIterator<Item = Entity>,
|
||||
) -> Result<Vec<EntityMut<'_>>, QueryEntityError> {
|
||||
let world_cell = self.as_unsafe_world_cell();
|
||||
|
||||
let mut cells = Vec::with_capacity(entities.len());
|
||||
for id in entities {
|
||||
cells.push(
|
||||
world_cell
|
||||
.get_entity(id)
|
||||
.ok_or(QueryEntityError::NoSuchEntity(id))?,
|
||||
);
|
||||
}
|
||||
|
||||
let borrows = cells
|
||||
.into_iter()
|
||||
// SAFETY:
|
||||
// - `world_cell` has exclusive access to the entire world.
|
||||
// - The caller ensures that each entity is unique, so none
|
||||
// of the borrows will conflict with one another.
|
||||
.map(|c| unsafe { EntityMut::new(c) })
|
||||
.collect();
|
||||
|
||||
Ok(borrows)
|
||||
}
|
||||
|
||||
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
|
||||
/// to add components to the entity or retrieve its id.
|
||||
///
|
||||
|
@ -3152,4 +3291,23 @@ mod tests {
|
|||
let mut world = World::new();
|
||||
world.spawn(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_unique_entities() {
|
||||
let mut world = World::new();
|
||||
let entity1 = world.spawn(()).id();
|
||||
let entity2 = world.spawn(()).id();
|
||||
let entity3 = world.spawn(()).id();
|
||||
let entity4 = world.spawn(()).id();
|
||||
let entity5 = world.spawn(()).id();
|
||||
|
||||
assert!(
|
||||
World::verify_unique_entities(&[entity1, entity2, entity3, entity4, entity5]).is_ok()
|
||||
);
|
||||
assert!(World::verify_unique_entities(&[entity1, entity1, entity2, entity5]).is_err());
|
||||
assert!(World::verify_unique_entities(&[
|
||||
entity1, entity2, entity3, entity4, entity5, entity1
|
||||
])
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue