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