Skip to content

Commit

Permalink
event stage added
Browse files Browse the repository at this point in the history
  • Loading branch information
alec-mccormick committed Dec 10, 2020
1 parent 5c435be commit 366a085
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@ path = "examples/diagnostics/print_diagnostics.rs"
name = "event"
path = "examples/ecs/event.rs"

[[example]]
name = "event_stage"
path = "examples/ecs/event_stage.rs"

[[example]]
name = "any_event_stage"
path = "examples/ecs/any_event_stage.rs"

[[example]]
name = "fixed_timestep"
path = "examples/ecs/fixed_timestep.rs"
Expand Down
118 changes: 118 additions & 0 deletions crates/bevy_app/src/any_event_stage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use super::event::{EventReader, Events};
use bevy_ecs::{Stage, World, Resources, System, IntoSystem, Local, Res, ShouldRun, SystemStage, IntoChainSystem};
use std::marker::PhantomData;

pub struct AnyEventStage<T> {
inner: SystemStage,
_marker: PhantomData<T>
}

impl<A, B, C> Default for AnyEventStage<(A, B, C)>
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
fn default() -> Self {
Self::parallel()
}
}

impl<A, B, C> AnyEventStage<(A, B, C)>
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
pub fn new(system_stage: SystemStage) -> Self {
let inner = system_stage
.with_run_criteria(any_event_stage_run_criteria::<A, B, C>);

Self {
inner,
_marker: PhantomData
}
}

pub fn serial() -> Self {
Self::new(SystemStage::serial())
}

pub fn parallel() -> Self {
Self::new(SystemStage::parallel())
}

pub fn with_system<S, Params, IntoS>(mut self, system: IntoS) -> Self
where
S: System<Input = (Option<A>, Option<B>, Option<C>), Output = ()>,
IntoS: IntoSystem<Params, S>,
{
self.inner.add_system_boxed(Box::new(any_event_system.chain(system)));
self
}

pub fn add_system<S, Params, IntoS>(&mut self, system: IntoS) -> &mut Self
where
S: System<Input = (Option<A>, Option<B>, Option<C>), Output = ()>,
IntoS: IntoSystem<Params, S>,
{
self.inner.add_system_boxed(Box::new(any_event_system.chain(system)));
self
}
}

impl<A, B, C> Stage for AnyEventStage<(A, B, C)>
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
fn run(&mut self, world: &mut World, resources: &mut Resources) {
self.inner.run(world, resources)
}
}

/// Execute systems if there exists an event to consume.
fn any_event_stage_run_criteria<A, B, C>(
mut reader_a: Local<EventReader<A>>,
events_a: Res<Events<A>>,
mut reader_b: Local<EventReader<B>>,
events_b: Res<Events<B>>,
mut reader_c: Local<EventReader<C>>,
events_c: Res<Events<C>>,
) -> ShouldRun
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
let a = reader_a.earliest(&events_a);
let b = reader_b.earliest(&events_b);
let c = reader_c.earliest(&events_c);

if a.is_some() || b.is_some() || c.is_some() {
ShouldRun::YesAndLoop
} else {
ShouldRun::No
}
}

fn any_event_system<A, B, C>(
mut reader_a: Local<EventReader<A>>,
events_a: Res<Events<A>>,
mut reader_b: Local<EventReader<B>>,
events_b: Res<Events<B>>,
mut reader_c: Local<EventReader<C>>,
events_c: Res<Events<C>>,
) -> (Option<A>, Option<B>, Option<C>)
where
A: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
let a: Option<A> = reader_a.earliest(&events_a).map(|e| e.clone());
let b: Option<B> = reader_b.earliest(&events_b).map(|e| e.clone());
let c: Option<C> = reader_c.earliest(&events_c).map(|e| e.clone());

(a, b, c)
}
90 changes: 90 additions & 0 deletions crates/bevy_app/src/event_stage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use super::event::{EventReader, Events};
use bevy_ecs::{Stage, World, Resources, System, IntoSystem, Local, Res, ShouldRun, SystemStage, IntoChainSystem};
use std::marker::PhantomData;


pub struct EventStage<T> {
inner: SystemStage,
_marker: PhantomData<T>
}

impl<T> Default for EventStage<T>
where
T: Clone + Send + Sync + 'static
{
fn default() -> Self {
Self::parallel()
}
}

impl<T> EventStage<T>
where
T: Clone + Send + Sync + 'static
{
pub fn new(system_stage: SystemStage) -> Self {
let inner = system_stage
.with_run_criteria(event_stage_run_criteria::<T>);

Self {
inner,
_marker: PhantomData
}
}

pub fn serial() -> Self {
Self::new(SystemStage::serial())
}

pub fn parallel() -> Self {
Self::new(SystemStage::parallel())
}

pub fn with_system<S, Params, IntoS>(mut self, system: IntoS) -> Self
where
S: System<Input = T, Output = ()>,
IntoS: IntoSystem<Params, S>,
{
self.inner.add_system_boxed(Box::new(next_event_system.chain(system)));
self
}

pub fn add_system<S, Params, IntoS>(&mut self, system: IntoS) -> &mut Self
where
S: System<Input = T, Output = ()>,
IntoS: IntoSystem<Params, S>,
{
self.inner.add_system_boxed(Box::new(next_event_system.chain(system)));
self
}
}

