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:
parent
31409ebc61
commit
584d14808a
@ -1637,13 +1637,13 @@ mod tests {
|
|||||||
"new entity is created immediately after world_a's max entity"
|
"new entity is created immediately after world_a's max entity"
|
||||||
);
|
);
|
||||||
assert!(world_b.get::<A>(e1).is_none());
|
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::<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::<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));
|
world_b.get_or_spawn(e1).unwrap().insert(B(1));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1694,7 +1694,7 @@ mod tests {
|
|||||||
|
|
||||||
let high_non_existent_but_reserved_entity = Entity::from_raw(5);
|
let high_non_existent_but_reserved_entity = Entity::from_raw(5);
|
||||||
assert!(
|
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"
|
"entities between high-newly allocated entity and continuous block of existing entities don't exist"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ impl Component for ObservedBy {
|
|||||||
};
|
};
|
||||||
for e in observed_by {
|
for e in observed_by {
|
||||||
let (total_entities, despawned_watched_entities) = {
|
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;
|
continue;
|
||||||
};
|
};
|
||||||
let Some(mut state) = entity_mut.get_mut::<ObserverState>() else {
|
let Some(mut state) = entity_mut.get_mut::<ObserverState>() else {
|
||||||
|
|||||||
@ -221,7 +221,7 @@ fn insert_reflect(
|
|||||||
.get_represented_type_info()
|
.get_represented_type_info()
|
||||||
.expect("component should represent a type.");
|
.expect("component should represent a type.");
|
||||||
let type_path = type_info.type_path();
|
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");
|
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 {
|
let Some(type_registration) = type_registry.get(type_info.type_id()) else {
|
||||||
@ -284,7 +284,7 @@ fn remove_reflect(
|
|||||||
type_registry: &TypeRegistry,
|
type_registry: &TypeRegistry,
|
||||||
component_type_path: Cow<'static, str>,
|
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;
|
return;
|
||||||
};
|
};
|
||||||
let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {
|
let Some(type_registration) = type_registry.get_with_type_path(&component_type_path) else {
|
||||||
|
|||||||
@ -1757,7 +1757,7 @@ fn try_despawn() -> impl EntityCommand {
|
|||||||
fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
|
fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
|
||||||
let caller = Location::caller();
|
let caller = Location::caller();
|
||||||
move |entity: Entity, world: &mut World| {
|
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(
|
entity.insert_with_caller(
|
||||||
bundle,
|
bundle,
|
||||||
mode,
|
mode,
|
||||||
@ -1776,7 +1776,7 @@ fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityC
|
|||||||
let caller = Location::caller();
|
let caller = Location::caller();
|
||||||
move |entity: Entity, world: &mut World| {
|
move |entity: Entity, world: &mut World| {
|
||||||
let value = T::from_world(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(
|
entity.insert_with_caller(
|
||||||
value,
|
value,
|
||||||
mode,
|
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 {
|
fn try_insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
|
||||||
#[cfg(feature = "track_change_detection")]
|
#[cfg(feature = "track_change_detection")]
|
||||||
let caller = Location::caller();
|
let caller = Location::caller();
|
||||||
move |entity, world: &mut World| {
|
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(
|
entity.insert_with_caller(
|
||||||
bundle,
|
bundle,
|
||||||
mode,
|
mode,
|
||||||
@ -1818,8 +1818,8 @@ unsafe fn insert_by_id<T: Send + 'static>(
|
|||||||
value: T,
|
value: T,
|
||||||
on_none_entity: impl FnOnce(Entity) + Send + 'static,
|
on_none_entity: impl FnOnce(Entity) + Send + 'static,
|
||||||
) -> impl EntityCommand {
|
) -> impl EntityCommand {
|
||||||
move |entity, world: &mut World| {
|
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) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// - `component_id` safety is ensured by the caller
|
// - `component_id` safety is ensured by the caller
|
||||||
// - `ptr` is valid within the `make` block;
|
// - `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.
|
/// 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.
|
/// Any components in the bundle that aren't found on the entity will be ignored.
|
||||||
fn remove<T: Bundle>(entity: Entity, world: &mut World) {
|
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>();
|
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`].
|
/// Panics if the provided [`ComponentId`] does not exist in the [`World`].
|
||||||
fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
|
fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
|
||||||
move |entity: Entity, world: &mut World| {
|
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);
|
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.
|
/// 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) {
|
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>();
|
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.
|
/// An [`EntityCommand`] that removes all components associated with a provided entity.
|
||||||
fn clear() -> impl EntityCommand {
|
fn clear() -> impl EntityCommand {
|
||||||
move |entity: Entity, world: &mut World| {
|
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();
|
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.
|
/// 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.
|
/// Any components in the bundle that aren't found on the entity will be ignored.
|
||||||
fn retain<T: Bundle>(entity: Entity, world: &mut World) {
|
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>();
|
entity_mut.retain::<T>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1919,8 +1919,8 @@ fn log_components(entity: Entity, world: &mut World) {
|
|||||||
fn observe<E: Event, B: Bundle, M>(
|
fn observe<E: Event, B: Bundle, M>(
|
||||||
observer: impl IntoObserverSystem<E, B, M>,
|
observer: impl IntoObserverSystem<E, B, M>,
|
||||||
) -> impl EntityCommand {
|
) -> impl EntityCommand {
|
||||||
move |entity, world: &mut World| {
|
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.observe_entity(observer);
|
entity.observe_entity(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -271,7 +271,7 @@ where
|
|||||||
/// let entity = world.run_system_once(|mut commands: Commands| {
|
/// let entity = world.run_system_once(|mut commands: Commands| {
|
||||||
/// commands.spawn_empty().id()
|
/// commands.spawn_empty().id()
|
||||||
/// }).unwrap();
|
/// }).unwrap();
|
||||||
/// # assert!(world.get_entity(entity).is_some());
|
/// # assert!(world.get_entity(entity).is_ok());
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Immediate Queries
|
/// ## Immediate Queries
|
||||||
|
|||||||
@ -181,7 +181,7 @@ impl World {
|
|||||||
O: 'static,
|
O: 'static,
|
||||||
{
|
{
|
||||||
match self.get_entity_mut(id.entity) {
|
match self.get_entity_mut(id.entity) {
|
||||||
Some(mut entity) => {
|
Ok(mut entity) => {
|
||||||
let registered_system = entity
|
let registered_system = entity
|
||||||
.take::<RegisteredSystem<I, O>>()
|
.take::<RegisteredSystem<I, O>>()
|
||||||
.ok_or(RegisteredSystemError::SelfRemove(id))?;
|
.ok_or(RegisteredSystemError::SelfRemove(id))?;
|
||||||
@ -191,7 +191,7 @@ impl World {
|
|||||||
system: registered_system.system,
|
system: registered_system.system,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
|
Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ impl World {
|
|||||||
// lookup
|
// lookup
|
||||||
let mut entity = self
|
let mut entity = self
|
||||||
.get_entity_mut(id.entity)
|
.get_entity_mut(id.entity)
|
||||||
.ok_or(RegisteredSystemError::SystemIdNotRegistered(id))?;
|
.map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
|
||||||
|
|
||||||
// take ownership of system trait object
|
// take ownership of system trait object
|
||||||
let RegisteredSystem {
|
let RegisteredSystem {
|
||||||
@ -350,7 +350,7 @@ impl World {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// return ownership of system trait object (if entity still exists)
|
// 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 {
|
entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
|
||||||
initialized,
|
initialized,
|
||||||
system,
|
system,
|
||||||
@ -398,7 +398,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.resource_scope(|world, mut id: Mut<CachedSystemId<S::System>>| {
|
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>>() {
|
if !entity.contains::<RegisteredSystem<I, O>>() {
|
||||||
entity.insert(system_bundle(Box::new(IntoSystem::into_system(system))));
|
entity.insert(system_bundle(Box::new(IntoSystem::into_system(system))));
|
||||||
}
|
}
|
||||||
@ -538,7 +538,7 @@ where
|
|||||||
O: Send + 'static,
|
O: Send + 'static,
|
||||||
{
|
{
|
||||||
fn apply(self, world: &mut World) {
|
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));
|
entity.insert(system_bundle(self.system));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,12 +11,10 @@ use crate::{
|
|||||||
query::{QueryData, QueryFilter},
|
query::{QueryData, QueryFilter},
|
||||||
system::{Commands, Query, Resource},
|
system::{Commands, Query, Resource},
|
||||||
traversal::Traversal,
|
traversal::Traversal,
|
||||||
|
world::{error::EntityFetchError, WorldEntityFetch},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World};
|
||||||
unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell},
|
|
||||||
EntityMut, Mut, World,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A [`World`] reference that disallows structural ECS changes.
|
/// A [`World`] reference that disallows structural ECS changes.
|
||||||
/// This includes initializing resources, registering components or spawning entities.
|
/// 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() }
|
unsafe { self.world.get_entity(entity)?.get_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an [`EntityMut`] that exposes read and write operations for the given `entity`.
|
/// Returns [`EntityMut`]s that expose read and write operations for the
|
||||||
/// Returns [`None`] if the `entity` does not exist.
|
/// given `entities`, returning [`Err`] if any of the given entities do not
|
||||||
/// Instead of unwrapping the value returned from this function, prefer [`Self::entity_mut`].
|
/// 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]
|
#[inline]
|
||||||
pub fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityMut> {
|
pub fn get_entity_mut<F: WorldEntityFetch>(
|
||||||
let location = self.entities.get(entity)?;
|
&mut self,
|
||||||
// SAFETY: if the Entity is invalid, the function returns early.
|
entities: F,
|
||||||
// Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
|
) -> Result<F::DeferredMut<'_>, EntityFetchError> {
|
||||||
let entity_cell = UnsafeEntityCell::new(self.as_unsafe_world_cell(), entity, location);
|
let cell = self.as_unsafe_world_cell();
|
||||||
// SAFETY: The UnsafeEntityCell has read access to the entire world.
|
// SAFETY: `&mut self` gives mutable access to the entire world,
|
||||||
let entity_ref = unsafe { EntityMut::new(entity_cell) };
|
// and prevents any other access to the world.
|
||||||
Some(entity_ref)
|
unsafe { entities.fetch_deferred_mut(cell) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an [`EntityMut`] that exposes read and write operations for the given `entity`.
|
/// Returns [`EntityMut`]s that expose read and write operations for the
|
||||||
/// This will panic if the `entity` does not exist. Use [`Self::get_entity_mut`] if you want
|
/// given `entities`. This will panic if any of the given entities do not
|
||||||
/// to check for entity existence instead of implicitly panic-ing.
|
/// 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]
|
#[inline]
|
||||||
pub fn entity_mut(&mut self, entity: Entity) -> EntityMut {
|
pub fn entity_mut<F: WorldEntityFetch>(&mut self, entities: F) -> F::DeferredMut<'_> {
|
||||||
#[inline(never)]
|
self.get_entity_mut(entities).unwrap()
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [`Query`] for the given [`QueryState`], which is used to efficiently
|
/// 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
|
if let Some(traverse_to) = self
|
||||||
.get_entity(entity)
|
.get_entity(entity)
|
||||||
|
.ok()
|
||||||
.and_then(|entity| entity.get_components::<T>())
|
.and_then(|entity| entity.get_components::<T>())
|
||||||
.and_then(T::traverse)
|
.and_then(T::traverse)
|
||||||
{
|
{
|
||||||
|
|||||||
331
crates/bevy_ecs/src/world/entity_fetch.rs
Normal file
331
crates/bevy_ecs/src/world/entity_fetch.rs
Normal 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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use thiserror::Error;
|
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.
|
/// 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.")]
|
#[error("The component with ID {0:?} was requested mutably more than once.")]
|
||||||
AliasedMutability(ComponentId),
|
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),
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
pub(crate) mod command_queue;
|
pub(crate) mod command_queue;
|
||||||
mod component_constants;
|
mod component_constants;
|
||||||
mod deferred_world;
|
mod deferred_world;
|
||||||
|
mod entity_fetch;
|
||||||
mod entity_ref;
|
mod entity_ref;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod filtered_resource;
|
mod filtered_resource;
|
||||||
@ -19,6 +20,7 @@ pub use crate::{
|
|||||||
};
|
};
|
||||||
pub use component_constants::*;
|
pub use component_constants::*;
|
||||||
pub use deferred_world::DeferredWorld;
|
pub use deferred_world::DeferredWorld;
|
||||||
|
pub use entity_fetch::WorldEntityFetch;
|
||||||
pub use entity_ref::{
|
pub use entity_ref::{
|
||||||
EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Entry,
|
EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Entry,
|
||||||
FilteredEntityMut, FilteredEntityRef, OccupiedEntry, VacantEntry,
|
FilteredEntityMut, FilteredEntityRef, OccupiedEntry, VacantEntry,
|
||||||
@ -43,14 +45,16 @@ use crate::{
|
|||||||
schedule::{Schedule, ScheduleLabel, Schedules},
|
schedule::{Schedule, ScheduleLabel, Schedules},
|
||||||
storage::{ResourceData, Storages},
|
storage::{ResourceData, Storages},
|
||||||
system::{Commands, Resource},
|
system::{Commands, Resource},
|
||||||
world::{command_queue::RawCommandQueue, error::TryRunScheduleError},
|
world::{
|
||||||
|
command_queue::RawCommandQueue,
|
||||||
|
error::{EntityFetchError, TryRunScheduleError},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use bevy_ptr::{OwningPtr, Ptr};
|
use bevy_ptr::{OwningPtr, Ptr};
|
||||||
use bevy_utils::tracing::warn;
|
use bevy_utils::tracing::warn;
|
||||||
use core::{
|
use core::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt,
|
fmt,
|
||||||
mem::MaybeUninit,
|
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -595,13 +599,28 @@ impl World {
|
|||||||
self.components.get_resource_id(TypeId::of::<T>())
|
self.components.get_resource_id(TypeId::of::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an [`EntityRef`] that exposes read-only operations for the given `entity`.
|
/// Returns [`EntityRef`]s that expose read-only operations for the given
|
||||||
/// This will panic if the `entity` does not exist. Use [`World::get_entity`] if you want
|
/// `entities`. This will panic if any of the given entities do not exist. Use
|
||||||
/// to check for entity existence instead of implicitly panic-ing.
|
/// [`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)]
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
@ -610,12 +629,76 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
||||||
|
///
|
||||||
/// let position = world.entity(entity).get::<Position>().unwrap();
|
/// let position = world.entity(entity).get::<Position>().unwrap();
|
||||||
/// assert_eq!(position.x, 0.0);
|
/// 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]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn entity(&self, entity: Entity) -> EntityRef {
|
pub fn entity<F: WorldEntityFetch>(&self, entities: F) -> F::Ref<'_> {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[cold]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -623,19 +706,42 @@ impl World {
|
|||||||
panic!("Entity {entity:?} does not exist");
|
panic!("Entity {entity:?} does not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.get_entity(entity) {
|
match self.get_entity(entities) {
|
||||||
Some(entity) => entity,
|
Ok(fetched) => fetched,
|
||||||
None => panic_no_entity(entity),
|
Err(entity) => panic_no_entity(entity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves an [`EntityWorldMut`] that exposes read and write operations for the given `entity`.
|
/// Returns [`EntityMut`]s that expose read and write operations for the
|
||||||
/// This will panic if the `entity` does not exist. Use [`World::get_entity_mut`] if you want
|
/// given `entities`. This will panic if any of the given entities do not
|
||||||
/// to check for entity existence instead of implicitly panic-ing.
|
/// 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)]
|
/// #[derive(Component)]
|
||||||
/// struct Position {
|
/// struct Position {
|
||||||
/// x: f32,
|
/// x: f32,
|
||||||
@ -644,23 +750,96 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// let mut world = World::new();
|
||||||
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
||||||
|
///
|
||||||
/// let mut entity_mut = world.entity_mut(entity);
|
/// let mut entity_mut = world.entity_mut(entity);
|
||||||
/// let mut position = entity_mut.get_mut::<Position>().unwrap();
|
/// 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]
|
#[inline]
|
||||||
#[track_caller]
|
#[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)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[cold]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn panic_no_entity(entity: Entity) -> ! {
|
fn panic_on_err(e: EntityFetchError) -> ! {
|
||||||
panic!("Entity {entity:?} does not exist");
|
panic!("{e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.get_entity_mut(entity) {
|
match self.get_entity_mut(entities) {
|
||||||
Some(entity) => entity,
|
Ok(fetched) => fetched,
|
||||||
None => panic_no_entity(entity),
|
Err(e) => panic_on_err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,18 +869,9 @@ impl World {
|
|||||||
/// world.despawn(id2);
|
/// world.despawn(id2);
|
||||||
/// world.many_entities([id1, 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] {
|
pub fn many_entities<const N: usize>(&mut self, entities: [Entity; N]) -> [EntityRef<'_>; N] {
|
||||||
#[inline(never)]
|
self.entity(entities)
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets mutable access to multiple entities at once.
|
/// Gets mutable access to multiple entities at once.
|
||||||
@ -732,21 +902,15 @@ impl World {
|
|||||||
/// # let id = world.spawn_empty().id();
|
/// # let id = world.spawn_empty().id();
|
||||||
/// world.many_entities_mut([id, 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>(
|
pub fn many_entities_mut<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
entities: [Entity; N],
|
entities: [Entity; N],
|
||||||
) -> [EntityMut<'_>; N] {
|
) -> [EntityMut<'_>; N] {
|
||||||
#[inline(never)]
|
self.entity_mut(entities)
|
||||||
#[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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the components of an [`Entity`] through [`ComponentInfo`].
|
/// 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 [`EntityRef`]s that expose read-only operations for the given
|
||||||
/// Returns [`None`] if the `entity` does not exist.
|
/// `entities`, returning [`Err`] if any of the given entities do not exist.
|
||||||
/// Instead of unwrapping the value returned from this function, prefer [`World::entity`].
|
/// Instead of immediately unwrapping the value returned from this function,
|
||||||
|
/// prefer [`World::entity`].
|
||||||
///
|
///
|
||||||
/// ```
|
/// This function supports fetching a single entity or multiple entities:
|
||||||
/// use bevy_ecs::{component::Component, world::World};
|
/// - 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)]
|
/// # Errors
|
||||||
/// struct Position {
|
|
||||||
/// x: f32,
|
|
||||||
/// y: f32,
|
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// let mut world = World::new();
|
/// If any of the given `entities` do not exist in the world, the first
|
||||||
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
/// [`Entity`] found to be missing will be returned in the [`Err`].
|
||||||
/// let entity_ref = world.get_entity(entity).unwrap();
|
///
|
||||||
/// let position = entity_ref.get::<Position>().unwrap();
|
/// # Examples
|
||||||
/// assert_eq!(position.x, 0.0);
|
///
|
||||||
/// ```
|
/// For examples, see [`World::entity`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_entity(&self, entity: Entity) -> Option<EntityRef> {
|
pub fn get_entity<F: WorldEntityFetch>(&self, entities: F) -> Result<F::Ref<'_>, Entity> {
|
||||||
let location = self.entities.get(entity)?;
|
let cell = self.as_unsafe_world_cell_readonly();
|
||||||
// SAFETY: if the Entity is invalid, the function returns early.
|
// SAFETY: `&self` gives read access to the entire world, and prevents mutable access.
|
||||||
// Additionally, Entities::get(entity) returns the correct EntityLocation if the entity exists.
|
unsafe { entities.fetch_ref(cell) }
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an [`EntityRef`] for multiple entities at once.
|
/// Gets an [`EntityRef`] for multiple entities at once.
|
||||||
@ -846,19 +1006,15 @@ impl World {
|
|||||||
/// world.despawn(id2);
|
/// world.despawn(id2);
|
||||||
/// assert!(world.get_many_entities([id1, id2]).is_err());
|
/// 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>(
|
pub fn get_many_entities<const N: usize>(
|
||||||
&self,
|
&self,
|
||||||
entities: [Entity; N],
|
entities: [Entity; N],
|
||||||
) -> Result<[EntityRef<'_>; N], Entity> {
|
) -> Result<[EntityRef<'_>; N], Entity> {
|
||||||
let mut refs = [MaybeUninit::uninit(); N];
|
self.get_entity(entities)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an [`EntityRef`] for multiple entities at once, whose number is determined at runtime.
|
/// Gets an [`EntityRef`] for multiple entities at once, whose number is determined at runtime.
|
||||||
@ -883,16 +1039,55 @@ impl World {
|
|||||||
/// world.despawn(id2);
|
/// world.despawn(id2);
|
||||||
/// assert!(world.get_many_entities_dynamic(&[id1, id2]).is_err());
|
/// 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>(
|
pub fn get_many_entities_dynamic<'w>(
|
||||||
&'w self,
|
&'w self,
|
||||||
entities: &[Entity],
|
entities: &[Entity],
|
||||||
) -> Result<Vec<EntityRef<'w>>, Entity> {
|
) -> Result<Vec<EntityRef<'w>>, Entity> {
|
||||||
let mut borrows = Vec::with_capacity(entities.len());
|
self.get_entity(entities)
|
||||||
for &id in entities {
|
}
|
||||||
borrows.push(self.get_entity(id).ok_or(id)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
/// 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.
|
/// Gets mutable access to multiple entities.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -1015,42 +1167,20 @@ impl World {
|
|||||||
/// // Trying to access the same entity multiple times will fail.
|
/// // Trying to access the same entity multiple times will fail.
|
||||||
/// assert!(world.get_many_entities_mut([id1, id1]).is_err());
|
/// 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>(
|
pub fn get_many_entities_mut<const N: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
entities: [Entity; N],
|
entities: [Entity; N],
|
||||||
) -> Result<[EntityMut<'_>; N], QueryEntityError> {
|
) -> Result<[EntityMut<'_>; N], QueryEntityError> {
|
||||||
Self::verify_unique_entities(&entities)?;
|
self.get_entity_mut(entities).map_err(|e| match e {
|
||||||
|
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
|
||||||
// SAFETY: Each entity is unique.
|
EntityFetchError::AliasedMutability(entity) => {
|
||||||
unsafe { self.get_entities_mut_unchecked(entities) }
|
QueryEntityError::AliasedMutability(entity)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
/// # 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets mutable access to multiple entities, whose number is determined at runtime.
|
/// 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.
|
/// // Trying to access the same entity multiple times will fail.
|
||||||
/// assert!(world.get_many_entities_dynamic_mut(&[id1, id1]).is_err());
|
/// 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>(
|
pub fn get_many_entities_dynamic_mut<'w>(
|
||||||
&'w mut self,
|
&'w mut self,
|
||||||
entities: &[Entity],
|
entities: &[Entity],
|
||||||
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
||||||
Self::verify_unique_entities(entities)?;
|
self.get_entity_mut(entities).map_err(|e| match e {
|
||||||
|
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
|
||||||
// SAFETY: Each entity is unique.
|
EntityFetchError::AliasedMutability(entity) => {
|
||||||
unsafe { self.get_entities_dynamic_mut_unchecked(entities.iter().copied()) }
|
QueryEntityError::AliasedMutability(entity)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets mutable access to multiple entities, contained in a [`EntityHashSet`].
|
/// 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 mut entities = world.get_many_entities_from_set_mut(&set).unwrap();
|
||||||
/// let entity1 = entities.get_mut(0).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>(
|
pub fn get_many_entities_from_set_mut<'w>(
|
||||||
&'w mut self,
|
&'w mut self,
|
||||||
entities: &EntityHashSet,
|
entities: &EntityHashSet,
|
||||||
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
) -> Result<Vec<EntityMut<'w>>, QueryEntityError> {
|
||||||
// SAFETY: Each entity is unique.
|
self.get_entity_mut(entities)
|
||||||
unsafe { self.get_entities_dynamic_mut_unchecked(entities.iter().copied()) }
|
.map(|fetched| fetched.into_values().collect())
|
||||||
}
|
.map_err(|e| match e {
|
||||||
|
EntityFetchError::NoSuchEntity(entity) => QueryEntityError::NoSuchEntity(entity),
|
||||||
/// # Safety
|
EntityFetchError::AliasedMutability(entity) => {
|
||||||
/// `entities` must produce no duplicate [`Entity`] IDs.
|
QueryEntityError::AliasedMutability(entity)
|
||||||
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
|
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
|
||||||
@ -1332,7 +1449,7 @@ impl World {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
|
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.
|
/// 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 mut world = World::new();
|
||||||
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
/// let entity = world.spawn(Position { x: 0.0, y: 0.0 }).id();
|
||||||
/// assert!(world.despawn(entity));
|
/// 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());
|
/// assert!(world.get::<Position>(entity).is_none());
|
||||||
/// ```
|
/// ```
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
@ -1406,7 +1523,7 @@ impl World {
|
|||||||
log_warning: bool,
|
log_warning: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.flush();
|
self.flush();
|
||||||
if let Some(entity) = self.get_entity_mut(entity) {
|
if let Ok(entity) = self.get_entity_mut(entity) {
|
||||||
entity.despawn();
|
entity.despawn();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@ -3282,8 +3399,10 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
change_detection::DetectChangesMut,
|
change_detection::DetectChangesMut,
|
||||||
component::{ComponentDescriptor, ComponentInfo, StorageType},
|
component::{ComponentDescriptor, ComponentInfo, StorageType},
|
||||||
|
entity::EntityHashSet,
|
||||||
ptr::OwningPtr,
|
ptr::OwningPtr,
|
||||||
system::Resource,
|
system::Resource,
|
||||||
|
world::error::EntityFetchError,
|
||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bevy_ecs_macros::Component;
|
use bevy_ecs_macros::Component;
|
||||||
@ -3822,21 +3941,102 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_unique_entities() {
|
fn get_entity() {
|
||||||
let mut world = World::new();
|
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!(
|
let e1 = world.spawn_empty().id();
|
||||||
World::verify_unique_entities(&[entity1, entity2, entity3, entity4, entity5]).is_ok()
|
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.
|
/// Removes the [`Children`] component from the parent if it's empty.
|
||||||
fn remove_from_children(world: &mut World, parent: Entity, child: Entity) {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
let Some(mut children) = parent.get_mut::<Children>() else {
|
let Some(mut children) = parent.get_mut::<Children>() else {
|
||||||
|
|||||||
@ -312,7 +312,7 @@ mod tests {
|
|||||||
// The parent's Children component should be removed.
|
// The parent's Children component should be removed.
|
||||||
assert!(world.entity(parent).get::<Children>().is_none());
|
assert!(world.entity(parent).get::<Children>().is_none());
|
||||||
// The child should be despawned.
|
// The child should be despawned.
|
||||||
assert!(world.get_entity(child).is_none());
|
assert!(world.get_entity(child).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -340,6 +340,6 @@ mod tests {
|
|||||||
assert!(children.is_some());
|
assert!(children.is_some());
|
||||||
assert_eq!(children.unwrap().len(), 2_usize);
|
assert_eq!(children.unwrap().len(), 2_usize);
|
||||||
// The original child should be despawned.
|
// The original child should be despawned.
|
||||||
assert!(world.get_entity(child).is_none());
|
assert!(world.get_entity(child).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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> {
|
fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {
|
||||||
world
|
world
|
||||||
.get_entity(entity)
|
.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
|
/// 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> {
|
fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {
|
||||||
world
|
world
|
||||||
.get_entity_mut(entity)
|
.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
|
/// Returns the [`TypeId`] and [`ComponentId`] of the components with the given
|
||||||
|
|||||||
@ -148,7 +148,7 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl
|
|||||||
for record in pending.drain(..) {
|
for record in pending.drain(..) {
|
||||||
match record {
|
match record {
|
||||||
EntityRecord::Added(e) => {
|
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>() {
|
match entity.entry::<RenderEntity>() {
|
||||||
bevy_ecs::world::Entry::Occupied(_) => {
|
bevy_ecs::world::Entry::Occupied(_) => {
|
||||||
panic!("Attempting to synchronize an entity that has already been synchronized!");
|
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) => {
|
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();
|
ec.despawn();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,7 +180,7 @@ mod tests {
|
|||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
// the scene entity does not exist anymore
|
// 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
|
// the root entity does not have any children anymore
|
||||||
assert!(app.world().entity(entity).get::<Children>().is_none());
|
assert!(app.world().entity(entity).get::<Children>().is_none());
|
||||||
|
|||||||
@ -191,7 +191,7 @@ impl SceneSpawner {
|
|||||||
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
|
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
|
||||||
if let Some(instance) = self.spawned_instances.remove(instance_id) {
|
if let Some(instance) = self.spawned_instances.remove(instance_id) {
|
||||||
for &entity in instance.entity_map.values() {
|
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.remove_parent();
|
||||||
entity_mut.despawn_recursive();
|
entity_mut.despawn_recursive();
|
||||||
};
|
};
|
||||||
@ -427,7 +427,7 @@ pub fn scene_spawner_system(world: &mut World) {
|
|||||||
scene_spawner
|
scene_spawner
|
||||||
.scenes_with_parent
|
.scenes_with_parent
|
||||||
.retain(|(instance, parent)| {
|
.retain(|(instance, parent)| {
|
||||||
let retain = world.get_entity(*parent).is_some();
|
let retain = world.get_entity(*parent).is_ok();
|
||||||
|
|
||||||
if !retain {
|
if !retain {
|
||||||
dead_instances.insert(*instance);
|
dead_instances.insert(*instance);
|
||||||
|
|||||||
@ -775,7 +775,7 @@ mod tests {
|
|||||||
assert!(dst_world
|
assert!(dst_world
|
||||||
.query_filtered::<&MyEntityRef, With<Foo>>()
|
.query_filtered::<&MyEntityRef, With<Foo>>()
|
||||||
.iter(&dst_world)
|
.iter(&dst_world)
|
||||||
.all(|r| world.get_entity(r.0).is_none()));
|
.all(|r| world.get_entity(r.0).is_err()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -29,9 +29,15 @@ impl Command for AddChildInPlace {
|
|||||||
hierarchy_command.apply(world);
|
hierarchy_command.apply(world);
|
||||||
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
|
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
|
||||||
let mut update_transform = || {
|
let mut update_transform = || {
|
||||||
let parent = *world.get_entity(self.parent)?.get::<GlobalTransform>()?;
|
let parent = *world
|
||||||
let child_global = *world.get_entity(self.child)?.get::<GlobalTransform>()?;
|
.get_entity(self.parent)
|
||||||
let mut child_entity = world.get_entity_mut(self.child)?;
|
.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>()?;
|
let mut child = child_entity.get_mut::<Transform>()?;
|
||||||
*child = child_global.reparented_to(&parent);
|
*child = child_global.reparented_to(&parent);
|
||||||
Some(())
|
Some(())
|
||||||
@ -54,8 +60,11 @@ impl Command for RemoveParentInPlace {
|
|||||||
hierarchy_command.apply(world);
|
hierarchy_command.apply(world);
|
||||||
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
|
// FIXME: Replace this closure with a `try` block. See: https://github.com/rust-lang/rust/issues/31436.
|
||||||
let mut update_transform = || {
|
let mut update_transform = || {
|
||||||
let child_global = *world.get_entity(self.child)?.get::<GlobalTransform>()?;
|
let child_global = *world
|
||||||
let mut child_entity = world.get_entity_mut(self.child)?;
|
.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>()?;
|
let mut child = child_entity.get_mut::<Transform>()?;
|
||||||
*child = child_global.compute_transform();
|
*child = child_global.compute_transform();
|
||||||
Some(())
|
Some(())
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user