Skip to content

Commit 20e04dd

Browse files
committed
feat(ecs): implement fallible observer systems
This commit builds on top of the work done in bevyengine#16589 and bevyengine#17051, by adding support for fallible observer systems. As with the previous work, the actual results of the observer system are suppressed by default, but the intention is to provide a way to handle errors in a global way. Until then, you can use a `PipeSystem` to manually handle results. Signed-off-by: Jean Mertz <[email protected]>
1 parent 3c8fae2 commit 20e04dd

File tree

2 files changed

+151
-18
lines changed

2 files changed

+151
-18
lines changed

crates/bevy_ecs/src/observer/runner.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
386386
unsafe {
387387
(*system).update_archetype_component_access(world);
388388
if (*system).validate_param_unsafe(world) {
389-
(*system).run_unsafe(trigger, world);
389+
// TODO: implement an error-handling API instead of suppressing a possible failure.
390+
let _ = (*system).run_unsafe(trigger, world);
390391
(*system).queue_deferred(world.into_deferred());
391392
}
392393
}

crates/bevy_ecs/src/system/observer_system.rs

Lines changed: 149 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1+
use alloc::{borrow::Cow, vec::Vec};
2+
use core::marker::PhantomData;
3+
14
use crate::{
5+
archetype::ArchetypeComponentId,
6+
component::{ComponentId, Tick},
27
prelude::{Bundle, Trigger},
3-
system::System,
8+
query::Access,
9+
result::Result,
10+
schedule::{Fallible, Infallible},
11+
system::{input::SystemIn, System},
12+
world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
413
};
514

615
use super::IntoSystem;
716

817
/// Implemented for [`System`]s that have a [`Trigger`] as the first argument.
9-
pub trait ObserverSystem<E: 'static, B: Bundle, Out = ()>:
18+
pub trait ObserverSystem<E: 'static, B: Bundle, Out = Result>:
1019
System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
1120
{
1221
}
1322

14-
impl<
15-
E: 'static,
16-
B: Bundle,
17-
Out,
18-
T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static,
19-
> ObserverSystem<E, B, Out> for T
23+
impl<E: 'static, B: Bundle, Out, T> ObserverSystem<E, B, Out> for T where
24+
T: System<In = Trigger<'static, E, B>, Out = Out> + Send + 'static
2025
{
2126
}
2227

@@ -32,31 +37,158 @@ impl<
3237
label = "the trait `IntoObserverSystem` is not implemented",
3338
note = "for function `ObserverSystem`s, ensure the first argument is a `Trigger<T>` and any subsequent ones are `SystemParam`"
3439
)]
35-
pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = ()>: Send + 'static {
40+
pub trait IntoObserverSystem<E: 'static, B: Bundle, M, Out = Result>: Send + 'static {
3641
/// The type of [`System`] that this instance converts into.
3742
type System: ObserverSystem<E, B, Out>;
3843

3944
/// Turns this value into its corresponding [`System`].
4045
fn into_system(this: Self) -> Self::System;
4146
}
4247

43-
impl<
44-
S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
45-
M,
46-
Out,
47-
E: 'static,
48-
B: Bundle,
49-
> IntoObserverSystem<E, B, M, Out> for S
48+
impl<E, B, M, Out, S> IntoObserverSystem<E, B, (Fallible, M), Out> for S
5049
where
50+
S: IntoSystem<Trigger<'static, E, B>, Out, M> + Send + 'static,
5151
S::System: ObserverSystem<E, B, Out>,
52+
E: 'static,
53+
B: Bundle,
5254
{
53-
type System = <S as IntoSystem<Trigger<'static, E, B>, Out, M>>::System;
55+
type System = S::System;
5456

5557
fn into_system(this: Self) -> Self::System {
5658
IntoSystem::into_system(this)
5759
}
5860
}
5961

