added back get_entity

it's not exact, but it should be good enough.
This commit is contained in:
Elliott Pierce 2025-05-30 23:44:07 -04:00
parent 7b044c0908
commit 5c582666be
5 changed files with 119 additions and 21 deletions

View File

@ -782,6 +782,14 @@ impl Entities {
.map(|meta| meta.location) .map(|meta| meta.location)
} }
/// Returns true if the entity exists in the world *now*:
/// It has a location, etc.
///
/// This will return false if the `entity` is reserved but has not been constructed.
pub fn contains(&self, entity: Entity) -> bool {
self.get(entity).is_some()
}
/// Provides information regarding if `entity` may be constructed. /// Provides information regarding if `entity` may be constructed.
#[inline] #[inline]
pub fn validate_construction(&self, entity: Entity) -> Result<(), ConstructionError> { pub fn validate_construction(&self, entity: Entity) -> Result<(), ConstructionError> {

View File

@ -18,7 +18,7 @@ use crate::{
bundle::{Bundle, InsertMode, NoBundleEffect}, bundle::{Bundle, InsertMode, NoBundleEffect},
change_detection::{MaybeLocation, Mut}, change_detection::{MaybeLocation, Mut},
component::{Component, ComponentId, Mutable}, component::{Component, ComponentId, Mutable},
entity::{Entities, EntitiesAllocator, Entity, EntityClonerBuilder}, entity::{Entities, EntitiesAllocator, Entity, EntityClonerBuilder, EntityDoesNotExistError},
error::{ignore, warn, BevyError, CommandWithEntity, ErrorContext, HandleError}, error::{ignore, warn, BevyError, CommandWithEntity, ErrorContext, HandleError},
event::Event, event::Event,
observer::{Observer, TriggerTargets}, observer::{Observer, TriggerTargets},
@ -99,7 +99,8 @@ use crate::{
/// [`ApplyDeferred`]: crate::schedule::ApplyDeferred /// [`ApplyDeferred`]: crate::schedule::ApplyDeferred
pub struct Commands<'w, 's> { pub struct Commands<'w, 's> {
queue: InternalQueue<'s>, queue: InternalQueue<'s>,
entities: &'w EntitiesAllocator, entities: &'w Entities,
allocator: &'w EntitiesAllocator,
} }
// SAFETY: All commands [`Command`] implement [`Send`] // SAFETY: All commands [`Command`] implement [`Send`]
@ -109,7 +110,11 @@ unsafe impl Send for Commands<'_, '_> {}
unsafe impl Sync for Commands<'_, '_> {} unsafe impl Sync for Commands<'_, '_> {}
const _: () = { const _: () = {
type __StructFieldsAlias<'w, 's> = (Deferred<'s, CommandQueue>, &'w Entities); type __StructFieldsAlias<'w, 's> = (
Deferred<'s, CommandQueue>,
&'w EntitiesAllocator,
&'w Entities,
);
#[doc(hidden)] #[doc(hidden)]
pub struct FetchState { pub struct FetchState {
state: <__StructFieldsAlias<'static, 'static> as bevy_ecs::system::SystemParam>::State, state: <__StructFieldsAlias<'static, 'static> as bevy_ecs::system::SystemParam>::State,
@ -162,7 +167,7 @@ const _: () = {
system_meta: &bevy_ecs::system::SystemMeta, system_meta: &bevy_ecs::system::SystemMeta,
world: UnsafeWorldCell, world: UnsafeWorldCell,
) -> Result<(), SystemParamValidationError> { ) -> Result<(), SystemParamValidationError> {
<(Deferred<CommandQueue>, &Entities) as bevy_ecs::system::SystemParam>::validate_param( <__StructFieldsAlias as bevy_ecs::system::SystemParam>::validate_param(
&mut state.state, &mut state.state,
system_meta, system_meta,
world, world,
@ -176,10 +181,16 @@ const _: () = {
world: UnsafeWorldCell<'w>, world: UnsafeWorldCell<'w>,
change_tick: bevy_ecs::component::Tick, change_tick: bevy_ecs::component::Tick,
) -> Self::Item<'w, 's> { ) -> Self::Item<'w, 's> {
let(f0, f1) = <(Deferred<'s, CommandQueue>, &'w EntitiesAllocator) as bevy_ecs::system::SystemParam>::get_param(&mut state.state, system_meta, world, change_tick); let params = <__StructFieldsAlias as bevy_ecs::system::SystemParam>::get_param(
&mut state.state,
system_meta,
world,
change_tick,
);
Commands { Commands {
queue: InternalQueue::CommandQueue(f0), queue: InternalQueue::CommandQueue(params.0),
entities: f1, allocator: params.1,
entities: params.2,
} }
} }
} }
@ -200,13 +211,18 @@ enum InternalQueue<'s> {
impl<'w, 's> Commands<'w, 's> { impl<'w, 's> Commands<'w, 's> {
/// Returns a new `Commands` instance from a [`CommandQueue`] and a [`World`]. /// Returns a new `Commands` instance from a [`CommandQueue`] and a [`World`].
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self { pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
Self::new_from_entities(queue, &world.allocator) Self::new_from_entities(queue, &world.allocator, &world.entities)
} }
/// Returns a new `Commands` instance from a [`CommandQueue`] and an [`Entities`] reference. /// Returns a new `Commands` instance from a [`CommandQueue`] and an [`Entities`] reference.
pub fn new_from_entities(queue: &'s mut CommandQueue, entities: &'w EntitiesAllocator) -> Self { pub fn new_from_entities(
queue: &'s mut CommandQueue,
allocator: &'w EntitiesAllocator,
entities: &'w Entities,
) -> Self {
Self { Self {
queue: InternalQueue::CommandQueue(Deferred(queue)), queue: InternalQueue::CommandQueue(Deferred(queue)),
allocator,
entities, entities,
} }
} }
@ -220,10 +236,12 @@ impl<'w, 's> Commands<'w, 's> {
/// * Caller ensures that `queue` must outlive `'w` /// * Caller ensures that `queue` must outlive `'w`
pub(crate) unsafe fn new_raw_from_entities( pub(crate) unsafe fn new_raw_from_entities(
queue: RawCommandQueue, queue: RawCommandQueue,
entities: &'w EntitiesAllocator, allocator: &'w EntitiesAllocator,
entities: &'w Entities,
) -> Self { ) -> Self {
Self { Self {
queue: InternalQueue::RawCommandQueue(queue), queue: InternalQueue::RawCommandQueue(queue),
allocator,
entities, entities,
} }
} }
@ -255,6 +273,7 @@ impl<'w, 's> Commands<'w, 's> {
InternalQueue::RawCommandQueue(queue.clone()) InternalQueue::RawCommandQueue(queue.clone())
} }
}, },
allocator: self.allocator,
entities: self.entities, entities: self.entities,
} }
} }
@ -304,7 +323,7 @@ impl<'w, 's> Commands<'w, 's> {
/// with the same combination of components. /// with the same combination of components.
#[track_caller] #[track_caller]
pub fn spawn_empty(&mut self) -> EntityCommands { pub fn spawn_empty(&mut self) -> EntityCommands {
let entity = self.entities.alloc(); let entity = self.allocator.alloc();
let caller = MaybeLocation::caller(); let caller = MaybeLocation::caller();
self.queue(move |world: &mut World| { self.queue(move |world: &mut World| {
world world
@ -358,7 +377,7 @@ impl<'w, 's> Commands<'w, 's> {
/// with the same combination of components. /// with the same combination of components.
#[track_caller] #[track_caller]
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands { pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands {
let entity = self.entities.alloc(); let entity = self.allocator.alloc();
let caller = MaybeLocation::caller(); let caller = MaybeLocation::caller();
self.queue(move |world: &mut World| { self.queue(move |world: &mut World| {
world world
@ -404,6 +423,62 @@ impl<'w, 's> Commands<'w, 's> {
} }
} }
/// Returns the [`EntityCommands`] for the requested [`Entity`] if it exists in the world *now*.
/// Note that for entities that have not been constructed, like ones from [`spawn`](Self::spawn), this will error.
///
/// This method does not guarantee that commands queued by the returned `EntityCommands`
/// will be successful, since the entity could be despawned before they are executed.
///
/// # Errors
///
/// Returns [`EntityDoesNotExistError`] if the requested entity does not exist.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Resource)]
/// struct PlayerEntity {
/// entity: Entity
/// }
///
/// #[derive(Component)]
/// struct Label(&'static str);
///
/// fn example_system(mut commands: Commands, player: Res<PlayerEntity>) -> Result {
/// // Get the entity if it still exists and store the `EntityCommands`.
/// // If it doesn't exist, the `?` operator will propagate the returned error
/// // to the system, and the system will pass it to an error handler.
/// let mut entity_commands = commands.get_entity(player.entity)?;
///
/// // Add a component to the entity.
/// entity_commands.insert(Label("hello world"));
///
/// // Return from the system successfully.
/// Ok(())
/// }
/// # bevy_ecs::system::assert_is_system(example_system);
/// ```
///
/// # See also
///
/// - [`entity`](Self::entity) for the infallible version.
#[inline]
#[track_caller]
pub fn get_entity(
&mut self,
entity: Entity,
) -> Result<EntityCommands, EntityDoesNotExistError> {
if self.entities.contains(entity) {
Ok(EntityCommands {
entity,
commands: self.reborrow(),
})
} else {
Err(EntityDoesNotExistError::new(entity, self.entities))
}
}
/// Spawns multiple entities with the same combination of components, /// Spawns multiple entities with the same combination of components,
/// based on a batch of [`Bundles`](Bundle). /// based on a batch of [`Bundles`](Bundle).
/// ///

View File

@ -1,7 +1,7 @@
use bevy_utils::Parallel; use bevy_utils::Parallel;
use crate::{ use crate::{
entity::EntitiesAllocator, entity::{Entities, EntitiesAllocator},
prelude::World, prelude::World,
system::{Deferred, SystemBuffer, SystemMeta, SystemParam}, system::{Deferred, SystemBuffer, SystemMeta, SystemParam},
}; };
@ -51,7 +51,8 @@ struct ParallelCommandQueue {
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct ParallelCommands<'w, 's> { pub struct ParallelCommands<'w, 's> {
state: Deferred<'s, ParallelCommandQueue>, state: Deferred<'s, ParallelCommandQueue>,
entities: &'w EntitiesAllocator, allocator: &'w EntitiesAllocator,
entities: &'w Entities,
} }
impl SystemBuffer for ParallelCommandQueue { impl SystemBuffer for ParallelCommandQueue {
@ -71,7 +72,7 @@ impl<'w, 's> ParallelCommands<'w, 's> {
/// For an example, see the type-level documentation for [`ParallelCommands`]. /// For an example, see the type-level documentation for [`ParallelCommands`].
pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R { pub fn command_scope<R>(&self, f: impl FnOnce(Commands) -> R) -> R {
self.state.thread_queues.scope(|queue| { self.state.thread_queues.scope(|queue| {
let commands = Commands::new_from_entities(queue, self.entities); let commands = Commands::new_from_entities(queue, self.allocator, self.entities);
f(commands) f(commands)
}) })
} }

View File

@ -69,7 +69,13 @@ impl<'w> DeferredWorld<'w> {
// SAFETY: &mut self ensure that there are no outstanding accesses to the queue // SAFETY: &mut self ensure that there are no outstanding accesses to the queue
let command_queue = unsafe { self.world.get_raw_command_queue() }; let command_queue = unsafe { self.world.get_raw_command_queue() };
// SAFETY: command_queue is stored on world and always valid while the world exists // SAFETY: command_queue is stored on world and always valid while the world exists
unsafe { Commands::new_raw_from_entities(command_queue, self.world.entities_allocator()) } unsafe {
Commands::new_raw_from_entities(
command_queue,
self.world.entities_allocator(),
self.world.entities(),
)
}
} }
/// 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.
@ -415,8 +421,9 @@ impl<'w> DeferredWorld<'w> {
// - Command queue access does not conflict with entity access. // - Command queue access does not conflict with entity access.
let raw_queue = unsafe { cell.get_raw_command_queue() }; let raw_queue = unsafe { cell.get_raw_command_queue() };
// SAFETY: `&mut self` ensures the commands does not outlive the world. // SAFETY: `&mut self` ensures the commands does not outlive the world.
let commands = let commands = unsafe {
unsafe { Commands::new_raw_from_entities(raw_queue, cell.entities_allocator()) }; Commands::new_raw_from_entities(raw_queue, cell.entities_allocator(), cell.entities())
};
(fetcher, commands) (fetcher, commands)
} }

View File

@ -267,7 +267,13 @@ impl World {
#[inline] #[inline]
pub fn commands(&mut self) -> Commands { pub fn commands(&mut self) -> Commands {
// SAFETY: command_queue is stored on world and always valid while the world exists // SAFETY: command_queue is stored on world and always valid while the world exists
unsafe { Commands::new_raw_from_entities(self.command_queue.clone(), &self.allocator) } unsafe {
Commands::new_raw_from_entities(
self.command_queue.clone(),
&self.allocator,
&self.entities,
)
}
} }
/// Registers a new [`Component`] type and returns the [`ComponentId`] created for it. /// Registers a new [`Component`] type and returns the [`ComponentId`] created for it.
@ -1046,8 +1052,9 @@ impl World {
// - Command queue access does not conflict with entity access. // - Command queue access does not conflict with entity access.
let raw_queue = unsafe { cell.get_raw_command_queue() }; let raw_queue = unsafe { cell.get_raw_command_queue() };
// SAFETY: `&mut self` ensures the commands does not outlive the world. // SAFETY: `&mut self` ensures the commands does not outlive the world.
let commands = let commands = unsafe {
unsafe { Commands::new_raw_from_entities(raw_queue, cell.entities_allocator()) }; Commands::new_raw_from_entities(raw_queue, cell.entities_allocator(), cell.entities())
};
(fetcher, commands) (fetcher, commands)
} }