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