Path: blob/main/crates/bevy_ecs/src/system/commands/entity_command.rs
9367 views
//! Contains the definition of the [`EntityCommand`] trait,1//! as well as the blanket implementation of the trait for closures.2//!3//! It also contains functions that return closures for use with4//! [`EntityCommands`](crate::system::EntityCommands).56use alloc::{string::ToString, vec::Vec};7#[cfg(not(feature = "trace"))]8use log::info;9#[cfg(feature = "trace")]10use tracing::info;1112use crate::{13bundle::{Bundle, InsertMode},14change_detection::MaybeLocation,15component::{Component, ComponentId},16entity::{Entity, EntityClonerBuilder, OptIn, OptOut},17name::Name,18observer::IntoEntityObserver,19relationship::RelationshipHookMode,20world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},21};22use bevy_ptr::{move_as_ptr, OwningPtr};2324/// A command which gets executed for a given [`Entity`].25///26/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).27///28/// The `Out` generic parameter is the returned "output" of the command.29///30/// # Examples31///32/// ```33/// # use std::collections::HashSet;34/// # use bevy_ecs::prelude::*;35/// use bevy_ecs::system::EntityCommand;36/// #37/// # #[derive(Component, PartialEq)]38/// # struct Name(String);39/// # impl Name {40/// # fn new(s: String) -> Self { Name(s) }41/// # fn as_str(&self) -> &str { &self.0 }42/// # }43///44/// #[derive(Resource, Default)]45/// struct Counter(i64);46///47/// /// A `Command` which names an entity based on a global counter.48/// fn count_name(mut entity: EntityWorldMut) {49/// // Get the current value of the counter, and increment it for next time.50/// let i = {51/// let mut counter = entity.resource_mut::<Counter>();52/// let i = counter.0;53/// counter.0 += 1;54/// i55/// };56/// // Name the entity after the value of the counter.57/// entity.insert(Name::new(format!("Entity #{i}")));58/// }59///60/// // App creation boilerplate omitted...61/// # let mut world = World::new();62/// # world.init_resource::<Counter>();63/// #64/// # let mut setup_schedule = Schedule::default();65/// # setup_schedule.add_systems(setup);66/// # let mut assert_schedule = Schedule::default();67/// # assert_schedule.add_systems(assert_names);68/// #69/// # setup_schedule.run(&mut world);70/// # assert_schedule.run(&mut world);71///72/// fn setup(mut commands: Commands) {73/// commands.spawn_empty().queue(count_name);74/// commands.spawn_empty().queue(count_name);75/// }76///77/// fn assert_names(named: Query<&Name>) {78/// // We use a HashSet because we do not care about the order.79/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();80/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));81/// }82/// ```83pub trait EntityCommand<Out = ()>: Send + 'static {84/// Executes this command for the given [`Entity`].85fn apply(self, entity: EntityWorldMut) -> Out;86}8788/// An error that occurs when running an [`EntityCommand`] on a specific entity.89#[derive(thiserror::Error, Debug)]90pub enum EntityCommandError<E> {91/// The entity this [`EntityCommand`] tried to run on could not be fetched.92#[error(transparent)]93EntityFetchError(#[from] EntityMutableFetchError),94/// An error that occurred while running the [`EntityCommand`].95#[error("{0}")]96CommandFailed(E),97}9899impl<Out, F> EntityCommand<Out> for F100where101F: FnOnce(EntityWorldMut) -> Out + Send + 'static,102{103fn apply(self, entity: EntityWorldMut) -> Out {104self(entity)105}106}107108/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.109#[track_caller]110pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {111let caller = MaybeLocation::caller();112move |mut entity: EntityWorldMut| {113move_as_ptr!(bundle);114entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);115}116}117118/// An [`EntityCommand`] that adds a dynamic component to an entity.119///120/// # Safety121///122/// - [`ComponentId`] must be from the same world as the target entity.123/// - `T` must have the same layout as the one passed during `component_id` creation.124#[track_caller]125pub unsafe fn insert_by_id<T: Send + 'static>(126component_id: ComponentId,127value: T,128mode: InsertMode,129) -> impl EntityCommand {130let caller = MaybeLocation::caller();131move |mut entity: EntityWorldMut| {132// SAFETY:133// - `component_id` safety is ensured by the caller134// - `ptr` is valid within the `make` block135OwningPtr::make(value, |ptr| unsafe {136entity.insert_by_id_with_caller(137component_id,138ptr,139mode,140caller,141RelationshipHookMode::Run,142);143});144}145}146147/// An [`EntityCommand`] that adds a component to an entity using148/// the component's [`FromWorld`] implementation.149///150/// `T::from_world` will only be invoked if the component will actually be inserted.151/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]152/// and the entity already has the component.153#[track_caller]154pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {155let caller = MaybeLocation::caller();156move |mut entity: EntityWorldMut| {157if !(mode == InsertMode::Keep && entity.contains::<T>()) {158let value = entity.world_scope(|world| T::from_world(world));159move_as_ptr!(value);160entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);161}162}163}164165/// An [`EntityCommand`] that adds a component to an entity using166/// some function that returns the component.167///168/// The function will only be invoked if the component will actually be inserted.169/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]170/// and the entity already has the component.171#[track_caller]172pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand173where174F: FnOnce() -> T + Send + 'static,175{176let caller = MaybeLocation::caller();177move |mut entity: EntityWorldMut| {178if !(mode == InsertMode::Keep && entity.contains::<T>()) {179let bundle = component_fn();180move_as_ptr!(bundle);181entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);182}183}184}185186/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.187#[track_caller]188pub fn remove<T: Bundle>() -> impl EntityCommand {189let caller = MaybeLocation::caller();190move |mut entity: EntityWorldMut| {191entity.remove_with_caller::<T>(caller);192}193}194195/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,196/// as well as the required components for each component removed.197#[track_caller]198pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {199let caller = MaybeLocation::caller();200move |mut entity: EntityWorldMut| {201entity.remove_with_requires_with_caller::<T>(caller);202}203}204205/// An [`EntityCommand`] that removes a dynamic component from an entity.206#[track_caller]207pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {208let caller = MaybeLocation::caller();209move |mut entity: EntityWorldMut| {210entity.remove_by_id_with_caller(component_id, caller);211}212}213214/// An [`EntityCommand`] that removes all components from an entity.215#[track_caller]216pub fn clear() -> impl EntityCommand {217let caller = MaybeLocation::caller();218move |mut entity: EntityWorldMut| {219entity.clear_with_caller(caller);220}221}222223/// An [`EntityCommand`] that removes all components from an entity,224/// except for those in the given [`Bundle`].225#[track_caller]226pub fn retain<T: Bundle>() -> impl EntityCommand {227let caller = MaybeLocation::caller();228move |mut entity: EntityWorldMut| {229entity.retain_with_caller::<T>(caller);230}231}232233/// An [`EntityCommand`] that despawns an entity.234///235/// # Note236///237/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)238/// that is configured to despawn descendants.239///240/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).241#[track_caller]242pub fn despawn() -> impl EntityCommand {243let caller = MaybeLocation::caller();244move |entity: EntityWorldMut| {245entity.despawn_with_caller(caller);246}247}248249/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)250/// watching for an [`EntityEvent`](crate::event::EntityEvent) of type `E` whose251/// [`event_target`](crate::event::EntityEvent::event_target) targets this entity.252///253/// Accepts any type that implements [`IntoEntityObserver`], including:254/// - Observer systems (closures or functions implementing [`IntoObserverSystem`](crate::system::IntoObserverSystem))255/// - Observer systems with run conditions (via `.run_if()`)256#[track_caller]257pub fn observe<M>(observer: impl IntoEntityObserver<M>) -> impl EntityCommand {258let caller = MaybeLocation::caller();259move |mut entity: EntityWorldMut| {260entity.observe_with_caller(observer, caller);261}262}263264/// An [`EntityCommand`] that clones parts of an entity onto another entity,265/// configured through [`EntityClonerBuilder`].266///267/// This builder tries to clone every component from the source entity except268/// for components that were explicitly denied, for example by using the269/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.270///271/// Required components are not considered by denied components and must be272/// explicitly denied as well if desired.273pub fn clone_with_opt_out(274target: Entity,275config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,276) -> impl EntityCommand {277move |mut entity: EntityWorldMut| {278entity.clone_with_opt_out(target, config);279}280}281282/// An [`EntityCommand`] that clones parts of an entity onto another entity,283/// configured through [`EntityClonerBuilder`].284///285/// This builder tries to clone every component that was explicitly allowed286/// from the source entity, for example by using the287/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.288///289/// Required components are also cloned when the target entity does not contain them.290pub fn clone_with_opt_in(291target: Entity,292config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,293) -> impl EntityCommand {294move |mut entity: EntityWorldMut| {295entity.clone_with_opt_in(target, config);296}297}298299/// An [`EntityCommand`] that clones the specified components of an entity300/// and inserts them into another entity.301pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {302move |mut entity: EntityWorldMut| {303entity.clone_components::<B>(target);304}305}306307/// An [`EntityCommand`] moves the specified components of this entity into another entity.308///309/// Components with [`Ignore`] clone behavior will not be moved, while components that310/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.311/// All other components will be moved without any other special handling.312///313/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.314///315/// # Panics316///317/// The command will panic when applied if the target entity does not exist.318///319/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore320/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom321pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {322move |mut entity: EntityWorldMut| {323entity.move_components::<B>(target);324}325}326327/// An [`EntityCommand`] that logs the components of an entity.328pub fn log_components() -> impl EntityCommand {329move |entity: EntityWorldMut| {330let name = entity.get::<Name>().map(ToString::to_string);331let id = entity.id();332let mut components: Vec<_> = entity333.world()334.inspect_entity(id)335.expect("Entity existence is verified before an EntityCommand is executed")336.map(|info| info.name().to_string())337.collect();338components.sort();339340#[cfg(not(feature = "debug"))]341{342let component_count = components.len();343#[cfg(feature = "trace")]344{345if let Some(name) = name {346info!(id=?id, name=?name, ?component_count, "log_components. Enable the `debug` feature to log component names.");347} else {348info!(id=?id, ?component_count, "log_components. Enable the `debug` feature to log component names.");349}350}351#[cfg(not(feature = "trace"))]352{353let name = name354.map(|name| alloc::format!(" ({name})"))355.unwrap_or_default();356info!("Entity {id}{name}: {component_count} components. Enable the `debug` feature to log component names.");357}358}359360#[cfg(feature = "debug")]361{362#[cfg(feature = "trace")]363{364if let Some(name) = name {365info!(id=?id, name=?name, ?components, "log_components");366} else {367info!(id=?id, ?components, "log_components");368}369}370#[cfg(not(feature = "trace"))]371{372let name = name373.map(|name| alloc::format!(" ({name})"))374.unwrap_or_default();375info!("Entity {id}{name}: {components:?}");376}377}378}379}380381382