
# 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>
96 lines
3.8 KiB
Rust
96 lines
3.8 KiB
Rust
use crate::converter::{convert_axis, convert_button, convert_gamepad_id};
|
|
use bevy_ecs::event::EventWriter;
|
|
use bevy_ecs::system::{NonSend, NonSendMut, Res, ResMut};
|
|
use bevy_input::gamepad::{
|
|
GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadConnection, GamepadConnectionEvent,
|
|
GamepadSettings,
|
|
};
|
|
use bevy_input::gamepad::{GamepadEvent, GamepadInfo};
|
|
use bevy_input::prelude::{GamepadAxis, GamepadButton};
|
|
use bevy_input::Axis;
|
|
use gilrs::{ev::filter::axis_dpad_to_button, EventType, Filter, Gilrs};
|
|
|
|
pub fn gilrs_event_startup_system(
|
|
gilrs: NonSend<Gilrs>,
|
|
mut connection_events: EventWriter<GamepadConnectionEvent>,
|
|
) {
|
|
for (id, gamepad) in gilrs.gamepads() {
|
|
let info = GamepadInfo {
|
|
name: gamepad.name().into(),
|
|
};
|
|
|
|
connection_events.send(GamepadConnectionEvent {
|
|
gamepad: convert_gamepad_id(id),
|
|
connection: GamepadConnection::Connected(info),
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn gilrs_event_system(
|
|
mut gilrs: NonSendMut<Gilrs>,
|
|
mut events: EventWriter<GamepadEvent>,
|
|
mut gamepad_buttons: ResMut<Axis<GamepadButton>>,
|
|
gamepad_axis: Res<Axis<GamepadAxis>>,
|
|
gamepad_settings: Res<GamepadSettings>,
|
|
) {
|
|
while let Some(gilrs_event) = gilrs
|
|
.next_event()
|
|
.filter_ev(&axis_dpad_to_button, &mut gilrs)
|
|
{
|
|
gilrs.update(&gilrs_event);
|
|
|
|
let gamepad = convert_gamepad_id(gilrs_event.id);
|
|
match gilrs_event.event {
|
|
EventType::Connected => {
|
|
let pad = gilrs.gamepad(gilrs_event.id);
|
|
let info = GamepadInfo {
|
|
name: pad.name().into(),
|
|
};
|
|
|
|
events.send(
|
|
GamepadConnectionEvent::new(gamepad, GamepadConnection::Connected(info)).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);
|
|
let old_value = gamepad_buttons.get(button);
|
|
let button_settings = gamepad_settings.get_button_axis_settings(button);
|
|
|
|
// Only send events that pass the user-defined change threshold
|
|
if let Some(filtered_value) = button_settings.filter(raw_value, old_value) {
|
|
events.send(
|
|
GamepadButtonChangedEvent::new(gamepad, button_type, filtered_value)
|
|
.into(),
|
|
);
|
|
// Update the current value prematurely so that `old_value` is correct in
|
|
// future iterations of the loop.
|
|
gamepad_buttons.set(button, filtered_value);
|
|
}
|
|
}
|
|
}
|
|
EventType::AxisChanged(gilrs_axis, raw_value, _) => {
|
|
if let Some(axis_type) = convert_axis(gilrs_axis) {
|
|
let axis = GamepadAxis::new(gamepad, axis_type);
|
|
let old_value = gamepad_axis.get(axis);
|
|
let axis_settings = gamepad_settings.get_axis_settings(axis);
|
|
|
|
// Only send events that pass the user-defined change threshold
|
|
if let Some(filtered_value) = axis_settings.filter(raw_value, old_value) {
|
|
events.send(
|
|
GamepadAxisChangedEvent::new(gamepad, axis_type, filtered_value).into(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
};
|
|
}
|
|
gilrs.inc();
|
|
}
|