1use std::os::raw::c_void;
11
12use yog_abi::{
13 YogAdvancementEvent, YogApi, YogAttackEntityEvent, YogBlockBreakEvent,
14 YogChatEvent, YogCommandEvent, YogContainerCloseEvent, YogContainerOpenEvent, YogCraftEvent,
15 YogEntityDamageEvent, YogEntityDeathEvent, YogEntityInteractEvent, YogEntitySpawnEvent,
16 YogExplosionEvent, YogGfxApi, YogItemInsertEvent, YogItemPickupEvent, YogMachineCraftEvent,
17 YogKeyPressEvent, YogPacketEvent, YogPlaceBlockEvent, YogPlayerDeathEvent, YogPlayerEvent,
18 YogPlayerMoveEvent, YogPlayerRespawnEvent, YogProjectileHitEvent, YogServer,
19 YogStr, YogUseBlockEvent, YogUseItemEvent,
20};
21use yog_book::Book;
22use yog_command::CommandContext;
23use yog_core::Server;
24use yog_dimensions::{ChunkWriter, YogDimensionDef};
25use yog_event::{
26 AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
27 ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
28 EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent, ItemInsertEvent, ItemPickupEvent,
29 KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent, PlayerLeaveEvent,
30 PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent, UseBlockEvent,
31 UseItemEvent,
32};
33use yog_gfx::GfxContext;
34use yog_network::{Packet, PacketEvent};
35use yog_registry::{ArmorMaterial, BlockDef, ItemDef, Recipe, SoundGroup, StartupGrant, ToolMaterial};
36
37pub struct CServer(pub *const YogServer);
42
43unsafe impl Send for CServer {}
44unsafe impl Sync for CServer {}
45
46macro_rules! srv {
47 ($self:ident) => {
48 unsafe { &*$self.0 }
49 };
50}
51
52impl Server for CServer {
53 fn broadcast(&self, message: &str) {
54 let s = srv!(self);
55 unsafe { (s.broadcast)(s.ctx, YogStr::from_str(message)) }
56 }
57
58 fn get_block(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
59 let s = srv!(self);
60 let owned = unsafe {
61 (s.get_block)(
62 s.ctx,
63 YogStr::from_str(dimension),
64 yog_abi::YogBlockPos {
65 x: pos.x,
66 y: pos.y,
67 z: pos.z,
68 },
69 )
70 };
71
72 take_owned(s.free_str, owned)
73 }
74
75 fn set_block(&self, dimension: &str, pos: yog_core::BlockPos, block_id: &str) -> bool {
76 let s = srv!(self);
77 unsafe {
78 (s.set_block)(
79 s.ctx,
80 YogStr::from_str(dimension),
81 yog_abi::YogBlockPos {
82 x: pos.x,
83 y: pos.y,
84 z: pos.z,
85 },
86 YogStr::from_str(block_id),
87 )
88 }
89 }
90
91 fn give_item(&self, player: &str, item_id: &str, count: u32) -> bool {
92 let s = srv!(self);
93 unsafe {
94 (s.give_item)(
95 s.ctx,
96 YogStr::from_str(player),
97 YogStr::from_str(item_id),
98 count,
99 )
100 }
101 }
102
103 fn teleport(&self, player: &str, x: f64, y: f64, z: f64) -> bool {
104 let s = srv!(self);
105 unsafe {
106 (s.player_teleport)(
107 s.ctx,
108 YogStr::from_str(player),
109 yog_abi::YogVec3 { x, y, z },
110 )
111 }
112 }
113
114 fn send_to_player(&self, player: &str, channel: &str, payload: &[u8]) -> bool {
115 let s = srv!(self);
116 unsafe {
117 (s.send_to_player)(
118 s.ctx,
119 YogStr::from_str(player),
120 YogStr::from_str(channel),
121 payload.as_ptr(),
122 payload.len() as u32,
123 )
124 }
125 }
126
127 fn send_to_server(&self, channel: &str, payload: &[u8]) -> bool {
128 let s = srv!(self);
129 unsafe {
130 (s.send_to_server)(
131 s.ctx,
132 YogStr::from_str(channel),
133 payload.as_ptr(),
134 payload.len() as u32,
135 )
136 }
137 }
138
139 fn entity_teleport(&self, uuid: &str, x: f64, y: f64, z: f64) -> bool {
140 let s = srv!(self);
141 unsafe { (s.entity_teleport)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x, y, z }) }
142 }
143
144 fn entity_position(&self, uuid: &str) -> Option<(f64, f64, f64)> {
145 let s = srv!(self);
146 let mut out = yog_abi::YogVec3 {
147 x: 0.0,
148 y: 0.0,
149 z: 0.0,
150 };
151 if unsafe { (s.entity_position)(s.ctx, YogStr::from_str(uuid), &mut out) } {
152 Some((out.x, out.y, out.z))
153 } else {
154 None
155 }
156 }
157
158 fn entity_health(&self, uuid: &str) -> Option<f32> {
159 let s = srv!(self);
160 let mut hp = 0f32;
161 if unsafe { (s.entity_health)(s.ctx, YogStr::from_str(uuid), &mut hp) } {
162 Some(hp)
163 } else {
164 None
165 }
166 }
167
168 fn entity_set_health(&self, uuid: &str, health: f32) -> bool {
169 let s = srv!(self);
170 unsafe { (s.entity_set_health)(s.ctx, YogStr::from_str(uuid), health) }
171 }
172
173 fn entity_kill(&self, uuid: &str) -> bool {
174 let s = srv!(self);
175 unsafe { (s.entity_kill)(s.ctx, YogStr::from_str(uuid)) }
176 }
177
178 fn spawn_entity(
179 &self,
180 entity_type: &str,
181 dimension: &str,
182 x: f64,
183 y: f64,
184 z: f64,
185 ) -> Option<String> {
186 let s = srv!(self);
187 let owned = unsafe {
188 (s.spawn_entity)(
189 s.ctx,
190 YogStr::from_str(entity_type),
191 YogStr::from_str(dimension),
192 yog_abi::YogVec3 { x, y, z },
193 )
194 };
195
196 take_owned(s.free_str, owned)
197 }
198
199 fn entity_add_effect(
200 &self,
201 uuid: &str,
202 effect_id: &str,
203 duration_ticks: i32,
204 amplifier: u8,
205 show_particles: bool,
206 ) -> bool {
207 let s = srv!(self);
208 unsafe {
209 (s.entity_add_effect)(
210 s.ctx,
211 YogStr::from_str(uuid),
212 YogStr::from_str(effect_id),
213 duration_ticks,
214 amplifier,
215 show_particles,
216 )
217 }
218 }
219
220 fn entity_remove_effect(&self, uuid: &str, effect_id: &str) -> bool {
221 let s = srv!(self);
222 unsafe {
223 (s.entity_remove_effect)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(effect_id))
224 }
225 }
226
227 fn entity_clear_effects(&self, uuid: &str) -> bool {
228 let s = srv!(self);
229 unsafe { (s.entity_clear_effects)(s.ctx, YogStr::from_str(uuid)) }
230 }
231
232 fn drop_loot(&self, table_id: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
233 let s = srv!(self);
234 unsafe {
235 (s.drop_loot)(
236 s.ctx,
237 YogStr::from_str(table_id),
238 YogStr::from_str(dimension),
239 yog_abi::YogVec3 { x, y, z },
240 )
241 }
242 }
243
244 fn has_item_tag(&self, item_id: &str, tag_id: &str) -> bool {
245 let s = srv!(self);
246 unsafe { (s.has_item_tag)(s.ctx, YogStr::from_str(item_id), YogStr::from_str(tag_id)) }
247 }
248
249 fn has_block_tag(&self, block_id: &str, tag_id: &str) -> bool {
250 let s = srv!(self);
251 unsafe { (s.has_block_tag)(s.ctx, YogStr::from_str(block_id), YogStr::from_str(tag_id)) }
252 }
253
254 fn world_time(&self, dimension: &str) -> Option<i64> {
255 let s = srv!(self);
256 let mut t = 0i64;
257 if unsafe { (s.world_time)(s.ctx, YogStr::from_str(dimension), &mut t) } {
258 Some(t)
259 } else {
260 None
261 }
262 }
263
264 fn world_set_time(&self, dimension: &str, time: i64) -> bool {
265 let s = srv!(self);
266 unsafe { (s.set_time)(s.ctx, YogStr::from_str(dimension), time) }
267 }
268
269 fn world_is_raining(&self, dimension: &str) -> bool {
270 let s = srv!(self);
271 unsafe { (s.is_raining)(s.ctx, YogStr::from_str(dimension)) }
272 }
273
274 fn world_set_weather(&self, dimension: &str, raining: bool, duration_ticks: i32) -> bool {
275 let s = srv!(self);
276 unsafe { (s.set_weather)(s.ctx, YogStr::from_str(dimension), raining, duration_ticks) }
277 }
278
279 fn entity_velocity(&self, uuid: &str) -> Option<(f64, f64, f64)> {
280 let s = srv!(self);
281 let mut v = yog_abi::YogVec3 {
282 x: 0.0,
283 y: 0.0,
284 z: 0.0,
285 };
286 if unsafe { (s.entity_velocity)(s.ctx, YogStr::from_str(uuid), &mut v) } {
287 Some((v.x, v.y, v.z))
288 } else {
289 None
290 }
291 }
292
293 fn entity_set_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
294 let s = srv!(self);
295 unsafe {
296 (s.entity_set_velocity)(
297 s.ctx,
298 YogStr::from_str(uuid),
299 yog_abi::YogVec3 {
300 x: vx,
301 y: vy,
302 z: vz,
303 },
304 )
305 }
306 }
307
308 fn entity_add_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
309 let s = srv!(self);
310 unsafe {
311 (s.entity_add_velocity)(
312 s.ctx,
313 YogStr::from_str(uuid),
314 yog_abi::YogVec3 {
315 x: vx,
316 y: vy,
317 z: vz,
318 },
319 )
320 }
321 }
322
323 fn scoreboard_get(&self, objective: &str, player: &str) -> Option<i32> {
324 let s = srv!(self);
325 let mut score = 0i32;
326 if unsafe {
327 (s.scoreboard_get)(
328 s.ctx,
329 YogStr::from_str(objective),
330 YogStr::from_str(player),
331 &mut score,
332 )
333 } {
334 Some(score)
335 } else {
336 None
337 }
338 }
339
340 fn scoreboard_set(&self, objective: &str, player: &str, score: i32) -> bool {
341 let s = srv!(self);
342 unsafe {
343 (s.scoreboard_set)(
344 s.ctx,
345 YogStr::from_str(objective),
346 YogStr::from_str(player),
347 score,
348 )
349 }
350 }
351
352 fn scoreboard_add(&self, objective: &str, player: &str, delta: i32) -> Option<i32> {
353 let s = srv!(self);
354 let mut new_score = 0i32;
355 if unsafe {
356 (s.scoreboard_add)(
357 s.ctx,
358 YogStr::from_str(objective),
359 YogStr::from_str(player),
360 delta,
361 &mut new_score,
362 )
363 } {
364 Some(new_score)
365 } else {
366 None
367 }
368 }
369
370 fn game_dir(&self) -> String {
371 let s = srv!(self);
372 let owned = unsafe { (s.game_dir)(s.ctx) };
373 take_owned(s.free_str, owned).unwrap_or_default()
374 }
375
376 fn world_dir(&self) -> String {
377 let s = srv!(self);
378 let owned = unsafe { (s.world_dir)(s.ctx) };
379 take_owned(s.free_str, owned).unwrap_or_default()
380 }
381
382 fn play_sound(
383 &self,
384 dimension: &str,
385 x: f64,
386 y: f64,
387 z: f64,
388 sound_id: &str,
389 volume: f32,
390 pitch: f32,
391 ) -> bool {
392 let s = srv!(self);
393 unsafe {
394 (s.play_sound)(
395 s.ctx,
396 YogStr::from_str(dimension),
397 yog_abi::YogVec3 { x, y, z },
398 YogStr::from_str(sound_id),
399 volume,
400 pitch,
401 )
402 }
403 }
404
405 fn play_sound_to_player(&self, player: &str, sound_id: &str, volume: f32, pitch: f32) -> bool {
406 let s = srv!(self);
407 unsafe {
408 (s.play_sound_player)(
409 s.ctx,
410 YogStr::from_str(player),
411 YogStr::from_str(sound_id),
412 volume,
413 pitch,
414 )
415 }
416 }
417
418 fn send_title(
419 &self,
420 player: &str,
421 title: &str,
422 subtitle: &str,
423 fadein: i32,
424 stay: i32,
425 fadeout: i32,
426 ) -> bool {
427 let s = srv!(self);
428 unsafe {
429 (s.send_title)(
430 s.ctx,
431 YogStr::from_str(player),
432 YogStr::from_str(title),
433 YogStr::from_str(subtitle),
434 fadein,
435 stay,
436 fadeout,
437 )
438 }
439 }
440
441 fn entity_rotation(&self, uuid: &str) -> Option<(f32, f32)> {
442 let s = srv!(self);
443 let mut out = yog_abi::YogVec3 {
444 x: 0.0,
445 y: 0.0,
446 z: 0.0,
447 };
448 let ok = unsafe { (s.entity_rotation)(s.ctx, YogStr::from_str(uuid), &mut out) };
449 if ok {
450 Some((out.x as f32, out.y as f32))
451 } else {
452 None
453 }
454 }
455
456 fn send_actionbar(&self, player: &str, message: &str) -> bool {
457 let s = srv!(self);
458 unsafe { (s.send_actionbar)(s.ctx, YogStr::from_str(player), YogStr::from_str(message)) }
459 }
460
461 fn kick_player(&self, player: &str, reason: &str) -> bool {
462 let s = srv!(self);
463 unsafe { (s.kick_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(reason)) }
464 }
465
466 fn set_gamemode(&self, player: &str, gamemode: &str) -> bool {
467 let s = srv!(self);
468 unsafe { (s.set_gamemode)(s.ctx, YogStr::from_str(player), YogStr::from_str(gamemode)) }
469 }
470
471 fn bossbar_create(&self, id: &str, title: &str, color: &str, style: &str) -> bool {
472 let s = srv!(self);
473 unsafe {
474 (s.bossbar_create)(
475 s.ctx,
476 YogStr::from_str(id),
477 YogStr::from_str(title),
478 YogStr::from_str(color),
479 YogStr::from_str(style),
480 )
481 }
482 }
483
484 fn bossbar_remove(&self, id: &str) -> bool {
485 let s = srv!(self);
486 unsafe { (s.bossbar_remove)(s.ctx, YogStr::from_str(id)) }
487 }
488
489 fn bossbar_set_title(&self, id: &str, title: &str) -> bool {
490 let s = srv!(self);
491 unsafe { (s.bossbar_set_title)(s.ctx, YogStr::from_str(id), YogStr::from_str(title)) }
492 }
493
494 fn bossbar_set_progress(&self, id: &str, progress: f32) -> bool {
495 let s = srv!(self);
496 unsafe { (s.bossbar_set_progress)(s.ctx, YogStr::from_str(id), progress) }
497 }
498
499 fn bossbar_set_color(&self, id: &str, color: &str) -> bool {
500 let s = srv!(self);
501 unsafe { (s.bossbar_set_color)(s.ctx, YogStr::from_str(id), YogStr::from_str(color)) }
502 }
503
504 fn bossbar_add_player(&self, id: &str, player: &str) -> bool {
505 let s = srv!(self);
506 unsafe { (s.bossbar_add_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
507 }
508
509 fn bossbar_remove_player(&self, id: &str, player: &str) -> bool {
510 let s = srv!(self);
511 unsafe { (s.bossbar_remove_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
512 }
513
514 fn bossbar_set_visible(&self, id: &str, visible: bool) -> bool {
515 let s = srv!(self);
516 unsafe { (s.bossbar_set_visible)(s.ctx, YogStr::from_str(id), visible) }
517 }
518
519 fn online_players(&self) -> Vec<String> {
520 let s = srv!(self);
521 let owned = unsafe { (s.online_players)(s.ctx) };
522 let Some(text) = take_owned(s.free_str, owned) else {
523 return Vec::new();
524 };
525 if text.is_empty() {
526 Vec::new()
527 } else {
528 text.lines().map(str::to_owned).collect()
529 }
530 }
531
532 fn get_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
533 let s = srv!(self);
534 let owned = unsafe {
535 (s.get_block_nbt)(
536 s.ctx,
537 YogStr::from_str(dimension),
538 yog_abi::YogBlockPos {
539 x: pos.x,
540 y: pos.y,
541 z: pos.z,
542 },
543 )
544 };
545
546 take_owned(s.free_str, owned)
547 }
548
549 fn set_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos, snbt: &str) -> bool {
550 let s = srv!(self);
551 unsafe {
552 (s.set_block_nbt)(
553 s.ctx,
554 YogStr::from_str(dimension),
555 yog_abi::YogBlockPos {
556 x: pos.x,
557 y: pos.y,
558 z: pos.z,
559 },
560 YogStr::from_str(snbt),
561 )
562 }
563 }
564
565 fn get_inventory_slot(
566 &self,
567 dimension: &str,
568 pos: yog_core::BlockPos,
569 slot: u32,
570 ) -> Option<(String, u32, String)> {
571 let s = srv!(self);
572 let owned = unsafe {
573 (s.get_inventory_slot)(
574 s.ctx,
575 YogStr::from_str(dimension),
576 yog_abi::YogBlockPos {
577 x: pos.x,
578 y: pos.y,
579 z: pos.z,
580 },
581 slot,
582 )
583 };
584 let text = take_owned(s.free_str, owned)?;
585 let mut it = text.splitn(3, '\t');
586 let item_id = it.next()?.to_owned();
587 let count = it.next()?.parse().ok()?;
588 let snbt = it.next().unwrap_or("{}").to_owned();
589 Some((item_id, count, snbt))
590 }
591
592 fn set_inventory_slot(
593 &self,
594 dimension: &str,
595 pos: yog_core::BlockPos,
596 slot: u32,
597 item_id: &str,
598 count: u32,
599 snbt: &str,
600 ) -> bool {
601 let item = if snbt.is_empty() { item_id.to_string() } else { format!("{item_id}\t{snbt}") };
603 let s = srv!(self);
604 unsafe {
605 (s.set_inventory_slot)(
606 s.ctx,
607 YogStr::from_str(dimension),
608 yog_abi::YogBlockPos {
609 x: pos.x,
610 y: pos.y,
611 z: pos.z,
612 },
613 slot,
614 YogStr::from_str(&item),
615 count,
616 )
617 }
618 }
619
620 fn player_inventory(&self, player: &str) -> Vec<(u32, String, u32)> {
621 let s = srv!(self);
622 let owned = unsafe { (s.player_inventory)(s.ctx, YogStr::from_str(player)) };
623 let Some(text) = take_owned(s.free_str, owned) else {
624 return Vec::new();
625 };
626 text.lines()
627 .filter_map(|line| {
628 let mut it = line.split('\t');
629 let slot: u32 = it.next()?.parse().ok()?;
630 let item_id = it.next()?.to_owned();
631 let count: u32 = it.next()?.parse().ok()?;
632 Some((slot, item_id, count))
633 })
634 .collect()
635 }
636
637 fn player_set_slot(&self, player: &str, slot: u32, item_id: &str, count: u32) -> bool {
638 let s = srv!(self);
639 unsafe {
640 (s.player_set_slot)(
641 s.ctx,
642 YogStr::from_str(player),
643 slot,
644 YogStr::from_str(item_id),
645 count,
646 )
647 }
648 }
649
650 fn teleport_to_dim(&self, player: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
651 let s = srv!(self);
652 unsafe {
653 (s.player_teleport_dim)(
654 s.ctx,
655 YogStr::from_str(player),
656 YogStr::from_str(dimension),
657 yog_abi::YogVec3 { x, y, z },
658 )
659 }
660 }
661
662 fn entity_teleport_to_dim(&self, uuid: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
663 let s = srv!(self);
664 unsafe {
665 (s.entity_teleport_dim)(
666 s.ctx,
667 YogStr::from_str(uuid),
668 YogStr::from_str(dimension),
669 yog_abi::YogVec3 { x, y, z },
670 )
671 }
672 }
673
674 fn world_entity_count(&self, dimension: &str, entity_type: &str) -> i32 {
675 let s = srv!(self);
676 unsafe {
677 (s.world_entity_count)(
678 s.ctx,
679 YogStr::from_str(dimension),
680 YogStr::from_str(entity_type),
681 )
682 }
683 }
684
685 fn entity_get_nbt(&self, uuid: &str) -> Option<String> {
686 let s = srv!(self);
687 let owned = unsafe { (s.entity_get_nbt)(s.ctx, YogStr::from_str(uuid)) };
688
689 take_owned(s.free_str, owned)
690 }
691
692 fn entity_set_nbt(&self, uuid: &str, snbt: &str) -> bool {
693 let s = srv!(self);
694 unsafe { (s.entity_set_nbt)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(snbt)) }
695 }
696
697 fn spawn_particles(
698 &self,
699 dimension: &str,
700 x: f64,
701 y: f64,
702 z: f64,
703 particle_type: &str,
704 count: i32,
705 dx: f64,
706 dy: f64,
707 dz: f64,
708 speed: f64,
709 ) -> bool {
710 let s = srv!(self);
711 unsafe {
712 (s.spawn_particles)(
713 s.ctx,
714 YogStr::from_str(dimension),
715 yog_abi::YogVec3 { x, y, z },
716 YogStr::from_str(particle_type),
717 count,
718 dx,
719 dy,
720 dz,
721 speed,
722 )
723 }
724 }
725
726 fn entity_attribute_value(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
727 let s = srv!(self);
728 let v = unsafe { (s.entity_attribute_value)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(attribute_id)) };
729 (!v.is_nan()).then_some(v)
730 }
731
732 fn entity_attribute_get(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
733 let s = srv!(self);
734 let v = unsafe {
735 (s.entity_attribute_get)(
736 s.ctx,
737 YogStr::from_str(uuid),
738 YogStr::from_str(attribute_id),
739 )
740 };
741 if v.is_nan() { None } else { Some(v) }
742 }
743
744 fn entity_attribute_set(&self, uuid: &str, attribute_id: &str, value: f64) -> bool {
745 let s = srv!(self);
746 unsafe {
747 (s.entity_attribute_set)(
748 s.ctx,
749 YogStr::from_str(uuid),
750 YogStr::from_str(attribute_id),
751 value,
752 )
753 }
754 }
755
756 fn get_held_item_nbt(&self, player: &str) -> Option<String> {
757 let s = srv!(self);
758 let owned = unsafe { (s.get_held_item_nbt)(s.ctx, YogStr::from_str(player)) };
759
760 take_owned(s.free_str, owned)
761 }
762
763 fn set_held_item_nbt(&self, player: &str, snbt: &str) -> bool {
764 let s = srv!(self);
765 unsafe { (s.set_held_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
766 }
767
768 fn get_offhand_item_nbt(&self, player: &str) -> Option<String> {
769 let s = srv!(self);
770 let owned = unsafe { (s.get_offhand_item_nbt)(s.ctx, YogStr::from_str(player)) };
771
772 take_owned(s.free_str, owned)
773 }
774
775 fn set_offhand_item_nbt(&self, player: &str, snbt: &str) -> bool {
776 let s = srv!(self);
777 unsafe { (s.set_offhand_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
778 }
779
780 fn get_slot_item(&self, player: &str, slot: u32) -> Option<(String, u32, String)> {
781 let s = srv!(self);
782 let owned = unsafe { (s.get_slot_item)(s.ctx, YogStr::from_str(player), slot) };
783 let text = take_owned(s.free_str, owned)?;
784 let mut it = text.splitn(3, '\t');
785 let item_id = it.next()?.to_owned();
786 let count: u32 = it.next()?.parse().ok()?;
787 let nbt = it.next().unwrap_or("{}").to_owned();
788 Some((item_id, count, nbt))
789 }
790
791 fn set_slot_item(
792 &self,
793 player: &str,
794 slot: u32,
795 item_id: &str,
796 count: u32,
797 snbt: &str,
798 ) -> bool {
799 let s = srv!(self);
800 unsafe {
801 (s.set_slot_item)(
802 s.ctx,
803 YogStr::from_str(player),
804 slot,
805 YogStr::from_str(item_id),
806 count,
807 YogStr::from_str(snbt),
808 )
809 }
810 }
811
812 fn entity_info(&self, uuid: &str) -> Option<yog_core::EntityInfo> {
815 let s = srv!(self);
816 let owned = unsafe { (s.entity_info)(s.ctx, YogStr::from_str(uuid)) };
817 take_owned(s.free_str, owned).and_then(|l| yog_core::EntityInfo::decode(&l))
818 }
819
820 fn entities_in_box(
821 &self,
822 dimension: &str,
823 min: (f64, f64, f64),
824 max: (f64, f64, f64),
825 ) -> Vec<String> {
826 let s = srv!(self);
827 let owned =
828 unsafe { (s.entities_in_box)(s.ctx, YogStr::from_str(dimension), v3(min), v3(max)) };
829 take_owned(s.free_str, owned)
830 .map(|t| {
831 t.lines()
832 .filter(|l| !l.is_empty())
833 .map(str::to_owned)
834 .collect()
835 })
836 .unwrap_or_default()
837 }
838
839 fn raycast_block(
840 &self,
841 dimension: &str,
842 from: (f64, f64, f64),
843 to: (f64, f64, f64),
844 ) -> Option<yog_core::BlockHit> {
845 let s = srv!(self);
846 let owned =
847 unsafe { (s.raycast_block)(s.ctx, YogStr::from_str(dimension), v3(from), v3(to)) };
848 take_owned(s.free_str, owned).and_then(|l| yog_core::BlockHit::decode(&l))
849 }
850
851 fn raycast_entity(
852 &self,
853 dimension: &str,
854 from: (f64, f64, f64),
855 to: (f64, f64, f64),
856 exclude: &str,
857 ) -> Option<String> {
858 let s = srv!(self);
859 let owned = unsafe {
860 (s.raycast_entity)(
861 s.ctx,
862 YogStr::from_str(dimension),
863 v3(from),
864 v3(to),
865 YogStr::from_str(exclude),
866 )
867 };
868 take_owned(s.free_str, owned).filter(|u| !u.is_empty())
869 }
870
871 fn block_info(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<yog_core::BlockInfo> {
872 let s = srv!(self);
873 let owned = unsafe { (s.block_info)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
874 take_owned(s.free_str, owned).and_then(|l| yog_core::BlockInfo::decode(&l))
875 }
876
877 fn break_block(
878 &self,
879 dimension: &str,
880 pos: yog_core::BlockPos,
881 drop: bool,
882 breaker: &str,
883 ) -> bool {
884 let s = srv!(self);
885 unsafe {
886 (s.break_block)(
887 s.ctx,
888 YogStr::from_str(dimension),
889 bp(pos),
890 drop,
891 YogStr::from_str(breaker),
892 )
893 }
894 }
895
896 fn place_block_from_slot(
897 &self,
898 player: &str,
899 slot: u32,
900 dimension: &str,
901 pos: yog_core::BlockPos,
902 ) -> bool {
903 let s = srv!(self);
904 unsafe {
905 (s.place_block_from_slot)(
906 s.ctx,
907 YogStr::from_str(player),
908 slot,
909 YogStr::from_str(dimension),
910 bp(pos),
911 )
912 }
913 }
914
915 fn bonemeal(&self, dimension: &str, pos: yog_core::BlockPos) -> bool {
916 let s = srv!(self);
917 unsafe { (s.bonemeal)(s.ctx, YogStr::from_str(dimension), bp(pos)) }
918 }
919
920 fn explode(
921 &self,
922 dimension: &str,
923 x: f64,
924 y: f64,
925 z: f64,
926 power: f32,
927 fire: bool,
928 source: &str,
929 ) -> bool {
930 let s = srv!(self);
931 unsafe {
932 (s.explode)(
933 s.ctx,
934 YogStr::from_str(dimension),
935 v3((x, y, z)),
936 power,
937 fire,
938 YogStr::from_str(source),
939 )
940 }
941 }
942
943 fn tellraw(&self, player: &str, json: &str) -> bool {
944 let s = srv!(self);
945 unsafe { (s.tellraw)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
946 }
947
948 fn entity_damage(&self, uuid: &str, amount: f32, damage_type: &str, attacker: &str) -> bool {
949 let s = srv!(self);
950 unsafe {
951 (s.entity_damage)(
952 s.ctx,
953 YogStr::from_str(uuid),
954 amount,
955 YogStr::from_str(damage_type),
956 YogStr::from_str(attacker),
957 )
958 }
959 }
960
961 fn entity_set_fire_ticks(&self, uuid: &str, ticks: i32) -> bool {
962 let s = srv!(self);
963 unsafe { (s.entity_set_fire_ticks)(s.ctx, YogStr::from_str(uuid), ticks) }
964 }
965
966 fn player_add_xp(&self, player: &str, amount: i32) -> bool {
967 let s = srv!(self);
968 unsafe { (s.player_add_xp)(s.ctx, YogStr::from_str(player), amount) }
969 }
970
971 fn player_set_may_fly(&self, player: &str, may_fly: bool) -> bool {
972 let s = srv!(self);
973 unsafe { (s.player_set_may_fly)(s.ctx, YogStr::from_str(player), may_fly) }
974 }
975
976 fn player_info(&self, player: &str) -> Option<yog_core::PlayerInfo> {
977 let s = srv!(self);
978 let owned = unsafe { (s.player_info)(s.ctx, YogStr::from_str(player)) };
979 take_owned(s.free_str, owned).and_then(|l| yog_core::PlayerInfo::decode(&l))
980 }
981
982 fn spawn_item(
983 &self,
984 dimension: &str,
985 pos: (f64, f64, f64),
986 vel: (f64, f64, f64),
987 item_id: &str,
988 count: u32,
989 snbt: &str,
990 ) -> Option<String> {
991 let s = srv!(self);
992 let owned = unsafe {
993 (s.spawn_item)(
994 s.ctx,
995 YogStr::from_str(dimension),
996 v3(pos),
997 v3(vel),
998 YogStr::from_str(item_id),
999 count,
1000 YogStr::from_str(snbt),
1001 )
1002 };
1003 take_owned(s.free_str, owned).filter(|u| !u.is_empty())
1004 }
1005
1006 fn item_entity_stack(&self, uuid: &str) -> Option<(String, u32, String)> {
1007 let s = srv!(self);
1008 let owned = unsafe { (s.item_entity_stack)(s.ctx, YogStr::from_str(uuid)) };
1009 let text = take_owned(s.free_str, owned)?;
1010 let mut it = text.splitn(3, '\t');
1011 let item_id = it.next()?.to_owned();
1012 let count = it.next()?.parse().ok()?;
1013 let nbt = it.next().unwrap_or("{}").to_owned();
1014 Some((item_id, count, nbt))
1015 }
1016
1017 fn item_entity_set_stack(&self, uuid: &str, item_id: &str, count: u32, snbt: &str) -> bool {
1018 let s = srv!(self);
1019 unsafe {
1020 (s.item_entity_set_stack)(
1021 s.ctx,
1022 YogStr::from_str(uuid),
1023 YogStr::from_str(item_id),
1024 count,
1025 YogStr::from_str(snbt),
1026 )
1027 }
1028 }
1029
1030 fn actionbar_json(&self, player: &str, json: &str) -> bool {
1031 let s = srv!(self);
1032 unsafe { (s.actionbar_json)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
1033 }
1034
1035 fn use_item_on_block(
1036 &self,
1037 dimension: &str,
1038 pos: yog_core::BlockPos,
1039 face: yog_core::Face,
1040 item_id: &str,
1041 player: &str,
1042 ) -> bool {
1043 let s = srv!(self);
1044 unsafe {
1045 (s.use_item_on_block)(
1046 s.ctx,
1047 YogStr::from_str(dimension),
1048 bp(pos),
1049 face.index(),
1050 YogStr::from_str(item_id),
1051 YogStr::from_str(player),
1052 )
1053 }
1054 }
1055
1056 fn pour_fluid(&self, dimension: &str, pos: yog_core::BlockPos, bucket_item_id: &str) -> bool {
1057 let s = srv!(self);
1058 unsafe {
1059 (s.pour_fluid)(
1060 s.ctx,
1061 YogStr::from_str(dimension),
1062 bp(pos),
1063 YogStr::from_str(bucket_item_id),
1064 )
1065 }
1066 }
1067
1068 fn has_entity_tag(&self, entity_type: &str, tag_id: &str) -> bool {
1069 let s = srv!(self);
1070 unsafe {
1071 (s.has_entity_tag)(
1072 s.ctx,
1073 YogStr::from_str(entity_type),
1074 YogStr::from_str(tag_id),
1075 )
1076 }
1077 }
1078
1079 fn item_block(&self, item_id: &str) -> Option<String> {
1080 let s = srv!(self);
1081 let owned = unsafe { (s.item_block)(s.ctx, YogStr::from_str(item_id)) };
1082 take_owned(s.free_str, owned).filter(|b| !b.is_empty())
1083 }
1084
1085 fn place_feature(&self, dimension: &str, pos: yog_core::BlockPos, feature_id: &str) -> bool {
1086 let s = srv!(self);
1087 unsafe {
1088 (s.place_feature)(
1089 s.ctx,
1090 YogStr::from_str(dimension),
1091 bp(pos),
1092 YogStr::from_str(feature_id),
1093 )
1094 }
1095 }
1096
1097 fn set_item_cooldown(&self, player: &str, item_id: &str, ticks: u32) -> bool {
1098 let s = srv!(self);
1099 unsafe { (s.set_item_cooldown)(s.ctx, YogStr::from_str(player), YogStr::from_str(item_id), ticks.min(i32::MAX as u32) as i32) }
1100 }
1101
1102 fn entity_vehicle(&self, uuid: &str) -> Option<String> {
1103 let s = srv!(self);
1104 let owned = unsafe { (s.entity_vehicle)(s.ctx, YogStr::from_str(uuid)) };
1105 take_owned(s.free_str, owned).filter(|v| !v.is_empty())
1106 }
1107
1108 fn entity_passengers(&self, uuid: &str) -> Vec<String> {
1109 let s = srv!(self);
1110 let owned = unsafe { (s.entity_passengers)(s.ctx, YogStr::from_str(uuid)) };
1111 take_owned(s.free_str, owned)
1112 .map(|t| t.lines().filter(|l| !l.is_empty()).map(str::to_string).collect())
1113 .unwrap_or_default()
1114 }
1115
1116 fn entity_mount(&self, rider: &str, vehicle: &str, force: bool) -> bool {
1117 let s = srv!(self);
1118 unsafe { (s.entity_mount)(s.ctx, YogStr::from_str(rider), YogStr::from_str(vehicle), force) }
1119 }
1120
1121 fn entity_dismount(&self, uuid: &str) -> bool {
1122 let s = srv!(self);
1123 unsafe { (s.entity_dismount)(s.ctx, YogStr::from_str(uuid)) }
1124 }
1125
1126 fn entity_data(&self, uuid: &str, key: &str) -> Option<Vec<u8>> {
1127 let s = srv!(self);
1128 let mut out = Vec::new();
1129 let found = unsafe {
1130 (s.entity_data_get)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(key), &mut out as *mut Vec<u8> as *mut c_void, crate::body::collect)
1131 };
1132 found.then_some(out)
1133 }
1134
1135 fn set_entity_data(&self, uuid: &str, key: &str, value: Option<&[u8]>, persist: bool) -> bool {
1136 let s = srv!(self);
1137 let flags = value.map_or(0, |_| 1) | if persist { 2 } else { 0 };
1138 let v = value.unwrap_or(&[]);
1139 unsafe { (s.entity_data_set)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(key), v.as_ptr(), v.len() as u32, flags) }
1140 }
1141
1142 fn entity_set_dimensions(&self, uuid: &str, size: Option<(f32, f32, f32)>) -> bool {
1143 let s = srv!(self);
1144 let (w, h, e) = size.unwrap_or((-1.0, 0.0, 0.0));
1145 unsafe { (s.entity_set_dimensions)(s.ctx, YogStr::from_str(uuid), w, h, e) }
1146 }
1147
1148 fn entity_set_fall_distance(&self, uuid: &str, distance: f32) -> bool {
1149 let s = srv!(self);
1150 unsafe { (s.entity_set_fall_distance)(s.ctx, YogStr::from_str(uuid), distance) }
1151 }
1152
1153 fn redstone_signal(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<u8> {
1154 let s = srv!(self);
1155 let p = unsafe { (s.redstone_signal)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
1156 (0..=15).contains(&p).then_some(p as u8)
1157 }
1158
1159 fn redstone_power(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<u8> {
1160 let s = srv!(self);
1161 let p = unsafe { (s.redstone_power)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
1162 (0..=15).contains(&p).then_some(p as u8)
1163 }
1164
1165 fn set_redstone_output(&self, dimension: &str, pos: yog_core::BlockPos, level: u8) -> bool {
1166 let s = srv!(self);
1167 unsafe { (s.set_redstone_output)(s.ctx, YogStr::from_str(dimension), bp(pos), level.min(15) as i32) }
1168 }
1169
1170 fn get_block_state(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
1171 let s = srv!(self);
1172 let owned = unsafe { (s.get_block_state)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
1173 take_owned(s.free_str, owned).filter(|b| !b.is_empty())
1174 }
1175
1176 fn set_block_state(&self, dimension: &str, pos: yog_core::BlockPos, state: &str) -> bool {
1177 let s = srv!(self);
1178 unsafe { (s.set_block_state)(s.ctx, YogStr::from_str(dimension), bp(pos), YogStr::from_str(state)) }
1179 }
1180
1181 fn world_is_thundering(&self, dimension: &str) -> bool {
1182 let s = srv!(self);
1183 unsafe { (s.world_is_thundering)(s.ctx, YogStr::from_str(dimension)) }
1184 }
1185
1186 fn world_set_thunder(&self, dimension: &str, thundering: bool, duration_ticks: i32) -> bool {
1187 let s = srv!(self);
1188 unsafe { (s.world_set_thunder)(s.ctx, YogStr::from_str(dimension), thundering, duration_ticks) }
1189 }
1190
1191 fn fill_region(&self, dimension: &str, from: yog_core::BlockPos, to: yog_core::BlockPos, state: &str) -> Option<u32> {
1192 let s = srv!(self);
1193 let n = unsafe { (s.fill_region)(s.ctx, YogStr::from_str(dimension), bp(from), bp(to), YogStr::from_str(state)) };
1194 u32::try_from(n).ok()
1195 }
1196
1197 fn scan_region(&self, dimension: &str, from: yog_core::BlockPos, to: yog_core::BlockPos) -> Vec<(yog_core::BlockPos, String)> {
1198 let s = srv!(self);
1199 let owned = unsafe { (s.scan_region)(s.ctx, YogStr::from_str(dimension), bp(from), bp(to)) };
1200 let Some(text) = take_owned(s.free_str, owned) else {
1201 return Vec::new();
1202 };
1203 text.lines()
1204 .filter_map(|line| {
1205 let mut it = line.splitn(4, ' ');
1206 let (x, y, z) = (it.next()?.parse().ok()?, it.next()?.parse().ok()?, it.next()?.parse().ok()?);
1207 Some((yog_core::BlockPos { x, y, z }, it.next()?.to_string()))
1208 })
1209 .collect()
1210 }
1211}
1212
1213pub(crate) fn take_owned(
1215 free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
1216 owned: yog_abi::YogOwnedStr,
1217) -> Option<String> {
1218 if owned.is_none() {
1219 return None;
1220 }
1221 let text = unsafe {
1223 String::from_utf8_lossy(std::slice::from_raw_parts(owned.ptr, owned.len as usize))
1224 .into_owned()
1225 };
1226 unsafe { free_str(owned.ptr, owned.len) };
1227 Some(text)
1228}
1229
1230fn v3((x, y, z): (f64, f64, f64)) -> yog_abi::YogVec3 {
1231 yog_abi::YogVec3 { x, y, z }
1232}
1233
1234fn bp(pos: yog_core::BlockPos) -> yog_abi::YogBlockPos {
1235 yog_abi::YogBlockPos {
1236 x: pos.x,
1237 y: pos.y,
1238 z: pos.z,
1239 }
1240}
1241
1242fn guard<R>(fallback: R, call: impl FnOnce() -> R) -> R {
1250 std::panic::catch_unwind(std::panic::AssertUnwindSafe(call)).unwrap_or_else(|_| {
1251 crate::error!("a mod's callback panicked (see above); carrying on without it");
1252 fallback
1253 })
1254}
1255macro_rules! trampoline_phased {
1272 ($name:ident, $abi_ev:ty, $rust_ev:ty, |$ev:ident| $build:expr_2021) => {
1273 unsafe extern "C" fn $name<F>(
1274 ud: *mut c_void,
1275 srv: *const YogServer,
1276 ev: *const $abi_ev,
1277 phase: u8,
1278 ) -> bool
1279 where
1280 F: Fn(&$rust_ev, EventPhase, &dyn Server) -> bool + Send + Sync,
1281 {
1282 unsafe {
1283 let f = &*(ud as *const F);
1284 let $ev = &*ev;
1285 let rust_ev = $build;
1286 let p = if phase == 0 {
1287 EventPhase::Pre
1288 } else {
1289 EventPhase::Post
1290 };
1291 guard(true, || f(&rust_ev, p, &CServer(srv)))
1292 }
1293 }
1294 };
1295}
1296
1297trampoline_phased!(
1298 trampoline_block_break,
1299 YogBlockBreakEvent,
1300 BlockBreakEvent,
1301 |ev| BlockBreakEvent {
1302 player_name: ev.player.as_str().to_owned(),
1303 block_id: ev.block.as_str().to_owned(),
1304 pos: yog_core::BlockPos {
1305 x: ev.pos.x,
1306 y: ev.pos.y,
1307 z: ev.pos.z
1308 },
1309 dimension: ev.dimension.as_str().to_owned(),
1310 }
1311);
1312
1313trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
1314 player_name: ev.player.as_str().to_owned(),
1315 message: ev.message.as_str().to_owned(),
1316 dimension: ev.dimension.as_str().to_owned(),
1317});
1318
1319trampoline_phased!(
1320 trampoline_player_join,
1321 YogPlayerEvent,
1322 PlayerJoinEvent,
1323 |ev| PlayerJoinEvent {
1324 player_name: ev.player.as_str().to_owned(),
1325 uuid: ev.uuid.as_str().to_owned(),
1326 dimension: ev.dimension.as_str().to_owned(),
1327 }
1328);
1329
1330trampoline_phased!(
1331 trampoline_player_leave,
1332 YogPlayerEvent,
1333 PlayerLeaveEvent,
1334 |ev| PlayerLeaveEvent {
1335 player_name: ev.player.as_str().to_owned(),
1336 uuid: ev.uuid.as_str().to_owned(),
1337 dimension: ev.dimension.as_str().to_owned(),
1338 }
1339);
1340
1341trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| {
1342 UseItemEvent {
1343 player_name: ev.player.as_str().to_owned(),
1344 item_id: ev.item.as_str().to_owned(),
1345 sneaking: ev.sneaking,
1346 dimension: ev.dimension.as_str().to_owned(),
1347 }
1348});
1349
1350trampoline_phased!(
1351 trampoline_use_block,
1352 YogUseBlockEvent,
1353 UseBlockEvent,
1354 |ev| UseBlockEvent {
1355 player_name: ev.player.as_str().to_owned(),
1356 block_id: ev.block.as_str().to_owned(),
1357 pos: yog_core::BlockPos {
1358 x: ev.pos.x,
1359 y: ev.pos.y,
1360 z: ev.pos.z
1361 },
1362 dimension: ev.dimension.as_str().to_owned(),
1363 sneaking: ev.sneaking,
1364 }
1365);
1366
1367trampoline_phased!(
1368 trampoline_attack_entity,
1369 YogAttackEntityEvent,
1370 AttackEntityEvent,
1371 |ev| AttackEntityEvent {
1372 player_name: ev.player.as_str().to_owned(),
1373 target_type: ev.target_type.as_str().to_owned(),
1374 target_uuid: ev.target_uuid.as_str().to_owned(),
1375 dimension: ev.dimension.as_str().to_owned(),
1376 }
1377);
1378
1379trampoline_phased!(
1380 trampoline_entity_damage,
1381 YogEntityDamageEvent,
1382 EntityDamageEvent,
1383 |ev| EntityDamageEvent {
1384 entity_type: ev.entity_type.as_str().to_owned(),
1385 uuid: ev.uuid.as_str().to_owned(),
1386 amount: ev.amount,
1387 source: ev.source.as_str().to_owned(),
1388 dimension: ev.dimension.as_str().to_owned(),
1389 }
1390);
1391
1392trampoline_phased!(
1393 trampoline_entity_death,
1394 YogEntityDeathEvent,
1395 EntityDeathEvent,
1396 |ev| EntityDeathEvent {
1397 entity_type: ev.entity_type.as_str().to_owned(),
1398 uuid: ev.uuid.as_str().to_owned(),
1399 source: ev.source.as_str().to_owned(),
1400 dimension: ev.dimension.as_str().to_owned(),
1401 }
1402);
1403
1404trampoline_phased!(
1405 trampoline_entity_spawn,
1406 YogEntitySpawnEvent,
1407 EntitySpawnEvent,
1408 |ev| EntitySpawnEvent {
1409 entity_type: ev.entity_type.as_str().to_owned(),
1410 uuid: ev.uuid.as_str().to_owned(),
1411 dimension: ev.dimension.as_str().to_owned(),
1412 }
1413);
1414
1415trampoline_phased!(
1416 trampoline_place_block,
1417 YogPlaceBlockEvent,
1418 PlaceBlockEvent,
1419 |ev| PlaceBlockEvent {
1420 player_name: ev.player.as_str().to_owned(),
1421 block_id: ev.block.as_str().to_owned(),
1422 pos: yog_core::BlockPos {
1423 x: ev.pos.x,
1424 y: ev.pos.y,
1425 z: ev.pos.z
1426 },
1427 dimension: ev.dimension.as_str().to_owned(),
1428 sneaking: ev.sneaking,
1429 }
1430);
1431
1432trampoline_phased!(
1433 trampoline_player_death,
1434 YogPlayerDeathEvent,
1435 PlayerDeathEvent,
1436 |ev| PlayerDeathEvent {
1437 player_name: ev.player.as_str().to_owned(),
1438 uuid: ev.uuid.as_str().to_owned(),
1439 source: ev.source.as_str().to_owned(),
1440 dimension: ev.dimension.as_str().to_owned(),
1441 }
1442);
1443
1444trampoline_phased!(
1445 trampoline_player_respawn,
1446 YogPlayerRespawnEvent,
1447 PlayerRespawnEvent,
1448 |ev| PlayerRespawnEvent {
1449 player_name: ev.player.as_str().to_owned(),
1450 uuid: ev.uuid.as_str().to_owned(),
1451 at_anchor: ev.at_anchor,
1452 dimension: ev.dimension.as_str().to_owned(),
1453 }
1454);
1455
1456trampoline_phased!(
1457 trampoline_advancement,
1458 YogAdvancementEvent,
1459 AdvancementEvent,
1460 |ev| AdvancementEvent {
1461 player_name: ev.player.as_str().to_owned(),
1462 uuid: ev.uuid.as_str().to_owned(),
1463 advancement_id: ev.advancement.as_str().to_owned(),
1464 dimension: ev.dimension.as_str().to_owned(),
1465 }
1466);
1467
1468trampoline_phased!(
1469 trampoline_entity_interact,
1470 YogEntityInteractEvent,
1471 EntityInteractEvent,
1472 |ev| EntityInteractEvent {
1473 player_name: ev.player.as_str().to_owned(),
1474 player_uuid: ev.player_uuid.as_str().to_owned(),
1475 entity_type: ev.entity_type.as_str().to_owned(),
1476 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1477 hand: ev.hand.as_str().to_owned(),
1478 dimension: ev.dimension.as_str().to_owned(),
1479 }
1480);
1481
1482trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| {
1483 CraftEvent {
1484 player_name: ev.player.as_str().to_owned(),
1485 player_uuid: ev.player_uuid.as_str().to_owned(),
1486 result_item: ev.result_item.as_str().to_owned(),
1487 result_count: ev.result_count,
1488 dimension: ev.dimension.as_str().to_owned(),
1489 }
1490});
1491
1492#[derive(Debug, Clone)]
1501pub struct MachineCraftEvent {
1502 pub machine: String,
1504 pub kind: String,
1506 pub recipe: String,
1507 pub result_item: String,
1508 pub result_count: u32,
1509 pub values: yog_data::Value,
1511 pub pos: yog_core::BlockPos,
1512 pub dimension: String,
1513 pub player: String,
1514}
1515
1516trampoline_phased!(trampoline_machine_craft, YogMachineCraftEvent, MachineCraftEvent, |ev| {
1517 MachineCraftEvent {
1518 machine: ev.machine.as_str().to_owned(),
1519 kind: ev.kind.as_str().to_owned(),
1520 recipe: ev.recipe.as_str().to_owned(),
1521 result_item: ev.result_item.as_str().to_owned(),
1522 result_count: ev.result_count,
1523 values: yog_data::json::from_json_text(ev.values.as_str()).unwrap_or(yog_data::Value::Null),
1524 pos: yog_core::BlockPos { x: ev.pos.x, y: ev.pos.y, z: ev.pos.z },
1525 dimension: ev.dimension.as_str().to_owned(),
1526 player: ev.player.as_str().to_owned(),
1527 }
1528});
1529
1530trampoline_phased!(
1531 trampoline_explosion,
1532 YogExplosionEvent,
1533 ExplosionEvent,
1534 |ev| ExplosionEvent {
1535 dimension: ev.dimension.as_str().to_owned(),
1536 x: ev.x,
1537 y: ev.y,
1538 z: ev.z,
1539 power: ev.power,
1540 cause_uuid: ev.cause_uuid.as_str().to_owned(),
1541 }
1542);
1543
1544trampoline_phased!(
1545 trampoline_item_insert,
1546 YogItemInsertEvent,
1547 ItemInsertEvent,
1548 |ev| ItemInsertEvent {
1549 block_id: ev.block.as_str().to_owned(),
1550 pos: yog_core::BlockPos {
1551 x: ev.pos.x,
1552 y: ev.pos.y,
1553 z: ev.pos.z
1554 },
1555 dimension: ev.dimension.as_str().to_owned(),
1556 item_id: ev.item_id.as_str().to_owned(),
1557 count: ev.count,
1558 nbt: ev.nbt.as_str().to_owned(),
1559 }
1560);
1561
1562trampoline_phased!(
1563 trampoline_item_pickup,
1564 YogItemPickupEvent,
1565 ItemPickupEvent,
1566 |ev| ItemPickupEvent {
1567 player_name: ev.player.as_str().to_owned(),
1568 player_uuid: ev.player_uuid.as_str().to_owned(),
1569 item_id: ev.item_id.as_str().to_owned(),
1570 item_count: ev.item_count,
1571 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1572 dimension: ev.dimension.as_str().to_owned(),
1573 }
1574);
1575
1576trampoline_phased!(
1577 trampoline_player_move,
1578 YogPlayerMoveEvent,
1579 PlayerMoveEvent,
1580 |ev| PlayerMoveEvent {
1581 player_name: ev.player.as_str().to_owned(),
1582 player_uuid: ev.player_uuid.as_str().to_owned(),
1583 x: ev.x,
1584 y: ev.y,
1585 z: ev.z,
1586 yaw: ev.yaw,
1587 pitch: ev.pitch,
1588 dimension: ev.dimension.as_str().to_owned(),
1589 }
1590);
1591
1592trampoline_phased!(
1593 trampoline_container_open,
1594 YogContainerOpenEvent,
1595 ContainerOpenEvent,
1596 |ev| ContainerOpenEvent {
1597 player_name: ev.player.as_str().to_owned(),
1598 player_uuid: ev.player_uuid.as_str().to_owned(),
1599 container_type: ev.container_type.as_str().to_owned(),
1600 dimension: ev.dimension.as_str().to_owned(),
1601 }
1602);
1603
1604trampoline_phased!(
1605 trampoline_container_close,
1606 YogContainerCloseEvent,
1607 ContainerCloseEvent,
1608 |ev| ContainerCloseEvent {
1609 player_name: ev.player.as_str().to_owned(),
1610 player_uuid: ev.player_uuid.as_str().to_owned(),
1611 dimension: ev.dimension.as_str().to_owned(),
1612 }
1613);
1614
1615trampoline_phased!(
1616 trampoline_projectile_hit,
1617 YogProjectileHitEvent,
1618 ProjectileHitEvent,
1619 |ev| ProjectileHitEvent {
1620 projectile_type: ev.projectile_type.as_str().to_owned(),
1621 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
1622 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
1623 hit_type: ev.hit_type.as_str().to_owned(),
1624 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
1625 x: ev.x,
1626 y: ev.y,
1627 z: ev.z,
1628 dimension: ev.dimension.as_str().to_owned(),
1629 }
1630);
1631
1632unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
1633where
1634 F: Fn(&dyn Server) + Send + Sync,
1635{
1636 unsafe {
1637 let f = &*(ud as *const F);
1638 guard((), || f(&CServer(srv)));
1639 }
1640}
1641
1642unsafe extern "C" fn trampoline_packet<F>(
1643 ud: *mut c_void,
1644 srv: *const YogServer,
1645 ev: *const YogPacketEvent,
1646) where
1647 F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
1648{
1649 unsafe {
1650 let f = &*(ud as *const F);
1651 let ev = &*ev;
1652 let rust_ev = PacketEvent {
1653 channel: ev.channel.as_str().to_owned(),
1654 player: ev.player.as_str().to_owned(),
1655 payload: if ev.payload.is_null() || ev.payload_len == 0 {
1657 Vec::new()
1658 } else {
1659 std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec()
1660 },
1661 };
1662 guard((), || f(&rust_ev, &CServer(srv)));
1663 }
1664}
1665
1666unsafe extern "C" fn trampoline_command<F>(
1667 ud: *mut c_void,
1668 srv: *const YogServer,
1669 ev: *const YogCommandEvent,
1670 reply_buf: *mut u8,
1671 reply_cap: u32,
1672 reply_len: *mut u32,
1673) where
1674 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
1675{
1676 unsafe {
1677 let f = &*(ud as *const F);
1678 let ev = &*ev;
1679 let ctx = CommandContext {
1680 name: ev.name.as_str().to_owned(),
1681 args: ev.args.as_str().to_owned(),
1682 source: ev.source.as_str().to_owned(),
1683 uuid: ev.uuid.as_str().to_owned(),
1684 dimension: ev.dimension.as_str().to_owned(),
1685 };
1686 *reply_len = 0;
1687 if let Some(reply) = guard(None, || f(&ctx, &CServer(srv))) {
1688 let bytes = reply.as_bytes();
1689 let mut n = bytes.len().min(reply_cap as usize);
1691 while !reply.is_char_boundary(n) {
1692 n -= 1;
1693 }
1694 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
1695 *reply_len = n as u32;
1696 }
1697 }
1698}
1699
1700unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
1701where
1702 F: Fn(&dyn Server) + Send + Sync,
1703{
1704 unsafe {
1705 let f = &*(ud as *const F);
1706 guard((), || f(&CServer(srv)));
1707 }
1708}
1709
1710unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
1713where
1714 F: Fn(&ClientTickEvent) + Send + Sync,
1715{
1716 unsafe {
1717 let f = &*(ud as *const F);
1718 guard((), || f(&ClientTickEvent {}));
1719 }
1720}
1721
1722unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1723where
1724 F: Fn(&GfxContext) + Send + Sync,
1725{
1726 unsafe {
1727 let f = &*(ud as *const F);
1728 guard((), || f(&GfxContext::from_raw(gfx)));
1729 }
1730}
1731
1732struct TooltipPicture<S, D> {
1734 size: S,
1735 draw: D,
1736}
1737
1738unsafe extern "C" fn trampoline_tooltip_size<S, D>(ud: *mut c_void, item: YogStr, snbt: YogStr, w: *mut f32, h: *mut f32) -> bool
1739where
1740 S: Fn(&str, &str) -> Option<(f32, f32)> + Send + Sync,
1741{
1742 unsafe {
1743 let t = &*(ud as *const TooltipPicture<S, D>);
1744 match guard(None, || (t.size)(item.as_str(), snbt.as_str())) {
1745 Some((pw, ph)) => {
1746 (*w, *h) = (pw, ph);
1747 true
1748 }
1749 None => false,
1750 }
1751 }
1752}
1753
1754unsafe extern "C" fn trampoline_item_text<F>(ud: *mut c_void, item: YogStr, snbt: YogStr, sink: *mut c_void, write: yog_abi::YogTextSink) -> bool
1755where
1756 F: Fn(&str, &str) -> Option<(Option<String>, Vec<String>)> + Send + Sync,
1757{
1758 unsafe {
1759 let f = &*(ud as *const F);
1760 let Some((name, lines)) = guard(None, || f(item.as_str(), snbt.as_str())) else { return false };
1761 let mut text = name.unwrap_or_default();
1762 for l in lines {
1763 text.push('\n');
1764 text.push_str(&l.replace('\n', " "));
1765 }
1766 write(sink, YogStr::from_str(&text));
1767 true
1768 }
1769}
1770
1771unsafe extern "C" fn trampoline_entity_tick<F>(ud: *mut c_void, srv: *const yog_abi::YogServer, uuid: YogStr)
1772where
1773 F: Fn(&dyn Server, &str) + Send + Sync,
1774{
1775 unsafe {
1776 let f = &*(ud as *const F);
1777 let s = CServer(srv);
1778 guard((), || f(&s, uuid.as_str()));
1779 }
1780}
1781
1782unsafe extern "C" fn trampoline_loot<F>(
1783 ud: *mut c_void,
1784 srv: *const yog_abi::YogServer,
1785 loot: *const u8,
1786 len: usize,
1787 sink: *mut c_void,
1788 write: yog_abi::YogBytesSink,
1789) where
1790 F: Fn(&dyn Server, &mut crate::loot::Loot) + Send + Sync,
1791{
1792 use yog_data::Schema;
1793 unsafe {
1794 let f = &*(ud as *const F);
1795 let Ok(mut loot) = yog_data::read_bytes::<crate::loot::Loot>(std::slice::from_raw_parts(loot, len)) else { return };
1796 let before = loot.items.clone();
1797 let s = CServer(srv);
1798 guard((), || f(&s, &mut loot));
1799 if loot.items != before {
1800 let bytes = yog_data::binary::encode(&loot.items.write());
1801 write(sink, bytes.as_ptr(), bytes.len());
1802 }
1803 }
1804}
1805
1806unsafe extern "C" fn trampoline_body<F>(ud: *mut c_void, frame: *const u8, len: usize, sink: *mut c_void, write: yog_abi::YogBytesSink)
1807where
1808 F: Fn(&yog_body::Frame, &mut yog_body::Ops) + Send + Sync,
1809{
1810 unsafe {
1811 let f = &*(ud as *const F);
1812 let Some(frame) = yog_body::Frame::decode(std::slice::from_raw_parts(frame, len)) else { return };
1813 let mut ops = yog_body::Ops::default();
1814 guard((), || f(&frame, &mut ops));
1815 if !ops.is_empty() {
1816 write(sink, ops.0.as_ptr(), ops.0.len());
1817 }
1818 }
1819}
1820
1821unsafe extern "C" fn trampoline_camera<F>(ud: *mut c_void, frame: *const yog_abi::YogCameraFrame, out: *mut yog_abi::YogCameraOut)
1822where
1823 F: Fn(&yog_body::CameraFrame, &mut yog_body::CameraOut) + Send + Sync,
1824{
1825 unsafe {
1826 let f = &*(ud as *const F);
1827 let c = &*frame;
1828 let frame = yog_body::CameraFrame {
1829 uuid: c.uuid.as_str().to_owned(),
1830 partial: c.partial,
1831 pos: c.pos,
1832 yaw: c.yaw,
1833 pitch: c.pitch,
1834 detached: c.detached,
1835 flags: c.flags,
1836 };
1837 let o = &mut *out;
1838 let mut cam = yog_body::CameraOut {
1839 local_offset: yog_body::Vec3::from_array(o.local_offset),
1840 world_offset: yog_body::Vec3::from_array(o.world_offset),
1841 yaw: o.yaw,
1842 pitch: o.pitch,
1843 roll: o.roll,
1844 fov: o.fov,
1845 };
1846 guard((), || f(&frame, &mut cam));
1847 *o = yog_abi::YogCameraOut {
1848 local_offset: cam.local_offset.to_array(),
1849 world_offset: cam.world_offset.to_array(),
1850 yaw: cam.yaw,
1851 pitch: cam.pitch,
1852 roll: cam.roll,
1853 fov: cam.fov,
1854 };
1855 }
1856}
1857
1858unsafe extern "C" fn trampoline_travel<F>(ud: *mut c_void, frame: *const yog_abi::YogTravelFrame, velocity: *mut [f32; 3]) -> bool
1859where
1860 F: Fn(&yog_body::TravelFrame) -> Option<yog_body::Vec3> + Send + Sync,
1861{
1862 unsafe {
1863 let f = &*(ud as *const F);
1864 let t = &*frame;
1865 let frame = yog_body::TravelFrame {
1866 uuid: t.uuid.as_str().to_owned(),
1867 velocity: yog_body::Vec3::from_array(t.velocity),
1868 look: yog_body::Vec3::from_array(t.look),
1869 yaw: t.yaw,
1870 pitch: t.pitch,
1871 forward: t.forward,
1872 strafe: t.strafe,
1873 jumping: t.jumping,
1874 sneaking: t.sneaking,
1875 gravity: t.gravity,
1876 flags: t.flags,
1877 };
1878 match guard(None, || f(&frame)) {
1879 Some(v) => {
1880 *velocity = v.to_array();
1881 true
1882 }
1883 None => false,
1884 }
1885 }
1886}
1887
1888#[allow(clippy::too_many_arguments)]
1889unsafe extern "C" fn trampoline_tooltip_draw<S, D>(ud: *mut c_void, gfx: *const YogGfxApi, item: YogStr, snbt: YogStr, x: f32, y: f32, w: f32, h: f32)
1890where
1891 D: Fn(&GfxContext, &str, &str, [f32; 4]) + Send + Sync,
1892{
1893 unsafe {
1894 let t = &*(ud as *const TooltipPicture<S, D>);
1895 guard((), || (t.draw)(&GfxContext::from_raw(gfx), item.as_str(), snbt.as_str(), [x, y, w, h]));
1896 }
1897}
1898
1899#[allow(clippy::too_many_arguments)]
1900unsafe extern "C" fn trampoline_book_page<F>(ud: *mut c_void, gfx: *const YogGfxApi, data: *const u8, len: usize, x: f32, y: f32, w: f32, h: f32)
1901where
1902 F: Fn(&GfxContext, &yog_data::Value, [f32; 4]) + Send + Sync,
1903{
1904 unsafe {
1905 let f = &*(ud as *const F);
1906 let bytes = if data.is_null() { &[][..] } else { std::slice::from_raw_parts(data, len) };
1907 let value = yog_data::binary::decode(bytes).unwrap_or(yog_data::Value::Null);
1908 guard((), || f(&GfxContext::from_raw(gfx), &value, [x, y, w, h]));
1909 }
1910}
1911
1912unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1913where
1914 F: Fn(&GfxContext) + Send + Sync,
1915{
1916 unsafe {
1917 let f = &*(ud as *const F);
1918 guard((), || f(&GfxContext::from_raw(gfx)));
1919 }
1920}
1921
1922unsafe extern "C" fn trampoline_key_press<F>(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool
1923where
1924 F: Fn(&KeyPressEvent) -> bool + Send + Sync,
1925{
1926 unsafe {
1927 let f = &*(ud as *const F);
1928 let e = &*ev;
1929 let ev = KeyPressEvent { key_code: e.key_code, scan_code: e.scan_code, action: e.action, modifiers: e.modifiers };
1930 guard(false, || f(&ev))
1931 }
1932}
1933
1934unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
1935where
1936 F: Fn(&ScreenEvent) + Send + Sync,
1937{
1938 unsafe {
1939 let f = &*(ud as *const F);
1940 let ev = ScreenEvent { screen_class: screen_class.as_str().to_owned() };
1941 guard((), || f(&ev));
1942 true
1943 }
1944}
1945
1946static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
1957 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
1958
1959pub(crate) fn global_api() -> *mut YogApi {
1961 GLOBAL_API.load(std::sync::atomic::Ordering::Acquire)
1962}
1963
1964#[derive(Debug, Clone)]
1966pub struct ModInfo {
1967 pub source: String,
1969 pub id: String,
1970 pub name: String,
1971 pub version: String,
1972 pub authors: String,
1974 pub description: String,
1975}
1976
1977pub fn installed_mods() -> Vec<ModInfo> {
1983 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1984 if api.is_null() {
1985 return Vec::new();
1986 }
1987 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
1988 let Some(text) = take_owned(unsafe { (*api).free_str }, owned) else {
1989 return Vec::new();
1990 };
1991 text.lines()
1992 .filter_map(|line| {
1993 let mut f = line.split('\t');
1994 Some(ModInfo {
1995 source: f.next()?.to_string(),
1996 id: f.next()?.to_string(),
1997 name: f.next().unwrap_or_default().to_string(),
1998 version: f.next().unwrap_or_default().to_string(),
1999 authors: f.next().unwrap_or_default().to_string(),
2000 description: f.next().unwrap_or_default().to_string(),
2001 })
2002 })
2003 .collect()
2004}
2005
2006pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
2010 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2011 if api.is_null() {
2012 return;
2013 }
2014 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
2015}
2016
2017pub fn close_ui(ui_id: &str) {
2022 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2023 if api.is_null() {
2024 return;
2025 }
2026 unsafe { ((*api).ui_close)((*api).ctx, YogStr::from_str(ui_id)) }
2027}
2028
2029pub trait UiHandled {
2033 fn handled(self) -> bool;
2034}
2035
2036impl UiHandled for () {
2037 fn handled(self) -> bool {
2038 false
2039 }
2040}
2041
2042impl UiHandled for bool {
2043 fn handled(self) -> bool {
2044 self
2045 }
2046}
2047
2048pub(crate) fn server_table() -> Option<&'static yog_abi::YogServer> {
2054 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2055 if api.is_null() {
2056 return None;
2057 }
2058 let srv = unsafe { (*api).server };
2059 (!srv.is_null()).then(|| unsafe { &*srv })
2060}
2061
2062pub fn server() -> Option<CServer> {
2063 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2064 if api.is_null() {
2065 return None;
2066 }
2067 let srv = unsafe { (*api).server };
2068 if srv.is_null() {
2069 return None;
2070 }
2071 Some(CServer(srv))
2072}
2073
2074pub struct Registry {
2075 api: *const YogApi,
2076}
2077
2078unsafe impl Send for Registry {}
2080unsafe impl Sync for Registry {}
2081
2082impl Registry {
2083 pub unsafe fn from_raw(api: *const YogApi) -> Self {
2090 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
2091 Self { api }
2092 }
2093
2094 #[inline]
2095 fn ctx(&self) -> *mut c_void {
2096 unsafe { (*self.api).ctx }
2097 }
2098
2099 fn leak<F: 'static>(f: F) -> *mut c_void {
2102 Box::into_raw(Box::new(f)) as *mut c_void
2103 }
2104
2105 pub fn on_block_break<F>(&mut self, handler: F)
2113 where
2114 F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2115 {
2116 let ud = Self::leak(handler);
2117 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
2118 }
2119
2120 pub fn on_chat<F>(&mut self, handler: F)
2121 where
2122 F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2123 {
2124 let ud = Self::leak(handler);
2125 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
2126 }
2127
2128 pub fn on_player_join<F>(&mut self, handler: F)
2129 where
2130 F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2131 {
2132 let ud = Self::leak(handler);
2133 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
2134 }
2135
2136 pub fn on_player_leave<F>(&mut self, handler: F)
2137 where
2138 F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2139 {
2140 let ud = Self::leak(handler);
2141 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
2142 }
2143
2144 pub fn on_use_item<F>(&mut self, handler: F)
2145 where
2146 F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2147 {
2148 let ud = Self::leak(handler);
2149 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
2150 }
2151
2152 pub fn on_use_block<F>(&mut self, handler: F)
2153 where
2154 F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2155 {
2156 let ud = Self::leak(handler);
2157 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
2158 }
2159
2160 pub fn on_attack_entity<F>(&mut self, handler: F)
2161 where
2162 F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2163 {
2164 let ud = Self::leak(handler);
2165 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
2166 }
2167
2168 pub fn on_entity_damage<F>(&mut self, handler: F)
2169 where
2170 F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2171 {
2172 let ud = Self::leak(handler);
2173 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
2174 }
2175
2176 pub fn on_entity_death<F>(&mut self, handler: F)
2177 where
2178 F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2179 {
2180 let ud = Self::leak(handler);
2181 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
2182 }
2183
2184 pub fn on_entity_spawn<F>(&mut self, handler: F)
2185 where
2186 F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2187 {
2188 let ud = Self::leak(handler);
2189 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
2190 }
2191
2192 pub fn on_player_place_block<F>(&mut self, handler: F)
2193 where
2194 F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2195 {
2196 let ud = Self::leak(handler);
2197 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
2198 }
2199
2200 pub fn on_player_death<F>(&mut self, handler: F)
2201 where
2202 F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2203 {
2204 let ud = Self::leak(handler);
2205 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
2206 }
2207
2208 pub fn on_player_respawn<F>(&mut self, handler: F)
2209 where
2210 F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2211 {
2212 let ud = Self::leak(handler);
2213 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
2214 }
2215
2216 pub fn on_advancement<F>(&mut self, handler: F)
2217 where
2218 F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2219 {
2220 let ud = Self::leak(handler);
2221 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
2222 }
2223
2224 pub fn on_entity_interact<F>(&mut self, handler: F)
2225 where
2226 F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2227 {
2228 let ud = Self::leak(handler);
2229 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
2230 }
2231
2232 pub fn on_item_craft<F>(&mut self, handler: F)
2233 where
2234 F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2235 {
2236 let ud = Self::leak(handler);
2237 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
2238 }
2239
2240 pub fn on_machine_craft<F>(&mut self, handler: F)
2242 where
2243 F: Fn(&MachineCraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2244 {
2245 let ud = Self::leak(handler);
2246 unsafe { ((*self.api).on_machine_craft)(self.ctx(), ud, trampoline_machine_craft::<F>) }
2247 }
2248
2249 pub fn on_explosion<F>(&mut self, handler: F)
2250 where
2251 F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2252 {
2253 let ud = Self::leak(handler);
2254 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
2255 }
2256
2257 pub fn on_item_insert<F>(&mut self, handler: F)
2261 where
2262 F: Fn(&ItemInsertEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2263 {
2264 let ud = Self::leak(handler);
2265 unsafe { ((*self.api).on_item_insert)(self.ctx(), ud, trampoline_item_insert::<F>) }
2266 }
2267
2268 pub fn on_item_pickup<F>(&mut self, handler: F)
2269 where
2270 F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2271 {
2272 let ud = Self::leak(handler);
2273 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
2274 }
2275
2276 pub fn on_player_move<F>(&mut self, handler: F)
2277 where
2278 F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2279 {
2280 let ud = Self::leak(handler);
2281 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
2282 }
2283
2284 pub fn on_container_open<F>(&mut self, handler: F)
2285 where
2286 F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2287 {
2288 let ud = Self::leak(handler);
2289 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
2290 }
2291
2292 pub fn on_container_close<F>(&mut self, handler: F)
2293 where
2294 F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2295 {
2296 let ud = Self::leak(handler);
2297 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
2298 }
2299
2300 pub fn on_projectile_hit<F>(&mut self, handler: F)
2301 where
2302 F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
2303 {
2304 let ud = Self::leak(handler);
2305 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
2306 }
2307
2308 pub fn on_tick<F>(&mut self, listener: F)
2309 where
2310 F: Fn(&dyn Server) + Send + Sync + 'static,
2311 {
2312 let ud = Self::leak(listener);
2313 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
2314 }
2315
2316 pub fn on_server_started<F>(&mut self, listener: F)
2317 where
2318 F: Fn(&dyn Server) + Send + Sync + 'static,
2319 {
2320 let ud = Self::leak(listener);
2321 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
2322 }
2323
2324 pub fn on_server_stopping<F>(&mut self, listener: F)
2325 where
2326 F: Fn(&dyn Server) + Send + Sync + 'static,
2327 {
2328 let ud = Self::leak(listener);
2329 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
2330 }
2331
2332 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
2335 where
2336 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
2337 {
2338 let name_ys = YogStr::from_str(name.as_ref());
2339 let ud = Self::leak(handler);
2340 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
2341 }
2342
2343 pub fn on_typed_command<F>(
2350 &mut self,
2351 name: impl AsRef<str>,
2352 schema: impl AsRef<str>,
2353 handler: F,
2354 ) where
2355 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
2356 {
2357 let name_ys = YogStr::from_str(name.as_ref());
2358 let schema_ys = YogStr::from_str(schema.as_ref());
2359 let ud = Self::leak(handler);
2360 unsafe {
2361 ((*self.api).register_typed_command)(
2362 self.ctx(),
2363 name_ys,
2364 schema_ys,
2365 ud,
2366 trampoline_command::<F>,
2367 );
2368 }
2369 }
2370
2371 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
2374 where
2375 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
2376 {
2377 let ch = YogStr::from_str(channel.as_ref());
2378 let ud = Self::leak(handler);
2379 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
2380 }
2381
2382 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
2383 where
2384 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
2385 {
2386 let ch = YogStr::from_str(channel.as_ref());
2387 let ud = Self::leak(handler);
2388 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
2389 }
2390
2391 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
2396 where
2397 P: Packet + Send + Sync + 'static,
2398 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
2399 {
2400 self.on_packet(channel, move |ev, srv| {
2401 if let Some(pkt) = P::decode(&ev.payload) {
2402 handler(&pkt, srv);
2403 }
2404 });
2405 }
2406
2407 fn content(&mut self, kind: &str, id: &str, def: &yog_data::Value) {
2412 let blob = yog_data::binary::encode(def);
2413 unsafe { ((*self.api).register_content)(self.ctx(), YogStr::from_str(kind), YogStr::from_str(id), blob.as_ptr(), blob.len()) }
2414 }
2415
2416 fn json(text: &str) -> yog_data::Value {
2418 yog_data::json::from_json_text(text).unwrap_or_default()
2419 }
2420
2421 pub fn add_recipe(&mut self, recipe: Recipe) {
2424 let (ns, name) = recipe.ns_name();
2425 let id = format!("{ns}:{name}");
2426 self.content("recipe", &id, &recipe.to_data());
2427 }
2428
2429 pub fn add_recipe_json(&mut self, id: &str, json: &str) {
2433 let id = if id.contains(':') { id.to_string() } else { format!("minecraft:{id}") };
2434 self.content("recipe", &id, &Self::json(json));
2435 }
2436
2437 pub fn register_item(&mut self, def: ItemDef) {
2438 use yog_data::Schema;
2439 self.content("item", &def.id.clone(), &def.write());
2440 }
2441
2442 pub fn register_block(&mut self, def: BlockDef) {
2443 use yog_data::Schema;
2444 self.content("block", &def.id.clone(), &def.write());
2445 }
2446
2447 pub fn register_entity(&mut self, def: yog_registry::EntityDef) {
2451 use yog_data::Schema;
2452 if crate::entities::drawn(&def) {
2453 self.on_body(crate::entities::draw);
2454 }
2455 self.content("entity", &def.id.clone(), &def.write());
2456 }
2457
2458 pub fn register_entity_data(&mut self, id: &str, data: &[u8]) {
2460 match yog_data::read_bytes::<yog_registry::EntityDef>(data) {
2461 Ok(def) => self.register_entity(def),
2462 Err(e) => crate::error!("entity {id}: {e:?}"),
2463 }
2464 }
2465
2466 pub fn on_entity_tick<F>(&mut self, entity_type: &str, handler: F)
2469 where
2470 F: Fn(&dyn Server, &str) + Send + Sync + 'static,
2471 {
2472 let ud = Self::leak(handler);
2473 unsafe { ((*self.api).on_entity_tick)(self.ctx(), YogStr::from_str(entity_type), ud, trampoline_entity_tick::<F>) }
2474 }
2475
2476 pub fn add_biome_feature(&mut self, feature: &str, biomes: &str, step: &str) {
2480 use yog_data::Schema;
2481 let f = yog_registry::BiomeFeature { feature: feature.into(), biomes: biomes.into(), step: step.into() };
2482 self.content("biome_feature", feature, &f.write());
2483 }
2484
2485 pub fn register_tool_material(&mut self, material: ToolMaterial) {
2488 use yog_data::Schema;
2489 self.content("tool_material", &material.id.clone(), &material.write());
2490 }
2491
2492 pub fn register_armor_material(&mut self, material: ArmorMaterial) {
2495 use yog_data::Schema;
2496 self.content("armor_material", &material.id.clone(), &material.write());
2497 }
2498
2499 pub fn register_sound_group(&mut self, group: SoundGroup) {
2502 use yog_data::Schema;
2503 self.content("sound_group", &group.id.clone(), &group.write());
2504 }
2505
2506 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
2510 use yog_data::Schema;
2511 self.content("inventory", &def.id.clone(), &def.write());
2512 }
2513
2514 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
2518 use yog_data::Schema;
2519 self.content("startup_grant", &grant.id.clone(), &grant.write());
2520 }
2521
2522 pub fn register_book(&mut self, book: &Book) {
2524 use yog_data::Schema;
2525 self.content("book", &book.id, &book.write());
2526 }
2527
2528 pub fn register_book_data(&mut self, id: &str, data: &[u8]) {
2531 unsafe { ((*self.api).register_content)(self.ctx(), YogStr::from_str("book"), YogStr::from_str(id), data.as_ptr(), data.len()) }
2532 }
2533
2534 pub fn register_dimension(&mut self, def: &YogDimensionDef) {
2549 let value = Self::json(&def.to_json());
2550 self.content("dimension", &def.id.clone(), &value);
2551 }
2552
2553 pub fn register_dimension_data(&mut self, id: &str, data: &[u8]) {
2557 unsafe { ((*self.api).register_content)(self.ctx(), YogStr::from_str("dimension"), YogStr::from_str(id), data.as_ptr(), data.len()) }
2558 }
2559
2560 pub fn register_chunk_generator<F>(&mut self, generator_type_id: &str, generator: F)
2572 where
2573 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2574 {
2575 let ud = Self::leak(generator);
2576 let id = YogStr::from_str(generator_type_id);
2577 unsafe {
2578 ((*self.api).register_chunk_generator)(
2579 self.ctx(),
2580 id,
2581 ud,
2582 trampoline_chunk_generator::<F>,
2583 );
2584 }
2585 }
2586
2587 pub fn installed_mods(&self) -> Vec<ModInfo> {
2594 installed_mods()
2595 }
2596
2597 pub fn interop(&self) -> crate::Interop {
2602 crate::Interop::new(self.api)
2603 }
2604
2605 pub fn raw_api(&self) -> *const yog_abi::YogApi {
2608 self.api
2609 }
2610
2611 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
2612 let l = YogStr::from_str(label);
2613 let u = YogStr::from_str(ui_id);
2614 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
2615 }
2616
2617 pub fn register_ui<F, R>(&mut self, ui_id: &str, handler: F)
2626 where
2627 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2628 R: UiHandled,
2629 {
2630 let ud = Self::leak(handler);
2631 let id = YogStr::from_str(ui_id);
2632 let empty = YogStr::from_str("{}");
2633 unsafe { ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F, R>) }
2634 }
2635
2636 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
2639 where
2640 F: Fn(&dyn Server) + Send + Sync + 'static,
2641 {
2642 let ud = Self::leak(handler);
2643 unsafe {
2644 ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>);
2645 }
2646 }
2647
2648 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
2649 where
2650 F: Fn(&dyn Server) + Send + Sync + 'static,
2651 {
2652 let ud = Self::leak(handler);
2653 unsafe {
2654 ((*self.api).schedule_repeating)(
2655 self.ctx(),
2656 period_ticks,
2657 ud,
2658 trampoline_scheduled::<F>,
2659 );
2660 }
2661 }
2662
2663 pub fn on_client_tick<F>(&mut self, handler: F)
2667 where
2668 F: Fn(&ClientTickEvent) + Send + Sync + 'static,
2669 {
2670 let ud = Self::leak(handler);
2671 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
2672 }
2673
2674 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
2683 where
2684 F: Fn(&GfxContext) + Send + Sync + 'static,
2685 {
2686 let ud = Self::leak(handler);
2687 let id = YogStr::from_str(ui_id);
2688 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
2689 }
2690
2691 pub fn on_book_page<F>(&mut self, drawer: &str, handler: F)
2701 where
2702 F: Fn(&GfxContext, &yog_data::Value, [f32; 4]) + Send + Sync + 'static,
2703 {
2704 let ud = Self::leak(handler);
2705 let id = YogStr::from_str(drawer);
2706 unsafe { ((*self.api).on_book_page)(self.ctx(), id, ud, trampoline_book_page::<F>) }
2707 }
2708
2709 pub fn on_item_tooltip<S, D>(&mut self, item: &str, size: S, draw: D)
2714 where
2715 S: Fn(&str, &str) -> Option<(f32, f32)> + Send + Sync + 'static,
2716 D: Fn(&GfxContext, &str, &str, [f32; 4]) + Send + Sync + 'static,
2717 {
2718 let ud = Self::leak(TooltipPicture { size, draw });
2719 let id = YogStr::from_str(item);
2720 unsafe {
2721 ((*self.api).on_item_tooltip)(self.ctx(), id, ud, trampoline_tooltip_size::<S, D>, trampoline_tooltip_draw::<S, D>)
2722 }
2723 }
2724
2725 pub fn on_item_text<F>(&mut self, item: &str, text: F)
2737 where
2738 F: Fn(&str, &str) -> Option<(Option<String>, Vec<String>)> + Send + Sync + 'static,
2739 {
2740 let ud = Self::leak(text);
2741 let id = YogStr::from_str(item);
2742 unsafe { ((*self.api).on_item_text)(self.ctx(), id, ud, trampoline_item_text::<F>) }
2743 }
2744
2745 pub fn register_body_model(&mut self, id: &str, data: &[u8]) {
2748 if let Err(e) = yog_body::register_model(id, data) {
2749 crate::error!("{e}");
2750 }
2751 }
2752
2753 pub fn register_body_anims(&mut self, id: &str, data: &[u8]) {
2756 if let Err(e) = yog_body::register_anims(id, data) {
2757 crate::error!("{e}");
2758 }
2759 }
2760
2761 pub fn on_body<F>(&mut self, handler: F)
2766 where
2767 F: Fn(&yog_body::Frame, &mut yog_body::Ops) + Send + Sync + 'static,
2768 {
2769 let ud = Self::leak(handler);
2770 unsafe { ((*self.api).on_body)(self.ctx(), ud, trampoline_body::<F>) }
2771 }
2772
2773 pub fn on_loot<F>(&mut self, handler: F)
2779 where
2780 F: Fn(&dyn Server, &mut crate::loot::Loot) + Send + Sync + 'static,
2781 {
2782 let ud = Self::leak(handler);
2783 unsafe { ((*self.api).on_loot)(self.ctx(), ud, trampoline_loot::<F>) }
2784 }
2785
2786 pub fn add_loot_modifier(&mut self, modifier: crate::loot::LootModifier) {
2788 self.on_loot(move |srv, loot| crate::loot::apply(srv, &modifier, loot));
2789 }
2790
2791 pub fn register_attribute(&mut self, def: yog_registry::AttributeDef) {
2794 use yog_data::Schema;
2795 self.content("attribute", &def.id.clone(), &def.write());
2796 }
2797
2798 pub fn register_effect(&mut self, def: yog_registry::EffectDef) {
2800 use yog_data::Schema;
2801 self.content("effect", &def.id.clone(), &def.write());
2802 }
2803
2804 pub fn register_potion(&mut self, def: yog_registry::PotionDef) {
2806 use yog_data::Schema;
2807 self.content("potion", &def.id.clone(), &def.write());
2808 }
2809
2810 pub fn register_definition_data(&mut self, kind: &str, id: &str, data: &[u8]) {
2813 unsafe { ((*self.api).register_content)(self.ctx(), YogStr::from_str(kind), YogStr::from_str(id), data.as_ptr(), data.len()) }
2814 }
2815
2816 pub fn add_loot_modifier_data(&mut self, id: &str, data: &[u8]) {
2818 match yog_data::read_bytes::<crate::loot::LootModifier>(data) {
2819 Ok(m) => self.add_loot_modifier(m),
2820 Err(e) => crate::error!("loot modifier {id}: {e:?}"),
2821 }
2822 }
2823
2824 pub fn loot_function<F>(&mut self, name: &str, function: F)
2828 where
2829 F: Fn(&dyn Server, &mut crate::loot::LootStack, &crate::loot::Loot) + Send + Sync + 'static,
2830 {
2831 if let Ok(mut f) = crate::loot::functions().write() {
2832 f.insert(name.to_string(), std::sync::Arc::new(function));
2833 }
2834 }
2835
2836 pub fn on_camera<F>(&mut self, handler: F)
2839 where
2840 F: Fn(&yog_body::CameraFrame, &mut yog_body::CameraOut) + Send + Sync + 'static,
2841 {
2842 let ud = Self::leak(handler);
2843 unsafe { ((*self.api).on_camera)(self.ctx(), ud, trampoline_camera::<F>) }
2844 }
2845
2846 pub fn on_travel<F>(&mut self, handler: F)
2850 where
2851 F: Fn(&yog_body::TravelFrame) -> Option<yog_body::Vec3> + Send + Sync + 'static,
2852 {
2853 let ud = Self::leak(handler);
2854 unsafe { ((*self.api).on_travel)(self.ctx(), ud, trampoline_travel::<F>) }
2855 }
2856
2857 pub fn on_hud_render<F>(&mut self, handler: F)
2862 where
2863 F: Fn(&GfxContext) + Send + Sync + 'static,
2864 {
2865 let ud = Self::leak(handler);
2866 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
2867 }
2868
2869 pub fn on_world_render<F>(&mut self, handler: F)
2875 where
2876 F: Fn(&GfxContext) + Send + Sync + 'static,
2877 {
2878 let ud = Self::leak(handler);
2879 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
2880 }
2881
2882 pub fn on_key_press<F>(&mut self, handler: F)
2885 where
2886 F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static,
2887 {
2888 let ud = Self::leak(handler);
2889 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
2890 }
2891
2892 pub fn on_screen_open<F>(&mut self, handler: F)
2894 where
2895 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2896 {
2897 let ud = Self::leak(handler);
2898 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
2899 }
2900
2901 pub fn on_screen_close<F>(&mut self, handler: F)
2903 where
2904 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2905 {
2906 let ud = Self::leak(handler);
2907 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
2908 }
2909}
2910
2911unsafe extern "C" fn trampoline_ui_event<F, R>(
2912 ud: *mut c_void,
2913 ui_id: yog_abi::YogStr,
2914 event_id: yog_abi::YogStr,
2915) where
2916 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2917 R: UiHandled,
2918{
2919 let f = unsafe { &*(ud as *const F) };
2920 let ui = unsafe { ui_id.as_str() };
2921 let ev = unsafe { event_id.as_str() };
2922 if guard(false, || f(ui, ev).handled()) {
2923 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2924 if !api.is_null() {
2925 unsafe { ((*api).ui_consume_event)((*api).ctx) }
2926 }
2927 }
2928}
2929
2930unsafe extern "C" fn trampoline_chunk_generator<F>(
2931 ud: *mut c_void,
2932 writer: *const yog_abi::YogChunkWriterApi,
2933) where
2934 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2935{
2936 let f = unsafe { &*(ud as *const F) };
2937 let writer = unsafe { ChunkWriter::from_raw(writer) };
2938 guard((), || f(&writer));
2939}
2940
2941pub trait Mod {
2945 fn register(registry: &mut Registry);
2946}
2947
2948#[cfg(test)]
2949mod trampoline_tests {
2950 use super::*;
2951 use yog_abi::{YogBlockPos, YogChatEvent, YogCommandEvent, YogPacketEvent};
2952
2953 fn ud<F>(f: &F) -> *mut c_void {
2954 f as *const F as *mut c_void
2955 }
2956
2957 fn chat<F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync>(f: &F, ev: &YogChatEvent, phase: u8) -> bool {
2958 unsafe { trampoline_chat::<F>(ud(f), std::ptr::null(), ev, phase) }
2959 }
2960
2961 fn block_break<F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync>(f: &F, ev: &YogBlockBreakEvent) -> bool {
2962 unsafe { trampoline_block_break::<F>(ud(f), std::ptr::null(), ev, 0) }
2963 }
2964
2965 fn command<F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync>(f: &F, ev: &YogCommandEvent, buf: &mut [u8]) -> u32 {
2966 let mut len = 99u32;
2967 unsafe { trampoline_command::<F>(ud(f), std::ptr::null(), ev, buf.as_mut_ptr(), buf.len() as u32, &mut len) };
2968 len
2969 }
2970
2971 fn packet<F: Fn(&PacketEvent, &dyn Server) + Send + Sync>(f: &F, ev: &YogPacketEvent) {
2972 unsafe { trampoline_packet::<F>(ud(f), std::ptr::null(), ev) }
2973 }
2974
2975 #[test]
2976 fn events_arrive_converted_with_their_phase() {
2977 let seen = std::sync::Mutex::new(Vec::new());
2978 let handler = |ev: &ChatEvent, p: EventPhase, _: &dyn Server| {
2979 seen.lock().unwrap().push((ev.player_name.clone(), ev.message.clone(), p));
2980 ev.message != "cancel me"
2981 };
2982 let ev = |msg: &'static str| YogChatEvent {
2983 player: YogStr::from_str("Dev"),
2984 message: YogStr::from_str(msg),
2985 dimension: YogStr::from_str("minecraft:overworld"),
2986 };
2987 assert!(chat(&handler, &ev("hi"), 0));
2988 assert!(!chat(&handler, &ev("cancel me"), 0));
2989 chat(&handler, &ev("after"), 1);
2990 let seen = seen.into_inner().unwrap();
2991 assert_eq!(seen[0], ("Dev".into(), "hi".into(), EventPhase::Pre));
2992 assert_eq!(seen[2].2, EventPhase::Post);
2993 }
2994
2995 #[test]
2996 fn a_panicking_handler_allows_the_action() {
2997 let handler = |_: &BlockBreakEvent, _: EventPhase, _: &dyn Server| -> bool { panic!("mod bug") };
2998 let ev = YogBlockBreakEvent {
2999 player: YogStr::from_str("Dev"),
3000 block: YogStr::from_str("minecraft:stone"),
3001 pos: YogBlockPos { x: 1, y: 2, z: 3 },
3002 dimension: YogStr::from_str("minecraft:overworld"),
3003 };
3004 assert!(block_break(&handler, &ev));
3005 }
3006
3007 #[test]
3008 fn command_replies_fit_the_buffer_whole_characters_only() {
3009 let handler = |c: &CommandContext, _: &dyn Server| Some(format!("{}: привет", c.args));
3010 let ev = YogCommandEvent {
3011 name: YogStr::from_str("hex"),
3012 args: YogStr::from_str("x"),
3013 source: YogStr::from_str("Dev"),
3014 uuid: YogStr::EMPTY,
3015 dimension: YogStr::EMPTY,
3016 };
3017 let mut buf = [0u8; 6];
3018 let len = command(&handler, &ev, &mut buf);
3019 assert_eq!(std::str::from_utf8(&buf[..len as usize]), Ok("x: п"));
3021 }
3022
3023 #[test]
3024 fn empty_packets_need_no_payload_pointer() {
3025 let got = std::sync::Mutex::new(None);
3026 let handler = |p: &PacketEvent, _: &dyn Server| *got.lock().unwrap() = Some(p.payload.len());
3027 let ev = YogPacketEvent {
3028 channel: YogStr::from_str("mymod:ping"),
3029 player: YogStr::from_str("Dev"),
3030 payload: std::ptr::null(),
3031 payload_len: 0,
3032 };
3033 packet(&handler, &ev);
3034 assert_eq!(*got.lock().unwrap(), Some(0));
3035 }
3036}