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