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},
15    event::Event,
16    relationship::RelationshipHookMode,
17    system::IntoObserverSystem,
18    world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},
19};
20use bevy_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        entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
112    }
113}
114
115/// An [`EntityCommand`] that adds a dynamic component to an entity.
116///
117/// # Safety
118///
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>(
123    component_id: ComponentId,
124    value: T,
125    mode: InsertMode,
126) -> impl EntityCommand {
127    let caller = MaybeLocation::caller();
128    move |mut entity: EntityWorldMut| {
129        // SAFETY:
130        // - `component_id` safety is ensured by the caller
131        // - `ptr` is valid within the `make` block
132        OwningPtr::make(value, |ptr| unsafe {
133            entity.insert_by_id_with_caller(
134                component_id,
135                ptr,
136                mode,
137                caller,
138                RelationshipHookMode::Run,
139            );
140        });
141    }
142}
143
144/// An [`EntityCommand`] that adds a component to an entity using
145/// the component's [`FromWorld`] implementation.
146#[track_caller]
147pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
148    let caller = MaybeLocation::caller();
149    move |mut entity: EntityWorldMut| {
150        let value = entity.world_scope(|world| T::from_world(world));
151        entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
152    }
153}
154
155/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
156#[track_caller]
157pub fn remove<T: Bundle>() -> impl EntityCommand {
158    let caller = MaybeLocation::caller();
159    move |mut entity: EntityWorldMut| {
160        entity.remove_with_caller::<T>(caller);
161    }
162}
163
164/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
165/// as well as the required components for each component removed.
166#[track_caller]
167pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
168    let caller = MaybeLocation::caller();
169    move |mut entity: EntityWorldMut| {
170        entity.remove_with_requires_with_caller::<T>(caller);
171    }
172}
173
174/// An [`EntityCommand`] that removes a dynamic component from an entity.
175#[track_caller]
176pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
177    let caller = MaybeLocation::caller();
178    move |mut entity: EntityWorldMut| {
179        entity.remove_by_id_with_caller(component_id, caller);
180    }
181}
182
183/// An [`EntityCommand`] that removes all components from an entity.
184#[track_caller]
185pub fn clear() -> impl EntityCommand {
186    let caller = MaybeLocation::caller();
187    move |mut entity: EntityWorldMut| {
188        entity.clear_with_caller(caller);
189    }
190}
191
192/// An [`EntityCommand`] that removes all components from an entity,
193/// except for those in the given [`Bundle`].
194#[track_caller]
195pub fn retain<T: Bundle>() -> impl EntityCommand {
196    let caller = MaybeLocation::caller();
197    move |mut entity: EntityWorldMut| {
198        entity.retain_with_caller::<T>(caller);
199    }
200}
201
202/// An [`EntityCommand`] that despawns an entity.
203///
204/// # Note
205///
206/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
207/// that is configured to despawn descendants.
208///
209/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
210#[track_caller]
211pub fn despawn() -> impl EntityCommand {
212    let caller = MaybeLocation::caller();
213    move |entity: EntityWorldMut| {
214        entity.despawn_with_caller(caller);
215    }
216}
217
218/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
219/// listening for events of type `E` targeting an entity
220#[track_caller]
221pub fn observe<E: Event, B: Bundle, M>(
222    observer: impl IntoObserverSystem<E, B, M>,
223) -> impl EntityCommand {
224    let caller = MaybeLocation::caller();
225    move |mut entity: EntityWorldMut| {
226        entity.observe_with_caller(observer, caller);
227    }
228}
229
230/// An [`EntityCommand`] that sends a [`Trigger`](crate::observer::Trigger) targeting an entity.
231///
232/// This will run any [`Observer`](crate::observer::Observer) of the given [`Event`] watching the entity.
233#[track_caller]
234pub fn trigger(event: impl Event) -> impl EntityCommand {
235    let caller = MaybeLocation::caller();
236    move |mut entity: EntityWorldMut| {
237        let id = entity.id();
238        entity.world_scope(|world| {
239            world.trigger_targets_with_caller(event, id, caller);
240        });
241    }
242}
243
244/// An [`EntityCommand`] that clones parts of an entity onto another entity,
245/// configured through [`EntityClonerBuilder`].
246pub fn clone_with(
247    target: Entity,
248    config: impl FnOnce(&mut EntityClonerBuilder) + Send + Sync + 'static,
249) -> impl EntityCommand {
250    move |mut entity: EntityWorldMut| {
251        entity.clone_with(target, config);
252    }
253}
254
255/// An [`EntityCommand`] that clones the specified components of an entity
256/// and inserts them into another entity.
257pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
258    move |mut entity: EntityWorldMut| {
259        entity.clone_components::<B>(target);
260    }
261}
262
263/// An [`EntityCommand`] that clones the specified components of an entity
264/// and inserts them into another entity, then removes them from the original entity.
265pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
266    move |mut entity: EntityWorldMut| {
267        entity.move_components::<B>(target);
268    }
269}
270
271/// An [`EntityCommand`] that logs the components of an entity.
272pub fn log_components() -> impl EntityCommand {
273    move |entity: EntityWorldMut| {
274        let debug_infos: Vec<_> = entity
275            .world()
276            .inspect_entity(entity.id())
277            .expect("Entity existence is verified before an EntityCommand is executed")
278            .map(ComponentInfo::name)
279            .collect();
280        info!("Entity {}: {debug_infos:?}", entity.id());
281    }
282}