1use std::os::raw::c_void;
11
12use yog_abi::{
13 YogAdvancementEvent, YogApi, YogAttackEntityEvent, YogBlockBreakEvent, YogBlockDef,
14 YogChatEvent, YogCommandEvent, YogContainerCloseEvent, YogContainerOpenEvent, YogCraftEvent,
15 YogEntityDamageEvent, YogEntityDeathEvent, YogEntityInteractEvent, YogEntitySpawnEvent,
16 YogExplosionEvent, YogGfxApi, YogInventoryDef, YogItemDef, YogItemPickupEvent,
17 YogKeyPressEvent, YogPacketEvent, YogPlaceBlockEvent, YogPlayerDeathEvent, YogPlayerEvent,
18 YogPlayerMoveEvent, YogPlayerRespawnEvent, YogProjectileHitEvent, YogServer,
19 YogStartupGrantDef, 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, 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::{BlockDef, FurnaceRecipe, ItemDef, ShapedRecipe, ShapelessRecipe, StartupGrant};
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 play_sound(
377 &self,
378 dimension: &str,
379 x: f64,
380 y: f64,
381 z: f64,
382 sound_id: &str,
383 volume: f32,
384 pitch: f32,
385 ) -> bool {
386 let s = srv!(self);
387 unsafe {
388 (s.play_sound)(
389 s.ctx,
390 YogStr::from_str(dimension),
391 yog_abi::YogVec3 { x, y, z },
392 YogStr::from_str(sound_id),
393 volume,
394 pitch,
395 )
396 }
397 }
398
399 fn play_sound_to_player(&self, player: &str, sound_id: &str, volume: f32, pitch: f32) -> bool {
400 let s = srv!(self);
401 unsafe {
402 (s.play_sound_player)(
403 s.ctx,
404 YogStr::from_str(player),
405 YogStr::from_str(sound_id),
406 volume,
407 pitch,
408 )
409 }
410 }
411
412 fn send_title(
413 &self,
414 player: &str,
415 title: &str,
416 subtitle: &str,
417 fadein: i32,
418 stay: i32,
419 fadeout: i32,
420 ) -> bool {
421 let s = srv!(self);
422 unsafe {
423 (s.send_title)(
424 s.ctx,
425 YogStr::from_str(player),
426 YogStr::from_str(title),
427 YogStr::from_str(subtitle),
428 fadein,
429 stay,
430 fadeout,
431 )
432 }
433 }
434
435 fn entity_rotation(&self, uuid: &str) -> Option<(f32, f32)> {
436 let s = srv!(self);
437 let mut out = yog_abi::YogVec3 {
438 x: 0.0,
439 y: 0.0,
440 z: 0.0,
441 };
442 let ok = unsafe { (s.entity_rotation)(s.ctx, YogStr::from_str(uuid), &mut out) };
443 if ok {
444 Some((out.x as f32, out.y as f32))
445 } else {
446 None
447 }
448 }
449
450 fn send_actionbar(&self, player: &str, message: &str) -> bool {
451 let s = srv!(self);
452 unsafe { (s.send_actionbar)(s.ctx, YogStr::from_str(player), YogStr::from_str(message)) }
453 }
454
455 fn kick_player(&self, player: &str, reason: &str) -> bool {
456 let s = srv!(self);
457 unsafe { (s.kick_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(reason)) }
458 }
459
460 fn set_gamemode(&self, player: &str, gamemode: &str) -> bool {
461 let s = srv!(self);
462 unsafe { (s.set_gamemode)(s.ctx, YogStr::from_str(player), YogStr::from_str(gamemode)) }
463 }
464
465 fn bossbar_create(&self, id: &str, title: &str, color: &str, style: &str) -> bool {
466 let s = srv!(self);
467 unsafe {
468 (s.bossbar_create)(
469 s.ctx,
470 YogStr::from_str(id),
471 YogStr::from_str(title),
472 YogStr::from_str(color),
473 YogStr::from_str(style),
474 )
475 }
476 }
477
478 fn bossbar_remove(&self, id: &str) -> bool {
479 let s = srv!(self);
480 unsafe { (s.bossbar_remove)(s.ctx, YogStr::from_str(id)) }
481 }
482
483 fn bossbar_set_title(&self, id: &str, title: &str) -> bool {
484 let s = srv!(self);
485 unsafe { (s.bossbar_set_title)(s.ctx, YogStr::from_str(id), YogStr::from_str(title)) }
486 }
487
488 fn bossbar_set_progress(&self, id: &str, progress: f32) -> bool {
489 let s = srv!(self);
490 unsafe { (s.bossbar_set_progress)(s.ctx, YogStr::from_str(id), progress) }
491 }
492
493 fn bossbar_set_color(&self, id: &str, color: &str) -> bool {
494 let s = srv!(self);
495 unsafe { (s.bossbar_set_color)(s.ctx, YogStr::from_str(id), YogStr::from_str(color)) }
496 }
497
498 fn bossbar_add_player(&self, id: &str, player: &str) -> bool {
499 let s = srv!(self);
500 unsafe { (s.bossbar_add_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
501 }
502
503 fn bossbar_remove_player(&self, id: &str, player: &str) -> bool {
504 let s = srv!(self);
505 unsafe { (s.bossbar_remove_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
506 }
507
508 fn bossbar_set_visible(&self, id: &str, visible: bool) -> bool {
509 let s = srv!(self);
510 unsafe { (s.bossbar_set_visible)(s.ctx, YogStr::from_str(id), visible) }
511 }
512
513 fn online_players(&self) -> Vec<String> {
514 let s = srv!(self);
515 let owned = unsafe { (s.online_players)(s.ctx) };
516 let Some(text) = take_owned(s.free_str, owned) else {
517 return Vec::new();
518 };
519 if text.is_empty() {
520 Vec::new()
521 } else {
522 text.lines().map(str::to_owned).collect()
523 }
524 }
525
526 fn get_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
527 let s = srv!(self);
528 let owned = unsafe {
529 (s.get_block_nbt)(
530 s.ctx,
531 YogStr::from_str(dimension),
532 yog_abi::YogBlockPos {
533 x: pos.x,
534 y: pos.y,
535 z: pos.z,
536 },
537 )
538 };
539
540 take_owned(s.free_str, owned)
541 }
542
543 fn set_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos, snbt: &str) -> bool {
544 let s = srv!(self);
545 unsafe {
546 (s.set_block_nbt)(
547 s.ctx,
548 YogStr::from_str(dimension),
549 yog_abi::YogBlockPos {
550 x: pos.x,
551 y: pos.y,
552 z: pos.z,
553 },
554 YogStr::from_str(snbt),
555 )
556 }
557 }
558
559 fn get_inventory_slot(
560 &self,
561 dimension: &str,
562 pos: yog_core::BlockPos,
563 slot: u32,
564 ) -> Option<(String, u32)> {
565 let s = srv!(self);
566 let owned = unsafe {
567 (s.get_inventory_slot)(
568 s.ctx,
569 YogStr::from_str(dimension),
570 yog_abi::YogBlockPos {
571 x: pos.x,
572 y: pos.y,
573 z: pos.z,
574 },
575 slot,
576 )
577 };
578 let text = take_owned(s.free_str, owned)?;
579 let (item_id, count) = text.split_once('\t')?;
580 Some((item_id.to_owned(), count.parse().ok()?))
581 }
582
583 fn set_inventory_slot(
584 &self,
585 dimension: &str,
586 pos: yog_core::BlockPos,
587 slot: u32,
588 item_id: &str,
589 count: u32,
590 ) -> bool {
591 let s = srv!(self);
592 unsafe {
593 (s.set_inventory_slot)(
594 s.ctx,
595 YogStr::from_str(dimension),
596 yog_abi::YogBlockPos {
597 x: pos.x,
598 y: pos.y,
599 z: pos.z,
600 },
601 slot,
602 YogStr::from_str(item_id),
603 count,
604 )
605 }
606 }
607
608 fn player_inventory(&self, player: &str) -> Vec<(u32, String, u32)> {
609 let s = srv!(self);
610 let owned = unsafe { (s.player_inventory)(s.ctx, YogStr::from_str(player)) };
611 let Some(text) = take_owned(s.free_str, owned) else {
612 return Vec::new();
613 };
614 text.lines()
615 .filter_map(|line| {
616 let mut it = line.split('\t');
617 let slot: u32 = it.next()?.parse().ok()?;
618 let item_id = it.next()?.to_owned();
619 let count: u32 = it.next()?.parse().ok()?;
620 Some((slot, item_id, count))
621 })
622 .collect()
623 }
624
625 fn player_set_slot(&self, player: &str, slot: u32, item_id: &str, count: u32) -> bool {
626 let s = srv!(self);
627 unsafe {
628 (s.player_set_slot)(
629 s.ctx,
630 YogStr::from_str(player),
631 slot,
632 YogStr::from_str(item_id),
633 count,
634 )
635 }
636 }
637
638 fn teleport_to_dim(&self, player: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
639 let s = srv!(self);
640 unsafe {
641 (s.player_teleport_dim)(
642 s.ctx,
643 YogStr::from_str(player),
644 YogStr::from_str(dimension),
645 yog_abi::YogVec3 { x, y, z },
646 )
647 }
648 }
649
650 fn entity_teleport_to_dim(&self, uuid: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
651 let s = srv!(self);
652 unsafe {
653 (s.entity_teleport_dim)(
654 s.ctx,
655 YogStr::from_str(uuid),
656 YogStr::from_str(dimension),
657 yog_abi::YogVec3 { x, y, z },
658 )
659 }
660 }
661
662 fn world_entity_count(&self, dimension: &str, entity_type: &str) -> i32 {
663 let s = srv!(self);
664 unsafe {
665 (s.world_entity_count)(
666 s.ctx,
667 YogStr::from_str(dimension),
668 YogStr::from_str(entity_type),
669 )
670 }
671 }
672
673 fn entity_get_nbt(&self, uuid: &str) -> Option<String> {
674 let s = srv!(self);
675 let owned = unsafe { (s.entity_get_nbt)(s.ctx, YogStr::from_str(uuid)) };
676
677 take_owned(s.free_str, owned)
678 }
679
680 fn entity_set_nbt(&self, uuid: &str, snbt: &str) -> bool {
681 let s = srv!(self);
682 unsafe { (s.entity_set_nbt)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(snbt)) }
683 }
684
685 fn spawn_particles(
686 &self,
687 dimension: &str,
688 x: f64,
689 y: f64,
690 z: f64,
691 particle_type: &str,
692 count: i32,
693 dx: f64,
694 dy: f64,
695 dz: f64,
696 speed: f64,
697 ) -> bool {
698 let s = srv!(self);
699 unsafe {
700 (s.spawn_particles)(
701 s.ctx,
702 YogStr::from_str(dimension),
703 yog_abi::YogVec3 { x, y, z },
704 YogStr::from_str(particle_type),
705 count,
706 dx,
707 dy,
708 dz,
709 speed,
710 )
711 }
712 }
713
714 fn entity_attribute_get(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
715 let s = srv!(self);
716 let v = unsafe {
717 (s.entity_attribute_get)(
718 s.ctx,
719 YogStr::from_str(uuid),
720 YogStr::from_str(attribute_id),
721 )
722 };
723 if v.is_nan() { None } else { Some(v) }
724 }
725
726 fn entity_attribute_set(&self, uuid: &str, attribute_id: &str, value: f64) -> bool {
727 let s = srv!(self);
728 unsafe {
729 (s.entity_attribute_set)(
730 s.ctx,
731 YogStr::from_str(uuid),
732 YogStr::from_str(attribute_id),
733 value,
734 )
735 }
736 }
737
738 fn get_held_item_nbt(&self, player: &str) -> Option<String> {
739 let s = srv!(self);
740 let owned = unsafe { (s.get_held_item_nbt)(s.ctx, YogStr::from_str(player)) };
741
742 take_owned(s.free_str, owned)
743 }
744
745 fn set_held_item_nbt(&self, player: &str, snbt: &str) -> bool {
746 let s = srv!(self);
747 unsafe { (s.set_held_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
748 }
749
750 fn get_offhand_item_nbt(&self, player: &str) -> Option<String> {
751 let s = srv!(self);
752 let owned = unsafe { (s.get_offhand_item_nbt)(s.ctx, YogStr::from_str(player)) };
753
754 take_owned(s.free_str, owned)
755 }
756
757 fn set_offhand_item_nbt(&self, player: &str, snbt: &str) -> bool {
758 let s = srv!(self);
759 unsafe { (s.set_offhand_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
760 }
761
762 fn get_slot_item(&self, player: &str, slot: u32) -> Option<(String, u32, String)> {
763 let s = srv!(self);
764 let owned = unsafe { (s.get_slot_item)(s.ctx, YogStr::from_str(player), slot) };
765 let text = take_owned(s.free_str, owned)?;
766 let mut it = text.splitn(3, '\t');
767 let item_id = it.next()?.to_owned();
768 let count: u32 = it.next()?.parse().ok()?;
769 let nbt = it.next().unwrap_or("{}").to_owned();
770 Some((item_id, count, nbt))
771 }
772
773 fn set_slot_item(
774 &self,
775 player: &str,
776 slot: u32,
777 item_id: &str,
778 count: u32,
779 snbt: &str,
780 ) -> bool {
781 let s = srv!(self);
782 unsafe {
783 (s.set_slot_item)(
784 s.ctx,
785 YogStr::from_str(player),
786 slot,
787 YogStr::from_str(item_id),
788 count,
789 YogStr::from_str(snbt),
790 )
791 }
792 }
793
794 fn entity_info(&self, uuid: &str) -> Option<yog_core::EntityInfo> {
797 let s = srv!(self);
798 let owned = unsafe { (s.entity_info)(s.ctx, YogStr::from_str(uuid)) };
799 take_owned(s.free_str, owned).and_then(|l| yog_core::EntityInfo::decode(&l))
800 }
801
802 fn entities_in_box(
803 &self,
804 dimension: &str,
805 min: (f64, f64, f64),
806 max: (f64, f64, f64),
807 ) -> Vec<String> {
808 let s = srv!(self);
809 let owned =
810 unsafe { (s.entities_in_box)(s.ctx, YogStr::from_str(dimension), v3(min), v3(max)) };
811 take_owned(s.free_str, owned)
812 .map(|t| {
813 t.lines()
814 .filter(|l| !l.is_empty())
815 .map(str::to_owned)
816 .collect()
817 })
818 .unwrap_or_default()
819 }
820
821 fn raycast_block(
822 &self,
823 dimension: &str,
824 from: (f64, f64, f64),
825 to: (f64, f64, f64),
826 ) -> Option<yog_core::BlockHit> {
827 let s = srv!(self);
828 let owned =
829 unsafe { (s.raycast_block)(s.ctx, YogStr::from_str(dimension), v3(from), v3(to)) };
830 take_owned(s.free_str, owned).and_then(|l| yog_core::BlockHit::decode(&l))
831 }
832
833 fn raycast_entity(
834 &self,
835 dimension: &str,
836 from: (f64, f64, f64),
837 to: (f64, f64, f64),
838 exclude: &str,
839 ) -> Option<String> {
840 let s = srv!(self);
841 let owned = unsafe {
842 (s.raycast_entity)(
843 s.ctx,
844 YogStr::from_str(dimension),
845 v3(from),
846 v3(to),
847 YogStr::from_str(exclude),
848 )
849 };
850 take_owned(s.free_str, owned).filter(|u| !u.is_empty())
851 }
852
853 fn block_info(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<yog_core::BlockInfo> {
854 let s = srv!(self);
855 let owned = unsafe { (s.block_info)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
856 take_owned(s.free_str, owned).and_then(|l| yog_core::BlockInfo::decode(&l))
857 }
858
859 fn break_block(
860 &self,
861 dimension: &str,
862 pos: yog_core::BlockPos,
863 drop: bool,
864 breaker: &str,
865 ) -> bool {
866 let s = srv!(self);
867 unsafe {
868 (s.break_block)(
869 s.ctx,
870 YogStr::from_str(dimension),
871 bp(pos),
872 drop,
873 YogStr::from_str(breaker),
874 )
875 }
876 }
877
878 fn place_block_from_slot(
879 &self,
880 player: &str,
881 slot: u32,
882 dimension: &str,
883 pos: yog_core::BlockPos,
884 ) -> bool {
885 let s = srv!(self);
886 unsafe {
887 (s.place_block_from_slot)(
888 s.ctx,
889 YogStr::from_str(player),
890 slot,
891 YogStr::from_str(dimension),
892 bp(pos),
893 )
894 }
895 }
896
897 fn bonemeal(&self, dimension: &str, pos: yog_core::BlockPos) -> bool {
898 let s = srv!(self);
899 unsafe { (s.bonemeal)(s.ctx, YogStr::from_str(dimension), bp(pos)) }
900 }
901
902 fn explode(
903 &self,
904 dimension: &str,
905 x: f64,
906 y: f64,
907 z: f64,
908 power: f32,
909 fire: bool,
910 source: &str,
911 ) -> bool {
912 let s = srv!(self);
913 unsafe {
914 (s.explode)(
915 s.ctx,
916 YogStr::from_str(dimension),
917 v3((x, y, z)),
918 power,
919 fire,
920 YogStr::from_str(source),
921 )
922 }
923 }
924
925 fn tellraw(&self, player: &str, json: &str) -> bool {
926 let s = srv!(self);
927 unsafe { (s.tellraw)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
928 }
929
930 fn entity_damage(&self, uuid: &str, amount: f32, damage_type: &str, attacker: &str) -> bool {
931 let s = srv!(self);
932 unsafe {
933 (s.entity_damage)(
934 s.ctx,
935 YogStr::from_str(uuid),
936 amount,
937 YogStr::from_str(damage_type),
938 YogStr::from_str(attacker),
939 )
940 }
941 }
942
943 fn entity_set_fire_ticks(&self, uuid: &str, ticks: i32) -> bool {
944 let s = srv!(self);
945 unsafe { (s.entity_set_fire_ticks)(s.ctx, YogStr::from_str(uuid), ticks) }
946 }
947
948 fn player_add_xp(&self, player: &str, amount: i32) -> bool {
949 let s = srv!(self);
950 unsafe { (s.player_add_xp)(s.ctx, YogStr::from_str(player), amount) }
951 }
952
953 fn player_set_may_fly(&self, player: &str, may_fly: bool) -> bool {
954 let s = srv!(self);
955 unsafe { (s.player_set_may_fly)(s.ctx, YogStr::from_str(player), may_fly) }
956 }
957
958 fn player_info(&self, player: &str) -> Option<yog_core::PlayerInfo> {
959 let s = srv!(self);
960 let owned = unsafe { (s.player_info)(s.ctx, YogStr::from_str(player)) };
961 take_owned(s.free_str, owned).and_then(|l| yog_core::PlayerInfo::decode(&l))
962 }
963
964 fn spawn_item(
965 &self,
966 dimension: &str,
967 pos: (f64, f64, f64),
968 vel: (f64, f64, f64),
969 item_id: &str,
970 count: u32,
971 snbt: &str,
972 ) -> Option<String> {
973 let s = srv!(self);
974 let owned = unsafe {
975 (s.spawn_item)(
976 s.ctx,
977 YogStr::from_str(dimension),
978 v3(pos),
979 v3(vel),
980 YogStr::from_str(item_id),
981 count,
982 YogStr::from_str(snbt),
983 )
984 };
985 take_owned(s.free_str, owned).filter(|u| !u.is_empty())
986 }
987
988 fn item_entity_stack(&self, uuid: &str) -> Option<(String, u32, String)> {
989 let s = srv!(self);
990 let owned = unsafe { (s.item_entity_stack)(s.ctx, YogStr::from_str(uuid)) };
991 let text = take_owned(s.free_str, owned)?;
992 let mut it = text.splitn(3, '\t');
993 let item_id = it.next()?.to_owned();
994 let count = it.next()?.parse().ok()?;
995 let nbt = it.next().unwrap_or("{}").to_owned();
996 Some((item_id, count, nbt))
997 }
998
999 fn item_entity_set_stack(&self, uuid: &str, item_id: &str, count: u32, snbt: &str) -> bool {
1000 let s = srv!(self);
1001 unsafe {
1002 (s.item_entity_set_stack)(
1003 s.ctx,
1004 YogStr::from_str(uuid),
1005 YogStr::from_str(item_id),
1006 count,
1007 YogStr::from_str(snbt),
1008 )
1009 }
1010 }
1011
1012 fn actionbar_json(&self, player: &str, json: &str) -> bool {
1013 let s = srv!(self);
1014 unsafe { (s.actionbar_json)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
1015 }
1016
1017 fn use_item_on_block(
1018 &self,
1019 dimension: &str,
1020 pos: yog_core::BlockPos,
1021 face: yog_core::Face,
1022 item_id: &str,
1023 player: &str,
1024 ) -> bool {
1025 let s = srv!(self);
1026 unsafe {
1027 (s.use_item_on_block)(
1028 s.ctx,
1029 YogStr::from_str(dimension),
1030 bp(pos),
1031 face.index(),
1032 YogStr::from_str(item_id),
1033 YogStr::from_str(player),
1034 )
1035 }
1036 }
1037
1038 fn pour_fluid(&self, dimension: &str, pos: yog_core::BlockPos, bucket_item_id: &str) -> bool {
1039 let s = srv!(self);
1040 unsafe {
1041 (s.pour_fluid)(
1042 s.ctx,
1043 YogStr::from_str(dimension),
1044 bp(pos),
1045 YogStr::from_str(bucket_item_id),
1046 )
1047 }
1048 }
1049
1050 fn has_entity_tag(&self, entity_type: &str, tag_id: &str) -> bool {
1051 let s = srv!(self);
1052 unsafe {
1053 (s.has_entity_tag)(
1054 s.ctx,
1055 YogStr::from_str(entity_type),
1056 YogStr::from_str(tag_id),
1057 )
1058 }
1059 }
1060
1061 fn item_block(&self, item_id: &str) -> Option<String> {
1062 let s = srv!(self);
1063 let owned = unsafe { (s.item_block)(s.ctx, YogStr::from_str(item_id)) };
1064 take_owned(s.free_str, owned).filter(|b| !b.is_empty())
1065 }
1066
1067 fn place_feature(&self, dimension: &str, pos: yog_core::BlockPos, feature_id: &str) -> bool {
1068 let s = srv!(self);
1069 unsafe {
1070 (s.place_feature)(
1071 s.ctx,
1072 YogStr::from_str(dimension),
1073 bp(pos),
1074 YogStr::from_str(feature_id),
1075 )
1076 }
1077 }
1078}
1079
1080pub(crate) fn take_owned(
1082 free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
1083 owned: yog_abi::YogOwnedStr,
1084) -> Option<String> {
1085 if owned.is_none() {
1086 return None;
1087 }
1088 let text = unsafe {
1090 String::from_utf8_lossy(std::slice::from_raw_parts(owned.ptr, owned.len as usize))
1091 .into_owned()
1092 };
1093 unsafe { free_str(owned.ptr, owned.len) };
1094 Some(text)
1095}
1096
1097fn v3((x, y, z): (f64, f64, f64)) -> yog_abi::YogVec3 {
1098 yog_abi::YogVec3 { x, y, z }
1099}
1100
1101fn bp(pos: yog_core::BlockPos) -> yog_abi::YogBlockPos {
1102 yog_abi::YogBlockPos {
1103 x: pos.x,
1104 y: pos.y,
1105 z: pos.z,
1106 }
1107}
1108
1109macro_rules! trampoline_phased {
1127 ($name:ident, $abi_ev:ty, $rust_ev:ty, |$ev:ident| $build:expr_2021) => {
1128 unsafe extern "C" fn $name<F>(
1129 ud: *mut c_void,
1130 srv: *const YogServer,
1131 ev: *const $abi_ev,
1132 phase: u8,
1133 ) -> bool
1134 where
1135 F: Fn(&$rust_ev, EventPhase, &dyn Server) -> bool + Send + Sync,
1136 {
1137 unsafe {
1138 let f = &*(ud as *const F);
1139 let $ev = &*ev;
1140 let rust_ev = $build;
1141 let p = if phase == 0 {
1142 EventPhase::Pre
1143 } else {
1144 EventPhase::Post
1145 };
1146 f(&rust_ev, p, &CServer(srv))
1147 }
1148 }
1149 };
1150}
1151
1152trampoline_phased!(
1153 trampoline_block_break,
1154 YogBlockBreakEvent,
1155 BlockBreakEvent,
1156 |ev| BlockBreakEvent {
1157 player_name: ev.player.as_str().to_owned(),
1158 block_id: ev.block.as_str().to_owned(),
1159 pos: yog_core::BlockPos {
1160 x: ev.pos.x,
1161 y: ev.pos.y,
1162 z: ev.pos.z
1163 },
1164 dimension: ev.dimension.as_str().to_owned(),
1165 }
1166);
1167
1168trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
1169 player_name: ev.player.as_str().to_owned(),
1170 message: ev.message.as_str().to_owned(),
1171 dimension: ev.dimension.as_str().to_owned(),
1172});
1173
1174trampoline_phased!(
1175 trampoline_player_join,
1176 YogPlayerEvent,
1177 PlayerJoinEvent,
1178 |ev| PlayerJoinEvent {
1179 player_name: ev.player.as_str().to_owned(),
1180 uuid: ev.uuid.as_str().to_owned(),
1181 dimension: ev.dimension.as_str().to_owned(),
1182 }
1183);
1184
1185trampoline_phased!(
1186 trampoline_player_leave,
1187 YogPlayerEvent,
1188 PlayerLeaveEvent,
1189 |ev| PlayerLeaveEvent {
1190 player_name: ev.player.as_str().to_owned(),
1191 uuid: ev.uuid.as_str().to_owned(),
1192 dimension: ev.dimension.as_str().to_owned(),
1193 }
1194);
1195
1196trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| {
1197 UseItemEvent {
1198 player_name: ev.player.as_str().to_owned(),
1199 item_id: ev.item.as_str().to_owned(),
1200 sneaking: ev.sneaking,
1201 dimension: ev.dimension.as_str().to_owned(),
1202 }
1203});
1204
1205trampoline_phased!(
1206 trampoline_use_block,
1207 YogUseBlockEvent,
1208 UseBlockEvent,
1209 |ev| UseBlockEvent {
1210 player_name: ev.player.as_str().to_owned(),
1211 block_id: ev.block.as_str().to_owned(),
1212 pos: yog_core::BlockPos {
1213 x: ev.pos.x,
1214 y: ev.pos.y,
1215 z: ev.pos.z
1216 },
1217 dimension: ev.dimension.as_str().to_owned(),
1218 }
1219);
1220
1221trampoline_phased!(
1222 trampoline_attack_entity,
1223 YogAttackEntityEvent,
1224 AttackEntityEvent,
1225 |ev| AttackEntityEvent {
1226 player_name: ev.player.as_str().to_owned(),
1227 target_type: ev.target_type.as_str().to_owned(),
1228 target_uuid: ev.target_uuid.as_str().to_owned(),
1229 dimension: ev.dimension.as_str().to_owned(),
1230 }
1231);
1232
1233trampoline_phased!(
1234 trampoline_entity_damage,
1235 YogEntityDamageEvent,
1236 EntityDamageEvent,
1237 |ev| EntityDamageEvent {
1238 entity_type: ev.entity_type.as_str().to_owned(),
1239 uuid: ev.uuid.as_str().to_owned(),
1240 amount: ev.amount,
1241 source: ev.source.as_str().to_owned(),
1242 dimension: ev.dimension.as_str().to_owned(),
1243 }
1244);
1245
1246trampoline_phased!(
1247 trampoline_entity_death,
1248 YogEntityDeathEvent,
1249 EntityDeathEvent,
1250 |ev| EntityDeathEvent {
1251 entity_type: ev.entity_type.as_str().to_owned(),
1252 uuid: ev.uuid.as_str().to_owned(),
1253 source: ev.source.as_str().to_owned(),
1254 dimension: ev.dimension.as_str().to_owned(),
1255 }
1256);
1257
1258trampoline_phased!(
1259 trampoline_entity_spawn,
1260 YogEntitySpawnEvent,
1261 EntitySpawnEvent,
1262 |ev| EntitySpawnEvent {
1263 entity_type: ev.entity_type.as_str().to_owned(),
1264 uuid: ev.uuid.as_str().to_owned(),
1265 dimension: ev.dimension.as_str().to_owned(),
1266 }
1267);
1268
1269trampoline_phased!(
1270 trampoline_place_block,
1271 YogPlaceBlockEvent,
1272 PlaceBlockEvent,
1273 |ev| PlaceBlockEvent {
1274 player_name: ev.player.as_str().to_owned(),
1275 block_id: ev.block.as_str().to_owned(),
1276 pos: yog_core::BlockPos {
1277 x: ev.pos.x,
1278 y: ev.pos.y,
1279 z: ev.pos.z
1280 },
1281 dimension: ev.dimension.as_str().to_owned(),
1282 }
1283);
1284
1285trampoline_phased!(
1286 trampoline_player_death,
1287 YogPlayerDeathEvent,
1288 PlayerDeathEvent,
1289 |ev| PlayerDeathEvent {
1290 player_name: ev.player.as_str().to_owned(),
1291 uuid: ev.uuid.as_str().to_owned(),
1292 source: ev.source.as_str().to_owned(),
1293 dimension: ev.dimension.as_str().to_owned(),
1294 }
1295);
1296
1297trampoline_phased!(
1298 trampoline_player_respawn,
1299 YogPlayerRespawnEvent,
1300 PlayerRespawnEvent,
1301 |ev| PlayerRespawnEvent {
1302 player_name: ev.player.as_str().to_owned(),
1303 uuid: ev.uuid.as_str().to_owned(),
1304 at_anchor: ev.at_anchor,
1305 dimension: ev.dimension.as_str().to_owned(),
1306 }
1307);
1308
1309trampoline_phased!(
1310 trampoline_advancement,
1311 YogAdvancementEvent,
1312 AdvancementEvent,
1313 |ev| AdvancementEvent {
1314 player_name: ev.player.as_str().to_owned(),
1315 uuid: ev.uuid.as_str().to_owned(),
1316 advancement_id: ev.advancement.as_str().to_owned(),
1317 dimension: ev.dimension.as_str().to_owned(),
1318 }
1319);
1320
1321trampoline_phased!(
1322 trampoline_entity_interact,
1323 YogEntityInteractEvent,
1324 EntityInteractEvent,
1325 |ev| EntityInteractEvent {
1326 player_name: ev.player.as_str().to_owned(),
1327 player_uuid: ev.player_uuid.as_str().to_owned(),
1328 entity_type: ev.entity_type.as_str().to_owned(),
1329 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1330 hand: ev.hand.as_str().to_owned(),
1331 dimension: ev.dimension.as_str().to_owned(),
1332 }
1333);
1334
1335trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| {
1336 CraftEvent {
1337 player_name: ev.player.as_str().to_owned(),
1338 player_uuid: ev.player_uuid.as_str().to_owned(),
1339 result_item: ev.result_item.as_str().to_owned(),
1340 result_count: ev.result_count,
1341 dimension: ev.dimension.as_str().to_owned(),
1342 }
1343});
1344
1345trampoline_phased!(
1346 trampoline_explosion,
1347 YogExplosionEvent,
1348 ExplosionEvent,
1349 |ev| ExplosionEvent {
1350 dimension: ev.dimension.as_str().to_owned(),
1351 x: ev.x,
1352 y: ev.y,
1353 z: ev.z,
1354 power: ev.power,
1355 cause_uuid: ev.cause_uuid.as_str().to_owned(),
1356 }
1357);
1358
1359trampoline_phased!(
1360 trampoline_item_pickup,
1361 YogItemPickupEvent,
1362 ItemPickupEvent,
1363 |ev| ItemPickupEvent {
1364 player_name: ev.player.as_str().to_owned(),
1365 player_uuid: ev.player_uuid.as_str().to_owned(),
1366 item_id: ev.item_id.as_str().to_owned(),
1367 item_count: ev.item_count,
1368 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1369 dimension: ev.dimension.as_str().to_owned(),
1370 }
1371);
1372
1373trampoline_phased!(
1374 trampoline_player_move,
1375 YogPlayerMoveEvent,
1376 PlayerMoveEvent,
1377 |ev| PlayerMoveEvent {
1378 player_name: ev.player.as_str().to_owned(),
1379 player_uuid: ev.player_uuid.as_str().to_owned(),
1380 x: ev.x,
1381 y: ev.y,
1382 z: ev.z,
1383 yaw: ev.yaw,
1384 pitch: ev.pitch,
1385 dimension: ev.dimension.as_str().to_owned(),
1386 }
1387);
1388
1389trampoline_phased!(
1390 trampoline_container_open,
1391 YogContainerOpenEvent,
1392 ContainerOpenEvent,
1393 |ev| ContainerOpenEvent {
1394 player_name: ev.player.as_str().to_owned(),
1395 player_uuid: ev.player_uuid.as_str().to_owned(),
1396 container_type: ev.container_type.as_str().to_owned(),
1397 dimension: ev.dimension.as_str().to_owned(),
1398 }
1399);
1400
1401trampoline_phased!(
1402 trampoline_container_close,
1403 YogContainerCloseEvent,
1404 ContainerCloseEvent,
1405 |ev| ContainerCloseEvent {
1406 player_name: ev.player.as_str().to_owned(),
1407 player_uuid: ev.player_uuid.as_str().to_owned(),
1408 dimension: ev.dimension.as_str().to_owned(),
1409 }
1410);
1411
1412trampoline_phased!(
1413 trampoline_projectile_hit,
1414 YogProjectileHitEvent,
1415 ProjectileHitEvent,
1416 |ev| ProjectileHitEvent {
1417 projectile_type: ev.projectile_type.as_str().to_owned(),
1418 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
1419 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
1420 hit_type: ev.hit_type.as_str().to_owned(),
1421 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
1422 x: ev.x,
1423 y: ev.y,
1424 z: ev.z,
1425 dimension: ev.dimension.as_str().to_owned(),
1426 }
1427);
1428
1429unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
1430where
1431 F: Fn(&dyn Server) + Send + Sync,
1432{
1433 unsafe {
1434 let f = &*(ud as *const F);
1435 f(&CServer(srv));
1436 }
1437}
1438
1439unsafe extern "C" fn trampoline_packet<F>(
1440 ud: *mut c_void,
1441 srv: *const YogServer,
1442 ev: *const YogPacketEvent,
1443) where
1444 F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
1445{
1446 unsafe {
1447 let f = &*(ud as *const F);
1448 let ev = &*ev;
1449 let rust_ev = PacketEvent {
1450 channel: ev.channel.as_str().to_owned(),
1451 player: ev.player.as_str().to_owned(),
1452 payload: std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec(),
1453 };
1454 f(&rust_ev, &CServer(srv));
1455 }
1456}
1457
1458unsafe extern "C" fn trampoline_command<F>(
1459 ud: *mut c_void,
1460 srv: *const YogServer,
1461 ev: *const YogCommandEvent,
1462 reply_buf: *mut u8,
1463 reply_cap: u32,
1464 reply_len: *mut u32,
1465) where
1466 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
1467{
1468 unsafe {
1469 let f = &*(ud as *const F);
1470 let ev = &*ev;
1471 let ctx = CommandContext {
1472 name: ev.name.as_str().to_owned(),
1473 args: ev.args.as_str().to_owned(),
1474 source: ev.source.as_str().to_owned(),
1475 uuid: ev.uuid.as_str().to_owned(),
1476 dimension: ev.dimension.as_str().to_owned(),
1477 };
1478 *reply_len = 0;
1479 if let Some(reply) = f(&ctx, &CServer(srv)) {
1480 let bytes = reply.as_bytes();
1481 let n = bytes.len().min(reply_cap as usize);
1482 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
1483 *reply_len = n as u32;
1484 }
1485 }
1486}
1487
1488unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
1489where
1490 F: Fn(&dyn Server) + Send + Sync,
1491{
1492 unsafe {
1493 let f = &*(ud as *const F);
1494 f(&CServer(srv));
1495 }
1496}
1497
1498unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
1501where
1502 F: Fn(&ClientTickEvent) + Send + Sync,
1503{
1504 unsafe {
1505 let f = &*(ud as *const F);
1506 f(&ClientTickEvent {});
1507 }
1508}
1509
1510unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1511where
1512 F: Fn(&GfxContext) + Send + Sync,
1513{
1514 unsafe {
1515 let f = &*(ud as *const F);
1516 f(&GfxContext::from_raw(gfx));
1517 }
1518}
1519
1520unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1521where
1522 F: Fn(&GfxContext) + Send + Sync,
1523{
1524 unsafe {
1525 let f = &*(ud as *const F);
1526 f(&GfxContext::from_raw(gfx));
1527 }
1528}
1529
1530unsafe extern "C" fn trampoline_key_press<F>(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool
1531where
1532 F: Fn(&KeyPressEvent) -> bool + Send + Sync,
1533{
1534 unsafe {
1535 let f = &*(ud as *const F);
1536 let e = &*ev;
1537 f(&KeyPressEvent {
1538 key_code: e.key_code,
1539 scan_code: e.scan_code,
1540 action: e.action,
1541 modifiers: e.modifiers,
1542 })
1543 }
1544}
1545
1546unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
1547where
1548 F: Fn(&ScreenEvent) + Send + Sync,
1549{
1550 unsafe {
1551 let f = &*(ud as *const F);
1552 f(&ScreenEvent {
1553 screen_class: screen_class.as_str().to_owned(),
1554 });
1555 true
1556 }
1557}
1558
1559static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
1570 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
1571
1572pub(crate) fn global_api() -> *mut YogApi {
1574 GLOBAL_API.load(std::sync::atomic::Ordering::Acquire)
1575}
1576
1577#[derive(Debug, Clone)]
1579pub struct ModInfo {
1580 pub source: String,
1582 pub id: String,
1583 pub name: String,
1584 pub version: String,
1585 pub authors: String,
1587 pub description: String,
1588}
1589
1590pub fn installed_mods() -> Vec<ModInfo> {
1596 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1597 if api.is_null() {
1598 return Vec::new();
1599 }
1600 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
1601 let Some(text) = take_owned(unsafe { (*api).free_str }, owned) else {
1602 return Vec::new();
1603 };
1604 text.lines()
1605 .filter_map(|line| {
1606 let mut f = line.split('\t');
1607 Some(ModInfo {
1608 source: f.next()?.to_string(),
1609 id: f.next()?.to_string(),
1610 name: f.next().unwrap_or_default().to_string(),
1611 version: f.next().unwrap_or_default().to_string(),
1612 authors: f.next().unwrap_or_default().to_string(),
1613 description: f.next().unwrap_or_default().to_string(),
1614 })
1615 })
1616 .collect()
1617}
1618
1619pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
1623 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1624 if api.is_null() {
1625 return;
1626 }
1627 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
1628}
1629
1630pub fn close_ui(ui_id: &str) {
1635 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1636 if api.is_null() {
1637 return;
1638 }
1639 unsafe { ((*api).ui_close)((*api).ctx, YogStr::from_str(ui_id)) }
1640}
1641
1642pub trait UiHandled {
1646 fn handled(self) -> bool;
1647}
1648
1649impl UiHandled for () {
1650 fn handled(self) -> bool {
1651 false
1652 }
1653}
1654
1655impl UiHandled for bool {
1656 fn handled(self) -> bool {
1657 self
1658 }
1659}
1660
1661pub fn server() -> Option<CServer> {
1666 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1667 if api.is_null() {
1668 return None;
1669 }
1670 let srv = unsafe { (*api).server };
1671 if srv.is_null() {
1672 return None;
1673 }
1674 Some(CServer(srv))
1675}
1676
1677pub struct Registry {
1678 api: *const YogApi,
1679}
1680
1681unsafe impl Send for Registry {}
1683unsafe impl Sync for Registry {}
1684
1685impl Registry {
1686 pub unsafe fn from_raw(api: *const YogApi) -> Self {
1693 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
1694 Self { api }
1695 }
1696
1697 #[inline]
1698 fn ctx(&self) -> *mut c_void {
1699 unsafe { (*self.api).ctx }
1700 }
1701
1702 fn leak<F: 'static>(f: F) -> *mut c_void {
1705 Box::into_raw(Box::new(f)) as *mut c_void
1706 }
1707
1708 pub fn on_block_break<F>(&mut self, handler: F)
1716 where
1717 F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1718 {
1719 let ud = Self::leak(handler);
1720 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
1721 }
1722
1723 pub fn on_chat<F>(&mut self, handler: F)
1724 where
1725 F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1726 {
1727 let ud = Self::leak(handler);
1728 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
1729 }
1730
1731 pub fn on_player_join<F>(&mut self, handler: F)
1732 where
1733 F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1734 {
1735 let ud = Self::leak(handler);
1736 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
1737 }
1738
1739 pub fn on_player_leave<F>(&mut self, handler: F)
1740 where
1741 F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1742 {
1743 let ud = Self::leak(handler);
1744 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
1745 }
1746
1747 pub fn on_use_item<F>(&mut self, handler: F)
1748 where
1749 F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1750 {
1751 let ud = Self::leak(handler);
1752 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
1753 }
1754
1755 pub fn on_use_block<F>(&mut self, handler: F)
1756 where
1757 F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1758 {
1759 let ud = Self::leak(handler);
1760 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
1761 }
1762
1763 pub fn on_attack_entity<F>(&mut self, handler: F)
1764 where
1765 F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1766 {
1767 let ud = Self::leak(handler);
1768 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
1769 }
1770
1771 pub fn on_entity_damage<F>(&mut self, handler: F)
1772 where
1773 F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1774 {
1775 let ud = Self::leak(handler);
1776 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
1777 }
1778
1779 pub fn on_entity_death<F>(&mut self, handler: F)
1780 where
1781 F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1782 {
1783 let ud = Self::leak(handler);
1784 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
1785 }
1786
1787 pub fn on_entity_spawn<F>(&mut self, handler: F)
1788 where
1789 F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1790 {
1791 let ud = Self::leak(handler);
1792 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
1793 }
1794
1795 pub fn on_player_place_block<F>(&mut self, handler: F)
1796 where
1797 F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1798 {
1799 let ud = Self::leak(handler);
1800 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
1801 }
1802
1803 pub fn on_player_death<F>(&mut self, handler: F)
1804 where
1805 F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1806 {
1807 let ud = Self::leak(handler);
1808 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
1809 }
1810
1811 pub fn on_player_respawn<F>(&mut self, handler: F)
1812 where
1813 F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1814 {
1815 let ud = Self::leak(handler);
1816 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
1817 }
1818
1819 pub fn on_advancement<F>(&mut self, handler: F)
1820 where
1821 F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1822 {
1823 let ud = Self::leak(handler);
1824 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
1825 }
1826
1827 pub fn on_entity_interact<F>(&mut self, handler: F)
1828 where
1829 F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1830 {
1831 let ud = Self::leak(handler);
1832 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
1833 }
1834
1835 pub fn on_item_craft<F>(&mut self, handler: F)
1836 where
1837 F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1838 {
1839 let ud = Self::leak(handler);
1840 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
1841 }
1842
1843 pub fn on_explosion<F>(&mut self, handler: F)
1844 where
1845 F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1846 {
1847 let ud = Self::leak(handler);
1848 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
1849 }
1850
1851 pub fn on_item_pickup<F>(&mut self, handler: F)
1852 where
1853 F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1854 {
1855 let ud = Self::leak(handler);
1856 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
1857 }
1858
1859 pub fn on_player_move<F>(&mut self, handler: F)
1860 where
1861 F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1862 {
1863 let ud = Self::leak(handler);
1864 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
1865 }
1866
1867 pub fn on_container_open<F>(&mut self, handler: F)
1868 where
1869 F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1870 {
1871 let ud = Self::leak(handler);
1872 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
1873 }
1874
1875 pub fn on_container_close<F>(&mut self, handler: F)
1876 where
1877 F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1878 {
1879 let ud = Self::leak(handler);
1880 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
1881 }
1882
1883 pub fn on_projectile_hit<F>(&mut self, handler: F)
1884 where
1885 F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1886 {
1887 let ud = Self::leak(handler);
1888 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
1889 }
1890
1891 pub fn on_tick<F>(&mut self, listener: F)
1892 where
1893 F: Fn(&dyn Server) + Send + Sync + 'static,
1894 {
1895 let ud = Self::leak(listener);
1896 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
1897 }
1898
1899 pub fn on_server_started<F>(&mut self, listener: F)
1900 where
1901 F: Fn(&dyn Server) + Send + Sync + 'static,
1902 {
1903 let ud = Self::leak(listener);
1904 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
1905 }
1906
1907 pub fn on_server_stopping<F>(&mut self, listener: F)
1908 where
1909 F: Fn(&dyn Server) + Send + Sync + 'static,
1910 {
1911 let ud = Self::leak(listener);
1912 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
1913 }
1914
1915 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
1918 where
1919 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1920 {
1921 let name_ys = YogStr::from_str(name.as_ref());
1922 let ud = Self::leak(handler);
1923 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
1924 }
1925
1926 pub fn on_typed_command<F>(
1933 &mut self,
1934 name: impl AsRef<str>,
1935 schema: impl AsRef<str>,
1936 handler: F,
1937 ) where
1938 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1939 {
1940 let name_ys = YogStr::from_str(name.as_ref());
1941 let schema_ys = YogStr::from_str(schema.as_ref());
1942 let ud = Self::leak(handler);
1943 unsafe {
1944 ((*self.api).register_typed_command)(
1945 self.ctx(),
1946 name_ys,
1947 schema_ys,
1948 ud,
1949 trampoline_command::<F>,
1950 );
1951 }
1952 }
1953
1954 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1957 where
1958 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1959 {
1960 let ch = YogStr::from_str(channel.as_ref());
1961 let ud = Self::leak(handler);
1962 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1963 }
1964
1965 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1966 where
1967 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1968 {
1969 let ch = YogStr::from_str(channel.as_ref());
1970 let ud = Self::leak(handler);
1971 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1972 }
1973
1974 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
1979 where
1980 P: Packet + Send + Sync + 'static,
1981 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
1982 {
1983 self.on_packet(channel, move |ev, srv| {
1984 if let Some(pkt) = P::decode(&ev.payload) {
1985 handler(&pkt, srv);
1986 }
1987 });
1988 }
1989
1990 fn recipe(&mut self, ns: &str, name: &str, json: &str) {
1993 unsafe {
1994 ((*self.api).register_recipe_json)(
1995 self.ctx(),
1996 YogStr::from_str(ns),
1997 YogStr::from_str(name),
1998 YogStr::from_str(json),
1999 );
2000 }
2001 }
2002
2003 pub fn add_shaped_recipe(&mut self, recipe: ShapedRecipe) {
2005 let (ns, name) = recipe.ns_name();
2006 self.recipe(ns, name, &recipe.to_json());
2007 }
2008
2009 pub fn add_shapeless_recipe(&mut self, recipe: ShapelessRecipe) {
2011 let (ns, name) = recipe.ns_name();
2012 self.recipe(ns, name, &recipe.to_json());
2013 }
2014
2015 pub fn add_furnace_recipe(&mut self, recipe: FurnaceRecipe) {
2017 let (ns, name) = recipe.ns_name();
2018 self.recipe(ns, name, &recipe.to_json());
2019 }
2020
2021 pub fn register_item(&mut self, def: ItemDef) {
2024 let food_nutrition = def.food.as_ref().map_or(0, |f| f.nutrition);
2028 let food_saturation = def.food.as_ref().map_or(0.0, |f| f.saturation);
2029 let food_always_eat = def.food.as_ref().is_some_and(|f| f.can_always_eat);
2030 let c = YogItemDef {
2031 id: YogStr::from_str(&def.id),
2032 max_stack: def.max_stack as u32,
2033 name: def
2034 .name
2035 .as_deref()
2036 .map(YogStr::from_str)
2037 .unwrap_or(YogStr::EMPTY),
2038 tooltip: def
2039 .tooltip
2040 .as_deref()
2041 .map(YogStr::from_str)
2042 .unwrap_or(YogStr::EMPTY),
2043 max_damage: def.max_damage,
2044 fire_resistant: def.fire_resistant,
2045 fuel_ticks: def.fuel_ticks,
2046 food_nutrition,
2047 food_saturation,
2048 food_always_eat,
2049 };
2050 unsafe { ((*self.api).register_item)(self.ctx(), &c) }
2051 }
2052
2053 pub fn register_block(&mut self, def: BlockDef) {
2054 let shape = def.shape.unwrap_or([0.0; 6]);
2055 let groups_joined = def.connect_groups.join(",");
2056 let c = YogBlockDef {
2057 id: YogStr::from_str(&def.id),
2058 hardness: def.hardness,
2059 resistance: def.resistance,
2060 name: def
2061 .name
2062 .as_deref()
2063 .map(YogStr::from_str)
2064 .unwrap_or(YogStr::EMPTY),
2065 light_level: def.light_level,
2066 sound: def
2067 .sound
2068 .as_deref()
2069 .map(YogStr::from_str)
2070 .unwrap_or(YogStr::EMPTY),
2071 requires_tool: def.requires_tool,
2072 no_collision: def.no_collision,
2073 slipperiness: def.slipperiness,
2074 shape,
2075 connects: def.connects,
2076 connect_groups: YogStr::from_str(&groups_joined),
2077 inventory_id: def
2078 .inventory_id
2079 .as_deref()
2080 .map(YogStr::from_str)
2081 .unwrap_or(YogStr::EMPTY),
2082 };
2083 unsafe { ((*self.api).register_block)(self.ctx(), &c) }
2084 }
2085
2086 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
2090 let layout = yog_inventory::encode_layout(&def.resolved_layout());
2091 let c = YogInventoryDef {
2092 id: YogStr::from_str(&def.id),
2093 slot_count: def.slot_count as u32,
2094 layout: YogStr::from_str(&layout),
2095 include_player_inventory: def.include_player_inventory,
2096 player_inv_x: def.player_inv_offset.0,
2097 player_inv_y: def.player_inv_offset.1,
2098 background_texture: def
2099 .background_texture
2100 .as_deref()
2101 .map(YogStr::from_str)
2102 .unwrap_or(YogStr::EMPTY),
2103 title: YogStr::from_str(&def.title),
2104 background_w: def.background_size.map(|(w, _)| w).unwrap_or(0.0),
2105 background_h: def.background_size.map(|(_, h)| h).unwrap_or(0.0),
2106 };
2107 unsafe { ((*self.api).register_inventory)(self.ctx(), &c) }
2108 }
2109
2110 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
2114 let items_str = grant.items.join("|");
2115 let c = YogStartupGrantDef {
2116 id: YogStr::from_str(&grant.id),
2117 items: YogStr::from_str(&items_str),
2118 book: grant
2119 .book
2120 .as_deref()
2121 .map(YogStr::from_str)
2122 .unwrap_or(YogStr::EMPTY),
2123 command: grant
2124 .command
2125 .as_deref()
2126 .map(YogStr::from_str)
2127 .unwrap_or(YogStr::EMPTY),
2128 };
2129 unsafe { ((*self.api).register_startup_grant)(self.ctx(), &c) }
2130 }
2131
2132 pub fn register_book(&mut self, book: &Book) {
2134 let json = book.to_json();
2135 let id = YogStr::from_str(&book.id);
2136 let j = YogStr::from_str(&json);
2137 unsafe { ((*self.api).register_book)(self.ctx(), id, j) }
2138 }
2139
2140 pub fn register_dimension(&mut self, def: &YogDimensionDef) {
2155 let json = def.to_json();
2156 let id = YogStr::from_str(&def.id);
2157 let j = YogStr::from_str(&json);
2158 unsafe { ((*self.api).register_dimension_json)(self.ctx(), id, j) }
2159 }
2160
2161 pub fn register_chunk_generator<F>(&mut self, generator_type_id: &str, generator: F)
2173 where
2174 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2175 {
2176 let ud = Self::leak(generator);
2177 let id = YogStr::from_str(generator_type_id);
2178 unsafe {
2179 ((*self.api).register_chunk_generator)(
2180 self.ctx(),
2181 id,
2182 ud,
2183 trampoline_chunk_generator::<F>,
2184 );
2185 }
2186 }
2187
2188 pub fn installed_mods(&self) -> Vec<ModInfo> {
2195 installed_mods()
2196 }
2197
2198 pub fn interop(&self) -> crate::Interop {
2203 crate::Interop::new(self.api)
2204 }
2205
2206 pub fn raw_api(&self) -> *const yog_abi::YogApi {
2209 self.api
2210 }
2211
2212 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
2213 let l = YogStr::from_str(label);
2214 let u = YogStr::from_str(ui_id);
2215 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
2216 }
2217
2218 pub fn register_ui<F, R>(&mut self, ui_id: &str, handler: F)
2227 where
2228 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2229 R: UiHandled,
2230 {
2231 let ud = Self::leak(handler);
2232 let id = YogStr::from_str(ui_id);
2233 let empty = YogStr::from_str("{}");
2234 unsafe { ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F, R>) }
2235 }
2236
2237 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
2240 where
2241 F: Fn(&dyn Server) + Send + Sync + 'static,
2242 {
2243 let ud = Self::leak(handler);
2244 unsafe {
2245 ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>);
2246 }
2247 }
2248
2249 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
2250 where
2251 F: Fn(&dyn Server) + Send + Sync + 'static,
2252 {
2253 let ud = Self::leak(handler);
2254 unsafe {
2255 ((*self.api).schedule_repeating)(
2256 self.ctx(),
2257 period_ticks,
2258 ud,
2259 trampoline_scheduled::<F>,
2260 );
2261 }
2262 }
2263
2264 pub fn on_client_tick<F>(&mut self, handler: F)
2268 where
2269 F: Fn(&ClientTickEvent) + Send + Sync + 'static,
2270 {
2271 let ud = Self::leak(handler);
2272 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
2273 }
2274
2275 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
2284 where
2285 F: Fn(&GfxContext) + Send + Sync + 'static,
2286 {
2287 let ud = Self::leak(handler);
2288 let id = YogStr::from_str(ui_id);
2289 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
2290 }
2291
2292 pub fn on_hud_render<F>(&mut self, handler: F)
2297 where
2298 F: Fn(&GfxContext) + Send + Sync + 'static,
2299 {
2300 let ud = Self::leak(handler);
2301 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
2302 }
2303
2304 pub fn on_world_render<F>(&mut self, handler: F)
2310 where
2311 F: Fn(&GfxContext) + Send + Sync + 'static,
2312 {
2313 let ud = Self::leak(handler);
2314 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
2315 }
2316
2317 pub fn on_key_press<F>(&mut self, handler: F)
2320 where
2321 F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static,
2322 {
2323 let ud = Self::leak(handler);
2324 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
2325 }
2326
2327 pub fn on_screen_open<F>(&mut self, handler: F)
2329 where
2330 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2331 {
2332 let ud = Self::leak(handler);
2333 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
2334 }
2335
2336 pub fn on_screen_close<F>(&mut self, handler: F)
2338 where
2339 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2340 {
2341 let ud = Self::leak(handler);
2342 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
2343 }
2344}
2345
2346unsafe extern "C" fn trampoline_ui_event<F, R>(
2347 ud: *mut c_void,
2348 ui_id: yog_abi::YogStr,
2349 event_id: yog_abi::YogStr,
2350) where
2351 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2352 R: UiHandled,
2353{
2354 let f = unsafe { &*(ud as *const F) };
2355 let ui = unsafe { ui_id.as_str() };
2356 let ev = unsafe { event_id.as_str() };
2357 if f(ui, ev).handled() {
2358 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2359 if !api.is_null() {
2360 unsafe { ((*api).ui_consume_event)((*api).ctx) }
2361 }
2362 }
2363}
2364
2365unsafe extern "C" fn trampoline_chunk_generator<F>(
2366 ud: *mut c_void,
2367 writer: *const yog_abi::YogChunkWriterApi,
2368) where
2369 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2370{
2371 let f = unsafe { &*(ud as *const F) };
2372 let writer = unsafe { ChunkWriter::from_raw(writer) };
2373 f(&writer);
2374}
2375
2376pub trait Mod {
2380 fn register(registry: &mut Registry);
2381}