#[cfg(feature = "multi_threaded")]
use crate::message::MessageParIter;
use crate::{
message::{Message, MessageCursor, MessageIterator, MessageIteratorWithId, Messages},
system::{Local, Res, SystemParam, SystemParamValidationError},
};
#[derive(SystemParam, Debug)]
pub struct MessageReader<'w, 's, M: Message> {
pub(super) reader: Local<'s, MessageCursor<M>>,
#[system_param(validation_message = "Message not initialized")]
messages: Res<'w, Messages<M>>,
}
impl<'w, 's, M: Message> MessageReader<'w, 's, M> {
pub fn read(&mut self) -> MessageIterator<'_, M> {
self.reader.read(&self.messages)
}
pub fn read_with_id(&mut self) -> MessageIteratorWithId<'_, M> {
self.reader.read_with_id(&self.messages)
}
#[cfg(feature = "multi_threaded")]
pub fn par_read(&mut self) -> MessageParIter<'_, M> {
self.reader.par_read(&self.messages)
}
pub fn len(&self) -> usize {
self.reader.len(&self.messages)
}
pub fn is_empty(&self) -> bool {
self.reader.is_empty(&self.messages)
}
pub fn clear(&mut self) {
self.reader.clear(&self.messages);
}
}
#[derive(Debug)]
pub struct PopulatedMessageReader<'w, 's, M: Message>(MessageReader<'w, 's, M>);
impl<'w, 's, M: Message> core::ops::Deref for PopulatedMessageReader<'w, 's, M> {
type Target = MessageReader<'w, 's, M>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'w, 's, M: Message> core::ops::DerefMut for PopulatedMessageReader<'w, 's, M> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
unsafe impl<'w, 's, M: Message> SystemParam for PopulatedMessageReader<'w, 's, M> {
type State = <MessageReader<'w, 's, M> as SystemParam>::State;
type Item<'world, 'state> = PopulatedMessageReader<'world, 'state, M>;
fn init_state(world: &mut crate::prelude::World) -> Self::State {
MessageReader::<M>::init_state(world)
}
fn init_access(
state: &Self::State,
system_meta: &mut crate::system::SystemMeta,
component_access_set: &mut crate::query::FilteredAccessSet,
world: &mut crate::prelude::World,
) {
MessageReader::<M>::init_access(state, system_meta, component_access_set, world);
}
unsafe fn get_param<'world, 'state>(
state: &'state mut Self::State,
system_meta: &crate::system::SystemMeta,
world: crate::world::unsafe_world_cell::UnsafeWorldCell<'world>,
change_tick: crate::change_detection::Tick,
) -> Result<Self::Item<'world, 'state>, SystemParamValidationError> {
let reader = unsafe { MessageReader::get_param(state, system_meta, world, change_tick)? };
if reader.is_empty() {
Err(SystemParamValidationError::skipped::<Self>(
"message queue is empty",
))
} else {
Ok(PopulatedMessageReader(reader))
}
}
}
#[cfg(test)]
mod tests {
use core::sync::atomic::{AtomicBool, Ordering};
use super::*;
use crate::message::MessageRegistry;
use crate::prelude::*;
use bevy_platform::sync::Arc;
#[test]
fn test_populated_message_reader() {
let system_ran = Arc::new(AtomicBool::new(false));
let mut world = World::new();
MessageRegistry::register_message::<TheMessage>(&mut world);
let mut schedule = Schedule::default();
schedule.add_systems({
let system_ran = system_ran.clone();
move |mut _reader: PopulatedMessageReader<TheMessage>| {
system_ran.store(true, Ordering::SeqCst);
}
});
schedule.run(&mut world);
assert!(
!system_ran.load(Ordering::SeqCst),
"system with PopulatedMessageReader should have been skipped"
);
world.write_message(TheMessage);
schedule.run(&mut world);
assert!(
system_ran.load(Ordering::SeqCst),
"system with PopulatedMessageReader should NOT have been skipped"
);
#[derive(Message)]
struct TheMessage;
}
}