1use std::marker::PhantomData;
5use std::os::raw::c_void;
6use yog_sync::Arc;
7
8use yog_abi::{YogBytes, YogHandlerFn, YogReply, YogStatus, YogStr, priority, status};
9use yog_event::{Event, EventPhase, keyed};
10
11use crate::registry::{Registry, guard};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Flow {
16 Continue,
18 Cancel,
20}
21
22pub trait IntoFlow {
25 fn into_flow(self) -> Flow;
26}
27
28impl IntoFlow for () {
29 fn into_flow(self) -> Flow {
30 Flow::Continue
31 }
32}
33
34impl IntoFlow for bool {
35 fn into_flow(self) -> Flow {
36 if self { Flow::Continue } else { Flow::Cancel }
37 }
38}
39
40impl IntoFlow for Flow {
41 fn into_flow(self) -> Flow {
42 self
43 }
44}
45
46pub struct EventCx {
48 phase: EventPhase,
49}
50
51impl EventCx {
52 pub fn phase(&self) -> EventPhase {
54 self.phase
55 }
56
57 pub fn is_pre(&self) -> bool {
58 self.phase == EventPhase::Pre
59 }
60}
61
62pub(crate) struct Subscription<E, F> {
65 handler: Arc<F>,
66 phase: EventPhase,
67 event: PhantomData<fn(&E)>,
68}
69
70unsafe extern "C" fn on_event<E: Event, F, R>(
73 ud: *mut c_void,
74 payload: YogBytes,
75 _reply: *const YogReply,
76) -> YogStatus
77where
78 F: Fn(&E, &EventCx) -> R + Send + Sync,
79 R: IntoFlow,
80{
81 let s = unsafe { &*(ud as *const Subscription<E, F>) };
82 let Some(event) = E::from_bytes(unsafe { payload.as_slice() }) else {
83 return status::BAD_PAYLOAD;
84 };
85 let flow = guard(Flow::Continue, || (s.handler)(&event, &s.cx()).into_flow());
86 if s.phase == EventPhase::Pre && flow == Flow::Cancel {
87 status::CANCELLED
88 } else {
89 status::OK
90 }
91}
92
93unsafe extern "C" fn on_change<E: Event, F>(
97 ud: *mut c_void,
98 payload: YogBytes,
99 reply: *const YogReply,
100) -> YogStatus
101where
102 F: Fn(&mut E, &EventCx) + Send + Sync,
103{
104 let s = unsafe { &*(ud as *const Subscription<E, F>) };
105 let Some(mut event) = E::from_bytes(unsafe { payload.as_slice() }) else {
106 return status::BAD_PAYLOAD;
107 };
108 guard((), || (s.handler)(&mut event, &s.cx()));
109 unsafe { YogReply::send(reply, &event.to_bytes()) };
110 status::OK
111}
112
113impl<E, F> Subscription<E, F> {
114 pub(crate) fn new(handler: Arc<F>, phase: EventPhase) -> Self {
115 Self {
116 handler,
117 phase,
118 event: PhantomData,
119 }
120 }
121
122 pub(crate) fn handler(&self) -> &F {
123 &self.handler
124 }
125
126 pub(crate) fn cx(&self) -> EventCx {
127 EventCx { phase: self.phase }
128 }
129}
130
131pub fn emit<E: Event>(event: &E) -> bool {
134 let api = crate::registry::global_api();
135 if api.is_null() {
136 return true;
137 }
138 let bus = unsafe { &*(*api).bus };
139 event.with_bytes(|bytes| {
140 let send = |at| unsafe { (bus.emit)(bus.ctx, at, YogBytes::new(bytes), std::ptr::null()) };
141 if let Some(pre) = event.pre_address()
142 && send(pre) == status::CANCELLED
143 {
144 return false;
145 }
146 send(event.address());
147 true
148 })
149}
150
151impl Registry {
152 pub fn on<E: Event + 'static, R: IntoFlow + 'static>(
155 &mut self,
156 handler: impl Fn(&E, &EventCx) -> R + Send + Sync + 'static,
157 ) {
158 self.both::<E, _, R>(E::PRE.map(str::to_owned), E::ADDRESS.to_owned(), handler);
159 }
160
161 pub fn on_key<E: Event + 'static, R: IntoFlow + 'static>(
164 &mut self,
165 key: &str,
166 handler: impl Fn(&E, &EventCx) -> R + Send + Sync + 'static,
167 ) {
168 self.both::<E, _, R>(
169 E::PRE.map(|p| keyed(p, key)),
170 keyed(E::ADDRESS, key),
171 handler,
172 );
173 }
174
175 pub fn before<E: Event + 'static, R: IntoFlow + 'static>(
178 &mut self,
179 handler: impl Fn(&E, &EventCx) -> R + Send + Sync + 'static,
180 ) {
181 if let Some(pre) = E::PRE {
182 self.at::<E, _, R>(pre, EventPhase::Pre, Arc::new(handler));
183 }
184 }
185
186 pub fn before_key<E: Event + 'static, R: IntoFlow + 'static>(
188 &mut self,
189 key: &str,
190 handler: impl Fn(&E, &EventCx) -> R + Send + Sync + 'static,
191 ) {
192 if let Some(pre) = E::PRE {
193 self.at::<E, _, R>(&keyed(pre, key), EventPhase::Pre, Arc::new(handler));
194 }
195 }
196
197 pub fn after<E: Event + 'static, R: IntoFlow + 'static>(
199 &mut self,
200 handler: impl Fn(&E, &EventCx) -> R + Send + Sync + 'static,
201 ) {
202 self.at::<E, _, R>(E::ADDRESS, EventPhase::Post, Arc::new(handler));
203 }
204
205 pub(crate) fn after_at<E: Event + 'static, R: IntoFlow + 'static, F>(
207 &self,
208 priority: i32,
209 handler: F,
210 ) where
211 F: Fn(&E, &EventCx) -> R + Send + Sync + 'static,
212 {
213 self.subscribe_at::<E, F>(
214 E::ADDRESS,
215 priority,
216 EventPhase::Post,
217 Arc::new(handler),
218 on_event::<E, F, R>,
219 );
220 }
221
222 pub fn change<E: Event + 'static>(
225 &mut self,
226 handler: impl Fn(&mut E, &EventCx) + Send + Sync + 'static,
227 ) {
228 self.changing::<E, _>(E::ADDRESS, priority::DEFAULT, Arc::new(handler));
229 }
230
231 pub(crate) fn change_key_at<E: Event + 'static>(
234 &mut self,
235 key: &str,
236 priority: i32,
237 handler: impl Fn(&mut E, &EventCx) + Send + Sync + 'static,
238 ) {
239 self.changing::<E, _>(&keyed(E::ADDRESS, key), priority, Arc::new(handler));
240 }
241
242 fn both<E: Event + 'static, F, R>(&mut self, pre: Option<String>, post: String, handler: F)
244 where
245 F: Fn(&E, &EventCx) -> R + Send + Sync + 'static,
246 R: IntoFlow + 'static,
247 {
248 let handler = Arc::new(handler);
249 if let Some(pre) = pre {
250 self.at::<E, F, R>(&pre, EventPhase::Pre, Arc::clone(&handler));
251 }
252 self.at::<E, F, R>(&post, EventPhase::Post, handler);
253 }
254
255 fn at<E: Event + 'static, F, R>(&mut self, address: &str, phase: EventPhase, handler: Arc<F>)
257 where
258 F: Fn(&E, &EventCx) -> R + Send + Sync + 'static,
259 R: IntoFlow + 'static,
260 {
261 self.subscribe::<E, F>(address, phase, handler, on_event::<E, F, R>);
262 }
263
264 fn changing<E: Event + 'static, F>(&mut self, address: &str, priority: i32, handler: Arc<F>)
265 where
266 F: Fn(&mut E, &EventCx) + Send + Sync + 'static,
267 {
268 self.subscribe_at::<E, F>(
269 address,
270 priority,
271 EventPhase::Post,
272 handler,
273 on_change::<E, F>,
274 );
275 }
276
277 fn subscribe<E: 'static, F: Send + Sync + 'static>(
278 &mut self,
279 address: &str,
280 phase: EventPhase,
281 handler: Arc<F>,
282 run: YogHandlerFn,
283 ) {
284 self.subscribe_at::<E, F>(address, priority::DEFAULT, phase, handler, run);
285 }
286
287 fn subscribe_at<E: 'static, F: Send + Sync + 'static>(
288 &self,
289 address: &str,
290 priority: i32,
291 phase: EventPhase,
292 handler: Arc<F>,
293 run: YogHandlerFn,
294 ) {
295 let api = self.api();
296 let bus = unsafe { &*api.bus };
297 let sub = Box::into_raw(Box::new(Subscription::<E, F>::new(handler, phase)));
298 unsafe {
299 let at = (bus.resolve)(bus.ctx, YogStr::from_str(address));
300 (bus.on)(bus.ctx, at, priority, sub.cast::<c_void>(), run);
301 }
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use yog_event::{block, player};
309
310 fn deliver_bytes<E: Event, R: IntoFlow, F: Fn(&E, &EventCx) -> R + Send + Sync>(
312 handler: F,
313 phase: EventPhase,
314 bytes: &[u8],
315 ) -> YogStatus {
316 let sub = Subscription::<E, F>::new(Arc::new(handler), phase);
317 unsafe {
318 on_event::<E, F, R>(
319 (&sub as *const Subscription<E, F>)
320 .cast_mut()
321 .cast::<c_void>(),
322 YogBytes::new(bytes),
323 std::ptr::null(),
324 )
325 }
326 }
327
328 fn deliver<E: Event, R: IntoFlow, F: Fn(&E, &EventCx) -> R + Send + Sync>(
329 handler: F,
330 phase: EventPhase,
331 event: &E,
332 ) -> YogStatus {
333 deliver_bytes(handler, phase, &event.to_bytes())
334 }
335
336 fn chat(message: &str) -> player::chat::Message {
337 player::chat::Message {
338 player_name: "Dev".into(),
339 message: message.into(),
340 dimension: "minecraft:overworld".into(),
341 }
342 }
343
344 #[test]
345 fn events_arrive_read_with_their_phase_and_a_pre_false_cancels() {
346 let allow = |e: &player::chat::Message, _: &EventCx| e.message != "cancel me";
347 assert_eq!(deliver(allow, EventPhase::Pre, &chat("hi")), status::OK);
348 assert_eq!(
349 deliver(allow, EventPhase::Pre, &chat("cancel me")),
350 status::CANCELLED
351 );
352 assert_eq!(
353 deliver(allow, EventPhase::Post, &chat("cancel me")),
354 status::OK,
355 "only a pre-event can be cancelled"
356 );
357 }
358
359 #[test]
360 fn a_panicking_handler_allows_the_action() {
361 let handler = |_: &block::Break, _: &EventCx| -> bool { panic!("mod bug") };
362 let event = block::Break {
363 player_name: "Dev".into(),
364 block_id: "minecraft:stone".into(),
365 pos: yog_core::BlockPos { x: 1, y: 2, z: 3 },
366 dimension: "minecraft:overworld".into(),
367 };
368 assert_eq!(deliver(handler, EventPhase::Pre, &event), status::OK);
369 }
370
371 #[test]
372 fn a_payload_that_isnt_the_event_is_refused() {
373 let handler = |_: &player::chat::Message, _: &EventCx| true;
374 assert_eq!(
375 deliver_bytes(handler, EventPhase::Post, b"junk"),
376 status::BAD_PAYLOAD
377 );
378 }
379
380 fn change<E: Event, F: Fn(&mut E, &EventCx) + Send + Sync>(
382 handler: F,
383 event: &E,
384 ) -> (YogStatus, Vec<u8>) {
385 let sub = Subscription::<E, F>::new(Arc::new(handler), EventPhase::Post);
386 let mut out = Vec::<u8>::new();
387 let reply = YogReply::to_vec(&mut out);
388 let ud = (&sub as *const Subscription<E, F>)
389 .cast_mut()
390 .cast::<c_void>();
391 let st = unsafe { on_change::<E, F>(ud, YogBytes::new(&event.to_bytes()), &reply) };
392 (st, out)
393 }
394
395 #[test]
396 fn a_changing_handler_replies_with_the_changed_event() {
397 use yog_event::Message;
398 use yog_event::world::loot::{Roll, Stack};
399 let more =
400 |loot: &mut Roll, _: &EventCx| loot.items.push(Stack::new("minecraft:diamond", 1));
401 let (st, out) = change(
402 more,
403 &Roll {
404 table: "minecraft:chests/igloo_chest".into(),
405 ..Default::default()
406 },
407 );
408 assert_eq!(st, status::OK);
409 let changed = Roll::from_bytes(&out).expect("the reply is the event");
410 assert_eq!(changed.items, vec![Stack::new("minecraft:diamond", 1)]);
411 }
412
413 #[test]
414 fn a_handler_returning_nothing_lets_it_happen() {
415 let handler = |_: &player::chat::Message, _: &EventCx| {};
416 assert_eq!(deliver(handler, EventPhase::Pre, &chat("hi")), status::OK);
417 let cancel = |_: &player::chat::Message, _: &EventCx| Flow::Cancel;
418 assert_eq!(
419 deliver(cancel, EventPhase::Pre, &chat("hi")),
420 status::CANCELLED
421 );
422 }
423}