Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make EventReader a SystemParam #1244

Merged
merged 7 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 80 additions & 116 deletions crates/bevy_app/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy_ecs::ResMut;
use bevy_ecs::{Local, Res, ResMut, SystemParam};
use bevy_utils::tracing::trace;
use std::{fmt, marker::PhantomData};

Expand Down Expand Up @@ -122,138 +122,102 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
}

/// Reads events of type `T` in order and tracks which events have already been read.
pub struct EventReader<T> {
#[derive(SystemParam)]
pub struct EventReader<'a, T: bevy_ecs::Resource> {
last_event_count: Local<'a, (usize, PhantomData<T>)>,
events: Res<'a, Events<T>>,
}

pub struct ManualEventReader<T> {
last_event_count: usize,
_marker: PhantomData<T>,
}

impl<T> Default for EventReader<T> {
impl<T> Default for ManualEventReader<T> {
fn default() -> Self {
Self {
ManualEventReader {
last_event_count: 0,
_marker: PhantomData::default(),
_marker: Default::default(),
}
}
}

impl<T> EventReader<T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
impl<T> ManualEventReader<T> {
/// See [`EventReader::iter`]
pub fn iter<'a>(&mut self, events: &'a Events<T>) -> impl DoubleEndedIterator<Item = &'a T> {
self.iter_with_id(events).map(|(event, _id)| event)
internal_event_reader(&mut self.last_event_count, events).map(|(e, _)| e)
}

/// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events.
/// See [`EventReader::iter_with_id`]
pub fn iter_with_id<'a>(
&mut self,
events: &'a Events<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
self.iter_internal(events).map(|(event, id)| {
trace!("EventReader::iter() -> {}", id);
(event, id)
})
}

/// Like [`iter_with_id`](Self::iter_with_id) except not emitting any traces for read messages.
fn iter_internal<'a>(
&mut self,
events: &'a Events<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
// if the reader has seen some of the events in a buffer, find the proper index offset.
// otherwise read all events in the buffer
let a_index = if self.last_event_count > events.a_start_event_count {
self.last_event_count - events.a_start_event_count
} else {
0
};
let b_index = if self.last_event_count > events.b_start_event_count {
self.last_event_count - events.b_start_event_count
} else {
0
};
self.last_event_count = events.event_count;
match events.state {
State::A => events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id)
.chain(
events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id),
),
State::B => events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id)
.chain(
events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id),
),
}
}

/// Retrieves the latest event that this EventReader hasn't seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn latest<'a>(&mut self, events: &'a Events<T>) -> Option<&'a T> {
self.latest_with_id(events).map(|(event, _)| event)
}

/// Like [`latest`](Self::latest), except also returning the [`EventId`] of the event.
pub fn latest_with_id<'a>(&mut self, events: &'a Events<T>) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events).rev().next().map(|(event, id)| {
trace!("EventReader::latest() -> {}", id);
(event, id)
})
}

/// Retrieves the latest event that matches the given `predicate` that this reader hasn't seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn find_latest<'a>(
&mut self,
events: &'a Events<T>,
predicate: impl FnMut(&&T) -> bool,
) -> Option<&'a T> {
self.find_latest_with_id(events, predicate)
.map(|(event, _)| event)
internal_event_reader(&mut self.last_event_count, events)
}
}

/// Like [`find_latest`](Self::find_latest), except also returning the [`EventId`] of the event.
pub fn find_latest_with_id<'a>(
&mut self,
events: &'a Events<T>,
mut predicate: impl FnMut(&&T) -> bool,
) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events)
.rev()
.find(|(event, _id)| predicate(event))
.map(|(event, id)| {
trace!("EventReader::find_latest() -> {}", id);
(event, id)
})
/// Like [`iter_with_id`](EventReader::iter_with_id) except not emitting any traces for read messages.
fn internal_event_reader<'a, T>(
last_event_count: &mut usize,
events: &'a Events<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
// if the reader has seen some of the events in a buffer, find the proper index offset.
// otherwise read all events in the buffer
let a_index = if *last_event_count > events.a_start_event_count {
*last_event_count - events.a_start_event_count
} else {
0
};
let b_index = if *last_event_count > events.b_start_event_count {
*last_event_count - events.b_start_event_count
} else {
0
};
*last_event_count = events.event_count;
match events.state {
State::A => events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id)
.chain(
events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id),
),
State::B => events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id)
.chain(
events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event_with_id),
),
}
}

/// Retrieves the earliest event in `events` that this reader hasn't seen yet. This updates the EventReader's
impl<'a, T: bevy_ecs::Resource> EventReader<'a, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn earliest<'a>(&mut self, events: &'a Events<T>) -> Option<&'a T> {
self.earliest_with_id(events).map(|(event, _)| event)
pub fn iter(&mut self) -> impl DoubleEndedIterator<Item = &T> {
self.iter_with_id().map(|(event, _id)| event)
}

