Skip to main content

bevy_ecs/system/
system.rs

1#![expect(
2    clippy::module_inception,
3    reason = "This instance of module inception is being discussed; see #17353."
4)]
5use bevy_utils::prelude::DebugName;
6use bitflags::bitflags;
7use core::fmt::{Debug, Display};
8use log::warn;
9
10use crate::{
11    change_detection::{CheckChangeTicks, Tick},
12    error::BevyError,
13    query::FilteredAccessSet,
14    schedule::InternedSystemSet,
15    system::{input::SystemInput, SystemIn},
16    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
17};
18
19use alloc::{boxed::Box, vec::Vec};
20use core::any::{Any, TypeId};
21
22use super::{IntoSystem, SystemParamValidationError};
23
24bitflags! {
25    /// Bitflags representing system states and requirements.
26    #[derive(Clone, Copy, PartialEq, Eq, Hash)]
27    pub struct SystemStateFlags: u8 {
28        /// Set if system cannot be sent across threads
29        const NON_SEND       = 1 << 0;
30        /// Set if system requires exclusive World access
31        const EXCLUSIVE      = 1 << 1;
32        /// Set if system has deferred buffers.
33        const DEFERRED       = 1 << 2;
34    }
35}
36/// An ECS system that can be added to a [`Schedule`](crate::schedule::Schedule)
37///
38/// Systems are functions with all arguments implementing
39/// [`SystemParam`](crate::system::SystemParam).
40///
41/// Systems are added to an application using `App::add_systems(Update, my_system)`
42/// or similar methods, and will generally run once per pass of the main loop.
43///
44/// Systems are executed in parallel, in opportunistic order; data access is managed automatically.
45/// It's possible to specify explicit execution order between specific systems,
46/// see [`IntoScheduleConfigs`](crate::schedule::IntoScheduleConfigs).
47#[diagnostic::on_unimplemented(message = "`{Self}` is not a system", label = "invalid system")]
48pub trait System: Send + Sync + 'static {
49    /// The system's input.
50    type In: SystemInput;
51    /// The system's output.
52    type Out;
53
54    /// Returns the system's name.
55    fn name(&self) -> DebugName;
56    /// Returns the [`TypeId`] of the underlying system type.
57    #[inline]
58    fn system_type(&self) -> TypeId {
59        TypeId::of::<Self>()
60    }
61
62    /// Returns the [`TypeId`] of the underlying system type.
63    ///
64    /// Use [`system_type`](System::system_type) instead.
65    #[deprecated(
66        since = "0.19.0",
67        note = "Use `system_type` instead. This method shadows `Any::type_id` and will be removed in a future release."
68    )]
69    #[inline]
70    fn type_id(&self) -> TypeId {
71        self.system_type()
72    }
73
74    /// Returns the [`SystemStateFlags`] of the system.
75    fn flags(&self) -> SystemStateFlags;
76
77    /// Returns true if the system is [`Send`].
78    #[inline]
79    fn is_send(&self) -> bool {
80        !self.flags().intersects(SystemStateFlags::NON_SEND)
81    }
82
83    /// Returns true if the system must be run exclusively.
84    #[inline]
85    fn is_exclusive(&self) -> bool {
86        self.flags().intersects(SystemStateFlags::EXCLUSIVE)
87    }
88
89    /// Returns true if system has deferred buffers.
90    #[inline]
91    fn has_deferred(&self) -> bool {
92        self.flags().intersects(SystemStateFlags::DEFERRED)
93    }
94
95    /// Runs the system with the given input in the world. Unlike [`System::run`], this function
96    /// can be called in parallel with other systems and may break Rust's aliasing rules
97    /// if used incorrectly, making it unsafe to call.
98    ///
99    /// Unlike [`System::run`], this will not apply deferred parameters, which must be independently
100    /// applied by calling [`System::apply_deferred`] at later point in time.
101    ///
102    /// # Safety
103    ///
104    /// - The caller must ensure that [`world`](UnsafeWorldCell) has permission to access any world data
105    ///   registered in the access returned from [`System::initialize`]. There must be no conflicting
106    ///   simultaneous accesses while the system is running.
107    /// - If [`System::is_exclusive`] returns `true`, then it must be valid to call
108    ///   [`UnsafeWorldCell::world_mut`] on `world`.
109    unsafe fn run_unsafe(
110        &mut self,
111        input: SystemIn<'_, Self>,
112        world: UnsafeWorldCell,
113    ) -> Result<Self::Out, RunSystemError>;
114
115    /// Refresh the inner pointer based on the latest hot patch jump table
116    #[cfg(feature = "hotpatching")]
117    fn refresh_hotpatch(&mut self);
118
119    /// Runs the system with the given input in the world.
120    ///
121    /// For [read-only](ReadOnlySystem) systems, see [`run_readonly`], which can be called using `&World`.
122    ///
123    /// Unlike [`System::run_unsafe`], this will apply deferred parameters *immediately*.
124    ///
125    /// [`run_readonly`]: ReadOnlySystem::run_readonly
126    fn run(
127        &mut self,
128        input: SystemIn<'_, Self>,
129        world: &mut World,
130    ) -> Result<Self::Out, RunSystemError> {
131        let ret = self.run_without_applying_deferred(input, world)?;
132        self.apply_deferred(world);
133        Ok(ret)
134    }
135
136    /// Runs the system with the given input in the world.
137    ///
138    /// [`run_readonly`]: ReadOnlySystem::run_readonly
139    fn run_without_applying_deferred(
140        &mut self,
141        input: SystemIn<'_, Self>,
142        world: &mut World,
143    ) -> Result<Self::Out, RunSystemError> {
144        let world_cell = world.as_unsafe_world_cell();
145        // SAFETY:
146        // - We have exclusive access to the entire world.
147        unsafe { self.run_unsafe(input, world_cell) }
148    }
149
150    /// Applies any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers) of this system to the world.
151    ///
152    /// This is where [`Commands`](crate::system::Commands) get applied.
153    fn apply_deferred(&mut self, world: &mut World);
154
155    /// Enqueues any [`Deferred`](crate::system::Deferred) system parameters (or other system buffers)
156    /// of this system into the world's command buffer.
157    fn queue_deferred(&mut self, world: DeferredWorld);
158
159    /// Initialize the system.
160    ///
161    /// Returns a [`FilteredAccessSet`] with the access required to run the system.
162    fn initialize(&mut self, _world: &mut World) -> FilteredAccessSet;
163
164    /// Checks any [`Tick`]s stored on this system and wraps their value if they get too old.
165    ///
166    /// This method must be called periodically to ensure that change detection behaves correctly.
167    /// When using bevy's default configuration, this will be called for you as needed.
168    fn check_change_tick(&mut self, check: CheckChangeTicks);
169
170    /// Returns the system's default [system sets](crate::schedule::SystemSet).
171    ///
172    /// Each system will create a default system set that contains the system.
173    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
174        Vec::new()
175    }
176
177    /// Gets the tick indicating the last time this system ran.
178    fn get_last_run(&self) -> Tick;
179
180    /// Overwrites the tick indicating the last time this system ran.
181    ///
182    /// # Warning
183    /// This is a complex and error-prone operation, that can have unexpected consequences on any system relying on this code.
184    /// However, it can be an essential escape hatch when, for example,
185    /// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
186    fn set_last_run(&mut self, last_run: Tick);
187}
188
189/// [`System`] types that do not modify the [`World`] when run.
190/// This is implemented for any systems whose parameters all implement [`ReadOnlySystemParam`].
191///
192/// Note that systems which perform [deferred](System::apply_deferred) mutations (such as with [`Commands`])
193/// may implement this trait.
194///
195/// [`ReadOnlySystemParam`]: crate::system::ReadOnlySystemParam
196/// [`Commands`]: crate::system::Commands
197///
198/// # Safety
199///
200/// This must only be implemented for system types which do not mutate the `World`
201/// when [`System::run_unsafe`] is called.
202#[diagnostic::on_unimplemented(
203    message = "`{Self}` is not a read-only system",
204    label = "invalid read-only system"
205)]
206pub unsafe trait ReadOnlySystem: System {
207    /// Runs this system with the given input in the world.
208    ///
209    /// Unlike [`System::run`], this can be called with a shared reference to the world,
210    /// since this system is known not to modify the world.
211    fn run_readonly(
212        &mut self,
213        input: SystemIn<'_, Self>,
214        world: &World,
215    ) -> Result<Self::Out, RunSystemError> {
216        let world = world.as_unsafe_world_cell_readonly();
217        // SAFETY:
218        // - We have read-only access to the entire world.
219        unsafe { self.run_unsafe(input, world) }
220    }
221}
222
223/// A convenience type alias for a boxed [`System`] trait object.
224pub type BoxedSystem<In = (), Out = ()> = Box<dyn System<In = In, Out = Out>>;
225
226/// A convenience type alias for a boxed [`ReadOnlySystem`] trait object.
227pub type BoxedReadOnlySystem<In = (), Out = ()> = Box<dyn ReadOnlySystem<In = In, Out = Out>>;
228
229pub(crate) fn check_system_change_tick(
230    last_run: &mut Tick,
231    check: CheckChangeTicks,
232    system_name: DebugName,
233) {
234    if last_run.check_tick(check) {
235        let age = check.present_tick().relative_to(*last_run).get();
236        warn!(
237            "System '{system_name}' has not run for {age} ticks. \
238            Changes older than {} ticks will not be detected.",
239            Tick::MAX.get() - 1,
240        );
241    }
242}
243
244impl<In, Out> Debug for dyn System<In = In, Out = Out>
245where
246    In: SystemInput + 'static,
247    Out: 'static,
248{
249    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
250        f.debug_struct("System")
251            .field("name", &self.name())
252            .field("is_exclusive", &self.is_exclusive())
253            .field("is_send", &self.is_send())
254            .finish_non_exhaustive()
255    }
256}
257
258/// Trait used to run a system immediately on a [`World`].
259///
260/// # Warning
261/// This function is not an efficient method of running systems and it's meant to be used as a utility
262/// for testing and/or diagnostics.
263///
264/// Systems called through [`run_system_once`](RunSystemOnce::run_system_once) do not hold onto any state,
265/// as they are created and destroyed every time [`run_system_once`](RunSystemOnce::run_system_once) is called.
266/// Practically, this means that [`Local`](crate::system::Local) variables are
267/// reset on every run and change detection does not work.
268///
269/// ```
270/// # use bevy_ecs::prelude::*;
271/// # use bevy_ecs::system::RunSystemOnce;
272/// #[derive(Resource, Default)]
273/// struct Counter(u8);
274///
275/// fn increment(mut counter: Local<Counter>) {
276///    counter.0 += 1;
277///    println!("{}", counter.0);
278/// }
279///
280/// let mut world = World::default();
281/// world.run_system_once(increment); // prints 1
282/// world.run_system_once(increment); // still prints 1
283/// ```
284///
285/// If you do need systems to hold onto state between runs, use [`World::run_system_cached`](World::run_system_cached)
286/// or [`World::run_system`](World::run_system).
287///
288/// # Usage
289/// Typically, to test a system, or to extract specific diagnostics information from a world,
290/// you'd need a [`Schedule`](crate::schedule::Schedule) to run the system. This can create redundant boilerplate code
291/// when writing tests or trying to quickly iterate on debug specific systems.
292///
293/// For these situations, this function can be useful because it allows you to execute a system
294/// immediately with some custom input and retrieve its output without requiring the necessary boilerplate.
295///
296/// # Examples
297///
298/// ## Immediate Command Execution
299///
300/// This usage is helpful when trying to test systems or functions that operate on [`Commands`](crate::system::Commands):
301/// ```
302/// # use bevy_ecs::prelude::*;
303/// # use bevy_ecs::system::RunSystemOnce;
304/// let mut world = World::default();
305/// let entity = world.run_system_once(|mut commands: Commands| {
306///     commands.spawn_empty().id()
307/// }).unwrap();
308/// # assert!(world.get_entity(entity).is_ok());
309/// ```
310///
311/// ## Immediate Queries
312///
313/// This usage is helpful when trying to run an arbitrary query on a world for testing or debugging purposes:
314/// ```
315/// # use bevy_ecs::prelude::*;
316/// # use bevy_ecs::system::RunSystemOnce;
317///
318/// #[derive(Component)]
319/// struct T(usize);
320///
321/// let mut world = World::default();
322/// world.spawn(T(0));
323/// world.spawn(T(1));
324/// world.spawn(T(1));
325/// let count = world.run_system_once(|query: Query<&T>| {
326///     query.iter().filter(|t| t.0 == 1).count()
327/// }).unwrap();
328///
329/// # assert_eq!(count, 2);
330/// ```
331///
332/// Note that instead of closures you can also pass in regular functions as systems:
333///
334/// ```
335/// # use bevy_ecs::prelude::*;
336/// # use bevy_ecs::system::RunSystemOnce;
337///
338/// #[derive(Component)]
339/// struct T(usize);
340///
341/// fn count(query: Query<&T>) -> usize {
342///     query.iter().filter(|t| t.0 == 1).count()
343/// }
344///
345/// let mut world = World::default();
346/// world.spawn(T(0));
347/// world.spawn(T(1));
348/// world.spawn(T(1));
349/// let count = world.run_system_once(count).unwrap();
350///
351/// # assert_eq!(count, 2);
352/// ```
353pub trait RunSystemOnce: Sized {
354    /// Tries to run a system and apply its deferred parameters.
355    fn run_system_once<T, Out, Marker>(self, system: T) -> Result<Out, RunSystemError>
356    where
357        T: IntoSystem<(), Out, Marker>,
358    {
359        self.run_system_once_with(system, ())
360    }
361
362    /// Tries to run a system with given input and apply deferred parameters.
363    fn run_system_once_with<T, In, Out, Marker>(
364        self,
365        system: T,
366        input: SystemIn<'_, T::System>,
367    ) -> Result<Out, RunSystemError>
368    where
369        T: IntoSystem<In, Out, Marker>,
370        In: SystemInput;
371}
372
373impl RunSystemOnce for &mut World {
374    fn run_system_once_with<T, In, Out, Marker>(
375        self,
376        system: T,
377        input: SystemIn<'_, T::System>,
378    ) -> Result<Out, RunSystemError>
379    where
380        T: IntoSystem<In, Out, Marker>,
381        In: SystemInput,
382    {
383        let mut system: T::System = IntoSystem::into_system(system);
384        system.initialize(self);
385        system.run(input, self)
386    }
387}
388
389/// Running system failed.
390#[derive(Debug)]
391pub enum RunSystemError {
392    /// System could not be run due to parameters that failed validation.
393    /// This is not considered an error.
394    Skipped(SystemParamValidationError),
395    /// System returned an error or failed required parameter validation.
396    Failed(BevyError),
397}
398
399impl Display for RunSystemError {
400    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
401        match self {
402            Self::Skipped(err) => write!(
403                f,
404                "System did not run due to failed parameter validation: {err}"
405            ),
406            Self::Failed(err) => write!(f, "{err}"),
407        }
408    }
409}
410
411impl<E: Any> From<E> for RunSystemError
412where
413    BevyError: From<E>,
414{
415    fn from(mut value: E) -> Self {
416        // Specialize the impl so that a skipped `SystemParamValidationError`
417        // is converted to `Skipped` instead of `Failed`.
418        // Note that the `downcast_mut` check is based on the static type,
419        // and can be optimized out after monomorphization.
420        let any: &mut dyn Any = &mut value;
421        if let Some(err) = any.downcast_mut::<SystemParamValidationError>()
422            && err.skipped
423        {
424            return Self::Skipped(core::mem::replace(err, SystemParamValidationError::EMPTY));
425        }
426        Self::Failed(From::from(value))
427    }
428}
429
430#[cfg(test)]
431mod tests {
432    use super::*;
433    use crate::prelude::*;
434    use alloc::string::ToString;
435
436    #[test]
437    fn run_system_once() {
438        #[derive(Resource)]
439        struct T(usize);
440
441        fn system(In(n): In<usize>, mut commands: Commands) -> usize {
442            commands.insert_resource(T(n));
443            n + 1
444        }
445
446        let mut world = World::default();
447        let n = world.run_system_once_with(system, 1).unwrap();
448        assert_eq!(n, 2);
449        assert_eq!(world.resource::<T>().0, 1);
450    }
451
452    #[derive(Resource, Default, PartialEq, Debug)]
453    struct Counter(u8);
454
455    fn count_up(mut counter: ResMut<Counter>) {
456        counter.0 += 1;
457    }
458
459    #[test]
460    fn run_two_systems() {
461        let mut world = World::new();
462        world.init_resource::<Counter>();
463        assert_eq!(*world.resource::<Counter>(), Counter(0));
464        world.run_system_once(count_up).unwrap();
465        assert_eq!(*world.resource::<Counter>(), Counter(1));
466        world.run_system_once(count_up).unwrap();
467        assert_eq!(*world.resource::<Counter>(), Counter(2));
468    }
469
470    #[derive(Component)]
471    struct A;
472
473    fn spawn_entity(mut commands: Commands) {
474        commands.spawn(A);
475    }
476
477    #[test]
478    fn command_processing() {
479        let mut world = World::new();
480        assert_eq!(world.query::<&A>().query(&world).count(), 0);
481        world.run_system_once(spawn_entity).unwrap();
482        assert_eq!(world.query::<&A>().query(&world).count(), 1);
483    }
484
485    #[test]
486    fn non_send() {
487        fn non_send_count_down(mut ns: NonSendMut<Counter>) {
488            ns.0 -= 1;
489        }
490
491        let mut world = World::new();
492        world.insert_non_send(Counter(10));
493        assert_eq!(*world.non_send::<Counter>(), Counter(10));
494        world.run_system_once(non_send_count_down).unwrap();
495        assert_eq!(*world.non_send::<Counter>(), Counter(9));
496    }
497
498    #[test]
499    fn run_system_once_invalid_params() {
500        #[derive(Resource)]
501        struct T;
502
503        fn system(_: Res<T>) {}
504
505        let mut world = World::default();
506        // This fails because `T` has not been added to the world yet.
507        let result = world.run_system_once(system);
508
509        assert!(matches!(result, Err(RunSystemError::Failed { .. })));
510
511        let expected = "Resource does not exist";
512        let actual = result.unwrap_err().to_string();
513
514        assert!(
515            actual.contains(expected),
516            "Expected error message to contain `{}` but got `{}`",
517            expected,
518            actual
519        );
520    }
521}