use core::ffi::c_void;
use crate::abi::Event;
use crate::host::Host;
use crate::services::{Emitter, ModEvent};
use super::{Load, Off, event_spec, hand_over, ok};
impl Load<'_> {
pub fn on<E: crate::events::Kind>(
&mut self,
handler: impl Fn(&E::Data) + Send + Sync + 'static,
) -> Result<(), Off> {
self.on_raw_event(E::EVENT, move |raw| {
if let Some(data) = unsafe { E::data(raw) } {
handler(&data);
}
})
}
pub fn on_raw_event<F>(&mut self, event: Event, handler: F) -> Result<(), Off>
where
F: Fn(*const c_void) + Send + Sync + 'static,
{
let host = self.host;
hand_over(
handler,
|user| host.subscribe(event, deliver::<F>, user),
&format!("event {}", event.0),
)
}
pub fn event_type(&self, name: &'static str) -> Result<crate::game_events::EventType, Off> {
crate::game_events::EventType::resolve(self, name)
}
pub fn on_raw_game_event<F>(
&mut self,
ty: crate::game_events::EventType,
handler: F,
) -> Result<(), Off>
where
F: Fn(&crate::game_events::RawGameEvent<'_>) + Send + Sync + 'static,
{
self.game_events(ty.hash(), ty.name(), handler)
}
pub fn on_game<E: crate::game_events::Kind>(
&mut self,
handler: impl Fn(&E) + Send + Sync + 'static,
) -> Result<(), Off> {
E::subscribe(self, handler)
}
pub fn on_every_raw_game_event<F>(&mut self, handler: F) -> Result<(), Off>
where
F: Fn(&crate::game_events::RawGameEvent<'_>) + Send + Sync + 'static,
{
self.game_events(0, "every type", handler)
}
pub(super) fn game_events<F>(
&mut self,
type_hash: u32,
what: &str,
handler: F,
) -> Result<(), Off>
where
F: Fn(&crate::game_events::RawGameEvent<'_>) + Send + Sync + 'static,
{
let api = self.api();
let host = self.host.handle();
hand_over(
handler,
|user| (api.game_event)(host, type_hash, deliver_game::<F>, user),
&format!("game events of {what}"),
)
}
pub fn define_event<E: ModEvent>(&mut self) -> Result<Emitter<E>, Off> {
let spec = event_spec::<E>();
let mut id = 0;
let status = (self.api().define_event)(self.host.handle(), &raw const spec, &raw mut id);
ok(status, Emitter::new(self.host, id), || {
format!(
"event {}: named after another mod or defined twice",
E::NAME
)
})
}
pub fn on_mod_event<E: ModEvent>(
&mut self,
handler: impl Fn(&E) + Send + Sync + 'static,
) -> Result<(), Off> {
subscribe_mod_event(self.host, handler)
}
}
pub(super) fn subscribe_mod_event<E: ModEvent, F: Fn(&E) + Send + Sync + 'static>(
host: Host,
handler: F,
) -> Result<(), Off> {
let spec = event_spec::<E>();
hand_over(
handler,
|user| {
(host.api().subscribe_event)(
host.handle(),
&raw const spec,
deliver_mod_event::<E, F>,
user,
)
},
&format!("event {}", E::NAME),
)
}
pub(super) unsafe extern "system" fn deliver_mod_event<E: ModEvent, F: Fn(&E)>(
data: *const c_void,
user: *mut c_void,
) {
let handler = unsafe { &*user.cast::<F>() };
let Some(event) = (unsafe { data.cast::<E>().as_ref() }) else {
return;
};
crate::host::guard("panic in a mod event handler", || (), || handler(event));
}
pub(super) unsafe extern "system" fn deliver<F: Fn(*const c_void)>(
_event: Event,
data: *const c_void,
user: *mut c_void,
) {
let handler = unsafe { &*user.cast::<F>() };
crate::host::guard("panic in an event handler", || (), || handler(data));
}
pub(super) unsafe extern "system" fn deliver_game<F: Fn(&crate::game_events::RawGameEvent<'_>)>(
event: *const crate::abi::GameEventData,
user: *mut c_void,
) {
let handler = unsafe { &*user.cast::<F>() };
let Some(event) = (unsafe { crate::game_events::RawGameEvent::from_raw(event) }) else {
return;
};
crate::host::guard("panic in a game event handler", || (), || handler(&event));
}