pub mod events;
pub mod handler;
pub mod priority;
pub mod result;
use std::any::TypeId;
use std::collections::HashMap;
use deepslate_protocol::types::GameProfile;
pub use events::*;
pub use priority::{Phase, PostOrder};
pub use result::{EventResult, ResultedEvent};
use handler::{
EventPipeline, HandlerRegistration, ObserverRegistration, TypedHandler, TypedObserver,
sort_handlers,
};
#[derive(Debug, Clone)]
pub struct PlayerInfo {
pub profile: GameProfile,
pub remote_addr: String,
pub protocol_version: i32,
pub virtual_host: String,
}
pub struct EventManager {
pipelines: HashMap<TypeId, EventPipeline>,
}
impl EventManager {
#[must_use]
pub fn new() -> Self {
Self {
pipelines: HashMap::new(),
}
}
pub fn subscribe<E: 'static + Send + Sync>(
&mut self,
priority: PostOrder,
handler: impl Fn(&mut E) + Send + Sync + 'static,
) {
let type_id = TypeId::of::<E>();
let registration = HandlerRegistration {
priority,
handler: Box::new(TypedHandler::new(handler)),
};
let pipeline = self.pipelines.entry(type_id).or_default();
pipeline.handlers.push(registration);
sort_handlers(&mut pipeline.handlers);
}
pub fn observe<E: 'static + Send + Sync>(
&mut self,
phase: Phase,
observer: impl Fn(&E) + Send + Sync + 'static,
) {
let type_id = TypeId::of::<E>();
let registration = ObserverRegistration {
observer: Box::new(TypedObserver::new(observer)),
};
let pipeline = self.pipelines.entry(type_id).or_default();
match phase {
Phase::Pre => pipeline.pre_observers.push(registration),
Phase::Post => pipeline.post_observers.push(registration),
}
}
pub fn fire<E: 'static + Send + Sync>(&self, mut event: E) -> E {
if let Some(pipeline) = self.pipelines.get(&TypeId::of::<E>()) {
for observer in &pipeline.pre_observers {
observer.observer.call(&event);
}
for handler in &pipeline.handlers {
handler.handler.call(&mut event);
}
for observer in &pipeline.post_observers {
observer.observer.call(&event);
}
}
event
}
}
impl Default for EventManager {
fn default() -> Self {
Self::new()
}
}
pub trait Plugin: Send + Sync + 'static {
fn register(&self, events: &mut EventManager);
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use super::*;
#[derive(Debug, PartialEq, Eq)]
struct TestEvent {
value: i32,
}
#[test]
fn pre_observer_runs_before_handlers() {
let order = Arc::new(AtomicU32::new(0));
let mut em = EventManager::new();
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Pre, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 0);
});
let o = Arc::clone(&order);
em.subscribe::<TestEvent>(PostOrder::NORMAL, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 1);
});
em.fire(TestEvent { value: 0 });
assert_eq!(order.load(Ordering::SeqCst), 2);
}
#[test]
fn post_observer_runs_after_handlers() {
let order = Arc::new(AtomicU32::new(0));
let mut em = EventManager::new();
let o = Arc::clone(&order);
em.subscribe::<TestEvent>(PostOrder::NORMAL, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 0);
});
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Post, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 1);
});
em.fire(TestEvent { value: 0 });
assert_eq!(order.load(Ordering::SeqCst), 2);
}
#[test]
fn full_pipeline_dispatches_in_order() {
let order = Arc::new(AtomicU32::new(0));
let mut em = EventManager::new();
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Post, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 3);
});
let o = Arc::clone(&order);
em.subscribe::<TestEvent>(PostOrder::NORMAL, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 2);
});
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Pre, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 0);
});
let o = Arc::clone(&order);
em.subscribe::<TestEvent>(PostOrder::FIRST, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 1);
});
em.fire(TestEvent { value: 0 });
assert_eq!(order.load(Ordering::SeqCst), 4);
}
#[test]
fn observers_see_handler_mutations() {
let mut em = EventManager::new();
em.subscribe::<TestEvent>(PostOrder::NORMAL, |event| {
event.value = 42;
});
let saw_value = Arc::new(AtomicU32::new(0));
let v = Arc::clone(&saw_value);
em.observe::<TestEvent>(Phase::Post, move |event| {
v.store(u32::try_from(event.value).unwrap(), Ordering::SeqCst);
});
em.fire(TestEvent { value: 0 });
assert_eq!(saw_value.load(Ordering::SeqCst), 42);
}
#[test]
fn pre_observers_see_initial_state() {
let mut em = EventManager::new();
let saw_value = Arc::new(AtomicU32::new(0));
let v = Arc::clone(&saw_value);
em.observe::<TestEvent>(Phase::Pre, move |event| {
v.store(u32::try_from(event.value).unwrap(), Ordering::SeqCst);
});
em.subscribe::<TestEvent>(PostOrder::NORMAL, |event| {
event.value = 99;
});
em.fire(TestEvent { value: 7 });
assert_eq!(saw_value.load(Ordering::SeqCst), 7);
}
#[test]
fn multiple_observers_run_in_registration_order() {
let order = Arc::new(AtomicU32::new(0));
let mut em = EventManager::new();
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Pre, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 0);
});
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Pre, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 1);
});
let o = Arc::clone(&order);
em.observe::<TestEvent>(Phase::Pre, move |_| {
assert_eq!(o.fetch_add(1, Ordering::SeqCst), 2);
});
em.fire(TestEvent { value: 0 });
assert_eq!(order.load(Ordering::SeqCst), 3);
}
#[test]
fn fire_with_no_registrations() {
let em = EventManager::new();
let event = em.fire(TestEvent { value: 5 });
assert_eq!(event.value, 5);
}
#[test]
fn fire_with_only_observers() {
let mut em = EventManager::new();
let called = Arc::new(AtomicU32::new(0));
let c = Arc::clone(&called);
em.observe::<TestEvent>(Phase::Pre, move |_| {
c.fetch_add(1, Ordering::SeqCst);
});
let c = Arc::clone(&called);
em.observe::<TestEvent>(Phase::Post, move |_| {
c.fetch_add(1, Ordering::SeqCst);
});
let event = em.fire(TestEvent { value: 10 });
assert_eq!(event.value, 10);
assert_eq!(called.load(Ordering::SeqCst), 2);
}
#[test]
fn handlers_still_work_without_observers() {
let mut em = EventManager::new();
em.subscribe::<TestEvent>(PostOrder::NORMAL, |event| {
event.value += 1;
});
let event = em.fire(TestEvent { value: 0 });
assert_eq!(event.value, 1);
}
}