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