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