use core::ffi::c_void;
use core::time::Duration;
use crate::abi::{Event, TickData, TickHandle};
use crate::events::{LoaderEvent, TickPeriod, Ticked};
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> {
E::subscribe(self, handler)
}
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 on_tick(
&mut self,
every: Duration,
handler: impl Fn(&TickData) + Send + Sync + 'static,
) -> Result<TickPeriod, Off> {
let host = self.host;
let handle = subscribe_tick(host, every, typed_tick(handler))?;
Ok(TickPeriod::new(host, handle))
}
pub fn on_every_step(
&mut self,
handler: impl Fn(&TickData) + Send + Sync + 'static,
) -> Result<(), Off> {
self.on_raw_event(Event::TICKED, typed_tick(handler))
}
pub fn game_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_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)
}
}
fn typed_tick(handler: impl Fn(&TickData)) -> impl Fn(*const c_void) {
move |raw| {
if let Some(tick) = unsafe { <Ticked as LoaderEvent>::data(raw) } {
handler(&tick);
}
}
}
fn subscribe_tick<F>(host: Host, every: Duration, handler: F) -> Result<TickHandle, Off>
where
F: Fn(*const c_void) + Send + Sync + 'static,
{
let mut handle = TickHandle(0);
let out = &raw mut handle;
hand_over(
handler,
|user| {
(host.api().subscribe_tick)(
host.handle(),
crate::events::nanos(every),
deliver::<F>,
user,
out,
)
},
"the tick",
)?;
Ok(handle)
}
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));
}