62+
impl<E, B, M, S> IntoObserverSystem<E, B, (Infallible, M), Result> for S
63+
where
64+
S: IntoSystem<Trigger<'static, E, B>, (), M> + Send + 'static,
65+
S::System: ObserverSystem<E, B, ()>,
66+
E: Send + Sync + 'static,
67+
B: Bundle,
68+
{
69+
type System = InfallibleObserverWrapper<E, B, S::System>;
70+
71+
fn into_system(this: Self) -> Self::System {
72+
InfallibleObserverWrapper::new(IntoSystem::into_system(this))
73+
}
74+
}
75+
76+
/// A wrapper that converts an observer system that returns `()` into one that returns `Ok(())`.
77+
pub struct InfallibleObserverWrapper<E, B, S> {
78+
observer: S,
79+
_marker: PhantomData<(E, B)>,
80+
}
81+
82+
impl<E, B, S> InfallibleObserverWrapper<E, B, S> {
83+
/// Create a new `InfallibleObserverWrapper`.
84+
pub fn new(observer: S) -> Self {
85+
Self {
86+
observer,
87+
_marker: PhantomData,
88+
}
89+
}
90+
}
91+
92+
impl<E, B, S> System for InfallibleObserverWrapper<E, B, S>
93+
where
94+
S: ObserverSystem<E, B, ()>,
95+
E: Send + Sync + 'static,
96+
B: Bundle,
97+
{
98+
type In = Trigger<'static, E, B>;
99+
type Out = Result;
100+
101+
#[inline]
102+
fn name(&self) -> Cow<'static, str> {
103+
self.observer.name()
104+
}
105+
106+
#[inline]
107+
fn component_access(&self) -> &Access<ComponentId> {
108+
self.observer.component_access()
109+
}
110+
111+
#[inline]
112+
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
113+
self.observer.archetype_component_access()
114+
}
115+
116+
#[inline]
117+
fn is_send(&self) -> bool {
118+
self.observer.is_send()
119+
}
120+
121+
#[inline]
122+
fn is_exclusive(&self) -> bool {
123+
self.observer.is_exclusive()
124+
}
125+
126+
#[inline]
127+
fn has_deferred(&self) -> bool {
128+
self.observer.has_deferred()
129+
}
130+
131+
#[inline]
132+
unsafe fn run_unsafe(
133+
&mut self,
134+
input: SystemIn<'_, Self>,
135+
world: UnsafeWorldCell,
136+
) -> Self::Out {
137+
self.observer.run_unsafe(input, world);
138+
Ok(())
139+
}
140+
141+
#[inline]
142+
fn run(&mut self, input: SystemIn<'_, Self>, world: &mut World) -> Self::Out {
143+
self.observer.run(input, world);
144+
Ok(())
145+
}
146+
147+
#[inline]
148+
fn apply_deferred(&mut self, world: &mut World) {
149+
self.observer.apply_deferred(world);
150+
}
151+
152+
#[inline]
153+
fn queue_deferred(&mut self, world: DeferredWorld) {
154+
self.observer.queue_deferred(world);
155+
}
156+
157+
#[inline]
158+
unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool {
159+
self.observer.validate_param_unsafe(world)
160+
}
161+
162+
#[inline]
163+
fn initialize(&mut self, world: &mut World) {
164+
self.observer.initialize(world);
165+
}
166+
167+
#[inline]
168+
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
169+
self.observer.update_archetype_component_access(world);
170+
}
171+
172+
#[inline]
173+
fn check_change_tick(&mut self, change_tick: Tick) {
174+
self.observer.check_change_tick(change_tick);
175+
}
176+
177+
#[inline]
178+
fn get_last_run(&self) -> Tick {
179+
self.observer.get_last_run()
180+
}
181+
182+
#[inline]
183+
fn set_last_run(&mut self, last_run: Tick) {
184+
self.observer.set_last_run(last_run);
185+
}
186+
187+
fn default_system_sets(&self) -> Vec<crate::schedule::InternedSystemSet> {
188+
self.observer.default_system_sets()
189+
}
190+
}
191+
60192
#[cfg(test)]
61193
mod tests {
62194
use crate::{

0 commit comments

Comments
 (0)