send_and_receive_messages/
send_and_receive_messages.rs

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