Path: blob/main/crates/bevy_ecs/src/system/commands/entity_command.rs
6849 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::vec::Vec;7use log::info;89use crate::{10bundle::{Bundle, InsertMode},11change_detection::MaybeLocation,12component::{Component, ComponentId, ComponentInfo},13entity::{Entity, EntityClonerBuilder, OptIn, OptOut},14event::EntityEvent,15relationship::RelationshipHookMode,16system::IntoObserverSystem,17world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},18};19use bevy_ptr::{move_as_ptr, OwningPtr};2021/// A command which gets executed for a given [`Entity`].22///23/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).24///25/// The `Out` generic parameter is the returned "output" of the command.26///27/// # Examples28///29/// ```30/// # use std::collections::HashSet;31/// # use bevy_ecs::prelude::*;32/// use bevy_ecs::system::EntityCommand;33/// #34/// # #[derive(Component, PartialEq)]35/// # struct Name(String);36/// # impl Name {37/// # fn new(s: String) -> Self { Name(s) }38/// # fn as_str(&self) -> &str { &self.0 }39/// # }40///41/// #[derive(Resource, Default)]42/// struct Counter(i64);43///44/// /// A `Command` which names an entity based on a global counter.45/// fn count_name(mut entity: EntityWorldMut) {46/// // Get the current value of the counter, and increment it for next time.47/// let i = {48/// let mut counter = entity.resource_mut::<Counter>();49/// let i = counter.0;50/// counter.0 += 1;51/// i52/// };53/// // Name the entity after the value of the counter.54/// entity.insert(Name::new(format!("Entity #{i}")));55/// }56///57/// // App creation boilerplate omitted...58/// # let mut world = World::new();59/// # world.init_resource::<Counter>();60/// #61/// # let mut setup_schedule = Schedule::default();62/// # setup_schedule.add_systems(setup);63/// # let mut assert_schedule = Schedule::default();64/// # assert_schedule.add_systems(assert_names);65/// #66/// # setup_schedule.run(&mut world);67/// # assert_schedule.run(&mut world);68///69/// fn setup(mut commands: Commands) {70/// commands.spawn_empty().queue(count_name);71/// commands.spawn_empty().queue(count_name);72/// }73///74/// fn assert_names(named: Query<&Name>) {75/// // We use a HashSet because we do not care about the order.76/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();77/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));78/// }79/// ```80pub trait EntityCommand<Out = ()>: Send + 'static {81/// Executes this command for the given [`Entity`].82fn apply(self, entity: EntityWorldMut) -> Out;83}8485/// An error that occurs when running an [`EntityCommand`] on a specific entity.86#[derive(thiserror::Error, Debug)]87pub enum EntityCommandError<E> {88/// The entity this [`EntityCommand`] tried to run on could not be fetched.89#[error(transparent)]90EntityFetchError(#[from] EntityMutableFetchError),91/// An error that occurred while running the [`EntityCommand`].92#[error("{0}")]93CommandFailed(E),94}9596impl<Out, F> EntityCommand<Out> for F97where98F: FnOnce(EntityWorldMut) -> Out + Send + 'static,99{100fn apply(self, entity: EntityWorldMut) -> Out {101self(entity)102}103}104105/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.106#[track_caller]107pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {108let caller = MaybeLocation::caller();109move |mut entity: EntityWorldMut| {110move_as_ptr!(bundle);111entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);112}113}114115/// An [`EntityCommand`] that adds a dynamic component to an entity.116///117/// # Safety118///119/// - [`ComponentId`] must be from the same world as the target entity.120/// - `T` must have the same layout as the one passed during `component_id` creation.121#[track_caller]122pub unsafe fn insert_by_id<T: Send + 'static>(123component_id: ComponentId,124value: T,125mode: InsertMode,126) -> impl EntityCommand {127let caller = MaybeLocation::caller();128move |mut entity: EntityWorldMut| {129// SAFETY:130// - `component_id` safety is ensured by the caller131// - `ptr` is valid within the `make` block132OwningPtr::make(value, |ptr| unsafe {133entity.insert_by_id_with_caller(134component_id,135ptr,136mode,137caller,138RelationshipHookMode::Run,139);140});141}142}143144/// An [`EntityCommand`] that adds a component to an entity using145/// the component's [`FromWorld`] implementation.146///147/// `T::from_world` will only be invoked if the component will actually be inserted.148/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]149/// and the entity already has the component.150#[track_caller]151pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {152let caller = MaybeLocation::caller();153move |mut entity: EntityWorldMut| {154if !(mode == InsertMode::Keep && entity.contains::<T>()) {155let value = entity.world_scope(|world| T::from_world(world));156move_as_ptr!(value);157entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);158}159}160}161162/// An [`EntityCommand`] that adds a component to an entity using163/// some function that returns the component.164///165/// The function will only be invoked if the component will actually be inserted.166/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]167/// and the entity already has the component.168#[track_caller]169pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand170where171F: FnOnce() -> T + Send + 'static,172{173let caller = MaybeLocation::caller();174move |mut entity: EntityWorldMut| {175if !(mode == InsertMode::Keep && entity.contains::<T>()) {176let bundle = component_fn();177move_as_ptr!(bundle);178entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);179}180}181}182183/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.184#[track_caller]185pub fn remove<T: Bundle>() -> impl EntityCommand {186let caller = MaybeLocation::caller();187move |mut entity: EntityWorldMut| {188entity.remove_with_caller::<T>(caller);189}190}191192/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,193/// as well as the required components for each component removed.194#[track_caller]195pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {196let caller = MaybeLocation::caller();197move |mut entity: EntityWorldMut| {198entity.remove_with_requires_with_caller::<T>(caller);199}200}201202/// An [`EntityCommand`] that removes a dynamic component from an entity.203#[track_caller]204pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {205let caller = MaybeLocation::caller();206move |mut entity: EntityWorldMut| {207entity.remove_by_id_with_caller(component_id, caller);208}209}210211/// An [`EntityCommand`] that removes all components from an entity.212#[track_caller]213pub fn clear() -> impl EntityCommand {214let caller = MaybeLocation::caller();215move |mut entity: EntityWorldMut| {216entity.clear_with_caller(caller);217}218}219220/// An [`EntityCommand`] that removes all components from an entity,221/// except for those in the given [`Bundle`].222#[track_caller]223pub fn retain<T: Bundle>() -> impl EntityCommand {224let caller = MaybeLocation::caller();225move |mut entity: EntityWorldMut| {226entity.retain_with_caller::<T>(caller);227}228}229230/// An [`EntityCommand`] that despawns an entity.231///232/// # Note233///234/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)235/// that is configured to despawn descendants.236///237/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).238#[track_caller]239pub fn despawn() -> impl EntityCommand {240let caller = MaybeLocation::caller();241move |entity: EntityWorldMut| {242entity.despawn_with_caller(caller);243}244}245246/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)247/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]248/// targets this entity.249#[track_caller]250pub fn observe<E: EntityEvent, B: Bundle, M>(251observer: impl IntoObserverSystem<E, B, M>,252) -> impl EntityCommand {253let caller = MaybeLocation::caller();254move |mut entity: EntityWorldMut| {255entity.observe_with_caller(observer, caller);256}257}258259/// An [`EntityCommand`] that clones parts of an entity onto another entity,260/// configured through [`EntityClonerBuilder`].261///262/// This builder tries to clone every component from the source entity except263/// for components that were explicitly denied, for example by using the264/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.265///266/// Required components are not considered by denied components and must be267/// explicitly denied as well if desired.268pub fn clone_with_opt_out(269target: Entity,270config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,271) -> impl EntityCommand {272move |mut entity: EntityWorldMut| {273entity.clone_with_opt_out(target, config);274}275}276277/// An [`EntityCommand`] that clones parts of an entity onto another entity,278/// configured through [`EntityClonerBuilder`].279///280/// This builder tries to clone every component that was explicitly allowed281/// from the source entity, for example by using the282/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.283///284/// Required components are also cloned when the target entity does not contain them.285pub fn clone_with_opt_in(286target: Entity,287config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,288) -> impl EntityCommand {289move |mut entity: EntityWorldMut| {290entity.clone_with_opt_in(target, config);291}292}293294/// An [`EntityCommand`] that clones the specified components of an entity295/// and inserts them into another entity.296pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {297move |mut entity: EntityWorldMut| {298entity.clone_components::<B>(target);299}300}301302/// An [`EntityCommand`] moves the specified components of this entity into another entity.303///304/// Components with [`Ignore`] clone behavior will not be moved, while components that305/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.306/// All other components will be moved without any other special handling.307///308/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.309///310/// # Panics311///312/// The command will panic when applied if the target entity does not exist.313///314/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore315/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom316pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {317move |mut entity: EntityWorldMut| {318entity.move_components::<B>(target);319}320}321322/// An [`EntityCommand`] that logs the components of an entity.323pub fn log_components() -> impl EntityCommand {324move |entity: EntityWorldMut| {325let debug_infos: Vec<_> = entity326.world()327.inspect_entity(entity.id())328.expect("Entity existence is verified before an EntityCommand is executed")329.map(ComponentInfo::name)330.collect();331info!("Entity {}: {debug_infos:?}", entity.id());332}333}334335336