mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Update Event
send methods to return EventId
(#10551)
# Objective - Fixes #10532 ## Solution I've updated the various `Event` send methods to return the sent `EventId`(s). Since these methods previously returned nothing, and this information is cheap to copy, there should be minimal negative consequences to providing this additional information. In the case of `send_batch`, an iterator is returned built from `Range` and `Map`, which only consumes 16 bytes on the stack with no heap allocations for all batch sizes. As such, the cost of this information is negligible. These changes are reflected for `EventWriter` and `World`. For `World`, the return types are optional to account for the possible lack of an `Events` resource. Again, these methods previously returned no information, so its inclusion should only be a benefit. ## Usage Now when sending events, the IDs of those events is available for immediate use: ```rust // Example of a request-response system where the requester can track handled requests. /// A system which can make and track requests fn requester( mut requests: EventWriter<Request>, mut handled: EventReader<Handled>, mut pending: Local<HashSet<EventId<Request>>>, ) { // Check status of previous requests for Handled(id) in handled.read() { pending.remove(&id); } if !pending.is_empty() { error!("Not all my requests were handled on the previous frame!"); pending.clear(); } // Send a new request and remember its ID for later let request_id = requests.send(Request::MyRequest { /* ... */ }); pending.insert(request_id); } /// A system which handles requests fn responder( mut requests: EventReader<Request>, mut handled: EventWriter<Handled>, ) { for (request, id) in requests.read_with_id() { if handle(request).is_ok() { handled.send(Handled(id)); } } } ``` In the above example, a `requester` system can send request events, and keep track of which ones are currently pending by `EventId`. Then, a `responder` system can act on that event, providing the ID as a reference that the `requester` can use. Before this PR, it was not trivial for a system sending events to keep track of events by ID. This is unfortunate, since for a system reading events, it is trivial to access the ID of a event. --- ## Changelog - Updated `Events`: - Added `send_batch` - Modified `send` to return the sent `EventId` - Modified `send_default` to return the sent `EventId` - Updated `EventWriter` - Modified `send_batch` to return all sent `EventId`s - Modified `send` to return the sent `EventId` - Modified `send_default` to return the sent `EventId` - Updated `World` - Modified `send_event` to return the sent `EventId` if sent, otherwise `None`. - Modified `send_event_default` to return the sent `EventId` if sent, otherwise `None`. - Modified `send_event_batch` to return all sent `EventId`s if sent, otherwise `None`. - Added unit test `test_send_events_ids` to ensure returned `EventId`s match the sent `Event`s - Updated uses of modified methods. ## Migration Guide ### `send` / `send_default` / `send_batch` For the following methods: - `Events::send` - `Events::send_default` - `Events::send_batch` - `EventWriter::send` - `EventWriter::send_default` - `EventWriter::send_batch` - `World::send_event` - `World::send_event_default` - `World::send_event_batch` Ensure calls to these methods either handle the returned value, or suppress the result with `;`. ```rust // Now fails to compile due to mismatched return type fn send_my_event(mut events: EventWriter<MyEvent>) { events.send_default() } // Fix fn send_my_event(mut events: EventWriter<MyEvent>) { events.send_default(); } ``` This will most likely be noticed within `match` statements: ```rust // Before match is_pressed { true => events.send(PlayerAction::Fire), // ^--^ No longer returns () false => {} } // After match is_pressed { true => { events.send(PlayerAction::Fire); }, false => {} } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
This commit is contained in:
parent
636d7738a8
commit
3c689b9ca8
6 changed files with 157 additions and 37 deletions
|
@ -191,7 +191,8 @@ impl<E: Event> Events<E> {
|
|||
|
||||
/// "Sends" an `event` by writing it to the current event buffer. [`EventReader`]s can then read
|
||||
/// the event.
|
||||
pub fn send(&mut self, event: E) {
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||
pub fn send(&mut self, event: E) -> EventId<E> {
|
||||
let event_id = EventId {
|
||||
id: self.event_count,
|
||||
_marker: PhantomData,
|
||||
|
@ -202,14 +203,32 @@ impl<E: Event> Events<E> {
|
|||
|
||||
self.events_b.push(event_instance);
|
||||
self.event_count += 1;
|
||||
|
||||
event_id
|
||||
}
|
||||
|
||||
/// Sends a list of `events` all at once, which can later be read by [`EventReader`]s.
|
||||
/// This is more efficient than sending each event individually.
|
||||
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
||||
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
||||
let last_count = self.event_count;
|
||||
|
||||
self.extend(events);
|
||||
|
||||
SendBatchIds {
|
||||
last_count,
|
||||
event_count: self.event_count,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends the default value of the event. Useful when the event is an empty struct.
|
||||
pub fn send_default(&mut self)
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||
pub fn send_default(&mut self) -> EventId<E>
|
||||
where
|
||||
E: Default,
|
||||
{
|
||||
self.send(Default::default());
|
||||
self.send(Default::default())
|
||||
}
|
||||
|
||||
/// Gets a new [`ManualEventReader`]. This will include all events already in the event buffers.
|
||||
|
@ -512,26 +531,31 @@ pub struct EventWriter<'w, E: Event> {
|
|||
|
||||
impl<'w, E: Event> EventWriter<'w, E> {
|
||||
/// Sends an `event`, which can later be read by [`EventReader`]s.
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||
///
|
||||
/// See [`Events`] for details.
|
||||
pub fn send(&mut self, event: E) {
|
||||
self.events.send(event);
|
||||
pub fn send(&mut self, event: E) -> EventId<E> {
|
||||
self.events.send(event)
|
||||
}
|
||||
|
||||
/// Sends a list of `events` all at once, which can later be read by [`EventReader`]s.
|
||||
/// This is more efficient than sending each event individually.
|
||||
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
||||
///
|
||||
/// See [`Events`] for details.
|
||||
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) {
|
||||
self.events.extend(events);
|
||||
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
||||
self.events.send_batch(events)
|
||||
}
|
||||
|
||||
/// Sends the default value of the event. Useful when the event is an empty struct.
|
||||
pub fn send_default(&mut self)
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||
///
|
||||
/// See [`Events`] for details.
|
||||
pub fn send_default(&mut self) -> EventId<E>
|
||||
where
|
||||
E: Default,
|
||||
{
|
||||
self.events.send_default();
|
||||
self.events.send_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -760,6 +784,38 @@ pub fn event_update_condition<T: Event>(events: Res<Events<T>>) -> bool {
|
|||
!events.events_a.is_empty() || !events.events_b.is_empty()
|
||||
}
|
||||
|
||||
/// [`Iterator`] over sent [`EventIds`](`EventId`) from a batch.
|
||||
pub struct SendBatchIds<E> {
|
||||
last_count: usize,
|
||||
event_count: usize,
|
||||
_marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Event> Iterator for SendBatchIds<E> {
|
||||
type Item = EventId<E>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.last_count >= self.event_count {
|
||||
return None;
|
||||
}
|
||||
|
||||
let result = Some(EventId {
|
||||
id: self.last_count,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
|
||||
self.last_count += 1;
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Event> ExactSizeIterator for SendBatchIds<E> {
|
||||
fn len(&self) -> usize {
|
||||
self.event_count.saturating_sub(self.last_count)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::system::assert_is_read_only_system;
|
||||
|
@ -1119,4 +1175,43 @@ mod tests {
|
|||
|
||||
assert_is_read_only_system(reader_system);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_events_ids() {
|
||||
let mut events = Events::<TestEvent>::default();
|
||||
let event_0 = TestEvent { i: 0 };
|
||||
let event_1 = TestEvent { i: 1 };
|
||||
let event_2 = TestEvent { i: 2 };
|
||||
|
||||
let event_0_id = events.send(event_0);
|
||||
|
||||
assert_eq!(
|
||||
events.get_event(event_0_id.id),
|
||||
Some((&event_0, event_0_id)),
|
||||
"Getting a sent event by ID should return the original event"
|
||||
);
|
||||
|
||||
let mut event_ids = events.send_batch([event_1, event_2]);
|
||||
|
||||
let event_id = event_ids.next().expect("Event 1 must have been sent");
|
||||
|
||||
assert_eq!(
|
||||
events.get_event(event_id.id),
|
||||
Some((&event_1, event_id)),
|
||||
"Getting a sent event by ID should return the original event"
|
||||
);
|
||||
|
||||
let event_id = event_ids.next().expect("Event 2 must have been sent");
|
||||
|
||||
assert_eq!(
|
||||
events.get_event(event_id.id),
|
||||
Some((&event_2, event_id)),
|
||||
"Getting a sent event by ID should return the original event"
|
||||
);
|
||||
|
||||
assert!(
|
||||
event_ids.next().is_none(),
|
||||
"Only sent two events; got more than two IDs"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
change_detection::{MutUntyped, TicksMut},
|
||||
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, Tick},
|
||||
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
|
||||
event::{Event, Events},
|
||||
event::{Event, EventId, Events, SendBatchIds},
|
||||
query::{DebugCheckedUnwrap, QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
||||
removal_detection::RemovedComponentEvents,
|
||||
schedule::{Schedule, ScheduleLabel, Schedules},
|
||||
|
@ -1594,27 +1594,37 @@ impl World {
|
|||
}
|
||||
|
||||
/// Sends an [`Event`].
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
||||
/// or [`None`] if the `event` could not be sent.
|
||||
#[inline]
|
||||
pub fn send_event<E: Event>(&mut self, event: E) {
|
||||
self.send_event_batch(std::iter::once(event));
|
||||
pub fn send_event<E: Event>(&mut self, event: E) -> Option<EventId<E>> {
|
||||
self.send_event_batch(std::iter::once(event))?.next()
|
||||
}
|
||||
|
||||
/// Sends the default value of the [`Event`] of type `E`.
|
||||
/// This method returns the [ID](`EventId`) of the sent `event`,
|
||||
/// or [`None`] if the `event` could not be sent.
|
||||
#[inline]
|
||||
pub fn send_event_default<E: Event + Default>(&mut self) {
|
||||
self.send_event_batch(std::iter::once(E::default()));
|
||||
pub fn send_event_default<E: Event + Default>(&mut self) -> Option<EventId<E>> {
|
||||
self.send_event(E::default())
|
||||
}
|
||||
|
||||
/// Sends a batch of [`Event`]s from an iterator.
|
||||
/// This method returns the [IDs](`EventId`) of the sent `events`,
|
||||
/// or [`None`] if the `event` could not be sent.
|
||||
#[inline]
|
||||
pub fn send_event_batch<E: Event>(&mut self, events: impl IntoIterator<Item = E>) {
|
||||
match self.get_resource_mut::<Events<E>>() {
|
||||
Some(mut events_resource) => events_resource.extend(events),
|
||||
None => bevy_utils::tracing::error!(
|
||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||
std::any::type_name::<E>()
|
||||
),
|
||||
}
|
||||
pub fn send_event_batch<E: Event>(
|
||||
&mut self,
|
||||
events: impl IntoIterator<Item = E>,
|
||||
) -> Option<SendBatchIds<E>> {
|
||||
let Some(mut events_resource) = self.get_resource_mut::<Events<E>>() else {
|
||||
bevy_utils::tracing::error!(
|
||||
"Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ",
|
||||
std::any::type_name::<E>()
|
||||
);
|
||||
return None;
|
||||
};
|
||||
Some(events_resource.send_batch(events))
|
||||
}
|
||||
|
||||
/// Inserts a new resource with the given `value`. Will replace the value if it already existed.
|
||||
|
|
|
@ -51,8 +51,11 @@ pub fn gilrs_event_system(
|
|||
GamepadConnectionEvent::new(gamepad, GamepadConnection::Connected(info)).into(),
|
||||
);
|
||||
}
|
||||
EventType::Disconnected => events
|
||||
.send(GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected).into()),
|
||||
EventType::Disconnected => {
|
||||
events.send(
|
||||
GamepadConnectionEvent::new(gamepad, GamepadConnection::Disconnected).into(),
|
||||
);
|
||||
}
|
||||
EventType::ButtonChanged(gilrs_button, raw_value, _) => {
|
||||
if let Some(button_type) = convert_button(gilrs_button) {
|
||||
let button = GamepadButton::new(gamepad, button_type);
|
||||
|
|
|
@ -1263,8 +1263,12 @@ pub fn gamepad_event_system(
|
|||
GamepadEvent::Connection(connection_event) => {
|
||||
connection_events.send(connection_event.clone());
|
||||
}
|
||||
GamepadEvent::Button(button_event) => button_events.send(button_event.clone()),
|
||||
GamepadEvent::Axis(axis_event) => axis_events.send(axis_event.clone()),
|
||||
GamepadEvent::Button(button_event) => {
|
||||
button_events.send(button_event.clone());
|
||||
}
|
||||
GamepadEvent::Axis(axis_event) => {
|
||||
axis_events.send(axis_event.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -693,16 +693,22 @@ pub fn winit_runner(mut app: App) {
|
|||
cursor,
|
||||
});
|
||||
}
|
||||
event::Ime::Commit(value) => event_writers.ime_input.send(Ime::Commit {
|
||||
window: window_entity,
|
||||
value,
|
||||
}),
|
||||
event::Ime::Enabled => event_writers.ime_input.send(Ime::Enabled {
|
||||
window: window_entity,
|
||||
}),
|
||||
event::Ime::Disabled => event_writers.ime_input.send(Ime::Disabled {
|
||||
window: window_entity,
|
||||
}),
|
||||
event::Ime::Commit(value) => {
|
||||
event_writers.ime_input.send(Ime::Commit {
|
||||
window: window_entity,
|
||||
value,
|
||||
});
|
||||
}
|
||||
event::Ime::Enabled => {
|
||||
event_writers.ime_input.send(Ime::Enabled {
|
||||
window: window_entity,
|
||||
});
|
||||
}
|
||||
event::Ime::Disabled => {
|
||||
event_writers.ime_input.send(Ime::Disabled {
|
||||
window: window_entity,
|
||||
});
|
||||
}
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
event_writers.window_theme_changed.send(WindowThemeChanged {
|
||||
|
|
|
@ -804,7 +804,9 @@ mod menu {
|
|||
for (interaction, menu_button_action) in &interaction_query {
|
||||
if *interaction == Interaction::Pressed {
|
||||
match menu_button_action {
|
||||
MenuButtonAction::Quit => app_exit_events.send(AppExit),
|
||||
MenuButtonAction::Quit => {
|
||||
app_exit_events.send(AppExit);
|
||||
}
|
||||
MenuButtonAction::Play => {
|
||||
game_state.set(GameState::Game);
|
||||
menu_state.set(MenuState::Disabled);
|
||||
|
|
Loading…
Reference in a new issue