Allow World::entity family of functions to take multiple entities and get multiple references back (#15614)

# Objective

Following the pattern established in #15593, we can reduce the API
surface of `World` by providing a single function to grab both a
singular entity reference, or multiple entity references.

## Solution

The following functions can now also take multiple entity IDs and will
return multiple entity references back:
- `World::entity`
- `World::get_entity`
- `World::entity_mut`
- `World::get_entity_mut`
- `DeferredWorld::entity_mut`
- `DeferredWorld::get_entity_mut`

If you pass in X, you receive Y:
- give a single `Entity`, receive a single `EntityRef`/`EntityWorldMut`
(matches current behavior)
- give a `[Entity; N]`/`&[Entity; N]` (array), receive an equally-sized
`[EntityRef; N]`/`[EntityMut; N]`
- give a `&[Entity]` (slice), receive a
`Vec<EntityRef>`/`Vec<EntityMut>`
- give a `&EntityHashSet`, receive a
`EntityHashMap<EntityRef>`/`EntityHashMap<EntityMut>`

Note that `EntityWorldMut` is only returned in the single-entity case,
because having multiple at the same time would lead to UB. Also,
`DeferredWorld` receives an `EntityMut` in the single-entity case
because it does not allow structural access.

## Testing

- Added doc-tests on `World::entity`, `World::entity_mut`, and
`DeferredWorld::entity_mut`
- Added tests for aliased mutability and entity existence

---

## Showcase

<details>
  <summary>Click to view showcase</summary>

The APIs for fetching `EntityRef`s and `EntityMut`s from the `World`
have been unified.

```rust
// This code will be referred to by subsequent code blocks.
let world = World::new();
let e1 = world.spawn_empty().id();
let e2 = world.spawn_empty().id();
let e3 = world.spawn_empty().id();
```

Querying for a single entity remains mostly the same:

```rust
// 0.14
let eref: EntityRef = world.entity(e1);
let emut: EntityWorldMut = world.entity_mut(e1);
let eref: Option<EntityRef> = world.get_entity(e1);
let emut: Option<EntityWorldMut> = world.get_entity_mut(e1);

// 0.15
let eref: EntityRef = world.entity(e1);
let emut: EntityWorldMut = world.entity_mut(e1);
let eref: Result<EntityRef, Entity> = world.get_entity(e1);
let emut: Result<EntityWorldMut, Entity> = world.get_entity_mut(e1);
```

Querying for multiple entities with an array has changed:

```rust
// 0.14
let erefs: [EntityRef; 2] = world.many_entities([e1, e2]);
let emuts: [EntityMut; 2] = world.many_entities_mut([e1, e2]);
let erefs: Result<[EntityRef; 2], Entity> = world.get_many_entities([e1, e2]);
let emuts: Result<[EntityMut; 2], QueryEntityError> = world.get_many_entities_mut([e1, e2]);

// 0.15
let erefs: [EntityRef; 2] = world.entity([e1, e2]);
let emuts: [EntityMut; 2] = world.entity_mut([e1, e2]);
let erefs: Result<[EntityRef; 2], Entity> = world.get_entity([e1, e2]);
let emuts: Result<[EntityMut; 2], EntityFetchError> = world.get_entity_mut([e1, e2]);
```

Querying for multiple entities with a slice has changed:

```rust
let ids = vec![e1, e2, e3]);

// 0.14
let erefs: Result<Vec<EntityRef>, Entity> = world.get_many_entities_dynamic(&ids[..]);
let emuts: Result<Vec<EntityMut>, QueryEntityError> = world.get_many_entities_dynamic_mut(&ids[..]);

// 0.15
let erefs: Result<Vec<EntityRef>, Entity> = world.get_entity(&ids[..]);
let emuts: Result<Vec<EntityMut>, EntityFetchError> = world.get_entity_mut(&ids[..]);
let erefs: Vec<EntityRef> = world.entity(&ids[..]); // Newly possible!
let emuts: Vec<EntityMut> = world.entity_mut(&ids[..]); // Newly possible!
```

Querying for multiple entities with an `EntityHashSet` has changed:

```rust
let set = EntityHashSet::from_iter([e1, e2, e3]);

// 0.14
let emuts: Result<Vec<EntityMut>, QueryEntityError> = world.get_many_entities_from_set_mut(&set);

// 0.15
let emuts: Result<EntityHashMap<EntityMut>, EntityFetchError> = world.get_entity_mut(&set);
let erefs: Result<EntityHashMap<EntityRef>, EntityFetchError> = world.get_entity(&set); // Newly possible!
let emuts: EntityHashMap<EntityMut> = world.entity_mut(&set); // Newly possible!
let erefs: EntityHashMap<EntityRef> = world.entity(&set); // Newly possible!
```

</details>

## Migration Guide

- `World::get_entity` now returns `Result<_, Entity>` instead of
`Option<_>`.
- Use `world.get_entity(..).ok()` to return to the previous behavior.
- `World::get_entity_mut` and `DeferredWorld::get_entity_mut` now return
`Result<_, EntityFetchError>` instead of `Option<_>`.
- Use `world.get_entity_mut(..).ok()` to return to the previous
behavior.
- Type inference for `World::entity`, `World::entity_mut`,
`World::get_entity`, `World::get_entity_mut`,
`DeferredWorld::entity_mut`, and `DeferredWorld::get_entity_mut` has
changed, and might now require the input argument's type to be
explicitly written when inside closures.
- The following functions have been deprecated, and should be replaced
as such:
    - `World::many_entities` -> `World::entity::<[Entity; N]>`
    - `World::many_entities_mut` -> `World::entity_mut::<[Entity; N]>`
    - `World::get_many_entities` -> `World::get_entity::<[Entity; N]>`
- `World::get_many_entities_dynamic` -> `World::get_entity::<&[Entity]>`
- `World::get_many_entities_mut` -> `World::get_entity_mut::<[Entity;
N]>`
- The equivalent return type has changed from `Result<_,
QueryEntityError>` to `Result<_, EntityFetchError>`
- `World::get_many_entities_dynamic_mut` ->
`World::get_entity_mut::<&[Entity]>1
- The equivalent return type has changed from `Result<_,
QueryEntityError>` to `Result<_, EntityFetchError>`
- `World::get_many_entities_from_set_mut` ->
`World::get_entity_mut::<&EntityHashSet>`
- The equivalent return type has changed from `Result<Vec<EntityMut>,
QueryEntityError>` to `Result<EntityHashMap<EntityMut>,
EntityFetchError>`. If necessary, you can still convert the
`EntityHashMap` into a `Vec`.
This commit is contained in:
Christian Hughes 2024-10-07 08:21:40 -07:00 committed by GitHub
parent 31409ebc61
commit 584d14808a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 966 additions and 283 deletions

View file

@ -1637,13 +1637,13 @@ mod tests {
"new entity is created immediately after world_a's max entity"
);
assert!(world_b.get::<A>(e1).is_none());
assert!(world_b.get_entity(e1).is_none());
assert!(world_b.get_entity(e1).is_err());
assert!(world_b.get::<A>(e2).is_none());
assert!(world_b.get_entity(e2).is_none());
assert!(world_b.get_entity(e2).is_err());
assert!(world_b.get::<A>(e3).is_none());
assert!(world_b.get_entity(e3).is_none());
assert!(world_b.get_entity(e3).is_err());
world_b.get_or_spawn(e1).unwrap().insert(B(1));
assert_eq!(
@ -1694,7 +1694,7 @@ mod tests {
let high_non_existent_but_reserved_entity = Entity::from_raw(5);
assert!(
world_b.get_entity(high_non_existent_but_reserved_entity).is_none(),
world_b.get_entity(high_non_existent_but_reserved_entity).is_err(),
"entities between high-newly allocated entity and continuous block of existing entities don't exist"
);

View file

@ -19,7 +19,7 @@ impl Component for ObservedBy {
};
for e in observed_by {
let (total_entities, despawned_watched_entities) = {
let Some(mut entity_mut) = world.get_entity_mut(e) else {
let Ok(mut entity_mut) = world.get_entity_mut(e) else {
continue;
};
let Some(mut state) = entity_mut.get_mut::<ObserverState>() else {

View file

@ -221,7 +221,7 @@ fn insert_reflect(
.get_represented_type_info()
.expect("component should represent a type.");
let type_path = type_info.type_path();
let Some(mut entity) = world.get_entity_mut(entity) else {
let Ok(mut entity) = world.get_entity_mut(entity) else {
panic!("error[B0003]: Could not insert a reflected component (of type {type_path}) for entity {entity:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003");
};
let Some(type_registration) = type_registry.get(type_info.type_id()) else {
@ -284,7 +284,7 @@ fn remove_reflect(
type_registry: &TypeRegistry,
component_type_path: Cow<'static, str>,
) {
let Some(mut entity) = world.get_entity_mut(entity) else {
let Ok(mut entity) = world.get_entity_mut(entity) else {
return;
};
let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {

View file

@ -1757,7 +1757,7 @@ fn try_despawn() -> impl EntityCommand {
fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
let caller = Location::caller();
move |entity: Entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.insert_with_caller(
bundle,
mode,
@ -1776,7 +1776,7 @@ fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityC
let caller = Location::caller();
move |entity: Entity, world: &mut World| {
let value = T::from_world(world);
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.insert_with_caller(
value,
mode,
@ -1795,8 +1795,8 @@ fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityC
fn try_insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
#[cfg(feature = "track_change_detection")]
let caller = Location::caller();
move |entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
move |entity: Entity, world: &mut World| {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.insert_with_caller(
bundle,
mode,
@ -1818,8 +1818,8 @@ unsafe fn insert_by_id<T: Send + 'static>(
value: T,
on_none_entity: impl FnOnce(Entity) + Send + 'static,
) -> impl EntityCommand {
move |entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
move |entity: Entity, world: &mut World| {
if let Ok(mut entity) = world.get_entity_mut(entity) {
// SAFETY:
// - `component_id` safety is ensured by the caller
// - `ptr` is valid within the `make` block;
@ -1837,7 +1837,7 @@ unsafe fn insert_by_id<T: Send + 'static>(
/// For a [`Bundle`] type `T`, this will remove any components in the bundle.
/// Any components in the bundle that aren't found on the entity will be ignored.
fn remove<T: Bundle>(entity: Entity, world: &mut World) {
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.remove::<T>();
}
}
@ -1848,7 +1848,7 @@ fn remove<T: Bundle>(entity: Entity, world: &mut World) {
/// Panics if the provided [`ComponentId`] does not exist in the [`World`].
fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
move |entity: Entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.remove_by_id(component_id);
}
}
@ -1856,7 +1856,7 @@ fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
/// An [`EntityCommand`] that remove all components in the bundle and remove all required components for each component in the bundle.
fn remove_with_requires<T: Bundle>(entity: Entity, world: &mut World) {
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.remove_with_requires::<T>();
}
}
@ -1864,7 +1864,7 @@ fn remove_with_requires<T: Bundle>(entity: Entity, world: &mut World) {
/// An [`EntityCommand`] that removes all components associated with a provided entity.
fn clear() -> impl EntityCommand {
move |entity: Entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.clear();
}
}
@ -1875,7 +1875,7 @@ fn clear() -> impl EntityCommand {
/// For a [`Bundle`] type `T`, this will remove all components except those in the bundle.
/// Any components in the bundle that aren't found on the entity will be ignored.
fn retain<T: Bundle>(entity: Entity, world: &mut World) {
if let Some(mut entity_mut) = world.get_entity_mut(entity) {
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
entity_mut.retain::<T>();
}
}
@ -1919,8 +1919,8 @@ fn log_components(entity: Entity, world: &mut World) {
fn observe<E: Event, B: Bundle, M>(
observer: impl IntoObserverSystem<E, B, M>,
) -> impl EntityCommand {
move |entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) {
move |entity: Entity, world: &mut World| {
if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.observe_entity(observer);
}
}

View file

@ -271,7 +271,7 @@ where
/// let entity = world.run_system_once(|mut commands: Commands| {
/// commands.spawn_empty().id()
/// }).unwrap();
/// # assert!(world.get_entity(entity).is_some());
/// # assert!(world.get_entity(entity).is_ok());
/// ```
///
/// ## Immediate Queries

View file

@ -181,7 +181,7 @@ impl World {
O: 'static,
{
match self.get_entity_mut(id.entity) {
Some(mut entity) => {
Ok(mut entity) => {
let registered_system = entity
.take::<RegisteredSystem<I, O>>()
.ok_or(RegisteredSystemError::SelfRemove(id))?;
@ -191,7 +191,7 @@ impl World {
system: registered_system.system,
})
}
None => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
}
}
@ -327,7 +327,7 @@ impl World {
// lookup
let mut entity = self
.get_entity_mut(id.entity)
.ok_or(RegisteredSystemError::SystemIdNotRegistered(id))?;
.map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
// take ownership of system trait object
let RegisteredSystem {
@ -350,7 +350,7 @@ impl World {
};
// return ownership of system trait object (if entity still exists)
if let Some(mut entity) = self.get_entity_mut(id.entity) {
if let Ok(mut entity) = self.get_entity_mut(id.entity) {
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
initialized,
system,
@ -398,7 +398,7 @@ impl World {
}
self.resource_scope(|world, mut id: Mut<CachedSystemId<S::System>>| {
if let Some(mut entity) = world.get_entity_mut(id.0.entity()) {
if let Ok(mut entity) = world.get_entity_mut(id.0.entity()) {
if !entity.contains::<RegisteredSystem<I, O>>() {
entity.insert(system_bundle(Box::new(IntoSystem::into_system(system))));
}
@ -538,7 +538,7 @@ where
O: Send + 'static,
{
fn apply(self, world: &mut World) {
if let Some(mut entity) = world.get_entity_mut(self.entity) {
if let Ok(mut entity) = world.get_entity_mut(self.entity) {
entity.insert(system_bundle(self.system));
}
}

View file

@ -11,12 +11,10 @@ use crate::{
query::{QueryData, QueryFilter},
system::{Commands, Query, Resource},
traversal::Traversal,
world::{error::EntityFetchError, WorldEntityFetch},
};
use super::{
unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell},
EntityMut, Mut, World,
};
use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World};
/// A [`World`] reference that disallows structural ECS changes.
/// This includes initializing resources, registering components or spawning entities.
@ -81,35 +79,168 @@ impl<'w> DeferredWorld<'w> {
unsafe { self.world.get_entity(entity)?.get_mut() }
}
/// Retrieves an [`EntityMut`] that exposes read and write operations for the given `entity`.
/// Returns [`None`] if the `entity` does not exist.
/// Instead of unwrapping the value returned from this function, prefer [`Self::entity_mut`].
/// Returns [`EntityMut`]s that expose read and write operations for the
/// given `entities`, returning [`Err`] if any of the given entities do not
/// exist. Instead of immediately unwrapping the value returned from this
/// function, prefer [`World::entity_mut`].
///
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityMut`].
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityMut>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityMut`]s.
/// - Pass an [`&EntityHashSet`] to receive an [`EntityHashMap<EntityMut>`].
///
/// **As [`DeferredWorld`] does not allow structural changes, all returned
/// references are [`EntityMut`]s, which do not allow structural changes
/// (i.e. adding/removing components or despawning the entity).**
///
/// # Errors
///
/// - Returns [`EntityFetchError::NoSuchEntity`] if any of the given `entities` do not exist in the world.
/// - Only the first entity found to be missing will be returned.
/// - Returns [`EntityFetchError::AliasedMutability`] if the same entity is requested multiple times.
///
/// # Examples
///
/// For examples, see [`DeferredWorld::entity_mut`].
///
/// [`EntityMut`]: crate::world::EntityMut
/// [`&EntityHashSet`]: crate::entity::EntityHashSet
/// [`EntityHashMap<EntityMut>`]: crate::entity::EntityHashMap
#[inline]
pub fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityMut> {
let location = self.entities.get(entity)?;
// SAFETY: if the Entity is invalid, the function returns early.
// Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
let entity_cell = UnsafeEntityCell::new(self.as_unsafe_world_cell(), entity, location);
// SAFETY: The UnsafeEntityCell has read access to the entire world.
let entity_ref = unsafe { EntityMut::new(entity_cell) };
Some(entity_ref)
pub fn get_entity_mut<F: WorldEntityFetch>(
&mut self,
entities: F,
) -> Result<F::DeferredMut<'_>, EntityFetchError> {
let cell = self.as_unsafe_world_cell();
// SAFETY: `&mut self` gives mutable access to the entire world,
// and prevents any other access to the world.
unsafe { entities.fetch_deferred_mut(cell) }
}
/// Retrieves an [`EntityMut`] that exposes read and write operations for the given `entity`.
/// This will panic if the `entity` does not exist. Use [`Self::get_entity_mut`] if you want
/// to check for entity existence instead of implicitly panic-ing.
/// Returns [`EntityMut`]s that expose read and write operations for the
/// given `entities`. This will panic if any of the given entities do not
/// exist. Use [`DeferredWorld::get_entity_mut`] if you want to check for
/// entity existence instead of implicitly panicking.
///
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityMut`].
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityMut>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityMut`]s.
/// - Pass an [`&EntityHashSet`] to receive an [`EntityHashMap<EntityMut>`].
///
/// **As [`DeferredWorld`] does not allow structural changes, all returned
/// references are [`EntityMut`]s, which do not allow structural changes
/// (i.e. adding/removing components or despawning the entity).**
///
/// # Panics
///
/// If any of the given `entities` do not exist in the world.
///
/// # Examples
///
/// ## Single [`Entity`]
///
/// ```
/// # use bevy_ecs::{prelude::*, world::DeferredWorld};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// # let mut world = World::new();
/// # let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// let mut world: DeferredWorld = // ...
/// # DeferredWorld::from(&mut world);
///
/// let mut entity_mut = world.entity_mut(entity);
/// let mut position = entity_mut.get_mut::<Position>().unwrap();
/// position.y = 1.0;
/// assert_eq!(position.x, 0.0);
/// ```
///
/// ## Array of [`Entity`]s
///
/// ```
/// # use bevy_ecs::{prelude::*, world::DeferredWorld};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// # let mut world = World::new();
/// # let e1 = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// # let e2 = world.spawn(Position { x: 1.0, y: 1.0 }).id();
/// let mut world: DeferredWorld = // ...
/// # DeferredWorld::from(&mut world);
///
/// let [mut e1_ref, mut e2_ref] = world.entity_mut([e1, e2]);
/// let mut e1_position = e1_ref.get_mut::<Position>().unwrap();
/// e1_position.x = 1.0;
/// assert_eq!(e1_position.x, 1.0);
/// let mut e2_position = e2_ref.get_mut::<Position>().unwrap();
/// e2_position.x = 2.0;
/// assert_eq!(e2_position.x, 2.0);
/// ```
///
/// ## Slice of [`Entity`]s
///
/// ```
/// # use bevy_ecs::{prelude::*, world::DeferredWorld};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// # let mut world = World::new();
/// # let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// # let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// # let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let mut world: DeferredWorld = // ...
/// # DeferredWorld::from(&mut world);
///
/// let ids = vec![e1, e2, e3];
/// for mut eref in world.entity_mut(&ids[..]) {
/// let mut pos = eref.get_mut::<Position>().unwrap();
/// pos.y = 2.0;
/// assert_eq!(pos.y, 2.0);
/// }
/// ```
///
/// ## [`&EntityHashSet`]
///
/// ```
/// # use bevy_ecs::{prelude::*, entity::EntityHashSet, world::DeferredWorld};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// # let mut world = World::new();
/// # let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// # let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// # let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let mut world: DeferredWorld = // ...
/// # DeferredWorld::from(&mut world);
///
/// let ids = EntityHashSet::from_iter([e1, e2, e3]);
/// for (_id, mut eref) in world.entity_mut(&ids) {
/// let mut pos = eref.get_mut::<Position>().unwrap();
/// pos.y = 2.0;
/// assert_eq!(pos.y, 2.0);
/// }
/// ```
///
/// [`EntityMut`]: crate::world::EntityMut
/// [`&EntityHashSet`]: crate::entity::EntityHashSet
/// [`EntityHashMap<EntityMut>`]: crate::entity::EntityHashMap
#[inline]
pub fn entity_mut(&mut self, entity: Entity) -> EntityMut {
#[inline(never)]
#[cold]
fn panic_no_entity(entity: Entity) -> ! {
panic!("Entity {entity:?} does not exist");
}
match self.get_entity_mut(entity) {
Some(entity) => entity,
None => panic_no_entity(entity),
}
pub fn entity_mut<F: WorldEntityFetch>(&mut self, entities: F) -> F::DeferredMut<'_> {
self.get_entity_mut(entities).unwrap()
}
/// Returns [`Query`] for the given [`QueryState`], which is used to efficiently
@ -411,6 +542,7 @@ impl<'w> DeferredWorld<'w> {
}
if let Some(traverse_to) = self
.get_entity(entity)
.ok()
.and_then(|entity| entity.get_components::<T>())
.and_then(T::traverse)
{

View file

@ -0,0 +1,331 @@
use core::mem::MaybeUninit;
use crate::{
entity::{Entity, EntityHash, EntityHashMap, EntityHashSet},
world::{
error::EntityFetchError, unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef,
EntityWorldMut,
},
};
/// Types that can be used to fetch [`Entity`] references from a [`World`].
///
/// Provided implementations are:
/// - [`Entity`]: Fetch a single entity.
/// - `[Entity; N]`/`&[Entity; N]`: Fetch multiple entities, receiving a
/// same-sized array of references.
/// - `&[Entity]`: Fetch multiple entities, receiving a vector of references.
/// - [`&EntityHashSet`](EntityHashSet): Fetch multiple entities, receiving a
/// hash map of [`Entity`] IDs to references.
///
/// # Performance
///
/// - The slice and array implementations perform an aliased mutabiltiy check
/// in [`WorldEntityFetch::fetch_mut`] that is `O(N^2)`.
/// - The [`EntityHashSet`] implementation performs no such check as the type
/// itself guarantees no duplicates.
/// - The single [`Entity`] implementation performs no such check as only one
/// reference is returned.
///
/// # Safety
///
/// Implementor must ensure that:
/// - No aliased mutability is caused by the returned references.
/// - [`WorldEntityFetch::fetch_ref`] returns only read-only references.
/// - [`WorldEntityFetch::fetch_deferred_mut`] returns only non-structurally-mutable references.
///
/// [`World`]: crate::world::World
pub unsafe trait WorldEntityFetch {
/// The read-only reference type returned by [`WorldEntityFetch::fetch_ref`].
type Ref<'w>;
/// The mutable reference type returned by [`WorldEntityFetch::fetch_mut`].
type Mut<'w>;
/// The mutable reference type returned by [`WorldEntityFetch::fetch_deferred_mut`],
/// but without structural mutability.
type DeferredMut<'w>;
/// Returns read-only reference(s) to the entities with the given
/// [`Entity`] IDs, as determined by `self`.
///
/// # Safety
///
/// It is the caller's responsibility to ensure that:
/// - The given [`UnsafeWorldCell`] has read-only access to the fetched entities.
/// - No other mutable references to the fetched entities exist at the same time.
///
/// # Errors
///
/// - Returns [`Entity`] if the entity does not exist.
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity>;
/// Returns mutable reference(s) to the entities with the given [`Entity`]
/// IDs, as determined by `self`.
///
/// # Safety
///
/// It is the caller's responsibility to ensure that:
/// - The given [`UnsafeWorldCell`] has mutable access to the fetched entities.
/// - No other references to the fetched entities exist at the same time.
///
/// # Errors
///
/// - Returns [`EntityFetchError::NoSuchEntity`] if the entity does not exist.
/// - Returns [`EntityFetchError::AliasedMutability`] if the entity was
/// requested mutably more than once.
unsafe fn fetch_mut(self, cell: UnsafeWorldCell<'_>)
-> Result<Self::Mut<'_>, EntityFetchError>;
/// Returns mutable reference(s) to the entities with the given [`Entity`]
/// IDs, as determined by `self`, but without structural mutability.
///
/// No structural mutability means components cannot be removed from the
/// entity, new components cannot be added to the entity, and the entity
/// cannot be despawned.
///
/// # Safety
///
/// It is the caller's responsibility to ensure that:
/// - The given [`UnsafeWorldCell`] has mutable access to the fetched entities.
/// - No other references to the fetched entities exist at the same time.
///
/// # Errors
///
/// - Returns [`EntityFetchError::NoSuchEntity`] if the entity does not exist.
/// - Returns [`EntityFetchError::AliasedMutability`] if the entity was
/// requested mutably more than once.
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError>;
}
// SAFETY:
// - No aliased mutability is caused because a single reference is returned.
// - No mutable references are returned by `fetch_ref`.
// - No structurally-mutable references are returned by `fetch_deferred_mut`.
unsafe impl WorldEntityFetch for Entity {
type Ref<'w> = EntityRef<'w>;
type Mut<'w> = EntityWorldMut<'w>;
type DeferredMut<'w> = EntityMut<'w>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> {
let ecell = cell.get_entity(self).ok_or(self)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity.
Ok(unsafe { EntityRef::new(ecell) })
}
unsafe fn fetch_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> {
let location = cell
.entities()
.get(self)
.ok_or(EntityFetchError::NoSuchEntity(self))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity.
let world = unsafe { cell.world_mut() };
// SAFETY: location was fetched from the same world's `Entities`.
Ok(unsafe { EntityWorldMut::new(world, self, location) })
}
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> {
let ecell = cell
.get_entity(self)
.ok_or(EntityFetchError::NoSuchEntity(self))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity.
Ok(unsafe { EntityMut::new(ecell) })
}
}
// SAFETY:
// - No aliased mutability is caused because the array is checked for duplicates.
// - No mutable references are returned by `fetch_ref`.
// - No structurally-mutable references are returned by `fetch_deferred_mut`.
unsafe impl<const N: usize> WorldEntityFetch for [Entity; N] {
type Ref<'w> = [EntityRef<'w>; N];
type Mut<'w> = [EntityMut<'w>; N];
type DeferredMut<'w> = [EntityMut<'w>; N];
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> {
<&Self>::fetch_ref(&self, cell)
}
unsafe fn fetch_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> {
<&Self>::fetch_mut(&self, cell)
}
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> {
<&Self>::fetch_deferred_mut(&self, cell)
}
}
// SAFETY:
// - No aliased mutability is caused because the array is checked for duplicates.
// - No mutable references are returned by `fetch_ref`.
// - No structurally-mutable references are returned by `fetch_deferred_mut`.
unsafe impl<const N: usize> WorldEntityFetch for &'_ [Entity; N] {
type Ref<'w> = [EntityRef<'w>; N];
type Mut<'w> = [EntityMut<'w>; N];
type DeferredMut<'w> = [EntityMut<'w>; N];
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> {
let mut refs = [MaybeUninit::uninit(); N];
for (r, &id) in core::iter::zip(&mut refs, self) {
let ecell = cell.get_entity(id).ok_or(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity.
*r = MaybeUninit::new(unsafe { EntityRef::new(ecell) });
}
// SAFETY: Each item was initialized in the loop above.
let refs = refs.map(|r| unsafe { MaybeUninit::assume_init(r) });
Ok(refs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> {
// Check for duplicate entities.
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityFetchError::AliasedMutability(self[i]));
}
}
}
let mut refs = [const { MaybeUninit::uninit() }; N];
for (r, &id) in core::iter::zip(&mut refs, self) {
let ecell = cell
.get_entity(id)
.ok_or(EntityFetchError::NoSuchEntity(id))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity.
*r = MaybeUninit::new(unsafe { EntityMut::new(ecell) });
}
// SAFETY: Each item was initialized in the loop above.
let refs = refs.map(|r| unsafe { MaybeUninit::assume_init(r) });
Ok(refs)
}
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) }
}
}
// SAFETY:
// - No aliased mutability is caused because the slice is checked for duplicates.
// - No mutable references are returned by `fetch_ref`.
// - No structurally-mutable references are returned by `fetch_deferred_mut`.
unsafe impl WorldEntityFetch for &'_ [Entity] {
type Ref<'w> = Vec<EntityRef<'w>>;
type Mut<'w> = Vec<EntityMut<'w>>;
type DeferredMut<'w> = Vec<EntityMut<'w>>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> {
let mut refs = Vec::with_capacity(self.len());
for &id in self {
let ecell = cell.get_entity(id).ok_or(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity.
refs.push(unsafe { EntityRef::new(ecell) });
}
Ok(refs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> {
// Check for duplicate entities.
for i in 0..self.len() {
for j in 0..i {
if self[i] == self[j] {
return Err(EntityFetchError::AliasedMutability(self[i]));
}
}
}
let mut refs = Vec::with_capacity(self.len());
for &id in self {
let ecell = cell
.get_entity(id)
.ok_or(EntityFetchError::NoSuchEntity(id))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity.
refs.push(unsafe { EntityMut::new(ecell) });
}
Ok(refs)
}
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) }
}
}
// SAFETY:
// - No aliased mutability is caused because `EntityHashSet` guarantees no duplicates.
// - No mutable references are returned by `fetch_ref`.
// - No structurally-mutable references are returned by `fetch_deferred_mut`.
unsafe impl WorldEntityFetch for &'_ EntityHashSet {
type Ref<'w> = EntityHashMap<EntityRef<'w>>;
type Mut<'w> = EntityHashMap<EntityMut<'w>>;
type DeferredMut<'w> = EntityHashMap<EntityMut<'w>>;
unsafe fn fetch_ref(self, cell: UnsafeWorldCell<'_>) -> Result<Self::Ref<'_>, Entity> {
let mut refs = EntityHashMap::with_capacity_and_hasher(self.len(), EntityHash);
for &id in self {
let ecell = cell.get_entity(id).ok_or(id)?;
// SAFETY: caller ensures that the world cell has read-only access to the entity.
refs.insert(id, unsafe { EntityRef::new(ecell) });
}
Ok(refs)
}
unsafe fn fetch_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::Mut<'_>, EntityFetchError> {
let mut refs = EntityHashMap::with_capacity_and_hasher(self.len(), EntityHash);
for &id in self {
let ecell = cell
.get_entity(id)
.ok_or(EntityFetchError::NoSuchEntity(id))?;
// SAFETY: caller ensures that the world cell has mutable access to the entity.
refs.insert(id, unsafe { EntityMut::new(ecell) });
}
Ok(refs)
}
unsafe fn fetch_deferred_mut(
self,
cell: UnsafeWorldCell<'_>,
) -> Result<Self::DeferredMut<'_>, EntityFetchError> {
// SAFETY: caller ensures that the world cell has mutable access to the entity,
// and `fetch_mut` does not return structurally-mutable references.
unsafe { self.fetch_mut(cell) }
}
}

