bevy_ecs/system/commands/
entity_command.rs

1//! Contains the definition of the [`EntityCommand`] trait,
2//! as well as the blanket implementation of the trait for closures.
3//!
4//! It also contains functions that return closures for use with
5//! [`EntityCommands`](crate::system::EntityCommands).
6
7use alloc::{string::ToString, vec::Vec};
8#[cfg(not(feature = "trace"))]
9use log::info;
10#[cfg(feature = "trace")]
11use tracing::info;
12
13use crate::{
14    bundle::{Bundle, InsertMode},
15    change_detection::MaybeLocation,
16    component::{Component, ComponentId},
17    entity::{Entity, EntityClonerBuilder, OptIn, OptOut},
18    event::EntityEvent,
19    name::Name,
20    relationship::RelationshipHookMode,
21    system::IntoObserverSystem,
22    world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},
23};
24use bevy_ptr::{move_as_ptr, OwningPtr};
25
26/// A command which gets executed for a given [`Entity`].
27///
28/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).
29///
30/// The `Out` generic parameter is the returned "output" of the command.
31///
32/// # Examples
33///
34/// ```
35/// # use std::collections::HashSet;
36/// # use bevy_ecs::prelude::*;
37/// use bevy_ecs::system::EntityCommand;
38/// #
39/// # #[derive(Component, PartialEq)]
40/// # struct Name(String);
41/// # impl Name {
42/// #   fn new(s: String) -> Self { Name(s) }
43/// #   fn as_str(&self) -> &str { &self.0 }
44/// # }
45///
46/// #[derive(Resource, Default)]
47/// struct Counter(i64);
48///
49/// /// A `Command` which names an entity based on a global counter.
50/// fn count_name(mut entity: EntityWorldMut) {
51///     // Get the current value of the counter, and increment it for next time.
52///     let i = {
53///         let mut counter = entity.resource_mut::<Counter>();
54///         let i = counter.0;
55///         counter.0 += 1;
56///         i
57///     };
58///     // Name the entity after the value of the counter.
59///     entity.insert(Name::new(format!("Entity #{i}")));
60/// }
61///
62/// // App creation boilerplate omitted...
63/// # let mut world = World::new();
64/// # world.init_resource::<Counter>();
65/// #
66/// # let mut setup_schedule = Schedule::default();
67/// # setup_schedule.add_systems(setup);
68/// # let mut assert_schedule = Schedule::default();
69/// # assert_schedule.add_systems(assert_names);
70/// #
71/// # setup_schedule.run(&mut world);
72/// # assert_schedule.run(&mut world);
73///
74/// fn setup(mut commands: Commands) {
75///     commands.spawn_empty().queue(count_name);
76///     commands.spawn_empty().queue(count_name);
77/// }
78///
79/// fn assert_names(named: Query<&Name>) {
80///     // We use a HashSet because we do not care about the order.
81///     let names: HashSet<_> = named.iter().map(Name::as_str).collect();
82///     assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));
83/// }
84/// ```
85pub trait EntityCommand<Out = ()>: Send + 'static {
86    /// Executes this command for the given [`Entity`].
87    fn apply(self, entity: EntityWorldMut) -> Out;
88}
89
90/// An error that occurs when running an [`EntityCommand`] on a specific entity.
91#[derive(thiserror::Error, Debug)]
92pub enum EntityCommandError<E> {
93    /// The entity this [`EntityCommand`] tried to run on could not be fetched.
94    #[error(transparent)]
95    EntityFetchError(#[from] EntityMutableFetchError),
96    /// An error that occurred while running the [`EntityCommand`].
97    #[error("{0}")]
98    CommandFailed(E),
99}
100
101impl<Out, F> EntityCommand<Out> for F
102where
103    F: FnOnce(EntityWorldMut) -> Out + Send + 'static,
104{
105    fn apply(self, entity: EntityWorldMut) -> Out {
106        self(entity)
107    }
108}
109
110/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
111#[track_caller]
112pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
113    let caller = MaybeLocation::caller();
114    move |mut entity: EntityWorldMut| {
115        move_as_ptr!(bundle);
116        entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
117    }
118}
119
120/// An [`EntityCommand`] that adds a dynamic component to an entity.
121///
122/// # Safety
123///
124/// - [`ComponentId`] must be from the same world as the target entity.
125/// - `T` must have the same layout as the one passed during `component_id` creation.
126#[track_caller]
127pub unsafe fn insert_by_id<T: Send + 'static>(
128    component_id: ComponentId,
129    value: T,
130    mode: InsertMode,
131) -> impl EntityCommand {
132    let caller = MaybeLocation::caller();
133    move |mut entity: EntityWorldMut| {
134        // SAFETY:
135        // - `component_id` safety is ensured by the caller
136        // - `ptr` is valid within the `make` block
137        OwningPtr::make(value, |ptr| unsafe {
138            entity.insert_by_id_with_caller(
139                component_id,
140                ptr,
141                mode,
142                caller,
143                RelationshipHookMode::Run,
144            );
145        });
146    }
147}
148
149/// An [`EntityCommand`] that adds a component to an entity using
150/// the component's [`FromWorld`] implementation.
151///
152/// `T::from_world` will only be invoked if the component will actually be inserted.
153/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]
154/// and the entity already has the component.
155#[track_caller]
156pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
157    let caller = MaybeLocation::caller();
158    move |mut entity: EntityWorldMut| {
159        if !(mode == InsertMode::Keep && entity.contains::<T>()) {
160            let value = entity.world_scope(|world| T::from_world(world));
161            move_as_ptr!(value);
162            entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
163        }
164    }
165}
166
167/// An [`EntityCommand`] that adds a component to an entity using
168/// some function that returns the component.
169///
170/// The function will only be invoked if the component will actually be inserted.
171/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]
172/// and the entity already has the component.
173#[track_caller]
174pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand
175where
176    F: FnOnce() -> T + Send + 'static,
177{
178    let caller = MaybeLocation::caller();
179    move |mut entity: EntityWorldMut| {
180        if !(mode == InsertMode::Keep && entity.contains::<T>()) {
181            let bundle = component_fn();
182            move_as_ptr!(bundle);
183            entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
184        }
185    }
186}
187
188/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
189#[track_caller]
190pub fn remove<T: Bundle>() -> impl EntityCommand {
191    let caller = MaybeLocation::caller();
192    move |mut entity: EntityWorldMut| {
193        entity.remove_with_caller::<T>(caller);
194    }
195}
196
197/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
198/// as well as the required components for each component removed.
199#[track_caller]
200pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
201    let caller = MaybeLocation::caller();
202    move |mut entity: EntityWorldMut| {
203        entity.remove_with_requires_with_caller::<T>(caller);
204    }
205}
206
207/// An [`EntityCommand`] that removes a dynamic component from an entity.
208#[track_caller]
209pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
210    let caller = MaybeLocation::caller();
211    move |mut entity: EntityWorldMut| {
212        entity.remove_by_id_with_caller(component_id, caller);
213    }
214}
215
216/// An [`EntityCommand`] that removes all components from an entity.
217#[track_caller]
218pub fn clear() -> impl EntityCommand {
219    let caller = MaybeLocation::caller();
220    move |mut entity: EntityWorldMut| {
221        entity.clear_with_caller(caller);
222    }
223}
224
225/// An [`EntityCommand`] that removes all components from an entity,
226/// except for those in the given [`Bundle`].
227#[track_caller]
228pub fn retain<T: Bundle>() -> impl EntityCommand {
229    let caller = MaybeLocation::caller();
230    move |mut entity: EntityWorldMut| {
231        entity.retain_with_caller::<T>(caller);
232    }
233}
234
235/// An [`EntityCommand`] that despawns an entity.
236///
237/// # Note
238///
239/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
240/// that is configured to despawn descendants.
241///
242/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
243#[track_caller]
244pub fn despawn() -> impl EntityCommand {
245    let caller = MaybeLocation::caller();
246    move |entity: EntityWorldMut| {
247        entity.despawn_with_caller(caller);
248    }
249}
250
251/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
252/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]
253/// targets this entity.
254#[track_caller]
255pub fn observe<E: EntityEvent, B: Bundle, M>(
256    observer: impl IntoObserverSystem<E, B, M>,
257) -> impl EntityCommand {
258    let caller = MaybeLocation::caller();
259    move |mut entity: EntityWorldMut| {
260        entity.observe_with_caller(observer, caller);
261    }
262}
263
264/// 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 except
268/// for components that were explicitly denied, for example by using the
269/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.
270///
271/// Required components are not considered by denied components and must be
272/// explicitly denied as well if desired.
273pub fn clone_with_opt_out(
274    target: Entity,
275    config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,
276) -> impl EntityCommand {
277    move |mut entity: EntityWorldMut| {
278        entity.clone_with_opt_out(target, config);
279    }
280}
281
282/// 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 allowed
286/// from the source entity, for example by using the
287/// [`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(
291    target: Entity,
292    config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,
293) -> impl EntityCommand {
294    move |mut entity: EntityWorldMut| {
295        entity.clone_with_opt_in(target, config);
296    }
297}
298
299/// An [`EntityCommand`] that clones the specified components of an entity
300/// and inserts them into another entity.
301pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
302    move |mut entity: EntityWorldMut| {
303        entity.clone_components::<B>(target);
304    }
305}
306
307/// 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 that
310/// 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/// # Panics
316///
317/// The command will panic when applied if the target entity does not exist.
318///
319/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore
320/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom
321pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
322    move |mut entity: EntityWorldMut| {
323        entity.move_components::<B>(target);
324    }
325}
326
327/// An [`EntityCommand`] that logs the components of an entity.
328pub fn log_components() -> impl EntityCommand {
329    move |entity: EntityWorldMut| {
330        let name = entity.get::<Name>().map(ToString::to_string);
331        let id = entity.id();
332        let mut components: Vec<_> = entity
333            .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();
338        components.sort();
339
340        #[cfg(not(feature = "debug"))]
341        {
342            let component_count = components.len();
343            #[cfg(feature = "trace")]
344            {
345                if let Some(name) = name {
346                    info!(id=?id, name=?name, ?component_count, "log_components. Enable the `debug` feature to log component names.");
347                } else {
348                    info!(id=?id, ?component_count, "log_components. Enable the `debug` feature to log component names.");
349                }
350            }
351            #[cfg(not(feature = "trace"))]
352            {
353                let name = name
354                    .map(|name| alloc::format!(" ({name})"))
355                    .unwrap_or_default();
356                info!("Entity {id}{name}: {component_count} components. Enable the `debug` feature to log component names.");
357            }
358        }
359
360        #[cfg(feature = "debug")]
361        {
362            #[cfg(feature = "trace")]
363            {
364                if let Some(name) = name {
365                    info!(id=?id, name=?name, ?components, "log_components");
366                } else {
367                    info!(id=?id, ?components, "log_components");
368                }
369            }
370            #[cfg(not(feature = "trace"))]
371            {
372                let name = name
373                    .map(|name| alloc::format!(" ({name})"))
374                    .unwrap_or_default();
375                info!("Entity {id}{name}: {components:?}");
376            }
377        }
378    }
379}