use std::any::TypeId;
use std::fmt;
use std::iter::Chain;
use std::marker::PhantomData;
use std::mem;
use std::slice;
use pragma_core::graph;
use crate::access::{Access, AccessKind};
use crate::system::{Env, Sealed, SystemParam};
use crate::{EcsError, World};
pub trait Event: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> Event for T {}
pub struct Events<T> {
previous: Vec<T>,
current: Vec<T>,
start: u64,
}
impl<T> Default for Events<T> {
fn default() -> Self {
Self {
previous: Vec::new(),
current: Vec::new(),
start: 0,
}
}
}
impl<T> fmt::Debug for Events<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Events")
.field("previous", &self.previous.len())
.field("current", &self.current.len())
.field("next_id", &self.end())
.finish()
}
}
impl<T> Events<T> {
#[must_use]
pub const fn new() -> Self {
Self {
previous: Vec::new(),
current: Vec::new(),
start: 0,
}
}
pub fn send(&mut self, event: T) -> Result<(), EcsError> {
self.current
.try_reserve(1)
.map_err(|_| EcsError::OutOfMemory {
entities: self.len().saturating_add(1),
})?;
self.current.push(event);
Ok(())
}
pub fn update(&mut self) {
let dropped = u64::try_from(self.previous.len()).unwrap_or(u64::MAX);
self.start = self.start.saturating_add(dropped);
self.previous.clear();
mem::swap(&mut self.previous, &mut self.current);
}
#[must_use]
pub fn len(&self) -> usize {
self.previous.len().saturating_add(self.current.len())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.previous.is_empty() && self.current.is_empty()
}
pub fn iter(&self) -> EventIter<'_, T> {
EventIter {
inner: self.previous.iter().chain(self.current.iter()),
}
}
fn end(&self) -> u64 {
let kept = u64::try_from(self.len()).unwrap_or(u64::MAX);
self.start.saturating_add(kept)
}
fn since(&self, from: u64) -> EventIter<'_, T> {
let skip = usize::try_from(from.saturating_sub(self.start)).unwrap_or(usize::MAX);
let previous = self.previous.get(skip..).unwrap_or_default();
let current = self
.current
.get(skip.saturating_sub(self.previous.len())..)
.unwrap_or_default();
EventIter {
inner: previous.iter().chain(current.iter()),
}
}
fn publish(&mut self, sent: &mut Vec<T>) -> Result<(), EcsError> {
self.current
.try_reserve(sent.len())
.map_err(|_| EcsError::OutOfMemory {
entities: self.len().saturating_add(sent.len()),
})?;
self.current.append(sent);
Ok(())
}
}
pub struct EventIter<'a, T> {
inner: Chain<slice::Iter<'a, T>, slice::Iter<'a, T>>,
}
impl<T> fmt::Debug for EventIter<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventIter").finish_non_exhaustive()
}
}
impl<'a, T> Iterator for EventIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct EventCursor<T> {
next: u64,
marker: PhantomData<fn() -> T>,
}
impl<T> Default for EventCursor<T> {
fn default() -> Self {
Self {
next: 0,
marker: PhantomData,
}
}
}
impl<T> fmt::Debug for EventCursor<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventCursor")
.field("next", &self.next)
.finish()
}
}
impl<T> EventCursor<T> {
pub fn read<'a>(&mut self, events: &'a Events<T>) -> EventIter<'a, T> {
let unread = events.since(self.next);
self.next = events.end();
unread
}
#[must_use]
pub fn unread(&self, events: &Events<T>) -> usize {
events.since(self.next).len_hint()
}
}
impl<T> EventIter<'_, T> {
fn len_hint(&self) -> usize {
self.inner.size_hint().0
}
}
#[derive(Clone, Copy)]
pub(crate) struct EventType {
pub(crate) ty: TypeId,
ensure: fn(&mut World),
update: fn(&mut World),
}
impl PartialEq for EventType {
fn eq(&self, other: &Self) -> bool {
self.ty == other.ty
}
}
impl Eq for EventType {}
impl EventType {
pub(crate) fn of<T: Event>() -> Self {
Self {
ty: TypeId::of::<T>(),
ensure: ensure::<T>,
update: update::<T>,
}
}
pub(crate) fn ensure(&self, world: &mut World) {
(self.ensure)(world);
}
pub(crate) fn update(&self, world: &mut World) {
(self.update)(world);
}
}
fn ensure<T: Event>(world: &mut World) {
if !world.contains_resource::<Events<T>>() {
world.insert_resource(Events::<T>::new());
}
}
fn update<T: Event>(world: &mut World) {
if let Some(events) = world.resource_mut::<Events<T>>() {
events.update();
}
}
pub struct EventWriter<'s, T: Event> {
sent: &'s mut Vec<T>,
}
impl<T: Event> fmt::Debug for EventWriter<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventWriter")
.field("sent", &self.sent.len())
.finish()
}
}
impl<T: Event> EventWriter<'_, T> {
pub fn send(&mut self, event: T) -> Result<(), EcsError> {
self.sent
.try_reserve(1)
.map_err(|_| EcsError::OutOfMemory {
entities: self.sent.len().saturating_add(1),
})?;
self.sent.push(event);
Ok(())
}
#[must_use]
pub fn len(&self) -> usize {
self.sent.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.sent.is_empty()
}
}
impl<T: Event> Sealed for EventWriter<'_, T> {}
impl<T: Event> SystemParam for EventWriter<'_, T> {
type State = Vec<T>;
type Item<'w, 's> = EventWriter<'s, T>;
fn init() -> Result<Self::State, EcsError> {
Ok(Vec::new())
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_event::<T>()
}
fn fetch<'w, 's>(
state: &'s mut Self::State,
_: Env<'w>,
) -> Result<Self::Item<'w, 's>, EcsError> {
Ok(EventWriter { sent: state })
}
fn apply(state: &mut Self::State, world: &mut World) -> Result<(), EcsError> {
ensure::<T>(world);
let result = match world.resource_mut::<Events<T>>() {
Some(events) => events.publish(state),
None => Ok(()),
};
state.clear();
result
}
fn discard(state: &mut Self::State) {
state.clear();
}
}
#[doc(hidden)]
pub struct ReaderState<T> {
cursor: EventCursor<T>,
world: Option<u64>,
}
impl<T> fmt::Debug for ReaderState<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReaderState")
.field("cursor", &self.cursor)
.finish_non_exhaustive()
}
}
pub struct EventReader<'w, 's, T: Event> {
events: graph::Res<'w, Events<T>>,
cursor: &'s mut EventCursor<T>,
}
impl<T: Event> fmt::Debug for EventReader<'_, '_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventReader")
.field("unread", &self.len())
.finish()
}
}
impl<T: Event> EventReader<'_, '_, T> {
pub fn read(&mut self) -> EventIter<'_, T> {
self.cursor.read(&self.events)
}
#[must_use]
pub fn len(&self) -> usize {
self.cursor.unread(&self.events)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T: Event> Sealed for EventReader<'_, '_, T> {}
impl<T: Event> SystemParam for EventReader<'_, '_, T> {
type State = ReaderState<T>;
type Item<'w, 's> = EventReader<'w, 's, T>;
fn init() -> Result<Self::State, EcsError> {
Ok(ReaderState {
cursor: EventCursor::default(),
world: None,
})
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_resource::<Events<T>>(AccessKind::Read)?;
access.add_event::<T>()
}
fn fetch<'w, 's>(
state: &'s mut Self::State,
env: Env<'w>,
) -> Result<Self::Item<'w, 's>, EcsError> {
let world = env.world().id();
if state.world != Some(world) {
state.world = Some(world);
state.cursor = EventCursor::default();
}
Ok(EventReader {
events: env.ctx().read::<Events<T>>().map_err(EcsError::graph)?,
cursor: &mut state.cursor,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_cursor_skips_dropped_events_and_never_rereads() {
let mut events = Events::new();
let mut early = EventCursor::default();
let mut late = EventCursor::default();
for i in 0..3 {
events.send(i).unwrap();
}
assert_eq!(early.read(&events).copied().collect::<Vec<_>>(), [0, 1, 2]);
events.update();
events.send(3).unwrap();
events.update();
events.send(4).unwrap();
assert_eq!(late.unread(&events), 2);
assert_eq!(late.read(&events).copied().collect::<Vec<_>>(), [3, 4]);
assert_eq!(early.read(&events).copied().collect::<Vec<_>>(), [3, 4]);
assert_eq!(early.unread(&events), 0);
let mut sent = vec![5, 6];
events.publish(&mut sent).unwrap();
assert!(sent.is_empty());
assert_eq!(late.read(&events).copied().collect::<Vec<_>>(), [5, 6]);
assert!(format!("{events:?}").contains("next_id: 7"));
assert!(format!("{late:?}").contains("next: 7"));
assert!(EventType::of::<u8>() == EventType::of::<u8>());
assert!(EventType::of::<u8>() != EventType::of::<u16>());
}
}