View file

@ -2,7 +2,7 @@
use thiserror::Error;
use crate::{component::ComponentId, schedule::InternedScheduleLabel};
use crate::{component::ComponentId, entity::Entity, schedule::InternedScheduleLabel};
/// The error type returned by [`World::try_run_schedule`] if the provided schedule does not exist.
///
@ -21,3 +21,14 @@ pub enum EntityComponentError {
#[error("The component with ID {0:?} was requested mutably more than once.")]
AliasedMutability(ComponentId),
}
/// An error that occurs when fetching entities mutably from a world.
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntityFetchError {
/// The entity with the given ID does not exist.
#[error("The entity with ID {0:?} does not exist.")]
NoSuchEntity(Entity),
/// The entity with the given ID was requested mutably more than once.
#[error("The entity with ID {0:?} was requested mutably more than once.")]
AliasedMutability(Entity),
}

View file

@ -3,6 +3,7 @@
pub(crate) mod command_queue;
mod component_constants;
mod deferred_world;
mod entity_fetch;
mod entity_ref;
pub mod error;
mod filtered_resource;
@ -19,6 +20,7 @@ pub use crate::{
};
pub use component_constants::*;
pub use deferred_world::DeferredWorld;
pub use entity_fetch::WorldEntityFetch;
pub use entity_ref::{
EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Entry,
FilteredEntityMut, FilteredEntityRef, OccupiedEntry, VacantEntry,
@ -43,14 +45,16 @@ use crate::{
schedule::{Schedule, ScheduleLabel, Schedules},
storage::{ResourceData, Storages},
system::{Commands, Resource},
world::{command_queue::RawCommandQueue, error::TryRunScheduleError},
world::{
command_queue::RawCommandQueue,
error::{EntityFetchError, TryRunScheduleError},
},
};
use bevy_ptr::{OwningPtr, Ptr};
use bevy_utils::tracing::warn;
use core::{
any::TypeId,
fmt,
mem::MaybeUninit,
sync::atomic::{AtomicU32, Ordering},
};
@ -595,13 +599,28 @@ impl World {
self.components.get_resource_id(TypeId::of::<T>())
}
/// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`.
/// This will panic if the `entity` does not exist. Use [`World::get_entity`] if you want
/// to check for entity existence instead of implicitly panic-ing.
/// Returns [`EntityRef`]s that expose read-only operations for the given
/// `entities`. This will panic if any of the given entities do not exist. Use
/// [`World::get_entity`] if you want to check for entity existence instead
/// of implicitly panicking.
///
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityRef`].
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityRef>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityRef`]s.
/// - Pass a reference to a [`EntityHashSet`] to receive an
/// [`EntityHashMap<EntityRef>`](crate::entity::EntityHashMap).
///
/// # Panics
///
/// If any of the given `entities` do not exist in the world.
///
/// # Examples
///
/// ## Single [`Entity`]
///
/// ```
/// use bevy_ecs::{component::Component, world::World};
///
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
@ -610,12 +629,76 @@ impl World {
///
/// let mut world = World::new();
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
///
/// let position = world.entity(entity).get::<Position>().unwrap();
/// assert_eq!(position.x, 0.0);
/// ```
///
/// ## Array of [`Entity`]s
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// let e2 = world.spawn(Position { x: 1.0, y: 1.0 }).id();
///
/// let [e1_ref, e2_ref] = world.entity([e1, e2]);
/// let e1_position = e1_ref.get::<Position>().unwrap();
/// assert_eq!(e1_position.x, 0.0);
/// let e2_position = e2_ref.get::<Position>().unwrap();
/// assert_eq!(e2_position.x, 1.0);
/// ```
///
/// ## Slice of [`Entity`]s
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
///
/// let ids = vec![e1, e2, e3];
/// for eref in world.entity(&ids[..]) {
/// assert_eq!(eref.get::<Position>().unwrap().y, 1.0);
/// }
/// ```
///
/// ## [`EntityHashSet`]
///
/// ```
/// # use bevy_ecs::{prelude::*, entity::EntityHashSet};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
///
/// let ids = EntityHashSet::from_iter([e1, e2, e3]);
/// for (_id, eref) in world.entity(&ids) {
/// assert_eq!(eref.get::<Position>().unwrap().y, 1.0);
/// }
/// ```
#[inline]
#[track_caller]
pub fn entity(&self, entity: Entity) -> EntityRef {
pub fn entity<F: WorldEntityFetch>(&self, entities: F) -> F::Ref<'_> {
#[inline(never)]
#[cold]
#[track_caller]
@ -623,19 +706,42 @@ impl World {
panic!("Entity {entity:?} does not exist");
}
match self.get_entity(entity) {
Some(entity) => entity,
None => panic_no_entity(entity),
match self.get_entity(entities) {
Ok(fetched) => fetched,
Err(entity) => panic_no_entity(entity),
}
}
/// Retrieves an [`EntityWorldMut`] that exposes read and write operations for the given `entity`.
/// This will panic if the `entity` does not exist. Use [`World::get_entity_mut`] if you want
/// to check for entity existence instead of implicitly panic-ing.
/// Returns [`EntityMut`]s that expose read and write operations for the
/// given `entities`. This will panic if any of the given entities do not
/// exist. Use [`World::get_entity_mut`] if you want to check for entity
/// existence instead of implicitly panicking.
///
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityWorldMut`].
/// - This reference type allows for structural changes to the entity,
/// such as adding or removing components, or despawning the entity.
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityMut>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityMut`]s.
/// - Pass a reference to a [`EntityHashSet`] to receive an
/// [`EntityHashMap<EntityMut>`](crate::entity::EntityHashMap).
///
/// In order to perform structural changes on the returned entity reference,
/// such as adding or removing components, or despawning the entity, only a
/// single [`Entity`] can be passed to this function. Allowing multiple
/// entities at the same time with structural access would lead to undefined
/// behavior, so [`EntityMut`] is returned when requesting multiple entities.
///
/// # Panics
///
/// If any of the given `entities` do not exist in the world.
///
/// # Examples
///
/// ## Single [`Entity`]
///
/// ```
/// use bevy_ecs::{component::Component, world::World};
///
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
@ -644,23 +750,96 @@ impl World {
///
/// let mut world = World::new();
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
///
/// let mut entity_mut = world.entity_mut(entity);
/// let mut position = entity_mut.get_mut::<Position>().unwrap();
/// position.x = 1.0;
/// position.y = 1.0;
/// assert_eq!(position.x, 0.0);
/// entity_mut.despawn();
/// # assert!(world.get_entity_mut(entity).is_err());
/// ```
///
/// ## Array of [`Entity`]s
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// let e2 = world.spawn(Position { x: 1.0, y: 1.0 }).id();
///
/// let [mut e1_ref, mut e2_ref] = world.entity_mut([e1, e2]);
/// let mut e1_position = e1_ref.get_mut::<Position>().unwrap();
/// e1_position.x = 1.0;
/// assert_eq!(e1_position.x, 1.0);
/// let mut e2_position = e2_ref.get_mut::<Position>().unwrap();
/// e2_position.x = 2.0;
/// assert_eq!(e2_position.x, 2.0);
/// ```
///
/// ## Slice of [`Entity`]s
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
///
/// let ids = vec![e1, e2, e3];
/// for mut eref in world.entity_mut(&ids[..]) {
/// let mut pos = eref.get_mut::<Position>().unwrap();
/// pos.y = 2.0;
/// assert_eq!(pos.y, 2.0);
/// }
/// ```
///
/// ## [`EntityHashSet`]
///
/// ```
/// # use bevy_ecs::{prelude::*, entity::EntityHashSet};
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let e1 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e2 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
/// let e3 = world.spawn(Position { x: 0.0, y: 1.0 }).id();
///
/// let ids = EntityHashSet::from_iter([e1, e2, e3]);
/// for (_id, mut eref) in world.entity_mut(&ids) {
/// let mut pos = eref.get_mut::<Position>().unwrap();
/// pos.y = 2.0;
/// assert_eq!(pos.y, 2.0);
/// }
/// ```
#[inline]
#[track_caller]
pub fn entity_mut(&mut self, entity: Entity) -> EntityWorldMut {
pub fn entity_mut<F: WorldEntityFetch>(&mut self, entities: F) -> F::Mut<'_> {
#[inline(never)]
#[cold]
#[track_caller]
fn panic_no_entity(entity: Entity) -> ! {
panic!("Entity {entity:?} does not exist");
fn panic_on_err(e: EntityFetchError) -> ! {
panic!("{e}");
}
match self.get_entity_mut(entity) {
Some(entity) => entity,
None => panic_no_entity(entity),
match self.get_entity_mut(entities) {
Ok(fetched) => fetched,
Err(e) => panic_on_err(e),
}
}
@ -690,18 +869,9 @@ impl World {
/// world.despawn(id2);
/// world.many_entities([id1, id2]);
/// ```
#[deprecated(since = "0.15.0", note = "Use `World::entity::<[Entity; N]>` instead")]
pub fn many_entities<const N: usize>(&mut self, entities: [Entity; N]) -> [EntityRef<'_>; N] {
#[inline(never)]
#[cold]
#[track_caller]
fn panic_no_entity(entity: Entity) -> ! {
panic!("Entity {entity:?} does not exist");
}
match self.get_many_entities(entities) {
Ok(refs) => refs,
Err(entity) => panic_no_entity(entity),
}
self.entity(entities)
}
/// Gets mutable access to multiple entities at once.
@ -732,21 +902,15 @@ impl World {
/// # let id = world.spawn_empty().id();
/// world.many_entities_mut([id, id]);
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::entity_mut::<[Entity; N]>` instead"
)]
pub fn many_entities_mut<const N: usize>(
&mut self,
entities: [Entity; N],
) -> [EntityMut<'_>; N] {
#[inline(never)]
#[cold]
#[track_caller]
fn panic_on_err(e: QueryEntityError) -> ! {
panic!("{e}");
}
match self.get_many_entities_mut(entities) {
Ok(borrows) => borrows,
Err(e) => panic_on_err(e),
}
self.entity_mut(entities)
}
/// Returns the components of an [`Entity`] through [`ComponentInfo`].
@ -795,35 +959,31 @@ impl World {
}
}
/// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`.
/// Returns [`None`] if the `entity` does not exist.
/// Instead of unwrapping the value returned from this function, prefer [`World::entity`].
/// Returns [`EntityRef`]s that expose read-only operations for the given
/// `entities`, returning [`Err`] if any of the given entities do not exist.
/// Instead of immediately unwrapping the value returned from this function,
/// prefer [`World::entity`].
///
/// ```
/// use bevy_ecs::{component::Component, world::World};
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityRef`].
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityRef>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityRef`]s.
/// - Pass a reference to a [`EntityHashSet`] to receive an
/// [`EntityHashMap<EntityRef>`](crate::entity::EntityHashMap).
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
/// # Errors
///
/// let mut world = World::new();
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// let entity_ref = world.get_entity(entity).unwrap();
/// let position = entity_ref.get::<Position>().unwrap();
/// assert_eq!(position.x, 0.0);
/// ```
/// If any of the given `entities` do not exist in the world, the first
/// [`Entity`] found to be missing will be returned in the [`Err`].
///
/// # Examples
///
/// For examples, see [`World::entity`].
#[inline]
pub fn get_entity(&self, entity: Entity) -> Option<EntityRef> {
let location = self.entities.get(entity)?;
// SAFETY: if the Entity is invalid, the function returns early.
// Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
let entity_cell =
UnsafeEntityCell::new(self.as_unsafe_world_cell_readonly(), entity, location);
// SAFETY: The UnsafeEntityCell has read access to the entire world.
let entity_ref = unsafe { EntityRef::new(entity_cell) };
Some(entity_ref)
pub fn get_entity<F: WorldEntityFetch>(&self, entities: F) -> Result<F::Ref<'_>, Entity> {
let cell = self.as_unsafe_world_cell_readonly();
// SAFETY: `&self` gives read access to the entire world, and prevents mutable access.
unsafe { entities.fetch_ref(cell) }
}
/// Gets an [`EntityRef`] for multiple entities at once.
@ -846,19 +1006,15 @@ impl World {
/// world.despawn(id2);
/// assert!(world.get_many_entities([id1, id2]).is_err());
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::get_entity::<[Entity; N]>` instead"
)]
pub fn get_many_entities<const N: usize>(
&self,
entities: [Entity; N],
) -> Result<[EntityRef<'_>; N], Entity> {
let mut refs = [MaybeUninit::uninit(); N];
for (r, id) in core::iter::zip(&mut refs, entities) {
*r = MaybeUninit::new(self.get_entity(id).ok_or(id)?);
}
// SAFETY: Each item was initialized in the above loop.
let refs = refs.map(|r| unsafe { MaybeUninit::assume_init(r) });
Ok(refs)
self.get_entity(entities)
}
/// Gets an [`EntityRef`] for multiple entities at once, whose number is determined at runtime.
@ -883,16 +1039,55 @@ impl World {
/// world.despawn(id2);
/// assert!(world.get_many_entities_dynamic(&[id1, id2]).is_err());
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::get_entity::<&[Entity]>` instead"
)]
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)?);
}
self.get_entity(entities)
}
Ok(borrows)
/// Returns [`EntityMut`]s that expose read and write operations for the
/// given `entities`, returning [`Err`] if any of the given entities do not
/// exist. Instead of immediately unwrapping the value returned from this
/// function, prefer [`World::entity_mut`].
///
/// This function supports fetching a single entity or multiple entities:
/// - Pass an [`Entity`] to receive a single [`EntityWorldMut`].
/// - This reference type allows for structural changes to the entity,
/// such as adding or removing components, or despawning the entity.
/// - Pass a slice of [`Entity`]s to receive a [`Vec<EntityMut>`].
/// - Pass an array of [`Entity`]s to receive an equally-sized array of [`EntityMut`]s.
/// - Pass a reference to a [`EntityHashSet`] to receive an
/// [`EntityHashMap<EntityMut>`](crate::entity::EntityHashMap).
///
/// In order to perform structural changes on the returned entity reference,
/// such as adding or removing components, or despawning the entity, only a
/// single [`Entity`] can be passed to this function. Allowing multiple
/// entities at the same time with structural access would lead to undefined
/// behavior, so [`EntityMut`] is returned when requesting multiple entities.
///
/// # Errors
///
/// - Returns [`EntityFetchError::NoSuchEntity`] if any of the given `entities` do not exist in the world.
/// - Only the first entity found to be missing will be returned.
/// - Returns [`EntityFetchError::AliasedMutability`] if the same entity is requested multiple times.
///
/// # Examples
///
/// For examples, see [`World::entity_mut`].
#[inline]
pub fn get_entity_mut<F: WorldEntityFetch>(
&mut self,
entities: F,
) -> Result<F::Mut<'_>, EntityFetchError> {
let cell = self.as_unsafe_world_cell();
// SAFETY: `&mut self` gives mutable access to the entire world,
// and prevents any other access to the world.
unsafe { entities.fetch_mut(cell) }
}
/// Returns an [`Entity`] iterator of current entities.
@ -952,49 +1147,6 @@ impl World {
})
}
/// Retrieves an [`EntityWorldMut`] that exposes read and write operations for the given `entity`.
/// Returns [`None`] if the `entity` does not exist.
/// Instead of unwrapping the value returned from this function, prefer [`World::entity_mut`].
///
/// ```
/// use bevy_ecs::{component::Component, world::World};
///
/// #[derive(Component)]
/// struct Position {
/// x: f32,
/// y: f32,
/// }
///
/// let mut world = World::new();
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// let mut entity_mut = world.get_entity_mut(entity).unwrap();
/// let mut position = entity_mut.get_mut::<Position>().unwrap();
/// position.x = 1.0;
/// ```
#[inline]
pub fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityWorldMut> {
let location = self.entities.get(entity)?;
// SAFETY: `entity` exists and `location` is that entity's location
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<'static>> {
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
@ -1015,42 +1167,20 @@ impl World {
/// // Trying to access the same entity multiple times will fail.
/// assert!(world.get_many_entities_mut([id1, id1]).is_err());
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::get_entity_mut::<[Entity; N]>` instead"
)]
pub fn get_many_entities_mut<const N: usize>(
&mut self,
entities: [Entity; N],
) -> Result<[EntityMut<'_>; N], QueryEntityError> {
Self::verify_unique_entities(&entities)?;
// SAFETY: Each entity is unique.
unsafe { self.get_entities_mut_unchecked(entities) }
}
/// # Safety
/// `entities` must contain no duplicate [`Entity`] IDs.
unsafe fn get_entities_mut_unchecked<const N: usize>(
&mut self,
entities: [Entity; N],
) -> Result<[EntityMut<'_>; N], QueryEntityError> {
let world_cell = self.as_unsafe_world_cell();
let mut cells = [MaybeUninit::uninit(); N];
for (cell, id) in core::iter::zip(&mut cells, entities) {
*cell = MaybeUninit::new(
world_cell
.get_entity(id)
.ok_or(QueryEntityError::NoSuchEntity(id))?,
);
}
// SAFETY: Each item was initialized in the loop above.
let cells = cells.map(|c| unsafe { MaybeUninit::assume_init(c) });
// 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.
let borrows = cells.map(|c| unsafe { EntityMut::new(c) });
Ok(borrows)
self.get_entity_mut(entities).map_err(|e| match e {
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
EntityFetchError::AliasedMutability(entity) => {
QueryEntityError::AliasedMutability(entity)
}
})
}
/// Gets mutable access to multiple entities, whose number is determined at runtime.
@ -1074,14 +1204,20 @@ impl World {
/// // Trying to access the same entity multiple times will fail.
/// assert!(world.get_many_entities_dynamic_mut(&[id1, id1]).is_err());
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::get_entity_mut::<&[Entity]>` instead"
)]
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()) }
self.get_entity_mut(entities).map_err(|e| match e {
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
EntityFetchError::AliasedMutability(entity) => {
QueryEntityError::AliasedMutability(entity)
}
})
}
/// Gets mutable access to multiple entities, contained in a [`EntityHashSet`].
@ -1111,41 +1247,22 @@ impl World {
/// let mut entities = world.get_many_entities_from_set_mut(&set).unwrap();
/// let entity1 = entities.get_mut(0).unwrap();
/// ```
#[deprecated(
since = "0.15.0",
note = "Use `World::get_entity_mut::<&EntityHashSet>` instead."
)]
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)
self.get_entity_mut(entities)
.map(|fetched| fetched.into_values().collect())
.map_err(|e| match e {
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
EntityFetchError::AliasedMutability(entity) => {
QueryEntityError::AliasedMutability(entity)
}
})
}
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
@ -1332,7 +1449,7 @@ impl World {
/// ```
#[inline]
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
self.get_entity(entity)?.get()
self.get_entity(entity).ok()?.get()
}
/// Retrieves a mutable reference to the given `entity`'s [`Component`] of the given type.
@ -1381,7 +1498,7 @@ impl World {
/// let mut world = World::new();
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
/// assert!(world.despawn(entity));
/// assert!(world.get_entity(entity).is_none());
/// assert!(world.get_entity(entity).is_err());
/// assert!(world.get::<Position>(entity).is_none());
/// ```
#[track_caller]
@ -1406,7 +1523,7 @@ impl World {
log_warning: bool,
) -> bool {
self.flush();
if let Some(entity) = self.get_entity_mut(entity) {
if let Ok(entity) = self.get_entity_mut(entity) {
entity.despawn();
true
} else {
@ -3282,8 +3399,10 @@ mod tests {
use crate::{
change_detection::DetectChangesMut,
component::{ComponentDescriptor, ComponentInfo, StorageType},
entity::EntityHashSet,
ptr::OwningPtr,
system::Resource,
world::error::EntityFetchError,
};
use alloc::sync::Arc;
use bevy_ecs_macros::Component;
@ -3822,21 +3941,102 @@ mod tests {
}
#[test]
fn test_verify_unique_entities() {
fn get_entity() {
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()
let e1 = world.spawn_empty().id();
let e2 = world.spawn_empty().id();
assert!(world.get_entity(e1).is_ok());
assert!(world.get_entity([e1, e2]).is_ok());
assert!(world
.get_entity(&[e1, e2] /* this is an array not a slice */)
.is_ok());
assert!(world.get_entity(&vec![e1, e2][..]).is_ok());
assert!(world
.get_entity(&EntityHashSet::from_iter([e1, e2]))
.is_ok());
world.entity_mut(e1).despawn();
assert_eq!(Err(e1), world.get_entity(e1).map(|_| {}));
assert_eq!(Err(e1), world.get_entity([e1, e2]).map(|_| {}));
assert_eq!(
Err(e1),
world
.get_entity(&[e1, e2] /* this is an array not a slice */)
.map(|_| {})
);
assert_eq!(Err(e1), world.get_entity(&vec![e1, e2][..]).map(|_| {}));
assert_eq!(
Err(e1),
world
.get_entity(&EntityHashSet::from_iter([e1, e2]))
.map(|_| {})
);
}
#[test]
fn get_entity_mut() {
let mut world = World::new();
let e1 = world.spawn_empty().id();
let e2 = world.spawn_empty().id();
assert!(world.get_entity_mut(e1).is_ok());
assert!(world.get_entity_mut([e1, e2]).is_ok());
assert!(world
.get_entity_mut(&[e1, e2] /* this is an array not a slice */)
.is_ok());
assert!(world.get_entity_mut(&vec![e1, e2][..]).is_ok());
assert!(world
.get_entity_mut(&EntityHashSet::from_iter([e1, e2]))
.is_ok());
assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)),
world.get_entity_mut([e1, e2, e1]).map(|_| {})
);
assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)),
world
.get_entity_mut(&[e1, e2, e1] /* this is an array not a slice */)
.map(|_| {})
);
assert_eq!(
Err(EntityFetchError::AliasedMutability(e1)),
world.get_entity_mut(&vec![e1, e2, e1][..]).map(|_| {})
);
// Aliased mutability isn't allowed by HashSets
assert!(world
.get_entity_mut(&EntityHashSet::from_iter([e1, e2, e1]))
.is_ok());
world.entity_mut(e1).despawn();
assert_eq!(
Err(EntityFetchError::NoSuchEntity(e1)),
world.get_entity_mut(e1).map(|_| {})
);
assert_eq!(
Err(EntityFetchError::NoSuchEntity(e1)),
world.get_entity_mut([e1, e2]).map(|_| {})
);
assert_eq!(
Err(EntityFetchError::NoSuchEntity(e1)),
world
.get_entity_mut(&[e1, e2] /* this is an array not a slice */)
.map(|_| {})
);
assert_eq!(
Err(EntityFetchError::NoSuchEntity(e1)),
world.get_entity_mut(&vec![e1, e2][..]).map(|_| {})
);
assert_eq!(
Err(EntityFetchError::NoSuchEntity(e1)),
world
.get_entity_mut(&EntityHashSet::from_iter([e1, e2]))
.map(|_| {})
);
assert!(World::verify_unique_entities(&[entity1, entity1, entity2, entity5]).is_err());
assert!(World::verify_unique_entities(&[
entity1, entity2, entity3, entity4, entity5, entity1
])
.is_err());
}
}

