forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
send_and_receive_events.rs
167 lines (150 loc) · 6.04 KB
/
send_and_receive_events.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! From time to time, you may find that you want to both send and receive an event of the same type in a single system.
//!
//! Of course, this results in an error: the borrows of [`EventWriter`] and [`EventReader`] overlap,
//! if and only if the [`Event`] type is the same.
//! One system parameter borrows the [`Events`] resource mutably, and another system parameter borrows the [`Events`] resource immutably.
//! If Bevy allowed this, this would violate Rust's rules against aliased mutability.
//! In other words, this would be Undefined Behavior (UB)!
//!
//! There are two ways to solve this problem:
//!
//! 1. Use [`ParamSet`] to check out the [`EventWriter`] and [`EventReader`] one at a time.
//! 2. Use a [`Local`] [`EventCursor`] instead of an [`EventReader`], and use [`ResMut`] to access [`Events`].
//!
//! In the first case, you're being careful to only check out only one of the [`EventWriter`] or [`EventReader`] at a time.
//! By "temporally" separating them, you avoid the overlap.
//!
//! In the second case, you only ever have one access to the underlying [`Events`] resource at a time.
//! But in exchange, you have to manually keep track of which events you've already read.
//!
//! Let's look at an example of each.
use bevy::core::FrameCount;
use bevy::ecs::event::EventCursor;
use bevy::prelude::*;
fn main() {
let mut app = App::new();
app.add_plugins(MinimalPlugins)
.add_event::<DebugEvent>()
.add_event::<A>()
.add_event::<B>()
.add_systems(Update, read_and_write_different_event_types)
.add_systems(
Update,
(
send_events,
debug_events,
send_and_receive_param_set,
debug_events,
send_and_receive_manual_event_reader,
debug_events,
)
.chain(),
);
// We're just going to run a few frames, so we can see and understand the output.
app.update();
// By running for longer than one frame, we can see that we're caching our cursor in the event queue properly.
app.update();
}
#[derive(Event)]
struct A;
#[derive(Event)]
struct B;
// This works fine, because the types are different,
// so the borrows of the `EventWriter` and `EventReader` don't overlap.
// Note that these borrowing rules are checked at system initialization time,
// not at compile time, as Bevy uses internal unsafe code to split the `World` into disjoint pieces.
fn read_and_write_different_event_types(mut a: EventWriter<A>, mut b: EventReader<B>) {
for _ in b.read() {}
a.send(A);
}
/// A dummy event type.
#[derive(Debug, Clone, Event)]
struct DebugEvent {
resend_from_param_set: bool,
resend_from_local_event_reader: bool,
times_sent: u8,
}
/// A system that sends all combinations of events.
fn send_events(mut events: EventWriter<DebugEvent>, frame_count: Res<FrameCount>) {
println!("Sending events for frame {:?}", frame_count.0);
events.send(DebugEvent {
resend_from_param_set: false,
resend_from_local_event_reader: false,
times_sent: 1,
});
events.send(DebugEvent {
resend_from_param_set: true,
resend_from_local_event_reader: false,
times_sent: 1,
});
events.send(DebugEvent {
resend_from_param_set: false,
resend_from_local_event_reader: true,
times_sent: 1,
});
events.send(DebugEvent {
resend_from_param_set: true,
resend_from_local_event_reader: true,
times_sent: 1,
});
}
/// A system that prints all events sent since the last time this system ran.
///
/// Note that some events will be printed twice, because they were sent twice.
fn debug_events(mut events: EventReader<DebugEvent>) {
for event in events.read() {
println!("{:?}", event);
}
}
/// A system that both sends and receives events using [`ParamSet`].
fn send_and_receive_param_set(
mut param_set: ParamSet<(EventReader<DebugEvent>, EventWriter<DebugEvent>)>,
frame_count: Res<FrameCount>,
) {
println!(
"Sending and receiving events for frame {} with a `ParamSet`",
frame_count.0
);
// We must collect the events to resend, because we can't access the writer while we're iterating over the reader.
let mut events_to_resend = Vec::new();
// This is p0, as the first parameter in the `ParamSet` is the reader.
for event in param_set.p0().read() {
if event.resend_from_param_set {
events_to_resend.push(event.clone());
}
}
// This is p1, as the second parameter in the `ParamSet` is the writer.
for mut event in events_to_resend {
event.times_sent += 1;
param_set.p1().send(event);
}
}
/// A system that both sends and receives events using a [`Local`] [`EventCursor`].
fn send_and_receive_manual_event_reader(
// The `Local` `SystemParam` stores state inside the system itself, rather than in the world.
// `EventCursor<T>` is the internal state of `EventReader<T>`, which tracks which events have been seen.
mut local_event_reader: Local<EventCursor<DebugEvent>>,
// We can access the `Events` resource mutably, allowing us to both read and write its contents.
mut events: ResMut<Events<DebugEvent>>,
frame_count: Res<FrameCount>,
) {
println!(
"Sending and receiving events for frame {} with a `Local<EventCursor>",
frame_count.0
);
// We must collect the events to resend, because we can't mutate events while we're iterating over the events.
let mut events_to_resend = Vec::new();
for event in local_event_reader.read(&events) {
if event.resend_from_local_event_reader {
// For simplicity, we're cloning the event.
// In this case, since we have mutable access to the `Events` resource,
// we could also just mutate the event in-place,
// or drain the event queue into our `events_to_resend` vector.
events_to_resend.push(event.clone());
}
}
for mut event in events_to_resend {
event.times_sent += 1;
events.send(event);
}
}