Path: blob/main/crates/bevy_ecs/src/system/commands/entity_command.rs
6609 views
//! Contains the definition of the [`EntityCommand`] trait,1//! as well as the blanket implementation of the trait for closures.2//!3//! It also contains functions that return closures for use with4//! [`EntityCommands`](crate::system::EntityCommands).56use alloc::vec::Vec;7use log::info;89use crate::{10bundle::{Bundle, InsertMode},11change_detection::MaybeLocation,12component::{Component, ComponentId, ComponentInfo},13entity::{Entity, EntityClonerBuilder, OptIn, OptOut},14event::EntityEvent,15relationship::RelationshipHookMode,16system::IntoObserverSystem,17world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},18};19use bevy_ptr::OwningPtr;2021/// A command which gets executed for a given [`Entity`].22///23/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).24///25/// The `Out` generic parameter is the returned "output" of the command.26///27/// # Examples28///29/// ```30/// # use std::collections::HashSet;31/// # use bevy_ecs::prelude::*;32/// use bevy_ecs::system::EntityCommand;33/// #34/// # #[derive(Component, PartialEq)]35/// # struct Name(String);36/// # impl Name {37/// # fn new(s: String) -> Self { Name(s) }38/// # fn as_str(&self) -> &str { &self.0 }39/// # }40///41/// #[derive(Resource, Default)]42/// struct Counter(i64);43///44/// /// A `Command` which names an entity based on a global counter.45/// fn count_name(mut entity: EntityWorldMut) {46/// // Get the current value of the counter, and increment it for next time.47/// let i = {48/// let mut counter = entity.resource_mut::<Counter>();49/// let i = counter.0;50/// counter.0 += 1;51/// i52/// };53/// // Name the entity after the value of the counter.54/// entity.insert(Name::new(format!("Entity #{i}")));55/// }56///57/// // App creation boilerplate omitted...58/// # let mut world = World::new();59/// # world.init_resource::<Counter>();60/// #61/// # let mut setup_schedule = Schedule::default();62/// # setup_schedule.add_systems(setup);63/// # let mut assert_schedule = Schedule::default();64/// # assert_schedule.add_systems(assert_names);65/// #66/// # setup_schedule.run(&mut world);67/// # assert_schedule.run(&mut world);68///69/// fn setup(mut commands: Commands) {70/// commands.spawn_empty().queue(count_name);71/// commands.spawn_empty().queue(count_name);72/// }73///74/// fn assert_names(named: Query<&Name>) {75/// // We use a HashSet because we do not care about the order.76/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();77/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));78/// }79/// ```80pub trait EntityCommand<Out = ()>: Send + 'static {81/// Executes this command for the given [`Entity`].82fn apply(self, entity: EntityWorldMut) -> Out;83}8485/// An error that occurs when running an [`EntityCommand`] on a specific entity.86#[derive(thiserror::Error, Debug)]87pub enum EntityCommandError<E> {88/// The entity this [`EntityCommand`] tried to run on could not be fetched.89#[error(transparent)]90EntityFetchError(#[from] EntityMutableFetchError),91/// An error that occurred while running the [`EntityCommand`].92#[error("{0}")]93CommandFailed(E),94}9596impl<Out, F> EntityCommand<Out> for F97where98F: FnOnce(EntityWorldMut) -> Out + Send + 'static,99{100fn apply(self, entity: EntityWorldMut) -> Out {101self(entity)102}103}104105/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.106#[track_caller]107pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {108let caller = MaybeLocation::caller();109move |mut entity: EntityWorldMut| {110entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);111}112}113114/// An [`EntityCommand`] that adds a dynamic component to an entity.115///116/// # Safety117///118/// - [`ComponentId`] must be from the same world as the target entity.119/// - `T` must have the same layout as the one passed during `component_id` creation.120#[track_caller]121pub unsafe fn insert_by_id<T: Send + 'static>(122component_id: ComponentId,123value: T,124mode: InsertMode,125) -> impl EntityCommand {126let caller = MaybeLocation::caller();127move |mut entity: EntityWorldMut| {128// SAFETY:129// - `component_id` safety is ensured by the caller130// - `ptr` is valid within the `make` block131OwningPtr::make(value, |ptr| unsafe {132entity.insert_by_id_with_caller(133component_id,134ptr,135mode,136caller,137RelationshipHookMode::Run,138);139});140}141}142143/// An [`EntityCommand`] that adds a component to an entity using144/// the component's [`FromWorld`] implementation.145///146/// `T::from_world` will only be invoked if the component will actually be inserted.147/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]148/// and the entity already has the component.149#[track_caller]150pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {151let caller = MaybeLocation::caller();152move |mut entity: EntityWorldMut| {153if !(mode == InsertMode::Keep && entity.contains::<T>()) {154let value = entity.world_scope(|world| T::from_world(world));155entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);156}157}158}159160/// An [`EntityCommand`] that adds a component to an entity using161/// some function that returns the component.162///163/// The function will only be invoked if the component will actually be inserted.164/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]165/// and the entity already has the component.166#[track_caller]167pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand168where169F: FnOnce() -> T + Send + 'static,170{171let caller = MaybeLocation::caller();172move |mut entity: EntityWorldMut| {173if !(mode == InsertMode::Keep && entity.contains::<T>()) {174let value = component_fn();175entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);176}177}178}179180/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.181#[track_caller]182pub fn remove<T: Bundle>() -> impl EntityCommand {183let caller = MaybeLocation::caller();184move |mut entity: EntityWorldMut| {185entity.remove_with_caller::<T>(caller);186}187}188189/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,190/// as well as the required components for each component removed.191#[track_caller]192pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {193let caller = MaybeLocation::caller();194move |mut entity: EntityWorldMut| {195entity.remove_with_requires_with_caller::<T>(caller);196}197}198199/// An [`EntityCommand`] that removes a dynamic component from an entity.200#[track_caller]201pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {202let caller = MaybeLocation::caller();203move |mut entity: EntityWorldMut| {204entity.remove_by_id_with_caller(component_id, caller);205}206}207208/// An [`EntityCommand`] that removes all components from an entity.209#[track_caller]210pub fn clear() -> impl EntityCommand {211let caller = MaybeLocation::caller();212move |mut entity: EntityWorldMut| {213entity.clear_with_caller(caller);214}215}216217/// An [`EntityCommand`] that removes all components from an entity,218/// except for those in the given [`Bundle`].219#[track_caller]220pub fn retain<T: Bundle>() -> impl EntityCommand {221let caller = MaybeLocation::caller();222move |mut entity: EntityWorldMut| {223entity.retain_with_caller::<T>(caller);224}225}226227/// An [`EntityCommand`] that despawns an entity.228///229/// # Note230///231/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)232/// that is configured to despawn descendants.233///234/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).235#[track_caller]236pub fn despawn() -> impl EntityCommand {237let caller = MaybeLocation::caller();238move |entity: EntityWorldMut| {239entity.despawn_with_caller(caller);240}241}242243/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)244/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]245/// targets this entity.246#[track_caller]247pub fn observe<E: EntityEvent, B: Bundle, M>(248observer: impl IntoObserverSystem<E, B, M>,249) -> impl EntityCommand {250let caller = MaybeLocation::caller();251move |mut entity: EntityWorldMut| {252entity.observe_with_caller(observer, caller);253}254}255256/// An [`EntityCommand`] that clones parts of an entity onto another entity,257/// configured through [`EntityClonerBuilder`].258///259/// This builder tries to clone every component from the source entity except260/// for components that were explicitly denied, for example by using the261/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.262///263/// Required components are not considered by denied components and must be264/// explicitly denied as well if desired.265pub fn clone_with_opt_out(266target: Entity,267config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,268) -> impl EntityCommand {269move |mut entity: EntityWorldMut| {270entity.clone_with_opt_out(target, config);271}272}273274/// An [`EntityCommand`] that clones parts of an entity onto another entity,275/// configured through [`EntityClonerBuilder`].276///277/// This builder tries to clone every component that was explicitly allowed278/// from the source entity, for example by using the279/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.280///281/// Required components are also cloned when the target entity does not contain them.282pub fn clone_with_opt_in(283target: Entity,284config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,285) -> impl EntityCommand {286move |mut entity: EntityWorldMut| {287entity.clone_with_opt_in(target, config);288}289}290291/// An [`EntityCommand`] that clones the specified components of an entity292/// and inserts them into another entity.293pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {294move |mut entity: EntityWorldMut| {295entity.clone_components::<B>(target);296}297}298299/// An [`EntityCommand`] moves the specified components of this entity into another entity.300///301/// Components with [`Ignore`] clone behavior will not be moved, while components that302/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.303/// All other components will be moved without any other special handling.304///305/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.306///307/// # Panics308///309/// The command will panic when applied if the target entity does not exist.310///311/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore312/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom313pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {314move |mut entity: EntityWorldMut| {315entity.move_components::<B>(target);316}317}318319/// An [`EntityCommand`] that logs the components of an entity.320pub fn log_components() -> impl EntityCommand {321move |entity: EntityWorldMut| {322let debug_infos: Vec<_> = entity323.world()324.inspect_entity(entity.id())325.expect("Entity existence is verified before an EntityCommand is executed")326.map(ComponentInfo::name)327.collect();328info!("Entity {}: {debug_infos:?}", entity.id());329}330}331332333