bevy_ecs/message/message_reader.rs
1#[cfg(feature = "multi_threaded")]
2use crate::message::MessageParIter;
3use crate::{
4 message::{Message, MessageCursor, MessageIterator, MessageIteratorWithId, Messages},
5 system::{Local, Res, SystemParam},
6};
7
8/// Reads [`Message`]s of type `T` in order and tracks which messages have already been read.
9///
10/// # Concurrency
11///
12/// Unlike [`MessageWriter<T>`], systems with `MessageReader<T>` param can be executed concurrently
13/// (but not concurrently with `MessageWriter<T>` or `MessageMutator<T>` systems for the same message type).
14///
15/// [`MessageWriter<T>`]: super::MessageWriter
16#[derive(SystemParam, Debug)]
17pub struct MessageReader<'w, 's, E: Message> {
18 pub(super) reader: Local<'s, MessageCursor<E>>,
19 #[system_param(validation_message = "Message not initialized")]
20 messages: Res<'w, Messages<E>>,
21}
22
23impl<'w, 's, E: Message> MessageReader<'w, 's, E> {
24 /// Iterates over the messages this [`MessageReader`] has not seen yet. This updates the
25 /// [`MessageReader`]'s message counter, which means subsequent message reads will not include messages
26 /// that happened before now.
27 pub fn read(&mut self) -> MessageIterator<'_, E> {
28 self.reader.read(&self.messages)
29 }
30
31 /// Like [`read`](Self::read), except also returning the [`MessageId`](super::MessageId) of the messages.
32 pub fn read_with_id(&mut self) -> MessageIteratorWithId<'_, E> {
33 self.reader.read_with_id(&self.messages)
34 }
35
36 /// Returns a parallel iterator over the messages this [`MessageReader`] has not seen yet.
37 /// See also [`for_each`](MessageParIter::for_each).
38 ///
39 /// # Example
40 /// ```
41 /// # use bevy_ecs::prelude::*;
42 /// # use std::sync::atomic::{AtomicUsize, Ordering};
43 ///
44 /// #[derive(Message)]
45 /// struct MyMessage {
46 /// value: usize,
47 /// }
48 ///
49 /// #[derive(Resource, Default)]
50 /// struct Counter(AtomicUsize);
51 ///
52 /// // setup
53 /// let mut world = World::new();
54 /// world.init_resource::<Messages<MyMessage>>();
55 /// world.insert_resource(Counter::default());
56 ///
57 /// let mut schedule = Schedule::default();
58 /// schedule.add_systems(|mut messages: MessageReader<MyMessage>, counter: Res<Counter>| {
59 /// messages.par_read().for_each(|MyMessage { value }| {
60 /// counter.0.fetch_add(*value, Ordering::Relaxed);
61 /// });
62 /// });
63 /// for value in 0..100 {
64 /// world.write_message(MyMessage { value });
65 /// }
66 /// schedule.run(&mut world);
67 /// let Counter(counter) = world.remove_resource::<Counter>().unwrap();
68 /// // all messages were processed
69 /// assert_eq!(counter.into_inner(), 4950);
70 /// ```
71 #[cfg(feature = "multi_threaded")]
72 pub fn par_read(&mut self) -> MessageParIter<'_, E> {
73 self.reader.par_read(&self.messages)
74 }
75
76 /// Determines the number of messages available to be read from this [`MessageReader`] without consuming any.
77 pub fn len(&self) -> usize {
78 self.reader.len(&self.messages)
79 }
80
81 /// Returns `true` if there are no messages available to read.
82 ///
83 /// # Example
84 ///
85 /// The following example shows a useful pattern where some behavior is triggered if new messages are available.
86 /// [`MessageReader::clear()`] is used so the same messages don't re-trigger the behavior the next time the system runs.
87 ///
88 /// ```
89 /// # use bevy_ecs::prelude::*;
90 /// #
91 /// #[derive(Message)]
92 /// struct Collision;
93 ///
94 /// fn play_collision_sound(mut messages: MessageReader<Collision>) {
95 /// if !messages.is_empty() {
96 /// messages.clear();
97 /// // Play a sound
98 /// }
99 /// }
100 /// # bevy_ecs::system::assert_is_system(play_collision_sound);
101 /// ```
102 pub fn is_empty(&self) -> bool {
103 self.reader.is_empty(&self.messages)
104 }
105
106 /// Consumes all available messages.
107 ///
108 /// This means these messages will not appear in calls to [`MessageReader::read()`] or
109 /// [`MessageReader::read_with_id()`] and [`MessageReader::is_empty()`] will return `true`.
110 ///
111 /// For usage, see [`MessageReader::is_empty()`].
112 pub fn clear(&mut self) {
113 self.reader.clear(&self.messages);
114 }
115}