Thanks to visit codestin.com
Credit goes to docs.rs

bevy_ecs/system/
combinator.rs

1use alloc::{format, vec::Vec};
2use bevy_utils::prelude::DebugName;
3use core::marker::PhantomData;
4
5use crate::{
6    component::{CheckChangeTicks, Tick},
7    prelude::World,
8    query::FilteredAccessSet,
9    schedule::InternedSystemSet,
10    system::{input::SystemInput, SystemIn, SystemParamValidationError},
11    world::unsafe_world_cell::UnsafeWorldCell,
12};
13
14use super::{IntoSystem, ReadOnlySystem, RunSystemError, System};
15
16/// Customizes the behavior of a [`CombinatorSystem`].
17///
18/// # Examples
19///
20/// ```
21/// use bevy_ecs::prelude::*;
22/// use bevy_ecs::system::{CombinatorSystem, Combine, RunSystemError};
23///
24/// // A system combinator that performs an exclusive-or (XOR)
25/// // operation on the output of two systems.
26/// pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
27///
28/// // This struct is used to customize the behavior of our combinator.
29/// pub struct XorMarker;
30///
31/// impl<A, B> Combine<A, B> for XorMarker
32/// where
33///     A: System<In = (), Out = bool>,
34///     B: System<In = (), Out = bool>,
35/// {
36///     type In = ();
37///     type Out = bool;
38///
39///     fn combine<T>(
40///         _input: Self::In,
41///         data: &mut T,
42///         a: impl FnOnce(A::In, &mut T) -> Result<A::Out, RunSystemError>,
43///         b: impl FnOnce(B::In, &mut T) -> Result<B::Out, RunSystemError>,
44///     ) -> Result<Self::Out, RunSystemError> {
45///         Ok(a((), data)? ^ b((), data)?)
46///     }
47/// }
48///
49/// # #[derive(Resource, PartialEq, Eq)] struct A(u32);
50/// # #[derive(Resource, PartialEq, Eq)] struct B(u32);
51/// # #[derive(Resource, Default)] struct RanFlag(bool);
52/// # let mut world = World::new();
53/// # world.init_resource::<RanFlag>();
54/// #
55/// # let mut app = Schedule::default();
56/// app.add_systems(my_system.run_if(Xor::new(
57///     IntoSystem::into_system(resource_equals(A(1))),
58///     IntoSystem::into_system(resource_equals(B(1))),
59///     // The name of the combined system.
60///     "a ^ b".into(),
61/// )));
62/// # fn my_system(mut flag: ResMut<RanFlag>) { flag.0 = true; }
63/// #
64/// # world.insert_resource(A(0));
65/// # world.insert_resource(B(0));
66/// # app.run(&mut world);
67/// # // Neither condition passes, so the system does not run.
68/// # assert!(!world.resource::<RanFlag>().0);
69/// #
70/// # world.insert_resource(A(1));
71/// # app.run(&mut world);
72/// # // Only the first condition passes, so the system runs.
73/// # assert!(world.resource::<RanFlag>().0);
74/// # world.resource_mut::<RanFlag>().0 = false;
75/// #
76/// # world.insert_resource(B(1));
77/// # app.run(&mut world);
78/// # // Both conditions pass, so the system does not run.
79/// # assert!(!world.resource::<RanFlag>().0);
80/// #
81/// # world.insert_resource(A(0));
82/// # app.run(&mut world);
83/// # // Only the second condition passes, so the system runs.
84/// # assert!(world.resource::<RanFlag>().0);
85/// # world.resource_mut::<RanFlag>().0 = false;
86/// ```
87#[diagnostic::on_unimplemented(
88    message = "`{Self}` can not combine systems `{A}` and `{B}`",
89    label = "invalid system combination",
90    note = "the inputs and outputs of `{A}` and `{B}` are not compatible with this combiner"
91)]
92pub trait Combine<A: System, B: System> {
93    /// The [input](System::In) type for a [`CombinatorSystem`].
94    type In: SystemInput;
95
96    /// The [output](System::Out) type for a [`CombinatorSystem`].
97    type Out;
98
99    /// When used in a [`CombinatorSystem`], this function customizes how
100    /// the two composite systems are invoked and their outputs are combined.
101    ///
102    /// See the trait-level docs for [`Combine`] for an example implementation.
103    fn combine<T>(
104        input: <Self::In as SystemInput>::Inner<'_>,
105        data: &mut T,
106        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
107        b: impl FnOnce(SystemIn<'_, B>, &mut T) -> Result<B::Out, RunSystemError>,
108    ) -> Result<Self::Out, RunSystemError>;
109}
110
111/// A [`System`] defined by combining two other systems.
112/// The behavior of this combinator is specified by implementing the [`Combine`] trait.
113/// For a full usage example, see the docs for [`Combine`].
114pub struct CombinatorSystem<Func, A, B> {
115    _marker: PhantomData<fn() -> Func>,
116    a: A,
117    b: B,
118    name: DebugName,
119}
120
121impl<Func, A, B> CombinatorSystem<Func, A, B> {
122    /// Creates a new system that combines two inner systems.
123    ///
124    /// The returned system will only be usable if `Func` implements [`Combine<A, B>`].
125    pub fn new(a: A, b: B, name: DebugName) -> Self {
126        Self {
127            _marker: PhantomData,
128            a,
129            b,
130            name,
131        }
132    }
133}
134
135impl<A, B, Func> System for CombinatorSystem<Func, A, B>
136where
137    Func: Combine<A, B> + 'static,
138    A: System,
139    B: System,
140{
141    type In = Func::In;
142    type Out = Func::Out;
143
144    fn name(&self) -> DebugName {
145        self.name.clone()
146    }
147
148    #[inline]
149    fn flags(&self) -> super::SystemStateFlags {
150        self.a.flags() | self.b.flags()
151    }
152
153    unsafe fn run_unsafe(
154        &mut self,
155        input: SystemIn<'_, Self>,
156        world: UnsafeWorldCell,
157    ) -> Result<Self::Out, RunSystemError> {
158        struct PrivateUnsafeWorldCell<'w>(UnsafeWorldCell<'w>);
159
160        Func::combine(
161            input,
162            &mut PrivateUnsafeWorldCell(world),
163            // SAFETY: The world accesses for both underlying systems have been registered,
164            // so the caller will guarantee that no other systems will conflict with `a` or `b`.
165            // If either system has `is_exclusive()`, then the combined system also has `is_exclusive`.
166            // Since we require a `combine` to pass in a mutable reference to `world` and that's a private type
167            // passed to a function as an unbound non-'static generic argument, they can never be called in parallel
168            // or re-entrantly because that would require forging another instance of `PrivateUnsafeWorldCell`.
169            // This means that the world accesses in the two closures will not conflict with each other.
170            |input, world| unsafe { self.a.run_unsafe(input, world.0) },
171            // `Self::validate_param_unsafe` already validated the first system,
172            // but we still need to validate the second system once the first one runs.
173            // SAFETY: See the comment above.
174            |input, world| unsafe {
175                self.b.validate_param_unsafe(world.0)?;
176                self.b.run_unsafe(input, world.0)
177            },
178        )
179    }
180
181    #[cfg(feature = "hotpatching")]
182    #[inline]
183    fn refresh_hotpatch(&mut self) {
184        self.a.refresh_hotpatch();
185        self.b.refresh_hotpatch();
186    }
187
188    #[inline]
189    fn apply_deferred(&mut self, world: &mut World) {
190        self.a.apply_deferred(world);
191        self.b.apply_deferred(world);
192    }
193
194    #[inline]
195    fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) {
196        self.a.queue_deferred(world.reborrow());
197        self.b.queue_deferred(world);
198    }
199
200    #[inline]
201    unsafe fn validate_param_unsafe(
202        &mut self,
203        world: UnsafeWorldCell,
204    ) -> Result<(), SystemParamValidationError> {
205        // We only validate parameters for the first system,
206        // since it may make changes to the world that affect
207        // whether the second system has valid parameters.
208        // The second system will be validated in `Self::run_unsafe`.
209        // SAFETY: Delegate to other `System` implementations.
210        unsafe { self.a.validate_param_unsafe(world) }
211    }
212
213    fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
214        let mut a_access = self.a.initialize(world);
215        let b_access = self.b.initialize(world);
216        a_access.extend(b_access);
217        a_access
218    }
219
220    fn check_change_tick(&mut self, check: CheckChangeTicks) {
221        self.a.check_change_tick(check);
222        self.b.check_change_tick(check);
223    }
224
225    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
226        let mut default_sets = self.a.default_system_sets();
227        default_sets.append(&mut self.b.default_system_sets());
228        default_sets
229    }
230
231    fn get_last_run(&self) -> Tick {
232        self.a.get_last_run()
233    }
234
235    fn set_last_run(&mut self, last_run: Tick) {
236        self.a.set_last_run(last_run);
237        self.b.set_last_run(last_run);
238    }
239}
240
241/// SAFETY: Both systems are read-only, so any system created by combining them will only read from the world.
242unsafe impl<Func, A, B> ReadOnlySystem for CombinatorSystem<Func, A, B>
243where
244    Func: Combine<A, B> + 'static,
245    A: ReadOnlySystem,
246    B: ReadOnlySystem,
247{
248}
249
250impl<Func, A, B> Clone for CombinatorSystem<Func, A, B>
251where
252    A: Clone,
253    B: Clone,
254{
255    /// Clone the combined system. The cloned instance must be `.initialize()`d before it can run.
256    fn clone(&self) -> Self {
257        CombinatorSystem::new(self.a.clone(), self.b.clone(), self.name.clone())
258    }
259}
260
261/// An [`IntoSystem`] creating an instance of [`PipeSystem`].
262#[derive(Clone)]
263pub struct IntoPipeSystem<A, B> {
264    a: A,
265    b: B,
266}
267
268impl<A, B> IntoPipeSystem<A, B> {
269    /// Creates a new [`IntoSystem`] that pipes two inner systems.
270    pub const fn new(a: A, b: B) -> Self {
271        Self { a, b }
272    }
273}
274
275#[doc(hidden)]
276pub struct IsPipeSystemMarker;
277
278impl<A, B, IA, OA, IB, OB, MA, MB> IntoSystem<IA, OB, (IsPipeSystemMarker, OA, IB, MA, MB)>
279    for IntoPipeSystem<A, B>
280where
281    IA: SystemInput,
282    A: IntoSystem<IA, OA, MA>,
283    B: IntoSystem<IB, OB, MB>,
284    for<'a> IB: SystemInput<Inner<'a> = OA>,
285{
286    type System = PipeSystem<A::System, B::System>;
287
288    fn into_system(this: Self) -> Self::System {
289        let system_a = IntoSystem::into_system(this.a);
290        let system_b = IntoSystem::into_system(this.b);
291        let name = format!("Pipe({}, {})", system_a.name(), system_b.name());
292        PipeSystem::new(system_a, system_b, DebugName::owned(name))
293    }
294}
295
296/// A [`System`] created by piping the output of the first system into the input of the second.
297///
298/// This can be repeated indefinitely, but system pipes cannot branch: the output is consumed by the receiving system.
299///
300/// Given two systems `A` and `B`, A may be piped into `B` as `A.pipe(B)` if the output type of `A` is
301/// equal to the input type of `B`.
302///
303/// Note that for [`FunctionSystem`](crate::system::FunctionSystem)s the output is the return value
304/// of the function and the input is the first [`SystemParam`](crate::system::SystemParam) if it is
305/// tagged with [`In`](crate::system::In) or `()` if the function has no designated input parameter.
306///
307/// # Examples
308///
309/// ```
310/// use std::num::ParseIntError;
311///
312/// use bevy_ecs::prelude::*;
313///
314/// fn main() {
315///     let mut world = World::default();
316///     world.insert_resource(Message("42".to_string()));
317///
318///     // pipe the `parse_message_system`'s output into the `filter_system`s input
319///     let mut piped_system = IntoSystem::into_system(parse_message_system.pipe(filter_system));
320///     piped_system.initialize(&mut world);
321///     assert_eq!(piped_system.run((), &mut world).unwrap(), Some(42));
322/// }
323///
324/// #[derive(Resource)]
325/// struct Message(String);
326///
327/// fn parse_message_system(message: Res<Message>) -> Result<usize, ParseIntError> {
328///     message.0.parse::<usize>()
329/// }
330///
331/// fn filter_system(In(result): In<Result<usize, ParseIntError>>) -> Option<usize> {
332///     result.ok().filter(|&n| n < 100)
333/// }
334/// ```
335pub struct PipeSystem<A, B> {
336    a: A,
337    b: B,
338    name: DebugName,
339}
340
341impl<A, B> PipeSystem<A, B>
342where
343    A: System,
344    B: System,
345    for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
346{
347    /// Creates a new system that pipes two inner systems.
348    pub fn new(a: A, b: B, name: DebugName) -> Self {
349        Self { a, b, name }
350    }
351}
352
353impl<A, B> System for PipeSystem<A, B>
354where
355    A: System,
356    B: System,
357    for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
358{
359    type In = A::In;
360    type Out = B::Out;
361
362    fn name(&self) -> DebugName {
363        self.name.clone()
364    }
365
366    #[inline]
367    fn flags(&self) -> super::SystemStateFlags {
368        self.a.flags() | self.b.flags()
369    }
370
371    unsafe fn run_unsafe(
372        &mut self,
373        input: SystemIn<'_, Self>,
374        world: UnsafeWorldCell,
375    ) -> Result<Self::Out, RunSystemError> {
376        let value = self.a.run_unsafe(input, world)?;
377        // `Self::validate_param_unsafe` already validated the first system,
378        // but we still need to validate the second system once the first one runs.
379        self.b.validate_param_unsafe(world)?;
380        self.b.run_unsafe(value, world)
381    }
382
383    #[cfg(feature = "hotpatching")]
384    #[inline]
385    fn refresh_hotpatch(&mut self) {
386        self.a.refresh_hotpatch();
387        self.b.refresh_hotpatch();
388    }
389
390    fn apply_deferred(&mut self, world: &mut World) {
391        self.a.apply_deferred(world);
392        self.b.apply_deferred(world);
393    }
394
395    fn queue_deferred(&mut self, mut world: crate::world::DeferredWorld) {
396        self.a.queue_deferred(world.reborrow());
397        self.b.queue_deferred(world);
398    }
399
400    unsafe fn validate_param_unsafe(
401        &mut self,
402        world: UnsafeWorldCell,
403    ) -> Result<(), SystemParamValidationError> {
404        // We only validate parameters for the first system,
405        // since it may make changes to the world that affect
406        // whether the second system has valid parameters.
407        // The second system will be validated in `Self::run_unsafe`.
408        // SAFETY: Delegate to the `System` implementation for `a`.
409        unsafe { self.a.validate_param_unsafe(world) }
410    }
411
412    fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
413        let mut a_access = self.a.initialize(world);
414        let b_access = self.b.initialize(world);
415        a_access.extend(b_access);
416        a_access
417    }
418
419    fn check_change_tick(&mut self, check: CheckChangeTicks) {
420        self.a.check_change_tick(check);
421        self.b.check_change_tick(check);
422    }
423
424    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
425        let mut default_sets = self.a.default_system_sets();
426        default_sets.append(&mut self.b.default_system_sets());
427        default_sets
428    }
429
430    fn get_last_run(&self) -> Tick {
431        self.a.get_last_run()
432    }
433
434    fn set_last_run(&mut self, last_run: Tick) {
435        self.a.set_last_run(last_run);
436        self.b.set_last_run(last_run);
437    }
438}
439
440/// SAFETY: Both systems are read-only, so any system created by piping them will only read from the world.
441unsafe impl<A, B> ReadOnlySystem for PipeSystem<A, B>
442where
443    A: ReadOnlySystem,
444    B: ReadOnlySystem,
445    for<'a> B::In: SystemInput<Inner<'a> = A::Out>,
446{
447}
448
449#[cfg(test)]
450mod tests {
451
452    #[test]
453    fn exclusive_system_piping_is_possible() {
454        use crate::prelude::*;
455
456        fn my_exclusive_system(_world: &mut World) -> u32 {
457            1
458        }
459
460        fn out_pipe(input: In<u32>) {
461            assert!(input.0 == 1);
462        }
463
464        let mut world = World::new();
465
466        let mut schedule = Schedule::default();
467        schedule.add_systems(my_exclusive_system.pipe(out_pipe));
468
469        schedule.run(&mut world);
470    }
471}