/// Like [`earliest`](Self::earliest), except also returning the [`EventId`] of the event.
pub fn earliest_with_id<'a>(&mut self, events: &'a Events<T>) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events).next().map(|(event, id)| {
trace!("EventReader::earliest() -> {}", id);
/// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events.
pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator<Item = (&T, EventId<T>)> {
internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| {
trace!("EventReader::iter() -> {}", id);
(event, id)
})
}
Expand All @@ -278,17 +242,17 @@ impl<T: bevy_ecs::Resource> Events<T> {
self.event_count += 1;
}

/// Gets a new [EventReader]. This will include all events already in the event buffers.
pub fn get_reader(&self) -> EventReader<T> {
EventReader {
/// Gets a new [ManualEventReader]. This will include all events already in the event buffers.
pub fn get_reader(&self) -> ManualEventReader<T> {
ManualEventReader {
last_event_count: 0,
_marker: PhantomData,
}
}

/// Gets a new [EventReader]. This will ignore all events already in the event buffers. It will read all future events.
pub fn get_reader_current(&self) -> EventReader<T> {
EventReader {
/// Gets a new [ManualEventReader]. This will ignore all events already in the event buffers. It will read all future events.
pub fn get_reader_current(&self) -> ManualEventReader<T> {
ManualEventReader {
last_event_count: self.event_count,
_marker: PhantomData,
}
Expand Down Expand Up @@ -461,7 +425,7 @@ mod tests {

fn get_events(
events: &Events<TestEvent>,
reader: &mut EventReader<TestEvent>,
reader: &mut ManualEventReader<TestEvent>,
) -> Vec<TestEvent> {
reader.iter(events).cloned().collect::<Vec<TestEvent>>()
}
Expand Down
14 changes: 6 additions & 8 deletions crates/bevy_app/src/schedule_runner.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use super::{App, AppBuilder};
use crate::{
app::AppExit,
event::{EventReader, Events},
plugin::Plugin,
};
use crate::{app::AppExit, event::Events, plugin::Plugin, ManualEventReader};
use bevy_utils::{Duration, Instant};

#[cfg(target_arch = "wasm32")]
Expand Down Expand Up @@ -56,7 +52,7 @@ impl Plugin for ScheduleRunnerPlugin {
.get_or_insert_with(ScheduleRunnerSettings::default)
.to_owned();
app.set_runner(move |mut app: App| {
let mut app_exit_event_reader = EventReader::<AppExit>::default();
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
match settings.run_mode {
RunMode::Once => {
app.update();
Expand All @@ -68,15 +64,17 @@ impl Plugin for ScheduleRunnerPlugin {
let start_time = Instant::now();

if let Some(app_exit_events) = app.resources.get_mut::<Events<AppExit>>() {
if let Some(exit) = app_exit_event_reader.latest(&app_exit_events) {
if let Some(exit) = app_exit_event_reader.iter(&app_exit_events).last()
{
return Err(exit.clone());
}
}

app.update();

if let Some(app_exit_events) = app.resources.get_mut::<Events<AppExit>>() {
if let Some(exit) = app_exit_event_reader.latest(&app_exit_events) {
if let Some(exit) = app_exit_event_reader.iter(&app_exit_events).last()
{
return Err(exit.clone());
}
}
Expand Down
38 changes: 33 additions & 5 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{
parse::ParseStream, parse_macro_input, Data, DataStruct, DeriveInput, Error, Field, Fields,
Ident, Index, Lifetime, Path, Result,
parse::ParseStream, parse_macro_input, punctuated::Punctuated, Data, DataStruct, DeriveInput,
Error, Field, Fields, GenericParam, Ident, Index, Lifetime, Path, Result, Token,
};

/// Implement `Bundle` for a monomorphic struct
Expand Down Expand Up @@ -410,16 +410,44 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
let generics = ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let lifetimeless_generics: Vec<_> = generics
.params
.iter()
.filter(|g| matches!(g, GenericParam::Type(_)))
.collect();

let phantoms = lifetimeless_generics
.iter()
.map(|g| {
let g = match g {
GenericParam::Type(g) => &g.ident,
_ => panic!(),
};
quote! { ::std::marker::PhantomData::<#g>, }
})
.fold(quote!(), |old, new| {
quote! { #old #new }
});

let mut punctuated_generics = Punctuated::<_, Token![,]>::new();
punctuated_generics.extend(lifetimeless_generics.iter());

let mut punctuated_generic_idents = Punctuated::<_, Token![,]>::new();
punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
GenericParam::Type(g) => &g.ident,
_ => panic!(),
}));

let struct_name = &ast.ident;
let fetch_struct_name = Ident::new(&format!("Fetch{}", struct_name), Span::call_site());

TokenStream::from(quote! {
pub struct #fetch_struct_name;
pub struct #fetch_struct_name<#punctuated_generics>(#phantoms);
impl #impl_generics #path::SystemParam for #struct_name#ty_generics #where_clause {
type Fetch = #fetch_struct_name;
type Fetch = #fetch_struct_name <#punctuated_generic_idents>;
}

impl #impl_generics #path::FetchSystemParam<'a> for #fetch_struct_name {
impl #impl_generics #path::FetchSystemParam<'a> for #fetch_struct_name<#punctuated_generic_idents> {
type Item = #struct_name#ty_generics;
fn init(system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) {
#(<<#field_types as SystemParam>::Fetch as #path::FetchSystemParam>::init(system_state, world, resources);)*
Expand Down
7 changes: 3 additions & 4 deletions crates/bevy_input/src/gamepad.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{Axis, Input};
use bevy_app::{EventReader, Events};
use bevy_ecs::{Local, Res, ResMut};
use bevy_ecs::{Res, ResMut};
use bevy_utils::HashMap;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -202,16 +202,15 @@ impl ButtonAxisSettings {
}

pub fn gamepad_event_system(
mut event_reader: Local<EventReader<GamepadEventRaw>>,
mut button_input: ResMut<Input<GamepadButton>>,
mut axis: ResMut<Axis<GamepadAxis>>,
mut button_axis: ResMut<Axis<GamepadButton>>,
raw_events: Res<Events<GamepadEventRaw>>,
mut raw_events: EventReader<GamepadEventRaw>,
mut events: ResMut<Events<GamepadEvent>>,
settings: Res<GamepadSettings>,
) {
button_input.update();
for event in event_reader.iter(&raw_events) {
for event in raw_events.iter() {
let (gamepad, event) = (event.0, &event.1);
match event {
GamepadEventType::Connected => {
Expand Down
Loading