Skip to main content

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