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