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}