Skip to main content

bevy_ecs/system/commands/
command.rs

1//! Contains the definition of the [`Command`] 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//! [`Commands`](crate::system::Commands).
6
7use bevy_utils::prelude::DebugName;
8
9use crate::{
10    bundle::{Bundle, InsertMode, NoBundleEffect},
11    change_detection::MaybeLocation,
12    entity::Entity,
13    error::{CommandOutput, ErrorContext, ErrorHandler, Result},
14    event::Event,
15    message::{Message, Messages},
16    resource::Resource,
17    schedule::ScheduleLabel,
18    system::{IntoSystem, SystemId, SystemInput},
19    world::{FromWorld, SpawnBatchIter, World},
20};
21
22/// A [`World`] mutation.
23///
24/// Should be used with [`Commands::queue`](crate::system::Commands::queue).
25///
26/// The `Out` generic parameter is the returned "output" of the command.
27///
28/// # Usage
29///
30/// ```
31/// # use bevy_ecs::prelude::*;
32/// // Our world resource
33/// #[derive(Resource, Default)]
34/// struct Counter(u64);
35///
36/// // Our custom command
37/// struct AddToCounter(u64);
38///
39/// impl Command for AddToCounter {
40///     type Out = ();
41///
42///     fn apply(self, world: &mut World) {
43///         let mut counter = world.get_resource_or_insert_with(Counter::default);
44///         counter.0 += self.0;
45///     }
46/// }
47///
48/// fn some_system(mut commands: Commands) {
49///     commands.queue(AddToCounter(42));
50/// }
51/// ```
52pub trait Command: Send + 'static {
53    /// The return type of [`apply`](Command::apply).
54    type Out: CommandOutput;
55
56    /// Applies this command, causing it to mutate the provided `world`.
57    ///
58    /// This method is used to define what a command "does" when it is ultimately applied.
59    /// Because this method takes `self`, you can store data or settings on the type that implements this trait.
60    /// This data is set by the system or other source of the command, and then ultimately read in this method.
61    fn apply(self, world: &mut World) -> Self::Out;
62
63    /// Takes a [`Command`] that returns a Result and uses a given error handler function to convert it into
64    /// a [`Command`] that internally handles an error if it occurs and returns `()`.
65    #[inline]
66    fn handle_error_with(self, error_handler: ErrorHandler) -> impl Command<Out = ()>
67    where
68        Self: Sized,
69    {
70        move |world: &mut World| {
71            if let Some(error) = self.apply(world).to_err() {
72                error_handler(
73                    error,
74                    ErrorContext::Command {
75                        name: DebugName::type_name::<Self>(),
76                    },
77                );
78            }
79        }
80    }
81
82    /// Takes a [`Command`] that returns a Result and uses the fallback error handler function to convert it into
83    /// a [`Command`] that internally handles an error if it occurs and returns `()`.
84    #[inline]
85    fn handle_error(self) -> impl Command<Out = ()>
86    where
87        Self: Sized,
88    {
89        move |world: &mut World| {
90            if let Some(error) = self.apply(world).to_err() {
91                world.fallback_error_handler()(
92                    error,
93                    ErrorContext::Command {
94                        name: DebugName::type_name::<Self>(),
95                    },
96                );
97            }
98        }
99    }
100
101    /// Takes a [`Command`] that returns a Result and ignores any error that occurs.
102    #[inline]
103    fn ignore_error(self) -> impl Command<Out = ()>
104    where
105        Self: Sized,
106    {
107        move |world: &mut World| {
108            let _ = self.apply(world);
109        }
110    }
111}
112
113impl<F, Out> Command for F
114where
115    F: FnOnce(&mut World) -> Out + Send + 'static,
116    Out: CommandOutput,
117{
118    type Out = Out;
119
120    fn apply(self, world: &mut World) -> Out {
121        self(world)
122    }
123}
124
125/// A [`Command`] that consumes an iterator of [`Bundles`](Bundle) to spawn a series of entities.
126///
127/// This is more efficient than spawning the entities individually.
128#[track_caller]
129pub fn spawn_batch<I>(bundles_iter: I) -> impl Command
130where
131    I: IntoIterator + Send + Sync + 'static,
132    I::Item: Bundle<Effect: NoBundleEffect>,
133{
134    let caller = MaybeLocation::caller();
135    move |world: &mut World| {
136        SpawnBatchIter::new(world, bundles_iter.into_iter(), caller);
137    }
138}
139
140/// A [`Command`] that consumes an iterator to add a series of [`Bundles`](Bundle) to a set of entities.
141///
142/// If any entities do not exist in the world, this command will return a
143/// [`TryInsertBatchError`](crate::world::error::TryInsertBatchError).
144///
145/// This is more efficient than inserting the bundles individually.
146#[track_caller]
147pub fn insert_batch<I, B>(batch: I, insert_mode: InsertMode) -> impl Command
148where
149    I: IntoIterator<Item = (Entity, B)> + Send + Sync + 'static,
150    B: Bundle<Effect: NoBundleEffect>,
151{
152    let caller = MaybeLocation::caller();
153    move |world: &mut World| -> Result {
154        world.try_insert_batch_with_caller(batch, insert_mode, caller)?;
155        Ok(())
156    }
157}
158
159/// A [`Command`] that inserts a [`Resource`] into the world using a value
160/// created with the [`FromWorld`] trait.
161#[track_caller]
162pub fn init_resource<R: Resource + FromWorld>() -> impl Command {
163    move |world: &mut World| {
164        world.init_resource::<R>();
165    }
166}
167
168/// A [`Command`] that inserts a [`Resource`] into the world.
169#[track_caller]
170pub fn insert_resource<R: Resource>(resource: R) -> impl Command {
171    let caller = MaybeLocation::caller();
172    move |world: &mut World| {
173        world.insert_resource_with_caller(resource, caller);
174    }
175}
176
177/// A [`Command`] that removes a [`Resource`] from the world.
178pub fn remove_resource<R: Resource>() -> impl Command {
179    move |world: &mut World| {
180        world.remove_resource::<R>();
181    }
182}
183
184/// A [`Command`] that runs the system corresponding to the given [`SystemId`].
185pub fn run_system<O: 'static>(id: impl Into<SystemId<(), O>> + Send) -> impl Command {
186    let id = id.into();
187    move |world: &mut World| -> Result {
188        world.run_system(id)?;
189        Ok(())
190    }
191}
192
193/// A [`Command`] that runs the system corresponding to the given [`SystemId`]
194/// and provides the given input value.
195pub fn run_system_with<I>(
196    id: impl Into<SystemId<I>> + Send,
197    input: I::Inner<'static>,
198) -> impl Command
199where
200    I: SystemInput<Inner<'static>: Send> + 'static,
201{
202    let id = id.into();
203    move |world: &mut World| -> Result {
204        world.run_system_with(id, input)?;
205        Ok(())
206    }
207}
208
209/// A [`Command`] that runs the given system,
210/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
211pub fn run_system_cached<M, S>(system: S) -> impl Command
212where
213    M: 'static,
214    S: IntoSystem<(), (), M> + Send + 'static,
215{
216    move |world: &mut World| -> Result {
217        world.run_system_cached(system)?;
218        Ok(())
219    }
220}
221
222/// A [`Command`] that runs the given system with the given input value,
223/// caching its [`SystemId`] in a [`CachedSystemId`](crate::system::CachedSystemId) resource.
224///
225/// To use the supplied input, the system should have a [`SystemInput`] as the first parameter.
226pub fn run_system_cached_with<I, M, S>(system: S, input: I::Inner<'static>) -> impl Command
227where
228    I: SystemInput<Inner<'static>: Send> + Send + 'static,
229    M: 'static,
230    S: IntoSystem<I, (), M> + Send + 'static,
231{
232    move |world: &mut World| -> Result {
233        world.run_system_cached_with(system, input)?;
234        Ok(())
235    }
236}
237
238/// A [`Command`] that removes a system previously registered with
239/// [`Commands::register_system`](crate::system::Commands::register_system) or
240/// [`World::register_system`].
241pub fn unregister_system<I, O>(system_id: SystemId<I, O>) -> impl Command
242where
243    I: SystemInput + Send + 'static,
244    O: Send + 'static,
245{
246    move |world: &mut World| -> Result {
247        world.unregister_system(system_id)?;
248        Ok(())
249    }
250}
251
252/// A [`Command`] that removes a system previously registered with one of the following:
253/// - [`Commands::run_system_cached`](crate::system::Commands::run_system_cached)
254/// - [`World::run_system_cached`]
255/// - [`World::register_system_cached`]
256pub fn unregister_system_cached<I, O, M, S>(system: S) -> impl Command
257where
258    I: SystemInput + Send + 'static,
259    O: 'static,
260    M: 'static,
261    S: IntoSystem<I, O, M> + Send + 'static,
262{
263    move |world: &mut World| -> Result {
264        world.unregister_system_cached(system)?;
265        Ok(())
266    }
267}
268
269/// A [`Command`] that runs the schedule corresponding to the given [`ScheduleLabel`].
270pub fn run_schedule(label: impl ScheduleLabel) -> impl Command {
271    move |world: &mut World| -> Result {
272        world.try_run_schedule(label)?;
273        Ok(())
274    }
275}
276
277/// Triggers the given [`Event`], which will run any [`Observer`]s watching for it.
278///
279/// [`Observer`]: crate::observer::Observer
280#[track_caller]
281pub fn trigger<'a, E: Event<Trigger<'a>: Default>>(mut event: E) -> impl Command {
282    let caller = MaybeLocation::caller();
283    move |world: &mut World| {
284        world.trigger_ref_with_caller(
285            &mut event,
286            &mut <E::Trigger<'_> as Default>::default(),
287            caller,
288        );
289    }
290}
291
292/// Triggers the given [`Event`] using the given [`Trigger`], which will run any [`Observer`]s watching for it.
293///
294/// [`Trigger`]: crate::event::Trigger
295/// [`Observer`]: crate::observer::Observer
296#[track_caller]
297pub fn trigger_with<E: Event<Trigger<'static>: Send + Sync>>(
298    mut event: E,
299    mut trigger: E::Trigger<'static>,
300) -> impl Command {
301    let caller = MaybeLocation::caller();
302    move |world: &mut World| {
303        world.trigger_ref_with_caller(&mut event, &mut trigger, caller);
304    }
305}
306
307/// A [`Command`] that writes an arbitrary [`Message`].
308#[track_caller]
309pub fn write_message<M: Message>(message: M) -> impl Command {
310    let caller = MaybeLocation::caller();
311    move |world: &mut World| {
312        let mut messages = world.resource_mut::<Messages<M>>();
313        messages.write_with_caller(message, caller);
314    }
315}