lyra-engine/lyra-game/src/events.rs

198 lines
5.7 KiB
Rust

use std::{any::{Any, TypeId}, collections::{HashMap, VecDeque}, marker::PhantomData, mem::{self, MaybeUninit}, ops::Range};
use crate::plugin::Plugin;
pub trait Event: Clone + Send + Sync + 'static {}
impl<T: Clone + Send + Sync + 'static> Event for T {}
pub type Events<T> = VecDeque<T>;
#[derive(Clone)]
struct TypeErasedBuffer {
type_id: TypeId,
item_size: usize,
data: Vec<MaybeUninit<u8>>,
fn_drop_item: fn(item: *mut ()),
}
impl Drop for TypeErasedBuffer {
fn drop(&mut self) {
let range = self.data.as_mut_ptr_range();
let mut current = range.start;
let end = range.end;
while current < end {
// SAFETY: current pointer will either be the start of the buffer, or at the start of
// the next item
(self.fn_drop_item)(current.cast());
// SAFETY: the items are placed right after eachother
current = unsafe { current.add(self.item_size) };
}
// Safety: all of the events were just dropped
unsafe { self.data.set_len(0) };
}
}
impl TypeErasedBuffer {
pub fn new<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
item_size: mem::size_of::<T>(),
data: Default::default(),
// dropped immediately after the read
fn_drop_item: |item| unsafe { item.cast::<T>().drop_in_place() },
}
}
pub fn push<T: 'static>(&mut self, value: T) {
debug_assert!(TypeId::of::<T>() == self.type_id, "The type of the buffer does not match the type that was added to it");
// reserve enough bytes to store T
let old_len = self.data.len();
self.data.reserve(mem::size_of::<T>());
// Get a pointer to the end of the data.
// SAFETY: we just reserved enough memory to store the data.
let end_ptr = unsafe { self.data.as_mut_ptr().add(old_len) };
unsafe {
// write the command and its runner into the buffer
end_ptr.cast::<T>()
// written unaligned to keep everything packed
.write_unaligned(value);
// we wrote to the vec's buffer without using its api, so we need manually
// set the length of the vec.
self.data.set_len(old_len + mem::size_of::<T>());
}
}
pub fn len(&self) -> usize {
self.data.len() / self.item_size
}
}
pub struct EventQueue {
events: HashMap<TypeId, TypeErasedBuffer>,
event_write_queue: HashMap<TypeId, TypeErasedBuffer>,
}
impl Default for EventQueue {
fn default() -> Self {
Self::new()
}
}
impl EventQueue {
pub fn new() -> Self {
Self {
event_write_queue: HashMap::new(),
events: HashMap::new(),
}
}
/// Trigger an event
pub fn trigger_event<E>(&mut self, event: E)
where
E: Event
{
// the compiler wants me to explicit right here for some reason
/* let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn AsSyncAny>);
// Get, or create, a list of events of this type
let type_id = event.type_id();
let mut events = self.event_write_queue.entry(type_id)
.or_insert_with(default)
.borrow_mut();
// downcast resource as an events storage
let e: &mut Events<E> = events.as_mut().as_any_mut().downcast_mut().unwrap();
e.push_back(event); */
let type_id = event.type_id();
let events = self.event_write_queue.entry(type_id)
.or_insert_with(|| TypeErasedBuffer::new::<E>());
events.push(event);
}
// Clear events, this should happen at the start of every tick since events are cloned
// before being given to the reader.
pub fn update_events(&mut self) {
self.events.clear();
// get all keys of events
let keys: Vec<TypeId> = self.event_write_queue.keys().copied().collect();
// remove all elements from self.event_write_queue and insert them to events
for k in keys.into_iter() {
let v = self.event_write_queue.remove(&k).unwrap();
self.events.insert(k, v);
}
}
pub fn read_events<E>(&self) -> Option<EventReader<E>>
where
E: Event
{
self.events.get(&TypeId::of::<E>())
.map(|event| EventReader::new(event.clone()))
}
}
pub struct EventReader<E: Event> {
buf: TypeErasedBuffer,
range: Option<Range<*mut MaybeUninit<u8>>>,
_marker: PhantomData<E>,
}
impl<E: Event> EventReader<E> {
fn new(buffer: TypeErasedBuffer) -> Self {
Self {
buf: buffer,
range: None,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.buf.len()
}
}
impl<E: Event> Iterator for EventReader<E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
if self.range.is_none() {
self.range = Some(self.buf.data.as_mut_ptr_range());
}
let range = self.range.as_mut().unwrap();
if range.start < range.end {
// Retrieve the event in the buffer.
// SAFETY: current pointer will either be the start of the buffer, or at the start
// of the next event.
let event = unsafe { range.start.cast::<E>().read_unaligned() };
// Advance the pointer to be after this event.
range.start = unsafe { range.start.add(mem::size_of::<E>()) };
Some(event)
} else {
None
}
}
}
#[derive(Default)]
pub struct EventsPlugin;
impl Plugin for EventsPlugin {
fn setup(&self, game: &mut crate::game::Game) {
game.world_mut().add_resource(EventQueue::new());
}
}