use std::any::{Any, TypeId};
use std::collections::{HashMap, VecDeque};
use serde::Serialize;
use serde_json::{Value, json};
use crate::reflect::{raw_json, short_name};
#[derive(Default)]
pub struct Events {
channels: Vec<Box<dyn Channel>>,
by_key: HashMap<Key, usize>,
last: Option<(Key, usize)>,
pub(crate) reader: u32,
frame: u64,
tick: u64,
pub(crate) keep_ticks: u32,
reflected: Vec<ReflectedEvent>,
pub(crate) feed: Option<Feed>,
}
const FEED_READER: u32 = u32::MAX;
struct ReflectedEvent {
name: String,
type_id: TypeId,
drain: fn(&mut Events, u32, &mut dyn FnMut(u64, Value)),
kept: fn(&Events) -> usize,
}
pub(crate) struct Feed {
pub(crate) types: Vec<String>,
pub(crate) items: VecDeque<Value>,
pub(crate) capacity: usize,
pub(crate) dropped: u64,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum Key {
Type(TypeId),
Raw(u32),
}
impl Events {
pub fn send<T: 'static>(&mut self, event: T) {
let (frame, tick) = (self.frame, self.tick);
let ch = self.typed::<T>();
ch.log.push(frame, tick);
ch.items.push(event);
}
pub fn read<T: 'static>(&mut self) -> &[T] {
let reader = self.reader;
let ch = self.typed::<T>();
let range = ch.log.read(reader);
&ch.items[range]
}
#[must_use]
pub fn peek<T: 'static>(&self) -> &[T] {
match self.by_key.get(&Key::Type(TypeId::of::<T>())) {
Some(&i) => {
let any: &dyn Any = self.channels[i].as_any();
&any.downcast_ref::<Typed<T>>().expect("channel type").items
}
None => &[],
}
}
fn typed<T: 'static>(&mut self) -> &mut Typed<T> {
let i = self.channel(Key::Type(TypeId::of::<T>()), || {
Box::new(Typed::<T> {
log: Log::default(),
items: Vec::new(),
})
});
self.channels[i]
.as_any_mut()
.downcast_mut::<Typed<T>>()
.expect("channel type")
}
#[inline]
fn channel(&mut self, key: Key, make: impl FnOnce() -> Box<dyn Channel>) -> usize {
if let Some((k, i)) = self.last
&& k == key
{
return i;
}
let i = match self.by_key.get(&key) {
Some(&i) => i,
None => {
self.channels.push(make());
let i = self.channels.len() - 1;
self.by_key.insert(key, i);
i
}
};
self.last = Some((key, i));
i
}
#[doc(hidden)]
pub fn send_raw(&mut self, id: u32, bytes: &[u8]) -> Result<(), &'static str> {
let (frame, tick) = (self.frame, self.tick);
let ch = self.raw(id, bytes.len());
if ch.size != bytes.len() {
return Err("event size does not match earlier sends of this event type");
}
ch.push(bytes);
ch.log.push(frame, tick);
Ok(())
}
#[doc(hidden)]
pub fn read_raw(&mut self, id: u32, size: usize) -> Result<(*const u8, usize), &'static str> {
let reader = self.reader;
let ch = self.raw(id, size);
if ch.size != size {
return Err("event size does not match earlier sends of this event type");
}
let range = ch.log.read(reader);
let bytes = ch.bytes();
let start = range.start * size;
Ok((bytes[start..].as_ptr(), range.len()))
}
fn raw(&mut self, id: u32, size: usize) -> &mut Raw {
let i = self.channel(Key::Raw(id), || {
Box::new(Raw {
log: Log::default(),
id,
size,
blocks: Vec::new(),
})
});
self.channels[i]
.as_any_mut()
.downcast_mut::<Raw>()
.expect("channel type")
}
#[track_caller]
pub fn reflect<T: Serialize + 'static>(&mut self) {
let type_name = std::any::type_name::<T>();
let name = short_name(type_name);
if let Some(r) = self.reflected.iter().find(|r| r.name == name) {
assert!(
r.type_id == TypeId::of::<T>(),
"Events::reflect: two event types would both be named `{name}`"
);
return;
}
fn drain<T: Serialize + 'static>(ev: &mut Events, reader: u32, f: &mut dyn FnMut(u64, Value)) {
let ch = ev.typed::<T>();
let range = ch.log.read(reader);
for i in range {
let v = serde_json::to_value(&ch.items[i]).unwrap_or_else(|e| json!({ "error": e.to_string() }));
f(ch.log.tick_at(i), v);
}
}
fn kept<T: 'static>(ev: &Events) -> usize {
ev.peek::<T>().len()
}
self.reflected.push(ReflectedEvent {
name,
type_id: TypeId::of::<T>(),
drain: drain::<T>,
kept: kept::<T>,
});
}
pub(crate) fn reflected_json(&self, world: &crate::World) -> Vec<Value> {
let mut out: Vec<Value> = (self.reflected.iter())
.map(|r| json!({ "name": r.name, "kept": (r.kept)(self) }))
.collect();
for ch in &self.channels {
if let Some(raw) = ch.as_any().downcast_ref::<Raw>()
&& let Some(name) = raw_name(world, raw.id, raw)
{
out.push(json!({ "name": name, "kept": raw.log.len }));
}
}
out
}
pub(crate) fn collect_feed(&mut self, world: &crate::World) {
let Some(mut feed) = self.feed.take() else {
return;
};
let wanted = |feed: &Feed, name: &str| feed.types.is_empty() || feed.types.iter().any(|t| t == name);
for i in 0..self.reflected.len() {
if !wanted(&feed, &self.reflected[i].name) {
continue;
}
let (drain, name) = (self.reflected[i].drain, self.reflected[i].name.clone());
drain(self, FEED_READER, &mut |tick, data| {
feed.push(json!({ "tick": tick, "type": name, "data": data }));
});
}
for i in 0..self.channels.len() {
let Some(raw) = self.channels[i].as_any_mut().downcast_mut::<Raw>() else {
continue;
};
let (id, size) = (raw.id, raw.size);
let Some(name) = raw_name(world, id, raw).map(str::to_string) else {
continue;
};
if !wanted(&feed, &name) {
continue;
}
let range = raw.log.read(FEED_READER);
let Some(component) = world.component_at(id as usize) else {
continue;
};
for e in range {
let p = raw.bytes()[e * size..].as_ptr();
if let Some((_, data)) = unsafe { raw_json(world, component, p) } {
feed.push(json!({ "tick": raw.log.tick_at(e), "type": name, "data": data }));
}
}
}
self.feed = Some(feed);
}
pub(crate) fn subscribe(&mut self, world: &crate::World, types: Vec<String>, capacity: usize) {
self.feed = Some(Feed {
types,
items: VecDeque::new(),
capacity,
dropped: 0,
});
self.collect_feed(world);
if let Some(f) = &mut self.feed {
f.items.clear();
}
}
pub(crate) fn begin_frame(&mut self, frame: u64, tick: u64) {
self.frame = frame;
self.tick = tick;
let keep = u64::from(self.keep_ticks.max(1));
for ch in &mut self.channels {
ch.drop_old(frame, tick, keep);
}
}
pub(crate) fn set_tick(&mut self, tick: u64) {
self.tick = tick;
}
}
impl Feed {
fn push(&mut self, v: Value) {
if self.items.len() == self.capacity {
self.items.pop_front();
self.dropped += 1;
}
if self.capacity > 0 {
self.items.push_back(v);
}
}
}
fn raw_name<'w>(world: &'w crate::World, id: u32, raw: &Raw) -> Option<&'w str> {
let component = world.component_at(id as usize)?;
if world.component_info(component).layout().size() != raw.size {
return None;
}
crate::reflect::raw_name(world, component)
}
trait Channel: Any {
fn drop_old(&mut self, frame: u64, tick: u64, keep_ticks: u64);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
#[derive(Default)]
struct Log {
base: u64,
len: usize,
runs: Vec<(u64, u64, u64)>,
cursors: Vec<(u32, u64)>,
}
impl Log {
#[inline]
fn push(&mut self, frame: u64, tick: u64) {
match self.runs.last() {
Some(&(_, f, t)) if f == frame && t == tick => {}
_ => self.runs.push((self.base + self.len as u64, frame, tick)),
}
self.len += 1;
}
fn read(&mut self, reader: u32) -> std::ops::Range<usize> {
let end = self.base + self.len as u64;
let cursor = match self.cursors.iter_mut().find(|c| c.0 == reader) {
Some(c) => std::mem::replace(&mut c.1, end),
None => {
self.cursors.push((reader, end));
self.base
}
};
let start = cursor.max(self.base) - self.base;
start as usize..self.len
}
fn tick_at(&self, index: usize) -> u64 {
let seq = self.base + index as u64;
let run = self.runs.partition_point(|r| r.0 <= seq) - 1;
self.runs[run].2
}
fn drop_expired(&mut self, frame: u64, tick: u64, keep_ticks: u64) -> usize {
let gone = self
.runs
.iter()
.take_while(|&&(_, f, t)| f + 1 < frame && t + keep_ticks < tick)
.count();
if gone == 0 {
return 0;
}
self.runs.drain(..gone);
let new_base = self.runs.first().map_or(self.base + self.len as u64, |r| r.0);
let n = (new_base - self.base) as usize;
self.base = new_base;
self.len -= n;
n
}
}
struct Typed<T> {
log: Log,
items: Vec<T>,
}
impl<T: 'static> Channel for Typed<T> {
fn drop_old(&mut self, frame: u64, tick: u64, keep_ticks: u64) {
let n = self.log.drop_expired(frame, tick, keep_ticks);
if n > 0 {
self.items.drain(..n);
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(C, align(16))]
struct Block([u8; 16]);
struct Raw {
log: Log,
id: u32,
size: usize,
blocks: Vec<Block>,
}
impl Raw {
fn bytes(&self) -> &[u8] {
let all: &[u8] = bytemuck::cast_slice(&self.blocks);
&all[..self.log.len * self.size]
}
fn push(&mut self, value: &[u8]) {
let start = self.log.len * self.size;
let end = start + value.len();
self.blocks.resize(end.div_ceil(16), Block([0; 16]));
let all: &mut [u8] = bytemuck::cast_slice_mut(&mut self.blocks);
all[start..end].copy_from_slice(value);
}
}
impl Channel for Raw {
fn drop_old(&mut self, frame: u64, tick: u64, keep_ticks: u64) {
let old_len = self.log.len;
let n = self.log.drop_expired(frame, tick, keep_ticks);
if n > 0 {
let all: &mut [u8] = bytemuck::cast_slice_mut(&mut self.blocks);
all.copy_within(n * self.size..old_len * self.size, 0);
self.blocks.truncate((self.log.len * self.size).div_ceil(16));
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_reader_sees_each_event_once() {
let mut ev = Events::default();
ev.send(1u32);
ev.reader = 1;
assert_eq!(ev.read::<u32>(), &[1]);
assert!(ev.read::<u32>().is_empty());
ev.send(2u32);
ev.reader = 2;
assert_eq!(ev.read::<u32>(), &[1, 2]);
ev.reader = 1;
assert_eq!(ev.read::<u32>(), &[2]);
assert_eq!(ev.peek::<u32>(), &[1, 2]);
}
#[test]
fn events_last_one_frame_and_keep_ticks() {
let mut ev = Events {
keep_ticks: 2,
..Default::default()
};
ev.begin_frame(1, 0);
ev.send("a");
ev.begin_frame(2, 1);
ev.begin_frame(3, 2);
assert_eq!(ev.peek::<&str>(), &["a"], "kept for keep_ticks ticks");
ev.begin_frame(4, 3);
assert!(ev.peek::<&str>().is_empty());
ev.send("b");
ev.begin_frame(5, 3);
ev.begin_frame(6, 3);
assert_eq!(ev.peek::<&str>(), &["b"]);
}
#[test]
fn a_reader_that_missed_dropped_events_gets_what_is_left() {
let mut ev = Events {
reader: 1,
..Default::default()
};
assert!(ev.read::<u8>().is_empty());
ev.begin_frame(1, 0);
ev.send(1u8);
ev.begin_frame(5, 5);
ev.send(2u8);
assert_eq!(ev.read::<u8>(), &[2]);
}
#[test]
fn raw_events_are_packed_aligned_and_dropped() {
let mut ev = Events::default();
for i in 0..5u8 {
ev.send_raw(9, &[i, i, i]).unwrap();
}
assert!(ev.send_raw(9, &[0; 4]).is_err());
assert!(ev.read_raw(9, 4).is_err());
ev.reader = 3;
let (ptr, n) = ev.read_raw(9, 3).unwrap();
assert_eq!(n, 5);
assert_eq!(ptr as usize % 16, 0);
assert_eq!(ev.raw(9, 3).bytes()[12..15], [4, 4, 4]);
ev.begin_frame(1, 0);
ev.send_raw(9, &[7, 7, 7]).unwrap();
ev.begin_frame(2, 3);
assert_eq!(ev.raw(9, 3).bytes(), &[7, 7, 7]);
let (_, n) = ev.read_raw(9, 3).unwrap();
assert_eq!(n, 1);
let (_, n) = ev.read_raw(9, 3).unwrap();
assert_eq!(n, 0);
}
#[test]
fn feeds_collect_reflected_rust_and_c_events() {
use crate::{Field, FieldType, World, WorldReflect};
#[derive(serde::Serialize)]
struct Ping(u32);
struct Hidden;
let mut world = World::new();
let hit = unsafe { world.register_component("Hit", 8, 4, None) };
unsafe {
world.reflect_fields(
hit,
&[
Field::new("damage", FieldType::F32, 0),
Field::new("target", FieldType::U32, 4),
],
);
}
let mut ev = Events::default();
ev.reflect::<Ping>();
ev.send(Ping(1)); ev.subscribe(&world, Vec::new(), 10);
ev.send(Ping(2));
ev.send(Hidden);
let mut bytes = [0u8; 8];
bytes[..4].copy_from_slice(&2.5f32.to_ne_bytes());
bytes[4..].copy_from_slice(&7u32.to_ne_bytes());
ev.send_raw(hit.index() as u32, &bytes).unwrap();
ev.collect_feed(&world);
let feed = ev.feed.as_ref().unwrap();
let items: Vec<&Value> = feed.items.iter().collect();
assert_eq!(
items,
[
&json!({ "tick": 0, "type": "Ping", "data": 2 }),
&json!({ "tick": 0, "type": "Hit", "data": { "damage": 2.5, "target": 7 } }),
]
);
let names: Vec<Value> = ev.reflected_json(&world).iter().map(|v| v["name"].clone()).collect();
assert_eq!(names, [json!("Ping"), json!("Hit")]);
ev.subscribe(&world, vec!["Hit".into()], 10);
ev.send(Ping(3));
ev.send_raw(hit.index() as u32, &bytes).unwrap();
ev.collect_feed(&world);
assert_eq!(ev.feed.as_ref().unwrap().items.len(), 1);
}
}