use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use std::marker::PhantomData;
use std::sync::atomic::Ordering::Relaxed;
use std::{collections::BinaryHeap, sync::atomic::AtomicU32};
pub trait PriorityEvent: Send + Sync + 'static {}
impl<E: Send + Sync + 'static> PriorityEvent for E {}
#[derive(Debug)]
struct EventInstance<E> {
prio: u32,
event_id: u32,
event: E,
}
impl<E> EventInstance<E> {
fn new(event: E, prio: u32) -> Self {
static COUNTER: AtomicU32 = AtomicU32::new(0);
Self {
prio,
event_id: COUNTER.fetch_add(1, Relaxed),
event,
}
}
}
impl<E> PartialEq for EventInstance<E> {
fn eq(&self, other: &Self) -> bool {
self.prio == other.prio && self.event_id == other.event_id
}
}
impl<E> Eq for EventInstance<E> {}
impl<E> Ord for EventInstance<E> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.prio.cmp(&other.prio) {
std::cmp::Ordering::Equal => self.event_id.cmp(&other.event_id),
v => v,
}
.reverse()
}
}
impl<E> PartialOrd for EventInstance<E> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<E: Clone> Clone for EventInstance<E> {
fn clone(&self) -> Self {
Self {
prio: self.prio,
event_id: self.event_id,
event: self.event.clone(),
}
}
}
#[derive(Debug, Resource)]
pub struct PriorityEvents<E> {
events: BinaryHeap<EventInstance<E>>,
}
impl<E> Default for PriorityEvents<E> {
fn default() -> Self {
Self {
events: BinaryHeap::new(),
}
}
}
#[derive(SystemParam)]
pub struct PriorityEventReader<'w, 's, E: PriorityEvent> {
events: ResMut<'w, PriorityEvents<E>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
pub struct PriorityIterator<'w, E: PriorityEvent> {
min: u32,
max: u32,
events: &'w mut PriorityEvents<E>,
}
impl<'w, E: PriorityEvent> Iterator for PriorityIterator<'w, E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
while let Some(e) = self.events.events.peek() {
if e.prio > self.min {
return None;
} else if e.prio < self.max {
self.events.events.pop();
} else {
break;
};
}
self.events.events.pop().map(|e| e.event)
}
}
impl<'s, E: PriorityEvent> PriorityEventReader<'_, 's, E> {
pub fn iter_prio_range(&mut self, max: u32, min: u32) -> impl Iterator<Item = E> + '_ {
PriorityIterator {
min,
max,
events: self.events.as_mut(),
}
}
pub fn len(&self) -> usize {
self.events.events.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(SystemParam)]
pub struct PriorityEventWriter<'w, 's, E: PriorityEvent> {
events: ResMut<'w, PriorityEvents<E>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'w, 's, E: PriorityEvent> PriorityEventWriter<'w, 's, E> {
pub fn send(&mut self, event: E, prio: u32) {
self.events.events.push(EventInstance::new(event, prio));
}
pub fn send_batch(&mut self, events: impl Iterator<Item = E>, prio: u32) {
self.events
.events
.extend(events.map(|v| EventInstance::new(v, prio)))
}
pub fn send_default(&mut self, prio: u32)
where
E: Default,
{
self.events
.events
.push(EventInstance::new(E::default(), prio))
}
}
pub trait AddPriorityEvent {
fn add_priority_event<E: PriorityEvent>(&mut self) -> &mut Self;
}
impl AddPriorityEvent for App {
fn add_priority_event<E: PriorityEvent>(&mut self) -> &mut Self {
self.init_resource::<PriorityEvents<E>>();
self
}
}
#[cfg(test)]
mod tests {
use bevy::{ecs::system::SystemState, prelude::World};
use super::*;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct TestEvent(usize);
fn collect_events<E: Copy>(events: BinaryHeap<EventInstance<E>>) -> Vec<E> {
events
.into_sorted_vec()
.iter()
.map(|e| e.event)
.rev()
.collect()
}
#[test]
fn test_events() {
let mut world = World::new();
let mut state_writer: SystemState<PriorityEventWriter<TestEvent>> =
SystemState::new(&mut world);
let mut state_reader: SystemState<PriorityEventReader<TestEvent>> =
SystemState::new(&mut world);
world.init_resource::<PriorityEvents<TestEvent>>();
{
let mut w = state_writer.get_mut(&mut world);
w.send(TestEvent(0), 5);
w.send(TestEvent(1), 1);
w.send(TestEvent(2), 0);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![TestEvent(2), TestEvent(1), TestEvent(0)]
);
{
let mut w = state_reader.get_mut(&mut world);
w.iter_prio_range(0, 0).for_each(drop);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![TestEvent(1), TestEvent(0)]
);
{
let mut w = state_reader.get_mut(&mut world);
w.iter_prio_range(1, 5).for_each(drop);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
Vec::default()
);
}
#[test]
fn test_not_cleared_events() {
let mut world = World::new();
let mut state_writer: SystemState<PriorityEventWriter<TestEvent>> =
SystemState::new(&mut world);
let mut state_reader: SystemState<PriorityEventReader<TestEvent>> =
SystemState::new(&mut world);
world.init_resource::<PriorityEvents<TestEvent>>();
{
let mut w = state_writer.get_mut(&mut world);
w.send(TestEvent(0), 1);
}
{
let mut w = state_reader.get_mut(&mut world);
w.iter_prio_range(0, 0).for_each(drop);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![TestEvent(0)]
);
{
let mut w = state_writer.get_mut(&mut world);
w.send(TestEvent(0), 1);
}
{
let mut w = state_reader.get_mut(&mut world);
w.iter_prio_range(0, 0).for_each(drop);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![TestEvent(0), TestEvent(0)]
);
{
let mut w = state_reader.get_mut(&mut world);
w.iter_prio_range(1, 1).for_each(drop);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
Vec::default()
);
}
#[test]
fn test_higher_prio_destroyed() {
let mut world = World::new();
let mut state_writer: SystemState<PriorityEventWriter<TestEvent>> =
SystemState::new(&mut world);
let mut state_reader: SystemState<PriorityEventReader<TestEvent>> =
SystemState::new(&mut world);
world.init_resource::<PriorityEvents<TestEvent>>();
{
let mut w = state_writer.get_mut(&mut world);
w.send(TestEvent(0), 0);
}
{
let mut w = state_reader.get_mut(&mut world);
assert_eq!(
w.iter_prio_range(1, 1).collect::<Vec<TestEvent>>(),
Vec::default()
);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![]
);
}
#[test]
fn test_prio_range() {
let mut world = World::new();
let mut state_writer: SystemState<PriorityEventWriter<TestEvent>> =
SystemState::new(&mut world);
let mut state_reader: SystemState<PriorityEventReader<TestEvent>> =
SystemState::new(&mut world);
world.init_resource::<PriorityEvents<TestEvent>>();
{
let mut w = state_writer.get_mut(&mut world);
w.send(TestEvent(0), 0);
w.send(TestEvent(1), 1);
w.send(TestEvent(2), 2);
w.send(TestEvent(3), 3);
w.send(TestEvent(4), 4);
w.send(TestEvent(5), 5);
}
{
let mut w = state_reader.get_mut(&mut world);
assert_eq!(
w.iter_prio_range(0, 1).collect::<Vec<TestEvent>>(),
vec![TestEvent(0), TestEvent(1)]
);
assert_eq!(
w.iter_prio_range(2, 2).collect::<Vec<TestEvent>>(),
vec![TestEvent(2)]
);
assert_eq!(
w.iter_prio_range(3, 3).collect::<Vec<TestEvent>>(),
vec![TestEvent(3)]
);
assert_eq!(
w.iter_prio_range(5, 5).collect::<Vec<TestEvent>>(),
vec![TestEvent(5)]
);
}
assert_eq!(
collect_events(world.resource::<PriorityEvents<TestEvent>>().events.clone()),
vec![]
);
}
}