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}