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