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}