Allow observer systems to have outputs (#14159)

Fixes https://github.com/bevyengine/bevy/issues/14157

- Update the ObserverSystem traits to accept an `Out` parameter

- Added a test where an observer system has a non-empty output which is
piped into another system

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Periwink 2024-07-15 10:59:12 -04:00 committed by François
parent d0583c8b54
commit 7f3fea9a5b
No known key found for this signature in database

View File

@ -8,31 +8,42 @@ use crate::{
use super::IntoSystem; use super::IntoSystem;
/// Implemented for systems that have an [`Observer`] as the first argument. /// Implemented for systems that have an [`Observer`] as the first argument.
pub trait ObserverSystem<E: 'static, B: Bundle>: ///
System<In = Trigger<'static, E, B>, Out = ()> + Send + 'static /// [`Observer`]: crate::observer::Observer
pub trait ObserverSystem<E: 'static, B: Bundle, Out = ()>:
System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
{ {
} }
impl<E: 'static, B: Bundle, T: System<In = Trigger<'static, E, B>, Out = ()> + Send + 'static> impl<
ObserverSystem<E, B> for T E: 'static,
B: Bundle,
Out,
T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static,
> ObserverSystem<E, B, Out> for T
{ {
} }
/// Implemented for systems that convert into [`ObserverSystem`]. /// Implemented for systems that convert into [`ObserverSystem`].
pub trait IntoObserverSystem<E: 'static, B: Bundle, M>: Send + 'static { pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = ()>: Send + 'static {
/// The type of [`System`] that this instance converts into. /// The type of [`System`] that this instance converts into.
type System: ObserverSystem<E, B>; type System: ObserverSystem<E, B, Out>;
/// Turns this value into its corresponding [`System`]. /// Turns this value into its corresponding [`System`].
fn into_system(this: Self) -> Self::System; fn into_system(this: Self) -> Self::System;
} }
impl<S: IntoSystem<Trigger<'static, E, B>, (), M> + Send + 'static, M, E: 'static, B: Bundle> impl<
IntoObserverSystem<E, B, M> for S S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
M,
Out,
E: 'static,
B: Bundle,
> IntoObserverSystem<E, B, M, Out> for S
where where
S::System: ObserverSystem<E, B>, S::System: ObserverSystem<E, B, Out>,
{ {
type System = <S as IntoSystem<Trigger<'static, E, B>, (), M>>::System; type System = <S as IntoSystem<Trigger<'static, E, B>, Out, M>>::System;
fn into_system(this: Self) -> Self::System { fn into_system(this: Self) -> Self::System {
IntoSystem::into_system(this) IntoSystem::into_system(this)
@ -42,23 +53,23 @@ where
macro_rules! impl_system_function { macro_rules! impl_system_function {
($($param: ident),*) => { ($($param: ident),*) => {
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl<E: 'static, B: Bundle, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(Trigger<E, B>, $($param,)*)> for Func impl<E: 'static, B: Bundle, Out, Func: Send + Sync + 'static, $($param: SystemParam),*> SystemParamFunction<fn(Trigger<E, B>, $($param,)*)> for Func
where where
for <'a> &'a mut Func: for <'a> &'a mut Func:
FnMut(Trigger<E, B>, $($param),*) + FnMut(Trigger<E, B>, $($param),*) -> Out +
FnMut(Trigger<E, B>, $(SystemParamItem<$param>),*) FnMut(Trigger<E, B>, $(SystemParamItem<$param>),*) -> Out, Out: 'static
{ {
type In = Trigger<'static, E, B>; type In = Trigger<'static, E, B>;
type Out = (); type Out = Out;
type Param = ($($param,)*); type Param = ($($param,)*);
#[inline] #[inline]
fn run(&mut self, input: Trigger<'static, E, B>, param_value: SystemParamItem< ($($param,)*)>) { fn run(&mut self, input: Trigger<'static, E, B>, param_value: SystemParamItem< ($($param,)*)>) -> Out {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn call_inner<E: 'static, B: Bundle, $($param,)*>( fn call_inner<E: 'static, B: Bundle, Out, $($param,)*>(
mut f: impl FnMut(Trigger<'static, E, B>, $($param,)*), mut f: impl FnMut(Trigger<'static, E, B>, $($param,)*) -> Out,
input: Trigger<'static, E, B>, input: Trigger<'static, E, B>,
$($param: $param,)* $($param: $param,)*
){ ) -> Out{
f(input, $($param,)*) f(input, $($param,)*)
} }
let ($($param,)*) = param_value; let ($($param,)*) = param_value;
@ -69,3 +80,37 @@ macro_rules! impl_system_function {
} }
all_tuples!(impl_system_function, 0, 16, F); all_tuples!(impl_system_function, 0, 16, F);
#[cfg(test)]
mod tests {
use crate::{
self as bevy_ecs,
event::Event,
observer::Trigger,
system::{In, IntoSystem},
world::World,
};
#[derive(Event)]
struct TriggerEvent;
#[test]
fn test_piped_observer_systems_no_input() {
fn a(_: Trigger<TriggerEvent>) {}
fn b() {}
let mut world = World::new();
world.observe(a.pipe(b));
}
#[test]
fn test_piped_observer_systems_with_inputs() {
fn a(_: Trigger<TriggerEvent>) -> u32 {
3
}
fn b(_: In<u32>) {}
let mut world = World::new();
world.observe(a.pipe(b));
}
}