use std::any::TypeId;
use crate::system_param::{Access, SystemParam};
use crate::world::UnsafeWorldCell;
pub struct Events<T: 'static> {
previous: Vec<T>,
current: Vec<T>,
}
impl<T: 'static> Events<T> {
pub fn new() -> Self {
Self {
previous: Vec::new(),
current: Vec::new(),
}
}
pub fn send(&mut self, event: T) {
self.current.push(event);
}
pub fn read(&self) -> impl Iterator<Item = &T> {
self.previous.iter()
}
pub fn read_current(&self) -> impl Iterator<Item = &T> {
self.current.iter()
}
pub fn current_len(&self) -> usize {
self.current.len()
}
pub fn update(&mut self) {
std::mem::swap(&mut self.previous, &mut self.current);
self.current.clear();
}
pub fn len(&self) -> usize {
self.previous.len()
}
pub fn is_empty(&self) -> bool {
self.previous.is_empty()
}
}
impl<T: 'static> Default for Events<T> {
fn default() -> Self {
Self::new()
}
}
pub struct EventWriter<'w, T: 'static> {
events: &'w mut Events<T>,
}
impl<'w, T: 'static> EventWriter<'w, T> {
pub fn send(&mut self, event: T) {
self.events.send(event);
}
}
unsafe impl<T: 'static> SystemParam for EventWriter<'_, T> {
type Item<'w> = EventWriter<'w, T>;
fn access() -> Vec<Access> {
vec![Access::ResWrite(TypeId::of::<Events<T>>())]
}
unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w> {
EventWriter {
events: unsafe { world.get_resource_mut::<Events<T>>() },
}
}
}
pub struct EventReader<'w, T: 'static> {
events: &'w Events<T>,
}
impl<'w, T: 'static> EventReader<'w, T> {
pub fn read(&self) -> impl Iterator<Item = &T> {
self.events.read()
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
}
unsafe impl<T: 'static> SystemParam for EventReader<'_, T> {
type Item<'w> = EventReader<'w, T>;
fn access() -> Vec<Access> {
vec![Access::ResRead(TypeId::of::<Events<T>>())]
}
unsafe fn fetch<'w>(world: UnsafeWorldCell) -> Self::Item<'w> {
EventReader {
events: unsafe { world.get_resource::<Events<T>>() },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::system_param::{SystemParam, has_conflicts};
use crate::world::World;
#[derive(Debug, PartialEq)]
struct DamageEvent {
amount: u32,
}
#[derive(Debug, PartialEq)]
struct SpawnEvent {
x: f32,
}
#[test]
fn events_send_and_read() {
let mut events: Events<DamageEvent> = Events::new();
assert!(events.is_empty());
assert_eq!(events.len(), 0);
events.send(DamageEvent { amount: 42 });
assert!(events.is_empty());
events.update();
assert!(!events.is_empty());
assert_eq!(events.len(), 1);
let collected: Vec<_> = events.read().collect();
assert_eq!(collected, vec![&DamageEvent { amount: 42 }]);
}
#[test]
fn read_current_returns_unswaped_events() {
let mut events: Events<DamageEvent> = Events::new();
events.send(DamageEvent { amount: 7 });
assert_eq!(events.read().count(), 0);
let current: Vec<_> = events.read_current().collect();
assert_eq!(current, vec![&DamageEvent { amount: 7 }]);
events.update();
assert_eq!(events.read().count(), 1);
assert_eq!(events.read_current().count(), 0);
}
#[test]
fn events_double_buffer_semantics() {
let mut events: Events<DamageEvent> = Events::new();
events.send(DamageEvent { amount: 10 });
events.update();
assert_eq!(events.len(), 1);
events.update();
assert!(events.is_empty());
}
#[test]
fn events_multiple_sends_same_tick() {
let mut events: Events<DamageEvent> = Events::new();
events.send(DamageEvent { amount: 1 });
events.send(DamageEvent { amount: 2 });
events.send(DamageEvent { amount: 3 });
events.update();
assert_eq!(events.len(), 3);
let amounts: Vec<u32> = events.read().map(|e| e.amount).collect();
assert_eq!(amounts, vec![1, 2, 3]);
}
#[test]
fn event_writer_access_is_res_write() {
let access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
assert_eq!(access.len(), 1);
assert_eq!(
access[0],
Access::ResWrite(TypeId::of::<Events<DamageEvent>>())
);
}
#[test]
fn event_reader_access_is_res_read() {
let access = <EventReader<'_, DamageEvent> as SystemParam>::access();
assert_eq!(access.len(), 1);
assert_eq!(
access[0],
Access::ResRead(TypeId::of::<Events<DamageEvent>>())
);
}
#[test]
fn event_writer_reader_different_types_no_conflict() {
let writer_access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
let reader_access = <EventReader<'_, SpawnEvent> as SystemParam>::access();
assert!(!has_conflicts(&writer_access, &reader_access));
}
#[test]
fn event_writer_reader_same_type_conflicts() {
let writer_access = <EventWriter<'_, DamageEvent> as SystemParam>::access();
let reader_access = <EventReader<'_, DamageEvent> as SystemParam>::access();
assert!(has_conflicts(&writer_access, &reader_access));
}
#[test]
fn event_reader_reader_same_type_no_conflict() {
let a = <EventReader<'_, DamageEvent> as SystemParam>::access();
let b = <EventReader<'_, DamageEvent> as SystemParam>::access();
assert!(!has_conflicts(&a, &b));
}
#[test]
fn event_writer_fetch_and_send() {
let mut world = World::new();
world.add_event::<DamageEvent>();
let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
unsafe {
let mut writer = <EventWriter<'_, DamageEvent> as SystemParam>::fetch(cell);
writer.send(DamageEvent { amount: 99 });
}
world.update_events();
assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
}
#[test]
fn event_reader_fetch_and_read() {
let mut world = World::new();
world.add_event::<DamageEvent>();
world
.resource_mut::<Events<DamageEvent>>()
.send(DamageEvent { amount: 7 });
world.update_events();
let cell = unsafe { UnsafeWorldCell::new(&mut world as *mut World) };
unsafe {
let reader = <EventReader<'_, DamageEvent> as SystemParam>::fetch(cell);
assert_eq!(reader.len(), 1);
let ev = reader.read().next().unwrap();
assert_eq!(ev.amount, 7);
}
}
#[test]
fn add_event_duplicate_no_updater_duplication() {
let mut world = World::new();
world.add_event::<DamageEvent>();
world.add_event::<DamageEvent>();
world
.resource_mut::<Events<DamageEvent>>()
.send(DamageEvent { amount: 5 });
world.update_events();
assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
}
#[test]
fn add_event_duplicate_does_not_drop_queued_events() {
let mut world = World::new();
world.add_event::<DamageEvent>();
world
.resource_mut::<Events<DamageEvent>>()
.send(DamageEvent { amount: 99 });
world.add_event::<DamageEvent>();
world.update_events();
assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
assert_eq!(
world
.resource::<Events<DamageEvent>>()
.read()
.next()
.unwrap()
.amount,
99
);
}
#[test]
fn add_event_after_take_resource_restores_without_duplicate_updater() {
let mut world = World::new();
world.add_event::<DamageEvent>();
let _old: Events<DamageEvent> = world.take_resource();
world.add_event::<DamageEvent>();
world
.resource_mut::<Events<DamageEvent>>()
.send(DamageEvent { amount: 77 });
world.update_events();
assert_eq!(world.resource::<Events<DamageEvent>>().len(), 1);
assert_eq!(
world
.resource::<Events<DamageEvent>>()
.read()
.next()
.unwrap()
.amount,
77
);
}
}