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