Skip to main content

bevy_ecs/message/
message_mutator.rs

1#[cfg(feature = "multi_threaded")]
2use crate::message::MessageMutParIter;
3use crate::{
4    message::{
5        Message, MessageCursor, MessageId, MessageMutIterator, MessageMutIteratorWithId, Messages,
6        WriteBatchIds,
7    },
8    system::{Local, ResMut, SystemParam},
9};
10
11/// Reads and writes [`Message`]s of type `T`, keeping track of which messages have already been read.
12///
13/// This can be used if a system needs to both read and write messages of the same type.
14///
15/// Since it has exclusive access to the underlying messages, it also permits messages to be modified as they are read.
16/// This is ideal for chains of systems that all want to modify the same messages.
17///
18/// # Usage
19///
20/// [`MessageMutator`]s are usually declared as a [`SystemParam`].
21/// ```
22/// # use bevy_ecs::prelude::*;
23///
24/// #[derive(Message, Debug)]
25/// pub struct MyMessage(pub u32); // Custom message type.
26/// fn my_system(mut mutator: MessageMutator<MyMessage>) {
27///     // This message will be read immediately by this system,
28///     // and will then be visible to other systems.
29///     mutator.write(MyMessage(0));
30///     for message in mutator.read() {
31///         message.0 += 1;
32///         println!("received message: {:?}", message);
33///     }
34///     // This message will be read on the next run of this system,
35///     // but will be visible immediately to other systems.
36///     mutator.write(MyMessage(0));
37/// }
38/// ```
39///
40/// # Concurrency
41///
42/// Multiple systems with `MessageMutator<T>` of the same message type can not run concurrently.
43/// They also can not be executed in parallel with [`MessageReader`] or [`MessageWriter`].
44///
45/// # Clearing, Reading, and Peeking
46///
47/// Messages are stored in a double buffered queue that switches each frame. This switch also clears the previous
48/// frame's messages. Messages should be read each frame otherwise they may be lost. For manual control over this
49/// behavior, see [`Messages`].
50///
51/// Most of the time systems will want to use [`MessageMutator::read()`]. This function creates an iterator over
52/// all messages that haven't been read yet by this system, marking the message as read in the process.
53///
54/// [`MessageReader`]: super::MessageReader
55/// [`MessageWriter`]: super::MessageWriter
56#[derive(SystemParam, Debug)]
57pub struct MessageMutator<'w, 's, M: Message> {
58    pub(super) reader: Local<'s, MessageCursor<M>>,
59    #[system_param(validation_message = "Message not initialized")]
60    messages: ResMut<'w, Messages<M>>,
61}
62
63impl<'w, 's, M: Message> MessageMutator<'w, 's, M> {
64    /// Iterates over the messages this [`MessageMutator`] has not seen yet. This updates the
65    /// [`MessageMutator`]'s message counter, which means subsequent message reads will not include messages
66    /// that happened before now.
67    pub fn read(&mut self) -> MessageMutIterator<'_, M> {
68        self.reader.read_mut(&mut self.messages)
69    }
70
71    /// Like [`read`](Self::read), except also returning the [`MessageId`] of the messages.
72    pub fn read_with_id(&mut self) -> MessageMutIteratorWithId<'_, M> {
73        self.reader.read_mut_with_id(&mut self.messages)
74    }
75
76    /// Returns a parallel iterator over the messages this [`MessageMutator`] has not seen yet.
77    /// See also [`for_each`](super::MessageParIter::for_each).
78    ///
79    /// # Example
80    /// ```
81    /// # use bevy_ecs::prelude::*;
82    /// # use std::sync::atomic::{AtomicUsize, Ordering};
83    ///
84    /// #[derive(Message)]
85    /// struct MyMessage {
86    ///     value: usize,
87    /// }
88    ///
89    /// #[derive(Resource, Default)]
90    /// struct Counter(AtomicUsize);
91    ///
92    /// // setup
93    /// let mut world = World::new();
94    /// world.init_resource::<Messages<MyMessage>>();
95    /// world.insert_resource(Counter::default());
96    ///
97    /// let mut schedule = Schedule::default();
98    /// schedule.add_systems(|mut messages: MessageMutator<MyMessage>, counter: Res<Counter>| {
99    ///     messages.par_read().for_each(|MyMessage { value }| {
100    ///         counter.0.fetch_add(*value, Ordering::Relaxed);
101    ///     });
102    /// });
103    /// for value in 0..100 {
104    ///     world.write_message(MyMessage { value });
105    /// }
106    /// schedule.run(&mut world);
107    /// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
108    /// // all messages were processed
109    /// assert_eq!(counter.into_inner(), 4950);
110    /// ```
111    #[cfg(feature = "multi_threaded")]
112    pub fn par_read(&mut self) -> MessageMutParIter<'_, M> {
113        self.reader.par_read_mut(&mut self.messages)
114    }
115
116    /// Determines the number of messages available to be read from this [`MessageMutator`] without consuming any.
117    pub fn len(&self) -> usize {
118        self.reader.len(&self.messages)
119    }
120
121    /// Returns `true` if there are no messages available to read.
122    ///
123    /// # Example
124    ///
125    /// The following example shows a useful pattern where some behavior is triggered if new messages are available.
126    /// [`MessageMutator::clear()`] is used so the same messages don't re-trigger the behavior the next time the system runs.
127    ///
128    /// ```
129    /// # use bevy_ecs::prelude::*;
130    /// #
131    /// #[derive(Message)]
132    /// struct Collision;
133    ///
134    /// fn play_collision_sound(mut messages: MessageMutator<Collision>) {
135    ///     if !messages.is_empty() {
136    ///         messages.clear();
137    ///         // Play a sound
138    ///     }
139    /// }
140    /// # bevy_ecs::system::assert_is_system(play_collision_sound);
141    /// ```
142    pub fn is_empty(&self) -> bool {
143        self.reader.is_empty(&self.messages)
144    }
145
146    /// Consumes all available messages.
147    ///
148    /// This means these messages will not appear in calls to [`MessageMutator::read()`] or
149    /// [`MessageMutator::read_with_id()`] and [`MessageMutator::is_empty()`] will return `true`.
150    ///
151    /// For usage, see [`MessageMutator::is_empty()`].
152    pub fn clear(&mut self) {
153        self.reader.clear(&self.messages);
154    }
155
156    /// Writes an `message`, which can later be read by [`MessageReader`](super::MessageReader)s.
157    /// This method returns the [ID](`MessageId`) of the written `message`.
158    ///
159    /// See [`Messages`] for details.
160    #[track_caller]
161    pub fn write(&mut self, message: M) -> MessageId<M> {
162        self.messages.write(message)
163    }
164
165    /// Writes a list of `messages` all at once, which can later be read by [`MessageReader`](super::MessageReader)s.
166    /// This is more efficient than writing each message individually.
167    /// This method returns the [IDs](`MessageId`) of the written `messages`.
168    ///
169    /// See [`Messages`] for details.
170    #[track_caller]
171    pub fn write_batch(&mut self, messages: impl IntoIterator<Item = M>) -> WriteBatchIds<M> {
172        self.messages.write_batch(messages)
173    }
174
175    /// Writes the default value of the message. Useful when the message is an empty struct.
176    /// This method returns the [ID](`MessageId`) of the written `message`.
177    ///
178    /// See [`Messages`] for details.
179    #[track_caller]
180    pub fn write_default(&mut self) -> MessageId<M>
181    where
182        M: Default,
183    {
184        self.messages.write_default()
185    }
186}