Remove vestigial helper functions for Commands
and EntityCommands
(#16936)
## Objective I believe these started as structs, back when that was how commands had to be implemented. Now they just hide implementation details. ## Solution Remove the helper functions and move each implementation into its respective method, except for the ones that actually reduce code duplication.
This commit is contained in:
parent
3f38424d43
commit
1669ca676a
@ -550,7 +550,16 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator + Send + Sync + 'static,
|
||||
I::Item: Bundle,
|
||||
{
|
||||
self.queue(spawn_batch(bundles_iter));
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
self.queue(move |world: &mut World| {
|
||||
SpawnBatchIter::new(
|
||||
world,
|
||||
bundles_iter.into_iter(),
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Pushes a generic [`Command`] to the command queue.
|
||||
@ -627,7 +636,21 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue(insert_or_spawn_batch(bundles_iter));
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
self.queue(move |world: &mut World| {
|
||||
if let Err(invalid_entities) = world.insert_or_spawn_batch_with_caller(
|
||||
bundles_iter,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
) {
|
||||
error!(
|
||||
"Failed to 'insert or spawn' bundle of type {} into the following invalid entities: {:?}",
|
||||
core::any::type_name::<B>(),
|
||||
invalid_entities
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for adding a [`Bundle`] type to a batch of [`Entities`](Entity).
|
||||
@ -654,7 +677,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue(insert_batch(batch));
|
||||
self.queue(insert_batch(batch, InsertMode::Replace));
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for adding a [`Bundle`] type to a batch of [`Entities`](Entity).
|
||||
@ -681,7 +704,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue(insert_batch_if_new(batch));
|
||||
self.queue(insert_batch(batch, InsertMode::Keep));
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for adding a [`Bundle`] type to a batch of [`Entities`](Entity).
|
||||
@ -706,7 +729,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue(try_insert_batch(batch));
|
||||
self.queue(try_insert_batch(batch, InsertMode::Replace));
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for adding a [`Bundle`] type to a batch of [`Entities`](Entity).
|
||||
@ -731,7 +754,7 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
self.queue(try_insert_batch_if_new(batch));
|
||||
self.queue(try_insert_batch(batch, InsertMode::Keep));
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for inserting a [`Resource`] in the [`World`] with an inferred value.
|
||||
@ -760,7 +783,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn init_resource<R: Resource + FromWorld>(&mut self) {
|
||||
self.queue(init_resource::<R>);
|
||||
self.queue(move |world: &mut World| {
|
||||
world.init_resource::<R>();
|
||||
});
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for inserting a [`Resource`] in the [`World`] with a specific value.
|
||||
@ -790,7 +815,15 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn insert_resource<R: Resource>(&mut self, resource: R) {
|
||||
self.queue(insert_resource(resource));
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
self.queue(move |world: &mut World| {
|
||||
world.insert_resource_with_caller(
|
||||
resource,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Pushes a [`Command`] to the queue for removing a [`Resource`] from the [`World`].
|
||||
@ -814,7 +847,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
/// # bevy_ecs::system::assert_is_system(system);
|
||||
/// ```
|
||||
pub fn remove_resource<R: Resource>(&mut self) {
|
||||
self.queue(remove_resource::<R>);
|
||||
self.queue(move |world: &mut World| {
|
||||
world.remove_resource::<R>();
|
||||
});
|
||||
}
|
||||
|
||||
/// Runs the system corresponding to the given [`SystemId`].
|
||||
@ -1289,7 +1324,7 @@ impl<'a> EntityCommands<'a> {
|
||||
F: FnOnce() -> bool,
|
||||
{
|
||||
if condition() {
|
||||
self.queue(insert(bundle, InsertMode::Replace))
|
||||
self.insert(bundle)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
@ -1309,6 +1344,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// The command will panic when applied if the associated entity does not exist.
|
||||
///
|
||||
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`] instead.
|
||||
#[track_caller]
|
||||
pub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||
self.queue(insert(bundle, InsertMode::Keep))
|
||||
}
|
||||
@ -1327,6 +1363,7 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// To avoid a panic in this case, use the command [`Self::try_insert_if_new`]
|
||||
/// instead.
|
||||
#[track_caller]
|
||||
pub fn insert_if_new_and<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce() -> bool,
|
||||
@ -1359,10 +1396,18 @@ impl<'a> EntityCommands<'a> {
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
let caller = Location::caller();
|
||||
// SAFETY: same invariants as parent call
|
||||
self.queue(unsafe {insert_by_id(component_id, value, move |world, entity| {
|
||||
panic!("error[B0003]: {caller}: Could not insert a component {component_id:?} (with type {}) for entity {entity:?}, which {}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::<T>(), world.entities().entity_does_not_exist_error_details_message(entity));
|
||||
})})
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
// SAFETY:
|
||||
// - `component_id` safety is ensured by the caller
|
||||
// - `ptr` is valid within the `make` block
|
||||
OwningPtr::make(value, |ptr| unsafe {
|
||||
entity.insert_by_id(component_id, ptr);
|
||||
});
|
||||
} else {
|
||||
panic!("error[B0003]: {caller}: Could not insert a component {component_id:?} (with type {}) for entity {entity:?}, which {}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::<T>(), world.entities().entity_does_not_exist_error_details_message(entity));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to add a dynamic component to an entity.
|
||||
@ -1373,13 +1418,22 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// - [`ComponentId`] must be from the same world as `self`.
|
||||
/// - `T` must have the same layout as the one passed during `component_id` creation.
|
||||
#[track_caller]
|
||||
pub unsafe fn try_insert_by_id<T: Send + 'static>(
|
||||
&mut self,
|
||||
component_id: ComponentId,
|
||||
value: T,
|
||||
) -> &mut Self {
|
||||
// SAFETY: same invariants as parent call
|
||||
self.queue(unsafe { insert_by_id(component_id, value, |_, _| {}) })
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
// SAFETY:
|
||||
// - `component_id` safety is ensured by the caller
|
||||
// - `ptr` is valid within the `make` block
|
||||
OwningPtr::make(value, |ptr| unsafe {
|
||||
entity.insert_by_id(component_id, ptr);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Tries to add a [`Bundle`] of components to the entity.
|
||||
@ -1467,7 +1521,7 @@ impl<'a> EntityCommands<'a> {
|
||||
F: FnOnce() -> bool,
|
||||
{
|
||||
if condition() {
|
||||
self.queue(try_insert(bundle, InsertMode::Replace))
|
||||
self.try_insert(bundle)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
@ -1508,6 +1562,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// }
|
||||
/// # bevy_ecs::system::assert_is_system(add_health_system);
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn try_insert_if_new_and<F>(&mut self, bundle: impl Bundle, condition: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce() -> bool,
|
||||
@ -1528,6 +1583,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// # Note
|
||||
///
|
||||
/// Unlike [`Self::insert_if_new`], this will not panic if the associated entity does not exist.
|
||||
#[track_caller]
|
||||
pub fn try_insert_if_new(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||
self.queue(try_insert(bundle, InsertMode::Keep))
|
||||
}
|
||||
@ -1571,7 +1627,11 @@ impl<'a> EntityCommands<'a> {
|
||||
where
|
||||
T: Bundle,
|
||||
{
|
||||
self.queue(remove::<T>)
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove::<T>();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Removes all components in the [`Bundle`] components and remove all required components for each component in the [`Bundle`] from entity.
|
||||
@ -1599,17 +1659,33 @@ impl<'a> EntityCommands<'a> {
|
||||
/// # bevy_ecs::system::assert_is_system(remove_with_requires_system);
|
||||
/// ```
|
||||
pub fn remove_with_requires<T: Bundle>(&mut self) -> &mut Self {
|
||||
self.queue(remove_with_requires::<T>)
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove_with_requires::<T>();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Removes a component from the entity.
|
||||
/// Removes a dynamic [`Component`] from the entity if it exists.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the provided [`ComponentId`] does not exist in the [`World`].
|
||||
pub fn remove_by_id(&mut self, component_id: ComponentId) -> &mut Self {
|
||||
self.queue(remove_by_id(component_id))
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove_by_id(component_id);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Removes all components associated with the entity.
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.queue(clear())
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clear();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Despawns the entity.
|
||||
@ -1641,7 +1717,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn despawn(&mut self) {
|
||||
self.queue(despawn());
|
||||
self.queue(despawn(true));
|
||||
}
|
||||
|
||||
/// Despawns the entity.
|
||||
@ -1649,7 +1725,7 @@ impl<'a> EntityCommands<'a> {
|
||||
/// the same function as [`Self::despawn`] without emitting warnings.
|
||||
#[track_caller]
|
||||
pub fn try_despawn(&mut self) {
|
||||
self.queue(try_despawn());
|
||||
self.queue(despawn(false));
|
||||
}
|
||||
|
||||
/// Pushes an [`EntityCommand`] to the queue, which will get executed for the current [`Entity`].
|
||||
@ -1714,7 +1790,11 @@ impl<'a> EntityCommands<'a> {
|
||||
where
|
||||
T: Bundle,
|
||||
{
|
||||
self.queue(retain::<T>)
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.retain::<T>();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Logs the components of the entity at the info level.
|
||||
@ -1723,7 +1803,13 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// The command will panic when applied if the associated entity does not exist.
|
||||
pub fn log_components(&mut self) -> &mut Self {
|
||||
self.queue(log_components)
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
let debug_infos: Vec<_> = world
|
||||
.inspect_entity(entity)
|
||||
.map(ComponentInfo::name)
|
||||
.collect();
|
||||
info!("Entity {entity}: {debug_infos:?}");
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the underlying [`Commands`].
|
||||
@ -1748,9 +1834,13 @@ impl<'a> EntityCommands<'a> {
|
||||
/// Creates an [`Observer`] listening for a trigger of type `T` that targets this entity.
|
||||
pub fn observe<E: Event, B: Bundle, M>(
|
||||
&mut self,
|
||||
system: impl IntoObserverSystem<E, B, M>,
|
||||
observer: impl IntoObserverSystem<E, B, M>,
|
||||
) -> &mut Self {
|
||||
self.queue(observe(system))
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.observe(observer);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Clones parts of an entity (components, observers, etc.) onto another entity,
|
||||
@ -1759,6 +1849,12 @@ impl<'a> EntityCommands<'a> {
|
||||
/// By default, the other entity will receive all the components of the original that implement
|
||||
/// [`Clone`] or [`Reflect`](bevy_reflect::Reflect).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if the target entity does not exist.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Configure through [`EntityCloneBuilder`] as follows:
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
@ -1787,16 +1883,16 @@ impl<'a> EntityCommands<'a> {
|
||||
/// - [`EntityCloneBuilder`]
|
||||
/// - [`CloneEntityWithObserversExt`](crate::observer::CloneEntityWithObserversExt)
|
||||
/// - `CloneEntityHierarchyExt`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
pub fn clone_with(
|
||||
&mut self,
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.queue(clone_with(target, config))
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_with(target, config);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Spawns a clone of this entity and returns the [`EntityCommands`] of the clone.
|
||||
@ -1807,9 +1903,10 @@ impl<'a> EntityCommands<'a> {
|
||||
/// To configure cloning behavior (such as only cloning certain components),
|
||||
/// use [`EntityCommands::clone_and_spawn_with`].
|
||||
///
|
||||
/// # Panics
|
||||
/// # Note
|
||||
///
|
||||
/// The command will panic when applied if the original entity does not exist.
|
||||
/// If the original entity does not exist when this command is applied,
|
||||
/// the returned entity will have no components.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1845,9 +1942,10 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// See the methods on [`EntityCloneBuilder`] for more options.
|
||||
///
|
||||
/// # Panics
|
||||
/// # Note
|
||||
///
|
||||
/// The command will panic when applied if the original entity does not exist.
|
||||
/// If the original entity does not exist when this command is applied,
|
||||
/// the returned entity will have no components.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1874,7 +1972,7 @@ impl<'a> EntityCommands<'a> {
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> EntityCommands<'_> {
|
||||
let entity_clone = self.commands().spawn_empty().id();
|
||||
self.queue(clone_with(entity_clone, config));
|
||||
self.clone_with(entity_clone, config);
|
||||
EntityCommands {
|
||||
commands: self.commands_mut().reborrow(),
|
||||
entity: entity_clone,
|
||||
@ -1888,9 +1986,13 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
/// The command will panic when applied if the target entity does not exist.
|
||||
pub fn clone_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.queue(clone_components::<B>(target))
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_components::<B>(target);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Clones the specified components of this entity and inserts them into another entity,
|
||||
@ -1901,9 +2003,13 @@ impl<'a> EntityCommands<'a> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The command will panic when applied if either of the entities do not exist.
|
||||
/// The command will panic when applied if the target entity does not exist.
|
||||
pub fn move_components<B: Bundle>(&mut self, target: Entity) -> &mut Self {
|
||||
self.queue(move_components::<B>(target))
|
||||
self.queue(move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.move_components::<B>(target);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1937,8 +2043,7 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
|
||||
/// See [`or_try_insert`](Self::or_try_insert) for a non-panicking version.
|
||||
#[track_caller]
|
||||
pub fn or_insert(&mut self, default: T) -> &mut Self {
|
||||
self.entity_commands
|
||||
.queue(insert(default, InsertMode::Keep));
|
||||
self.entity_commands.insert_if_new(default);
|
||||
self
|
||||
}
|
||||
|
||||
@ -1949,8 +2054,7 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
|
||||
/// See also [`or_insert_with`](Self::or_insert_with).
|
||||
#[track_caller]
|
||||
pub fn or_try_insert(&mut self, default: T) -> &mut Self {
|
||||
self.entity_commands
|
||||
.queue(try_insert(default, InsertMode::Keep));
|
||||
self.entity_commands.try_insert_if_new(default);
|
||||
self
|
||||
}
|
||||
|
||||
@ -1989,8 +2093,6 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
#[allow(clippy::unwrap_or_default)]
|
||||
// FIXME: use `expect` once stable
|
||||
self.or_insert(T::default())
|
||||
}
|
||||
|
||||
@ -2006,8 +2108,20 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> {
|
||||
where
|
||||
T: FromWorld,
|
||||
{
|
||||
self.entity_commands
|
||||
.queue(insert_from_world::<T>(InsertMode::Keep));
|
||||
let caller = Location::caller();
|
||||
self.entity_commands.queue(move |entity: Entity, world: &mut World| {
|
||||
let value = T::from_world(world);
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.insert_with_caller(
|
||||
value,
|
||||
InsertMode::Keep,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
} else {
|
||||
panic!("error[B0003]: {caller}: Could not insert a bundle (of type `{}`) for {entity:?}, which {}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::<T>(), world.entities().entity_does_not_exist_error_details_message(entity) );
|
||||
}
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -2039,60 +2153,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that consumes an iterator of [`Bundle`]s to spawn a series of entities.
|
||||
///
|
||||
/// This is more efficient than spawning the entities individually.
|
||||
#[track_caller]
|
||||
fn spawn_batch<I, B>(bundles_iter: I) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = B> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
move |world: &mut World| {
|
||||
SpawnBatchIter::new(
|
||||
world,
|
||||
bundles_iter.into_iter(),
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that consumes an iterator to add a series of [`Bundle`]s to a set of entities.
|
||||
/// If any entities do not already exist in the world, they will be spawned.
|
||||
///
|
||||
/// This is more efficient than inserting the bundles individually.
|
||||
#[track_caller]
|
||||
fn insert_or_spawn_batch<I, B>(bundles_iter: I) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
move |world: &mut World| {
|
||||
if let Err(invalid_entities) = world.insert_or_spawn_batch_with_caller(
|
||||
bundles_iter,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
) {
|
||||
error!(
|
||||
"Failed to 'insert or spawn' bundle of type {} into the following invalid entities: {:?}",
|
||||
core::any::type_name::<B>(),
|
||||
invalid_entities
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that consumes an iterator to add a series of [`Bundles`](Bundle) to a set of entities.
|
||||
/// If any entities do not exist in the world, this command will panic.
|
||||
///
|
||||
/// This is more efficient than inserting the bundles individually.
|
||||
#[track_caller]
|
||||
fn insert_batch<I, B>(batch: I) -> impl Command
|
||||
fn insert_batch<I, B>(batch: I, mode: InsertMode) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
@ -2102,29 +2168,7 @@ where
|
||||
move |world: &mut World| {
|
||||
world.insert_batch_with_caller(
|
||||
batch,
|
||||
InsertMode::Replace,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that consumes an iterator to add a series of [`Bundles`](Bundle) to a set of entities.
|
||||
/// If any entities do not exist in the world, this command will panic.
|
||||
///
|
||||
/// This is more efficient than inserting the bundles individually.
|
||||
#[track_caller]
|
||||
fn insert_batch_if_new<I, B>(batch: I) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
move |world: &mut World| {
|
||||
world.insert_batch_with_caller(
|
||||
batch,
|
||||
InsertMode::Keep,
|
||||
mode,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
@ -2136,7 +2180,7 @@ where
|
||||
///
|
||||
/// This is more efficient than inserting the bundles individually.
|
||||
#[track_caller]
|
||||
fn try_insert_batch<I, B>(batch: I) -> impl Command
|
||||
fn try_insert_batch<I, B>(batch: I, mode: InsertMode) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
@ -2146,29 +2190,7 @@ where
|
||||
move |world: &mut World| {
|
||||
world.try_insert_batch_with_caller(
|
||||
batch,
|
||||
InsertMode::Replace,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that consumes an iterator to add a series of [`Bundles`](Bundle) to a set of entities.
|
||||
/// If any entities do not exist in the world, this command will ignore them.
|
||||
///
|
||||
/// This is more efficient than inserting the bundles individually.
|
||||
#[track_caller]
|
||||
fn try_insert_batch_if_new<I, B>(batch: I) -> impl Command
|
||||
where
|
||||
I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
|
||||
B: Bundle,
|
||||
{
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
move |world: &mut World| {
|
||||
world.try_insert_batch_with_caller(
|
||||
batch,
|
||||
InsertMode::Keep,
|
||||
mode,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
@ -2183,25 +2205,10 @@ where
|
||||
/// This won't clean up external references to the entity (such as parent-child relationships
|
||||
/// if you're using `bevy_hierarchy`), which may leave the world in an invalid state.
|
||||
#[track_caller]
|
||||
fn despawn() -> impl EntityCommand {
|
||||
fn despawn(log_warning: bool) -> impl EntityCommand {
|
||||
let caller = Location::caller();
|
||||
move |entity: Entity, world: &mut World| {
|
||||
world.despawn_with_caller(entity, caller, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that despawns a specific entity.
|
||||
/// This will not emit a warning if the entity does not exist.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This won't clean up external references to the entity (such as parent-child relationships
|
||||
/// if you're using `bevy_hierarchy`), which may leave the world in an invalid state.
|
||||
#[track_caller]
|
||||
fn try_despawn() -> impl EntityCommand {
|
||||
let caller = Location::caller();
|
||||
move |entity: Entity, world: &mut World| {
|
||||
world.despawn_with_caller(entity, caller, false);
|
||||
world.despawn_with_caller(entity, caller, log_warning);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2223,25 +2230,6 @@ fn insert<T: Bundle>(bundle: T, mode: InsertMode) -> impl EntityCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that adds the component using its `FromWorld` implementation.
|
||||
#[track_caller]
|
||||
fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
|
||||
let caller = Location::caller();
|
||||
move |entity: Entity, world: &mut World| {
|
||||
let value = T::from_world(world);
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.insert_with_caller(
|
||||
value,
|
||||
mode,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
} else {
|
||||
panic!("error[B0003]: {caller}: Could not insert a bundle (of type `{}`) for {entity:?}, which {}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::<T>(), world.entities().entity_does_not_exist_error_details_message(entity) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that attempts to add the components in a [`Bundle`] to an entity.
|
||||
/// Does nothing if the entity does not exist.
|
||||
#[track_caller]
|
||||
@ -2260,156 +2248,6 @@ fn try_insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that attempts to add the dynamic component to an entity.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The returned `EntityCommand` must be queued for the world where `component_id` was created.
|
||||
/// - `T` must be the type represented by `component_id`.
|
||||
unsafe fn insert_by_id<T: Send + 'static>(
|
||||
component_id: ComponentId,
|
||||
value: T,
|
||||
on_none_entity: impl FnOnce(&mut World, Entity) + Send + 'static,
|
||||
) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
// SAFETY:
|
||||
// - `component_id` safety is ensured by the caller
|
||||
// - `ptr` is valid within the `make` block;
|
||||
OwningPtr::make(value, |ptr| unsafe {
|
||||
entity.insert_by_id(component_id, ptr);
|
||||
});
|
||||
} else {
|
||||
on_none_entity(world, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that removes components from an entity.
|
||||
///
|
||||
/// For a [`Bundle`] type `T`, this will remove any components in the bundle.
|
||||
/// Any components in the bundle that aren't found on the entity will be ignored.
|
||||
fn remove<T: Bundle>(entity: Entity, world: &mut World) {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that removes components with a provided [`ComponentId`] from an entity.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the provided [`ComponentId`] does not exist in the [`World`].
|
||||
fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove_by_id(component_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that remove all components in the bundle and remove all required components for each component in the bundle.
|
||||
fn remove_with_requires<T: Bundle>(entity: Entity, world: &mut World) {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.remove_with_requires::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that removes all components associated with a provided entity.
|
||||
fn clear() -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that removes components from an entity.
|
||||
///
|
||||
/// For a [`Bundle`] type `T`, this will remove all components except those in the bundle.
|
||||
/// Any components in the bundle that aren't found on the entity will be ignored.
|
||||
fn retain<T: Bundle>(entity: Entity, world: &mut World) {
|
||||
if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
|
||||
entity_mut.retain::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Command`] that inserts a [`Resource`] into the world using a value
|
||||
/// created with the [`FromWorld`] trait.
|
||||
#[track_caller]
|
||||
fn init_resource<R: Resource + FromWorld>(world: &mut World) {
|
||||
world.init_resource::<R>();
|
||||
}
|
||||
|
||||
/// A [`Command`] that removes the [resource](Resource) `R` from the world.
|
||||
#[track_caller]
|
||||
fn remove_resource<R: Resource>(world: &mut World) {
|
||||
world.remove_resource::<R>();
|
||||
}
|
||||
|
||||
/// A [`Command`] that inserts a [`Resource`] into the world.
|
||||
#[track_caller]
|
||||
fn insert_resource<R: Resource>(resource: R) -> impl Command {
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
let caller = Location::caller();
|
||||
move |world: &mut World| {
|
||||
world.insert_resource_with_caller(
|
||||
resource,
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
caller,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// [`EntityCommand`] to log the components of a given entity. See [`EntityCommands::log_components`].
|
||||
fn log_components(entity: Entity, world: &mut World) {
|
||||
let debug_infos: Vec<_> = world
|
||||
.inspect_entity(entity)
|
||||
.map(ComponentInfo::name)
|
||||
.collect();
|
||||
info!("Entity {entity}: {debug_infos:?}");
|
||||
}
|
||||
|
||||
fn observe<E: Event, B: Bundle, M>(
|
||||
observer: impl IntoObserverSystem<E, B, M>,
|
||||
) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.observe(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones an entity with configurable cloning behavior.
|
||||
fn clone_with(
|
||||
target: Entity,
|
||||
config: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
|
||||
) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_with(target, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones the specified components into another entity.
|
||||
fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.clone_components::<B>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`EntityCommand`] that clones the specified components into another entity
|
||||
/// and removes them from the original entity.
|
||||
fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
|
||||
move |entity: Entity, world: &mut World| {
|
||||
if let Ok(mut entity) = world.get_entity_mut(entity) {
|
||||
entity.move_components::<B>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::float_cmp, clippy::approx_constant)]
|
||||
mod tests {
|
||||
|
Loading…
Reference in New Issue
Block a user