View file

@ -45,7 +45,7 @@ fn update_parent(world: &mut World, child: Entity, new_parent: Entity) -> Option
///
/// Removes the [`Children`] component from the parent if it's empty.
fn remove_from_children(world: &mut World, parent: Entity, child: Entity) {
let Some(mut parent) = world.get_entity_mut(parent) else {
let Ok(mut parent) = world.get_entity_mut(parent) else {
return;
};
let Some(mut children) = parent.get_mut::<Children>() else {

View file

@ -312,7 +312,7 @@ mod tests {
// The parent's Children component should be removed.
assert!(world.entity(parent).get::<Children>().is_none());
// The child should be despawned.
assert!(world.get_entity(child).is_none());
assert!(world.get_entity(child).is_err());
}
#[test]
@ -340,6 +340,6 @@ mod tests {
assert!(children.is_some());
assert_eq!(children.unwrap().len(), 2_usize);
// The original child should be despawned.
assert!(world.get_entity(child).is_none());
assert!(world.get_entity(child).is_err());
}
}

View file

@ -601,7 +601,7 @@ pub fn process_remote_list_request(In(params): In<Option<Value>>, world: &World)
fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {
world
.get_entity(entity)
.ok_or_else(|| BrpError::entity_not_found(entity))
.map_err(|_| BrpError::entity_not_found(entity))
}
/// Mutably retrieves an entity from the [`World`], returning an error if the
@ -609,7 +609,7 @@ fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError>
fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {
world
.get_entity_mut(entity)
.ok_or_else(|| BrpError::entity_not_found(entity))
.map_err(|_| BrpError::entity_not_found(entity))
}
/// Returns the [`TypeId`] and [`ComponentId`] of the components with the given

View file

@ -148,7 +148,7 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl
for record in pending.drain(..) {
match record {
EntityRecord::Added(e) => {
if let Some(mut entity) = world.get_entity_mut(e) {
if let Ok(mut entity) = world.get_entity_mut(e) {
match entity.entry::<RenderEntity>() {
bevy_ecs::world::Entry::Occupied(_) => {
panic!("Attempting to synchronize an entity that has already been synchronized!");
@ -162,7 +162,7 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl
}
}
EntityRecord::Removed(e) => {
if let Some(ec) = render_world.get_entity_mut(e) {
if let Ok(ec) = render_world.get_entity_mut(e) {
ec.despawn();
};
}

View file

@ -180,7 +180,7 @@ mod tests {
app.update();
// the scene entity does not exist anymore
assert!(app.world().get_entity(scene_entity).is_none());
assert!(app.world().get_entity(scene_entity).is_err());
// the root entity does not have any children anymore
assert!(app.world().entity(entity).get::<Children>().is_none());

View file

@ -191,7 +191,7 @@ impl SceneSpawner {
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
if let Some(instance) = self.spawned_instances.remove(instance_id) {
for &entity in instance.entity_map.values() {
if let Some(mut entity_mut) = world.get_entity_mut(entity) {
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
entity_mut.remove_parent();
entity_mut.despawn_recursive();
};
@ -427,7 +427,7 @@ pub fn scene_spawner_system(world: &mut World) {
scene_spawner
.scenes_with_parent
.retain(|(instance, parent)| {
let retain = world.get_entity(*parent).is_some();
let retain = world.get_entity(*parent).is_ok();
if !retain {
dead_instances.insert(*instance);

View file

@ -775,7 +775,7 @@ mod tests {
assert!(dst_world
.query_filtered::<&MyEntityRef, With<Foo>>()
.iter(&dst_world)
.all(|r| world.get_entity(r.0).is_none()));
.all(|r| world.get_entity(r.0).is_err()));
}
#[test]

View file

@ -29,9 +29,15 @@ impl Command for AddChildInPlace {
hierarchy_command.apply(world);
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
let mut update_transform = || {
let parent = *world.get_entity(self.parent)?.get::<GlobalTransform>()?;
let child_global = *world.get_entity(self.child)?.get::<GlobalTransform>()?;
let mut child_entity = world.get_entity_mut(self.child)?;
let parent = *world
.get_entity(self.parent)
.ok()?
.get::<GlobalTransform>()?;
let child_global = *world
.get_entity(self.child)
.ok()?
.get::<GlobalTransform>()?;
let mut child_entity = world.get_entity_mut(self.child).ok()?;
let mut child = child_entity.get_mut::<Transform>()?;
*child = child_global.reparented_to(&parent);
Some(())
@ -54,8 +60,11 @@ impl Command for RemoveParentInPlace {
hierarchy_command.apply(world);
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
let mut update_transform = || {
let child_global = *world.get_entity(self.child)?.get::<GlobalTransform>()?;
let mut child_entity = world.get_entity_mut(self.child)?;
let child_global = *world
.get_entity(self.child)
.ok()?
.get::<GlobalTransform>()?;
let mut child_entity = world.get_entity_mut(self.child).ok()?;
let mut child = child_entity.get_mut::<Transform>()?;
*child = child_global.compute_transform();
Some(())