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).
67use alloc::{string::ToString, vec::Vec};
8#[cfg(not(feature = "trace"))]
9use log::info;
10#[cfg(feature = "trace")]
11use tracing::info;
1213use crate::{
14 bundle::{Bundle, InsertMode},
15change_detection::MaybeLocation,
16 component::{Component, ComponentId},
17 entity::{Entity, EntityClonerBuilder, OptIn, OptOut},
18error::EntityCommandOutput,
19name::Name,
20observer::IntoEntityObserver,
21relationship::RelationshipHookMode,
22system::Command,
23 world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld, World},
24};
25use bevy_ptr::{move_as_ptr, OwningPtr};
2627use bevy_platform::sync::Arc;
2829/// 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).
90type Out: EntityCommandOutput;
9192/// Executes this command for the given [`Entity`].
93fn apply(self, entity: EntityWorldMut) -> Self::Out;
9495/// Passes in a specific entity to an [`EntityCommand`], resulting in a [`Command`] that
96 /// internally runs the [`EntityCommand`] on that entity.
97#[inline]
98fn with_entity(self, entity: Entity) -> impl Command99where
100Self: Sized,
101 {
102move |world: &mut World| {
103let entity = world.get_entity_mut(entity)?;
104self.apply(entity).into_result()
105 }
106 }
107}
108109/// An error that occurs when running an [`EntityCommand`] on a specific entity.
110#[derive(#[allow(unused_qualifications)]
#[automatically_derived]
impl<E> ::thiserror::__private21::Error for EntityCommandError<E> where
Self: ::core::fmt::Debug + ::core::fmt::Display {
fn source(&self)
->
::core::option::Option<&(dyn ::thiserror::__private21::Error +
'static)> {
use ::thiserror::__private21::AsDynError as _;
#[allow(deprecated)]
match self {
EntityCommandError::EntityFetchError { 0: transparent } =>
::thiserror::__private21::Error::source(transparent.as_dyn_error()),
EntityCommandError::CommandFailed { .. } =>
::core::option::Option::None,
}
}
}
#[allow(unused_qualifications)]
#[automatically_derived]
impl<E> ::core::fmt::Display for EntityCommandError<E> where
E: ::core::fmt::Display {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter)
-> ::core::fmt::Result {
use ::thiserror::__private21::AsDisplay as _;
#[allow(unused_variables, deprecated, clippy ::
used_underscore_binding)]
match self {
EntityCommandError::EntityFetchError(_0) =>
::core::fmt::Display::fmt(_0, __formatter),
EntityCommandError::CommandFailed(_0) =>
match (_0.as_display(),) {
(__display0,) =>
__formatter.write_fmt(format_args!("{0}", __display0)),
},
}
}
}
EntityCommandError<E>
#[allow(clippy :: redundant_field_names)]
fn from(source: EntityMutableFetchError) -> Self {
EntityCommandError::EntityFetchError { 0: source }
}thiserror::Error, #[automatically_derived]
impl<E: ::core::fmt::Debug> ::core::fmt::Debug for EntityCommandError<E> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::EntityFetchError(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"EntityFetchError", &__self_0),
Self::CommandFailed(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"CommandFailed", &__self_0),
}
}
}Debug)]
111pub enum EntityCommandError<E> {
112/// The entity this [`EntityCommand`] tried to run on could not be fetched.
113#[error(transparent)]
114EntityFetchError(#[from] EntityMutableFetchError),
115/// An error that occurred while running the [`EntityCommand`].
116#[error("{0}")]
117CommandFailed(E),
118}
119120impl<Out, F> EntityCommandfor F
121where
122F: FnOnce(EntityWorldMut) -> Out + Send + 'static,
123 Out: EntityCommandOutput,
124{
125type Out = Out;
126127fn apply(self, entity: EntityWorldMut) -> Self::Out {
128self(entity)
129 }
130}
131132impl<Out, F> EntityCommandfor Arc<F>
133where
134F: Fn(EntityWorldMut) -> Out + Send + Sync + ?Sized + 'static,
135 Out: EntityCommandOutput + 'static,
136{
137type Out = Out;
138139fn apply(self, entity: EntityWorldMut) -> Self::Out {
140self(entity)
141 }
142}
143144/// 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 {
147let caller = MaybeLocation::caller();
148move |mut entity: EntityWorldMut| {
149let mut bundle = ::core::mem::MaybeUninit::new(bundle);
let bundle = unsafe { ::bevy_ptr::MovingPtr::from_value(&mut bundle) };move_as_ptr!(bundle);
150entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
151 }
152}
153154/// 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 {
166let caller = MaybeLocation::caller();
167move |mut entity: EntityWorldMut| {
168// SAFETY:
169 // - `component_id` safety is ensured by the caller
170 // - `ptr` is valid within the `make` block
171OwningPtr::make(value, |ptr| unsafe {
172entity.insert_by_id_with_caller(
173component_id,
174ptr,
175mode,
176caller,
177 RelationshipHookMode::Run,
178 );
179 });
180 }
181}
182183/// 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 {
191let caller = MaybeLocation::caller();
192move |mut entity: EntityWorldMut| {
193if !(mode == InsertMode::Keep && entity.contains::<T>()) {
194let value = entity.world_scope(|world| T::from_world(world));
195let mut value = ::core::mem::MaybeUninit::new(value);
let value = unsafe { ::bevy_ptr::MovingPtr::from_value(&mut value) };move_as_ptr!(value);
196entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
197 }
198 }
199}
200201/// 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 EntityCommand209where
210F: FnOnce() -> T + Send + 'static,
211{
212let caller = MaybeLocation::caller();
213move |mut entity: EntityWorldMut| {
214if !(mode == InsertMode::Keep && entity.contains::<T>()) {
215let bundle = component_fn();
216let mut bundle = ::core::mem::MaybeUninit::new(bundle);
let bundle = unsafe { ::bevy_ptr::MovingPtr::from_value(&mut bundle) };move_as_ptr!(bundle);
217entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
218 }
219 }
220}
221222/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
223#[track_caller]
224pub fn remove<T: Bundle>() -> impl EntityCommand {
225let caller = MaybeLocation::caller();
226move |mut entity: EntityWorldMut| {
227entity.remove_with_caller::<T>(caller);
228 }
229}
230231/// 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 {
235let caller = MaybeLocation::caller();
236move |mut entity: EntityWorldMut| {
237entity.remove_with_requires_with_caller::<T>(caller);
238 }
239}
240241/// An [`EntityCommand`] that removes a dynamic component from an entity.
242#[track_caller]
243pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
244let caller = MaybeLocation::caller();
245move |mut entity: EntityWorldMut| {
246entity.remove_by_id_with_caller(component_id, caller);
247 }
248}
249250/// An [`EntityCommand`] that removes all components from an entity.
251#[track_caller]
252pub fn clear() -> impl EntityCommand {
253let caller = MaybeLocation::caller();
254move |mut entity: EntityWorldMut| {
255entity.clear_with_caller(caller);
256 }
257}
258259/// 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 {
263let caller = MaybeLocation::caller();
264move |mut entity: EntityWorldMut| {
265entity.retain_with_caller::<T>(caller);
266 }
267}
268269/// 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 {
279let caller = MaybeLocation::caller();
280move |entity: EntityWorldMut| {
281entity.despawn_with_caller(caller);
282 }
283}
284285/// 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 {
294let caller = MaybeLocation::caller();
295move |mut entity: EntityWorldMut| {
296entity.observe_with_caller(observer, caller);
297 }
298}
299300/// 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 {
313move |mut entity: EntityWorldMut| {
314entity.clone_with_opt_out(target, config);
315 }
316}
317318/// 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 {
330move |mut entity: EntityWorldMut| {
331entity.clone_with_opt_in(target, config);
332 }
333}
334335/// 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 {
338move |mut entity: EntityWorldMut| {
339entity.clone_components::<B>(target);
340 }
341}
342343/// 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 {
358move |mut entity: EntityWorldMut| {
359entity.move_components::<B>(target);
360 }
361}
362363/// An [`EntityCommand`] that logs the components of an entity.
364pub fn log_components() -> impl EntityCommand {
365move |entity: EntityWorldMut| {
366let name = entity.get::<Name>().map(ToString::to_string);
367let id = entity.id();
368let mut components: Vec<_> = entity369 .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();
374components.sort();
375376#[cfg(not(feature = "debug"))]
377{
378let component_count = components.len();
379#[cfg(feature = "trace")]
380{
381if let Some(name) = name {
382info!(id=?id, name=?name, ?component_count, "log_components. Enable the `debug` feature to log component names.");
383 } else {
384info!(id=?id, ?component_count, "log_components. Enable the `debug` feature to log component names.");
385 }
386 }
387#[cfg(not(feature = "trace"))]
388{
389let name = name
390 .map(|name| alloc::format!(" ({name})"))
391 .unwrap_or_default();
392info!("Entity {id}{name}: {component_count} components. Enable the `debug` feature to log component names.");
393 }
394 }
395396#[cfg(feature = "debug")]
397{
398#[cfg(feature = "trace")]
399{
400if let Some(name) = name {
401{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/system/commands/entity_command.rs:401",
"bevy_ecs::system::commands::entity_command",
::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("src/system/commands/entity_command.rs"),
::tracing_core::__macro_support::Option::Some(401u32),
::tracing_core::__macro_support::Option::Some("bevy_ecs::system::commands::entity_command"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("id")
}> =
::tracing::__macro_support::FieldName::new("id");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("name")
}> =
::tracing::__macro_support::FieldName::new("name");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("components")
}> =
::tracing::__macro_support::FieldName::new("components");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("log_components")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&id)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&name)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&components)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!(id=?id, name=?name, ?components, "log_components");
402 } else {
403{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/system/commands/entity_command.rs:403",
"bevy_ecs::system::commands::entity_command",
::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("src/system/commands/entity_command.rs"),
::tracing_core::__macro_support::Option::Some(403u32),
::tracing_core::__macro_support::Option::Some("bevy_ecs::system::commands::entity_command"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("id")
}> =
::tracing::__macro_support::FieldName::new("id");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("components")
}> =
::tracing::__macro_support::FieldName::new("components");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("log_components")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&id)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&components)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!(id=?id, ?components, "log_components");
404 }
405 }
406#[cfg(not(feature = "trace"))]
407{
408let name = name
409 .map(|name| alloc::format!(" ({name})"))
410 .unwrap_or_default();
411info!("Entity {id}{name}: {components:?}");
412 }
413 }
414 }
415}