impl<T> Stage for EventStage<T>
where
T: Clone + Send + Sync + 'static
{
fn run(&mut self, world: &mut World, resources: &mut Resources) {
self.inner.run(world, resources)
}
}

/// Execute systems if there exists an event to consume.
fn event_stage_run_criteria<T: Send + Sync + 'static>(
mut reader: Local<EventReader<T>>,
events: Res<Events<T>>
) -> ShouldRun {
if reader.earliest(&events).is_some() {
ShouldRun::YesAndLoop
} else {
ShouldRun::No
}
}

/// Fetch the next event and return it. This system is chained into all systems added to EventStage.
///
/// Unwrap is okay here because this system will only be run if there exists an event to consume
fn next_event_system<T: Clone + Send + Sync + 'static>(
mut reader: Local<EventReader<T>>,
events: Res<Events<T>>
) -> T {
reader.earliest(&events).unwrap().clone()
}
5 changes: 5 additions & 0 deletions crates/bevy_app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ pub mod startup_stage;
mod app;
mod app_builder;
mod event;
mod event_stage;
mod plugin;
mod plugin_group;
mod schedule_runner;
mod any_event_stage;

pub use app::*;
pub use app_builder::*;
pub use bevy_derive::DynamicPlugin;
pub use event::*;
pub use event_stage::*;
pub use any_event_stage::*;
pub use plugin::*;
pub use plugin_group::*;
pub use schedule_runner::*;
Expand All @@ -23,6 +27,7 @@ pub mod prelude {
app::App,
app_builder::AppBuilder,
event::{EventReader, Events},
event_stage::EventStage,
stage, DynamicPlugin, Plugin, PluginGroup,
};
}
71 changes: 71 additions & 0 deletions examples/ecs/any_event_stage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use bevy::prelude::*;
use bevy::app::AnyEventStage;

/// This example creates a three new events & demonstrates a system that runs when any of those
/// events have fired.
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_event::<EventA>()
.add_event::<EventB>()
.add_event::<EventC>()
.init_resource::<EventTriggerState>()
.add_system(event_trigger_system)
.add_stage_after(
stage::UPDATE,
"event_handlers",
AnyEventStage::<(EventA, EventB, EventC)>::default().with_system(event_listener_system)
)
.run();
}

#[derive(Clone, Debug)]
struct EventA;

#[derive(Clone, Debug)]
struct EventB;

#[derive(Clone, Debug)]
struct EventC;

struct EventTriggerState {
event_timer_a: Timer,
event_timer_b: Timer,
event_timer_c: Timer,
}

impl Default for EventTriggerState {
fn default() -> Self {
EventTriggerState {
event_timer_a: Timer::from_seconds(1.0, true),
event_timer_b: Timer::from_seconds(0.5, true),
event_timer_c: Timer::from_seconds(0.8, true),
}
}
}

// sends EventA every second, EventB every 0.5 seconds, and EventC every 0.8 seconds
fn event_trigger_system(
time: Res<Time>,
mut state: ResMut<EventTriggerState>,
mut events_a: ResMut<Events<EventA>>,
mut events_b: ResMut<Events<EventB>>,
mut events_c: ResMut<Events<EventC>>,
) {
if state.event_timer_a.tick(time.delta_seconds()).finished() {
events_a.send(EventA);
}

if state.event_timer_b.tick(time.delta_seconds()).finished() {
events_b.send(EventB);
}

if state.event_timer_c.tick(time.delta_seconds()).finished() {
events_c.send(EventC);
}
}

// prints events as they come in
fn event_listener_system(In((a, b, c)): In<(Option<EventA>, Option<EventB>, Option<EventC>)>) {
println!("Received Events A: {:?} | B: {:?} | C: {:?}", a, b, c);
}
52 changes: 52 additions & 0 deletions examples/ecs/event_stage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use bevy::prelude::*;

/// This example creates a new event, a system that triggers the event once per second,
/// and a system that prints a message whenever the event is received.
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_event::<MyEvent>()
.init_resource::<EventTriggerState>()
.add_system(event_trigger_system)
.add_stage_after(
stage::UPDATE,
"event_handlers",
EventStage::<MyEvent>::default().with_system(event_listener_system)
)
.run();
}

#[derive(Clone)]
struct MyEvent {
pub message: String,
}

struct EventTriggerState {
event_timer: Timer,
}

impl Default for EventTriggerState {
fn default() -> Self {
EventTriggerState {
event_timer: Timer::from_seconds(1.0, true),
}
}
}

// sends MyEvent every second
fn event_trigger_system(
time: Res<Time>,
mut state: ResMut<EventTriggerState>,
mut my_events: ResMut<Events<MyEvent>>,
) {
if state.event_timer.tick(time.delta_seconds()).finished() {
my_events.send(MyEvent {
message: "MyEvent just happened!".to_string(),
});
}
}

// prints events as they come in
fn event_listener_system(In(my_event): In<MyEvent>) {
println!("{}", my_event.message);
}

0 comments on commit 366a085

Please sign in to comment.