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_event::{
25 AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
26 ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
27 EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent, ItemPickupEvent,
28 KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent, PlayerLeaveEvent,
29 PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent, UseBlockEvent,
30 UseItemEvent,
31};
32use yog_gfx::GfxContext;
33use yog_network::{Packet, PacketEvent};
34use yog_registry::{BlockDef, FurnaceRecipe, ItemDef, ShapedRecipe, ShapelessRecipe, StartupGrant};
35
36pub struct CServer(pub *const YogServer);
41
42unsafe impl Send for CServer {}
43unsafe impl Sync for CServer {}
44
45macro_rules! srv {
46 ($self:ident) => {
47 unsafe { &*$self.0 }
48 };
49}
50
51impl Server for CServer {
52 fn broadcast(&self, message: &str) {
53 let s = srv!(self);
54 unsafe { (s.broadcast)(s.ctx, YogStr::from_str(message)) }
55 }
56
57 fn get_block(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
58 let s = srv!(self);
59 let owned = unsafe {
60 (s.get_block)(
61 s.ctx,
62 YogStr::from_str(dimension),
63 yog_abi::YogBlockPos {
64 x: pos.x,
65 y: pos.y,
66 z: pos.z,
67 },
68 )
69 };
70 if owned.is_none() {
71 return None;
72 }
73 let result = unsafe {
74 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
75 .ok()
76 };
77 unsafe { (s.free_str)(owned.ptr, owned.len) };
78 result
79 }
80
81 fn set_block(&self, dimension: &str, pos: yog_core::BlockPos, block_id: &str) -> bool {
82 let s = srv!(self);
83 unsafe {
84 (s.set_block)(
85 s.ctx,
86 YogStr::from_str(dimension),
87 yog_abi::YogBlockPos {
88 x: pos.x,
89 y: pos.y,
90 z: pos.z,
91 },
92 YogStr::from_str(block_id),
93 )
94 }
95 }
96
97 fn give_item(&self, player: &str, item_id: &str, count: u32) -> bool {
98 let s = srv!(self);
99 unsafe {
100 (s.give_item)(
101 s.ctx,
102 YogStr::from_str(player),
103 YogStr::from_str(item_id),
104 count,
105 )
106 }
107 }
108
109 fn teleport(&self, player: &str, x: f64, y: f64, z: f64) -> bool {
110 let s = srv!(self);
111 unsafe {
112 (s.player_teleport)(
113 s.ctx,
114 YogStr::from_str(player),
115 yog_abi::YogVec3 { x, y, z },
116 )
117 }
118 }
119
120 fn send_to_player(&self, player: &str, channel: &str, payload: &[u8]) -> bool {
121 let s = srv!(self);
122 unsafe {
123 (s.send_to_player)(
124 s.ctx,
125 YogStr::from_str(player),
126 YogStr::from_str(channel),
127 payload.as_ptr(),
128 payload.len() as u32,
129 )
130 }
131 }
132
133 fn send_to_server(&self, channel: &str, payload: &[u8]) -> bool {
134 let s = srv!(self);
135 unsafe {
136 (s.send_to_server)(
137 s.ctx,
138 YogStr::from_str(channel),
139 payload.as_ptr(),
140 payload.len() as u32,
141 )
142 }
143 }
144
145 fn entity_teleport(&self, uuid: &str, x: f64, y: f64, z: f64) -> bool {
146 let s = srv!(self);
147 unsafe { (s.entity_teleport)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x, y, z }) }
148 }
149
150 fn entity_position(&self, uuid: &str) -> Option<(f64, f64, f64)> {
151 let s = srv!(self);
152 let mut out = yog_abi::YogVec3 {
153 x: 0.0,
154 y: 0.0,
155 z: 0.0,
156 };
157 if unsafe { (s.entity_position)(s.ctx, YogStr::from_str(uuid), &mut out) } {
158 Some((out.x, out.y, out.z))
159 } else {
160 None
161 }
162 }
163
164 fn entity_health(&self, uuid: &str) -> Option<f32> {
165 let s = srv!(self);
166 let mut hp = 0f32;
167 if unsafe { (s.entity_health)(s.ctx, YogStr::from_str(uuid), &mut hp) } {
168 Some(hp)
169 } else {
170 None
171 }
172 }
173
174 fn entity_set_health(&self, uuid: &str, health: f32) -> bool {
175 let s = srv!(self);
176 unsafe { (s.entity_set_health)(s.ctx, YogStr::from_str(uuid), health) }
177 }
178
179 fn entity_kill(&self, uuid: &str) -> bool {
180 let s = srv!(self);
181 unsafe { (s.entity_kill)(s.ctx, YogStr::from_str(uuid)) }
182 }
183
184 fn spawn_entity(
185 &self,
186 entity_type: &str,
187 dimension: &str,
188 x: f64,
189 y: f64,
190 z: f64,
191 ) -> Option<String> {
192 let s = srv!(self);
193 let owned = unsafe {
194 (s.spawn_entity)(
195 s.ctx,
196 YogStr::from_str(entity_type),
197 YogStr::from_str(dimension),
198 yog_abi::YogVec3 { x, y, z },
199 )
200 };
201 if owned.is_none() {
202 return None;
203 }
204 let result = unsafe {
205 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
206 .ok()
207 };
208 unsafe { (s.free_str)(owned.ptr, owned.len) };
209 result
210 }
211
212 fn entity_add_effect(
213 &self,
214 uuid: &str,
215 effect_id: &str,
216 duration_ticks: i32,
217 amplifier: u8,
218 show_particles: bool,
219 ) -> bool {
220 let s = srv!(self);
221 unsafe {
222 (s.entity_add_effect)(
223 s.ctx,
224 YogStr::from_str(uuid),
225 YogStr::from_str(effect_id),
226 duration_ticks,
227 amplifier,
228 show_particles,
229 )
230 }
231 }
232
233 fn entity_remove_effect(&self, uuid: &str, effect_id: &str) -> bool {
234 let s = srv!(self);
235 unsafe {
236 (s.entity_remove_effect)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(effect_id))
237 }
238 }
239
240 fn entity_clear_effects(&self, uuid: &str) -> bool {
241 let s = srv!(self);
242 unsafe { (s.entity_clear_effects)(s.ctx, YogStr::from_str(uuid)) }
243 }
244
245 fn drop_loot(&self, table_id: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
246 let s = srv!(self);
247 unsafe {
248 (s.drop_loot)(
249 s.ctx,
250 YogStr::from_str(table_id),
251 YogStr::from_str(dimension),
252 yog_abi::YogVec3 { x, y, z },
253 )
254 }
255 }
256
257 fn has_item_tag(&self, item_id: &str, tag_id: &str) -> bool {
258 let s = srv!(self);
259 unsafe { (s.has_item_tag)(s.ctx, YogStr::from_str(item_id), YogStr::from_str(tag_id)) }
260 }
261
262 fn has_block_tag(&self, block_id: &str, tag_id: &str) -> bool {
263 let s = srv!(self);
264 unsafe { (s.has_block_tag)(s.ctx, YogStr::from_str(block_id), YogStr::from_str(tag_id)) }
265 }
266
267 fn world_time(&self, dimension: &str) -> Option<i64> {
268 let s = srv!(self);
269 let mut t = 0i64;
270 if unsafe { (s.world_time)(s.ctx, YogStr::from_str(dimension), &mut t) } {
271 Some(t)
272 } else {
273 None
274 }
275 }
276
277 fn world_set_time(&self, dimension: &str, time: i64) -> bool {
278 let s = srv!(self);
279 unsafe { (s.set_time)(s.ctx, YogStr::from_str(dimension), time) }
280 }
281
282 fn world_is_raining(&self, dimension: &str) -> bool {
283 let s = srv!(self);
284 unsafe { (s.is_raining)(s.ctx, YogStr::from_str(dimension)) }
285 }
286
287 fn world_set_weather(&self, dimension: &str, raining: bool, duration_ticks: i32) -> bool {
288 let s = srv!(self);
289 unsafe { (s.set_weather)(s.ctx, YogStr::from_str(dimension), raining, duration_ticks) }
290 }
291
292 fn entity_velocity(&self, uuid: &str) -> Option<(f64, f64, f64)> {
293 let s = srv!(self);
294 let mut v = yog_abi::YogVec3 {
295 x: 0.0,
296 y: 0.0,
297 z: 0.0,
298 };
299 if unsafe { (s.entity_velocity)(s.ctx, YogStr::from_str(uuid), &mut v) } {
300 Some((v.x, v.y, v.z))
301 } else {
302 None
303 }
304 }
305
306 fn entity_set_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
307 let s = srv!(self);
308 unsafe {
309 (s.entity_set_velocity)(
310 s.ctx,
311 YogStr::from_str(uuid),
312 yog_abi::YogVec3 {
313 x: vx,
314 y: vy,
315 z: vz,
316 },
317 )
318 }
319 }
320
321 fn entity_add_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
322 let s = srv!(self);
323 unsafe {
324 (s.entity_add_velocity)(
325 s.ctx,
326 YogStr::from_str(uuid),
327 yog_abi::YogVec3 {
328 x: vx,
329 y: vy,
330 z: vz,
331 },
332 )
333 }
334 }
335
336 fn scoreboard_get(&self, objective: &str, player: &str) -> Option<i32> {
337 let s = srv!(self);
338 let mut score = 0i32;
339 if unsafe {
340 (s.scoreboard_get)(
341 s.ctx,
342 YogStr::from_str(objective),
343 YogStr::from_str(player),
344 &mut score,
345 )
346 } {
347 Some(score)
348 } else {
349 None
350 }
351 }
352
353 fn scoreboard_set(&self, objective: &str, player: &str, score: i32) -> bool {
354 let s = srv!(self);
355 unsafe {
356 (s.scoreboard_set)(
357 s.ctx,
358 YogStr::from_str(objective),
359 YogStr::from_str(player),
360 score,
361 )
362 }
363 }
364
365 fn scoreboard_add(&self, objective: &str, player: &str, delta: i32) -> Option<i32> {
366 let s = srv!(self);
367 let mut new_score = 0i32;
368 if unsafe {
369 (s.scoreboard_add)(
370 s.ctx,
371 YogStr::from_str(objective),
372 YogStr::from_str(player),
373 delta,
374 &mut new_score,
375 )
376 } {
377 Some(new_score)
378 } else {
379 None
380 }
381 }
382
383 fn game_dir(&self) -> String {
384 let s = srv!(self);
385 let owned = unsafe { (s.game_dir)(s.ctx) };
386 if owned.is_none() {
387 return String::new();
388 }
389 let result = unsafe {
390 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
391 .unwrap_or_default()
392 };
393 unsafe { (s.free_str)(owned.ptr, owned.len) };
394 result
395 }
396
397 fn play_sound(
398 &self,
399 dimension: &str,
400 x: f64,
401 y: f64,
402 z: f64,
403 sound_id: &str,
404 volume: f32,
405 pitch: f32,
406 ) -> bool {
407 let s = srv!(self);
408 unsafe {
409 (s.play_sound)(
410 s.ctx,
411 YogStr::from_str(dimension),
412 yog_abi::YogVec3 { x, y, z },
413 YogStr::from_str(sound_id),
414 volume,
415 pitch,
416 )
417 }
418 }
419
420 fn play_sound_to_player(&self, player: &str, sound_id: &str, volume: f32, pitch: f32) -> bool {
421 let s = srv!(self);
422 unsafe {
423 (s.play_sound_player)(
424 s.ctx,
425 YogStr::from_str(player),
426 YogStr::from_str(sound_id),
427 volume,
428 pitch,
429 )
430 }
431 }
432
433 fn send_title(
434 &self,
435 player: &str,
436 title: &str,
437 subtitle: &str,
438 fadein: i32,
439 stay: i32,
440 fadeout: i32,
441 ) -> bool {
442 let s = srv!(self);
443 unsafe {
444 (s.send_title)(
445 s.ctx,
446 YogStr::from_str(player),
447 YogStr::from_str(title),
448 YogStr::from_str(subtitle),
449 fadein,
450 stay,
451 fadeout,
452 )
453 }
454 }
455
456 fn entity_rotation(&self, uuid: &str) -> Option<(f32, f32)> {
457 let s = srv!(self);
458 let mut out = yog_abi::YogVec3 {
459 x: 0.0,
460 y: 0.0,
461 z: 0.0,
462 };
463 let ok = unsafe { (s.entity_rotation)(s.ctx, YogStr::from_str(uuid), &mut out) };
464 if ok {
465 Some((out.x as f32, out.y as f32))
466 } else {
467 None
468 }
469 }
470
471 fn send_actionbar(&self, player: &str, message: &str) -> bool {
472 let s = srv!(self);
473 unsafe { (s.send_actionbar)(s.ctx, YogStr::from_str(player), YogStr::from_str(message)) }
474 }
475
476 fn kick_player(&self, player: &str, reason: &str) -> bool {
477 let s = srv!(self);
478 unsafe { (s.kick_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(reason)) }
479 }
480
481 fn set_gamemode(&self, player: &str, gamemode: &str) -> bool {
482 let s = srv!(self);
483 unsafe { (s.set_gamemode)(s.ctx, YogStr::from_str(player), YogStr::from_str(gamemode)) }
484 }
485
486 fn bossbar_create(&self, id: &str, title: &str, color: &str, style: &str) -> bool {
487 let s = srv!(self);
488 unsafe {
489 (s.bossbar_create)(
490 s.ctx,
491 YogStr::from_str(id),
492 YogStr::from_str(title),
493 YogStr::from_str(color),
494 YogStr::from_str(style),
495 )
496 }
497 }
498
499 fn bossbar_remove(&self, id: &str) -> bool {
500 let s = srv!(self);
501 unsafe { (s.bossbar_remove)(s.ctx, YogStr::from_str(id)) }
502 }
503
504 fn bossbar_set_title(&self, id: &str, title: &str) -> bool {
505 let s = srv!(self);
506 unsafe { (s.bossbar_set_title)(s.ctx, YogStr::from_str(id), YogStr::from_str(title)) }
507 }
508
509 fn bossbar_set_progress(&self, id: &str, progress: f32) -> bool {
510 let s = srv!(self);
511 unsafe { (s.bossbar_set_progress)(s.ctx, YogStr::from_str(id), progress) }
512 }
513
514 fn bossbar_set_color(&self, id: &str, color: &str) -> bool {
515 let s = srv!(self);
516 unsafe { (s.bossbar_set_color)(s.ctx, YogStr::from_str(id), YogStr::from_str(color)) }
517 }
518
519 fn bossbar_add_player(&self, id: &str, player: &str) -> bool {
520 let s = srv!(self);
521 unsafe { (s.bossbar_add_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
522 }
523
524 fn bossbar_remove_player(&self, id: &str, player: &str) -> bool {
525 let s = srv!(self);
526 unsafe { (s.bossbar_remove_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
527 }
528
529 fn bossbar_set_visible(&self, id: &str, visible: bool) -> bool {
530 let s = srv!(self);
531 unsafe { (s.bossbar_set_visible)(s.ctx, YogStr::from_str(id), visible) }
532 }
533
534 fn online_players(&self) -> Vec<String> {
535 let s = srv!(self);
536 let owned = unsafe { (s.online_players)(s.ctx) };
537 if owned.is_none() {
538 return Vec::new();
539 }
540 let text = unsafe {
541 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
542 .unwrap_or_default()
543 };
544 unsafe { (s.free_str)(owned.ptr, owned.len) };
545 if text.is_empty() {
546 Vec::new()
547 } else {
548 text.lines().map(str::to_owned).collect()
549 }
550 }
551
552 fn get_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
553 let s = srv!(self);
554 let owned = unsafe {
555 (s.get_block_nbt)(
556 s.ctx,
557 YogStr::from_str(dimension),
558 yog_abi::YogBlockPos {
559 x: pos.x,
560 y: pos.y,
561 z: pos.z,
562 },
563 )
564 };
565 if owned.is_none() {
566 return None;
567 }
568 let result = unsafe {
569 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
570 .ok()
571 };
572 unsafe { (s.free_str)(owned.ptr, owned.len) };
573 result
574 }
575
576 fn set_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos, snbt: &str) -> bool {
577 let s = srv!(self);
578 unsafe {
579 (s.set_block_nbt)(
580 s.ctx,
581 YogStr::from_str(dimension),
582 yog_abi::YogBlockPos {
583 x: pos.x,
584 y: pos.y,
585 z: pos.z,
586 },
587 YogStr::from_str(snbt),
588 )
589 }
590 }
591
592 fn get_inventory_slot(
593 &self,
594 dimension: &str,
595 pos: yog_core::BlockPos,
596 slot: u32,
597 ) -> Option<(String, u32)> {
598 let s = srv!(self);
599 let owned = unsafe {
600 (s.get_inventory_slot)(
601 s.ctx,
602 YogStr::from_str(dimension),
603 yog_abi::YogBlockPos {
604 x: pos.x,
605 y: pos.y,
606 z: pos.z,
607 },
608 slot,
609 )
610 };
611 if owned.is_none() {
612 return None;
613 }
614 let text = unsafe {
615 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
616 .unwrap_or_default()
617 };
618 unsafe { (s.free_str)(owned.ptr, owned.len) };
619 let (item_id, count) = text.split_once('\t')?;
620 Some((item_id.to_owned(), count.parse().ok()?))
621 }
622
623 fn set_inventory_slot(
624 &self,
625 dimension: &str,
626 pos: yog_core::BlockPos,
627 slot: u32,
628 item_id: &str,
629 count: u32,
630 ) -> bool {
631 let s = srv!(self);
632 unsafe {
633 (s.set_inventory_slot)(
634 s.ctx,
635 YogStr::from_str(dimension),
636 yog_abi::YogBlockPos {
637 x: pos.x,
638 y: pos.y,
639 z: pos.z,
640 },
641 slot,
642 YogStr::from_str(item_id),
643 count,
644 )
645 }
646 }
647
648 fn player_inventory(&self, player: &str) -> Vec<(u32, String, u32)> {
649 let s = srv!(self);
650 let owned = unsafe { (s.player_inventory)(s.ctx, YogStr::from_str(player)) };
651 if owned.is_none() {
652 return Vec::new();
653 }
654 let text = unsafe {
655 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
656 .unwrap_or_default()
657 };
658 unsafe { (s.free_str)(owned.ptr, owned.len) };
659 text.lines()
660 .filter_map(|line| {
661 let mut it = line.split('\t');
662 let slot: u32 = it.next()?.parse().ok()?;
663 let item_id = it.next()?.to_owned();
664 let count: u32 = it.next()?.parse().ok()?;
665 Some((slot, item_id, count))
666 })
667 .collect()
668 }
669
670 fn player_set_slot(&self, player: &str, slot: u32, item_id: &str, count: u32) -> bool {
671 let s = srv!(self);
672 unsafe {
673 (s.player_set_slot)(
674 s.ctx,
675 YogStr::from_str(player),
676 slot,
677 YogStr::from_str(item_id),
678 count,
679 )
680 }
681 }
682
683 fn teleport_to_dim(&self, player: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
684 let s = srv!(self);
685 unsafe {
686 (s.player_teleport_dim)(
687 s.ctx,
688 YogStr::from_str(player),
689 YogStr::from_str(dimension),
690 yog_abi::YogVec3 { x, y, z },
691 )
692 }
693 }
694
695 fn entity_teleport_to_dim(&self, uuid: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
696 let s = srv!(self);
697 unsafe {
698 (s.entity_teleport_dim)(
699 s.ctx,
700 YogStr::from_str(uuid),
701 YogStr::from_str(dimension),
702 yog_abi::YogVec3 { x, y, z },
703 )
704 }
705 }
706
707 fn world_entity_count(&self, dimension: &str, entity_type: &str) -> i32 {
708 let s = srv!(self);
709 unsafe {
710 (s.world_entity_count)(
711 s.ctx,
712 YogStr::from_str(dimension),
713 YogStr::from_str(entity_type),
714 )
715 }
716 }
717
718 fn entity_get_nbt(&self, uuid: &str) -> Option<String> {
719 let s = srv!(self);
720 let owned = unsafe { (s.entity_get_nbt)(s.ctx, YogStr::from_str(uuid)) };
721 if owned.is_none() {
722 return None;
723 }
724 let result = unsafe {
725 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
726 .ok()
727 };
728 unsafe { (s.free_str)(owned.ptr, owned.len) };
729 result
730 }
731
732 fn entity_set_nbt(&self, uuid: &str, snbt: &str) -> bool {
733 let s = srv!(self);
734 unsafe { (s.entity_set_nbt)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(snbt)) }
735 }
736
737 fn spawn_particles(
738 &self,
739 dimension: &str,
740 x: f64,
741 y: f64,
742 z: f64,
743 particle_type: &str,
744 count: i32,
745 dx: f64,
746 dy: f64,
747 dz: f64,
748 speed: f64,
749 ) -> bool {
750 let s = srv!(self);
751 unsafe {
752 (s.spawn_particles)(
753 s.ctx,
754 YogStr::from_str(dimension),
755 yog_abi::YogVec3 { x, y, z },
756 YogStr::from_str(particle_type),
757 count,
758 dx,
759 dy,
760 dz,
761 speed,
762 )
763 }
764 }
765
766 fn entity_attribute_get(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
767 let s = srv!(self);
768 let v = unsafe {
769 (s.entity_attribute_get)(
770 s.ctx,
771 YogStr::from_str(uuid),
772 YogStr::from_str(attribute_id),
773 )
774 };
775 if v.is_nan() {
776 None
777 } else {
778 Some(v)
779 }
780 }
781
782 fn entity_attribute_set(&self, uuid: &str, attribute_id: &str, value: f64) -> bool {
783 let s = srv!(self);
784 unsafe {
785 (s.entity_attribute_set)(
786 s.ctx,
787 YogStr::from_str(uuid),
788 YogStr::from_str(attribute_id),
789 value,
790 )
791 }
792 }
793
794 fn get_held_item_nbt(&self, player: &str) -> Option<String> {
795 let s = srv!(self);
796 let owned = unsafe { (s.get_held_item_nbt)(s.ctx, YogStr::from_str(player)) };
797 if owned.is_none() {
798 return None;
799 }
800 let result = unsafe {
801 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
802 .ok()
803 };
804 unsafe { (s.free_str)(owned.ptr, owned.len) };
805 result
806 }
807
808 fn set_held_item_nbt(&self, player: &str, snbt: &str) -> bool {
809 let s = srv!(self);
810 unsafe { (s.set_held_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
811 }
812
813 fn get_offhand_item_nbt(&self, player: &str) -> Option<String> {
814 let s = srv!(self);
815 let owned = unsafe { (s.get_offhand_item_nbt)(s.ctx, YogStr::from_str(player)) };
816 if owned.is_none() {
817 return None;
818 }
819 let result = unsafe {
820 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
821 .ok()
822 };
823 unsafe { (s.free_str)(owned.ptr, owned.len) };
824 result
825 }
826
827 fn set_offhand_item_nbt(&self, player: &str, snbt: &str) -> bool {
828 let s = srv!(self);
829 unsafe { (s.set_offhand_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
830 }
831
832 fn get_slot_item(&self, player: &str, slot: u32) -> Option<(String, u32, String)> {
833 let s = srv!(self);
834 let owned = unsafe { (s.get_slot_item)(s.ctx, YogStr::from_str(player), slot) };
835 if owned.is_none() {
836 return None;
837 }
838 let text = unsafe {
839 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
840 .unwrap_or_default()
841 };
842 unsafe { (s.free_str)(owned.ptr, owned.len) };
843 let mut it = text.splitn(3, '\t');
844 let item_id = it.next()?.to_owned();
845 let count: u32 = it.next()?.parse().ok()?;
846 let nbt = it.next().unwrap_or("{}").to_owned();
847 Some((item_id, count, nbt))
848 }
849
850 fn set_slot_item(
851 &self,
852 player: &str,
853 slot: u32,
854 item_id: &str,
855 count: u32,
856 snbt: &str,
857 ) -> bool {
858 let s = srv!(self);
859 unsafe {
860 (s.set_slot_item)(
861 s.ctx,
862 YogStr::from_str(player),
863 slot,
864 YogStr::from_str(item_id),
865 count,
866 YogStr::from_str(snbt),
867 )
868 }
869 }
870}
871
872macro_rules! trampoline_phased {
890 ($name:ident, $abi_ev:ty, $rust_ev:ty, |$ev:ident| $build:expr) => {
891 unsafe extern "C" fn $name<F>(
892 ud: *mut c_void,
893 srv: *const YogServer,
894 ev: *const $abi_ev,
895 phase: u8,
896 ) -> bool
897 where
898 F: Fn(&$rust_ev, EventPhase, &dyn Server) -> bool + Send + Sync,
899 {
900 let f = &*(ud as *const F);
901 let $ev = &*ev;
902 let rust_ev = $build;
903 let p = if phase == 0 {
904 EventPhase::Pre
905 } else {
906 EventPhase::Post
907 };
908 f(&rust_ev, p, &CServer(srv))
909 }
910 };
911}
912
913trampoline_phased!(
914 trampoline_block_break,
915 YogBlockBreakEvent,
916 BlockBreakEvent,
917 |ev| BlockBreakEvent {
918 player_name: ev.player.as_str().to_owned(),
919 block_id: ev.block.as_str().to_owned(),
920 pos: yog_core::BlockPos {
921 x: ev.pos.x,
922 y: ev.pos.y,
923 z: ev.pos.z
924 },
925 }
926);
927
928trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
929 player_name: ev.player.as_str().to_owned(),
930 message: ev.message.as_str().to_owned(),
931});
932
933trampoline_phased!(
934 trampoline_player_join,
935 YogPlayerEvent,
936 PlayerJoinEvent,
937 |ev| PlayerJoinEvent {
938 player_name: ev.player.as_str().to_owned(),
939 uuid: ev.uuid.as_str().to_owned(),
940 }
941);
942
943trampoline_phased!(
944 trampoline_player_leave,
945 YogPlayerEvent,
946 PlayerLeaveEvent,
947 |ev| PlayerLeaveEvent {
948 player_name: ev.player.as_str().to_owned(),
949 uuid: ev.uuid.as_str().to_owned(),
950 }
951);
952
953trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| {
954 UseItemEvent {
955 player_name: ev.player.as_str().to_owned(),
956 item_id: ev.item.as_str().to_owned(),
957 sneaking: ev.sneaking,
958 }
959});
960
961trampoline_phased!(
962 trampoline_use_block,
963 YogUseBlockEvent,
964 UseBlockEvent,
965 |ev| UseBlockEvent {
966 player_name: ev.player.as_str().to_owned(),
967 block_id: ev.block.as_str().to_owned(),
968 pos: yog_core::BlockPos {
969 x: ev.pos.x,
970 y: ev.pos.y,
971 z: ev.pos.z
972 },
973 }
974);
975
976trampoline_phased!(
977 trampoline_attack_entity,
978 YogAttackEntityEvent,
979 AttackEntityEvent,
980 |ev| AttackEntityEvent {
981 player_name: ev.player.as_str().to_owned(),
982 target_type: ev.target_type.as_str().to_owned(),
983 target_uuid: ev.target_uuid.as_str().to_owned(),
984 }
985);
986
987trampoline_phased!(
988 trampoline_entity_damage,
989 YogEntityDamageEvent,
990 EntityDamageEvent,
991 |ev| EntityDamageEvent {
992 entity_type: ev.entity_type.as_str().to_owned(),
993 uuid: ev.uuid.as_str().to_owned(),
994 amount: ev.amount,
995 source: ev.source.as_str().to_owned(),
996 }
997);
998
999trampoline_phased!(
1000 trampoline_entity_death,
1001 YogEntityDeathEvent,
1002 EntityDeathEvent,
1003 |ev| EntityDeathEvent {
1004 entity_type: ev.entity_type.as_str().to_owned(),
1005 uuid: ev.uuid.as_str().to_owned(),
1006 source: ev.source.as_str().to_owned(),
1007 }
1008);
1009
1010trampoline_phased!(
1011 trampoline_entity_spawn,
1012 YogEntitySpawnEvent,
1013 EntitySpawnEvent,
1014 |ev| EntitySpawnEvent {
1015 entity_type: ev.entity_type.as_str().to_owned(),
1016 uuid: ev.uuid.as_str().to_owned(),
1017 dimension: ev.dimension.as_str().to_owned(),
1018 }
1019);
1020
1021trampoline_phased!(
1022 trampoline_place_block,
1023 YogPlaceBlockEvent,
1024 PlaceBlockEvent,
1025 |ev| PlaceBlockEvent {
1026 player_name: ev.player.as_str().to_owned(),
1027 block_id: ev.block.as_str().to_owned(),
1028 pos: yog_core::BlockPos {
1029 x: ev.pos.x,
1030 y: ev.pos.y,
1031 z: ev.pos.z
1032 },
1033 }
1034);
1035
1036trampoline_phased!(
1037 trampoline_player_death,
1038 YogPlayerDeathEvent,
1039 PlayerDeathEvent,
1040 |ev| PlayerDeathEvent {
1041 player_name: ev.player.as_str().to_owned(),
1042 uuid: ev.uuid.as_str().to_owned(),
1043 source: ev.source.as_str().to_owned(),
1044 }
1045);
1046
1047trampoline_phased!(
1048 trampoline_player_respawn,
1049 YogPlayerRespawnEvent,
1050 PlayerRespawnEvent,
1051 |ev| PlayerRespawnEvent {
1052 player_name: ev.player.as_str().to_owned(),
1053 uuid: ev.uuid.as_str().to_owned(),
1054 at_anchor: ev.at_anchor,
1055 }
1056);
1057
1058trampoline_phased!(
1059 trampoline_advancement,
1060 YogAdvancementEvent,
1061 AdvancementEvent,
1062 |ev| AdvancementEvent {
1063 player_name: ev.player.as_str().to_owned(),
1064 uuid: ev.uuid.as_str().to_owned(),
1065 advancement_id: ev.advancement.as_str().to_owned(),
1066 }
1067);
1068
1069trampoline_phased!(
1070 trampoline_entity_interact,
1071 YogEntityInteractEvent,
1072 EntityInteractEvent,
1073 |ev| EntityInteractEvent {
1074 player_name: ev.player.as_str().to_owned(),
1075 player_uuid: ev.player_uuid.as_str().to_owned(),
1076 entity_type: ev.entity_type.as_str().to_owned(),
1077 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1078 hand: ev.hand.as_str().to_owned(),
1079 }
1080);
1081
1082trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| {
1083 CraftEvent {
1084 player_name: ev.player.as_str().to_owned(),
1085 player_uuid: ev.player_uuid.as_str().to_owned(),
1086 result_item: ev.result_item.as_str().to_owned(),
1087 result_count: ev.result_count,
1088 }
1089});
1090
1091trampoline_phased!(
1092 trampoline_explosion,
1093 YogExplosionEvent,
1094 ExplosionEvent,
1095 |ev| ExplosionEvent {
1096 dimension: ev.dimension.as_str().to_owned(),
1097 x: ev.x,
1098 y: ev.y,
1099 z: ev.z,
1100 power: ev.power,
1101 cause_uuid: ev.cause_uuid.as_str().to_owned(),
1102 }
1103);
1104
1105trampoline_phased!(
1106 trampoline_item_pickup,
1107 YogItemPickupEvent,
1108 ItemPickupEvent,
1109 |ev| ItemPickupEvent {
1110 player_name: ev.player.as_str().to_owned(),
1111 player_uuid: ev.player_uuid.as_str().to_owned(),
1112 item_id: ev.item_id.as_str().to_owned(),
1113 item_count: ev.item_count,
1114 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1115 }
1116);
1117
1118trampoline_phased!(
1119 trampoline_player_move,
1120 YogPlayerMoveEvent,
1121 PlayerMoveEvent,
1122 |ev| PlayerMoveEvent {
1123 player_name: ev.player.as_str().to_owned(),
1124 player_uuid: ev.player_uuid.as_str().to_owned(),
1125 x: ev.x,
1126 y: ev.y,
1127 z: ev.z,
1128 yaw: ev.yaw,
1129 pitch: ev.pitch,
1130 }
1131);
1132
1133trampoline_phased!(
1134 trampoline_container_open,
1135 YogContainerOpenEvent,
1136 ContainerOpenEvent,
1137 |ev| ContainerOpenEvent {
1138 player_name: ev.player.as_str().to_owned(),
1139 player_uuid: ev.player_uuid.as_str().to_owned(),
1140 container_type: ev.container_type.as_str().to_owned(),
1141 }
1142);
1143
1144trampoline_phased!(
1145 trampoline_container_close,
1146 YogContainerCloseEvent,
1147 ContainerCloseEvent,
1148 |ev| ContainerCloseEvent {
1149 player_name: ev.player.as_str().to_owned(),
1150 player_uuid: ev.player_uuid.as_str().to_owned(),
1151 }
1152);
1153
1154trampoline_phased!(
1155 trampoline_projectile_hit,
1156 YogProjectileHitEvent,
1157 ProjectileHitEvent,
1158 |ev| ProjectileHitEvent {
1159 projectile_type: ev.projectile_type.as_str().to_owned(),
1160 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
1161 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
1162 hit_type: ev.hit_type.as_str().to_owned(),
1163 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
1164 x: ev.x,
1165 y: ev.y,
1166 z: ev.z,
1167 dimension: ev.dimension.as_str().to_owned(),
1168 }
1169);
1170
1171unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
1172where
1173 F: Fn(&dyn Server) + Send + Sync,
1174{
1175 let f = &*(ud as *const F);
1176 f(&CServer(srv));
1177}
1178
1179unsafe extern "C" fn trampoline_packet<F>(
1180 ud: *mut c_void,
1181 srv: *const YogServer,
1182 ev: *const YogPacketEvent,
1183) where
1184 F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
1185{
1186 let f = &*(ud as *const F);
1187 let ev = &*ev;
1188 let rust_ev = PacketEvent {
1189 channel: ev.channel.as_str().to_owned(),
1190 player: ev.player.as_str().to_owned(),
1191 payload: std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec(),
1192 };
1193 f(&rust_ev, &CServer(srv));
1194}
1195
1196unsafe extern "C" fn trampoline_command<F>(
1197 ud: *mut c_void,
1198 srv: *const YogServer,
1199 ev: *const YogCommandEvent,
1200 reply_buf: *mut u8,
1201 reply_cap: u32,
1202 reply_len: *mut u32,
1203) where
1204 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
1205{
1206 let f = &*(ud as *const F);
1207 let ev = &*ev;
1208 let ctx = CommandContext {
1209 name: ev.name.as_str().to_owned(),
1210 args: ev.args.as_str().to_owned(),
1211 source: ev.source.as_str().to_owned(),
1212 uuid: ev.uuid.as_str().to_owned(),
1213 };
1214 *reply_len = 0;
1215 if let Some(reply) = f(&ctx, &CServer(srv)) {
1216 let bytes = reply.as_bytes();
1217 let n = bytes.len().min(reply_cap as usize);
1218 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
1219 *reply_len = n as u32;
1220 }
1221}
1222
1223unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
1224where
1225 F: Fn(&dyn Server) + Send + Sync,
1226{
1227 let f = &*(ud as *const F);
1228 f(&CServer(srv));
1229}
1230
1231unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
1234where
1235 F: Fn(&ClientTickEvent) + Send + Sync,
1236{
1237 let f = &*(ud as *const F);
1238 f(&ClientTickEvent {});
1239}
1240
1241unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1242where
1243 F: Fn(&GfxContext) + Send + Sync,
1244{
1245 let f = &*(ud as *const F);
1246 f(&GfxContext::from_raw(gfx));
1247}
1248
1249unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1250where
1251 F: Fn(&GfxContext) + Send + Sync,
1252{
1253 let f = &*(ud as *const F);
1254 f(&GfxContext::from_raw(gfx));
1255}
1256
1257unsafe extern "C" fn trampoline_key_press<F>(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool
1258where
1259 F: Fn(&KeyPressEvent) -> bool + Send + Sync,
1260{
1261 let f = &*(ud as *const F);
1262 let e = &*ev;
1263 f(&KeyPressEvent {
1264 key_code: e.key_code,
1265 scan_code: e.scan_code,
1266 action: e.action,
1267 modifiers: e.modifiers,
1268 })
1269}
1270
1271unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
1272where
1273 F: Fn(&ScreenEvent) + Send + Sync,
1274{
1275 let f = &*(ud as *const F);
1276 f(&ScreenEvent {
1277 screen_class: screen_class.as_str().to_owned(),
1278 });
1279 true
1280}
1281
1282static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
1293 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
1294
1295#[derive(Debug, Clone)]
1297pub struct ModInfo {
1298 pub source: String,
1300 pub id: String,
1301 pub name: String,
1302 pub version: String,
1303 pub authors: String,
1305 pub description: String,
1306}
1307
1308pub fn installed_mods() -> Vec<ModInfo> {
1314 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1315 if api.is_null() {
1316 return Vec::new();
1317 }
1318 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
1319 if owned.is_none() {
1320 return Vec::new();
1321 }
1322 let text = unsafe {
1323 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
1324 .unwrap_or_default()
1325 };
1326 unsafe { ((*api).free_str)(owned.ptr, owned.len) };
1327 text.lines()
1328 .filter_map(|line| {
1329 let mut f = line.split('\t');
1330 Some(ModInfo {
1331 source: f.next()?.to_string(),
1332 id: f.next()?.to_string(),
1333 name: f.next().unwrap_or_default().to_string(),
1334 version: f.next().unwrap_or_default().to_string(),
1335 authors: f.next().unwrap_or_default().to_string(),
1336 description: f.next().unwrap_or_default().to_string(),
1337 })
1338 })
1339 .collect()
1340}
1341
1342pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
1346 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1347 if api.is_null() {
1348 return;
1349 }
1350 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
1351}
1352
1353pub fn server() -> Option<CServer> {
1358 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1359 if api.is_null() {
1360 return None;
1361 }
1362 let srv = unsafe { (*api).server };
1363 if srv.is_null() {
1364 return None;
1365 }
1366 Some(CServer(srv))
1367}
1368
1369pub struct Registry {
1370 api: *const YogApi,
1371}
1372
1373unsafe impl Send for Registry {}
1375unsafe impl Sync for Registry {}
1376
1377impl Registry {
1378 pub unsafe fn from_raw(api: *const YogApi) -> Self {
1380 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
1381 Self { api }
1382 }
1383
1384 #[inline]
1385 fn ctx(&self) -> *mut c_void {
1386 unsafe { (*self.api).ctx }
1387 }
1388
1389 fn leak<F: 'static>(f: F) -> *mut c_void {
1392 Box::into_raw(Box::new(f)) as *mut c_void
1393 }
1394
1395 pub fn on_block_break<F>(&mut self, handler: F)
1403 where
1404 F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1405 {
1406 let ud = Self::leak(handler);
1407 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
1408 }
1409
1410 pub fn on_chat<F>(&mut self, handler: F)
1411 where
1412 F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1413 {
1414 let ud = Self::leak(handler);
1415 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
1416 }
1417
1418 pub fn on_player_join<F>(&mut self, handler: F)
1419 where
1420 F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1421 {
1422 let ud = Self::leak(handler);
1423 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
1424 }
1425
1426 pub fn on_player_leave<F>(&mut self, handler: F)
1427 where
1428 F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1429 {
1430 let ud = Self::leak(handler);
1431 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
1432 }
1433
1434 pub fn on_use_item<F>(&mut self, handler: F)
1435 where
1436 F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1437 {
1438 let ud = Self::leak(handler);
1439 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
1440 }
1441
1442 pub fn on_use_block<F>(&mut self, handler: F)
1443 where
1444 F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1445 {
1446 let ud = Self::leak(handler);
1447 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
1448 }
1449
1450 pub fn on_attack_entity<F>(&mut self, handler: F)
1451 where
1452 F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1453 {
1454 let ud = Self::leak(handler);
1455 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
1456 }
1457
1458 pub fn on_entity_damage<F>(&mut self, handler: F)
1459 where
1460 F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1461 {
1462 let ud = Self::leak(handler);
1463 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
1464 }
1465
1466 pub fn on_entity_death<F>(&mut self, handler: F)
1467 where
1468 F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1469 {
1470 let ud = Self::leak(handler);
1471 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
1472 }
1473
1474 pub fn on_entity_spawn<F>(&mut self, handler: F)
1475 where
1476 F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1477 {
1478 let ud = Self::leak(handler);
1479 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
1480 }
1481
1482 pub fn on_player_place_block<F>(&mut self, handler: F)
1483 where
1484 F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1485 {
1486 let ud = Self::leak(handler);
1487 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
1488 }
1489
1490 pub fn on_player_death<F>(&mut self, handler: F)
1491 where
1492 F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1493 {
1494 let ud = Self::leak(handler);
1495 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
1496 }
1497
1498 pub fn on_player_respawn<F>(&mut self, handler: F)
1499 where
1500 F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1501 {
1502 let ud = Self::leak(handler);
1503 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
1504 }
1505
1506 pub fn on_advancement<F>(&mut self, handler: F)
1507 where
1508 F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1509 {
1510 let ud = Self::leak(handler);
1511 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
1512 }
1513
1514 pub fn on_entity_interact<F>(&mut self, handler: F)
1515 where
1516 F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1517 {
1518 let ud = Self::leak(handler);
1519 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
1520 }
1521
1522 pub fn on_item_craft<F>(&mut self, handler: F)
1523 where
1524 F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1525 {
1526 let ud = Self::leak(handler);
1527 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
1528 }
1529
1530 pub fn on_explosion<F>(&mut self, handler: F)
1531 where
1532 F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1533 {
1534 let ud = Self::leak(handler);
1535 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
1536 }
1537
1538 pub fn on_item_pickup<F>(&mut self, handler: F)
1539 where
1540 F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1541 {
1542 let ud = Self::leak(handler);
1543 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
1544 }
1545
1546 pub fn on_player_move<F>(&mut self, handler: F)
1547 where
1548 F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1549 {
1550 let ud = Self::leak(handler);
1551 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
1552 }
1553
1554 pub fn on_container_open<F>(&mut self, handler: F)
1555 where
1556 F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1557 {
1558 let ud = Self::leak(handler);
1559 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
1560 }
1561
1562 pub fn on_container_close<F>(&mut self, handler: F)
1563 where
1564 F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1565 {
1566 let ud = Self::leak(handler);
1567 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
1568 }
1569
1570 pub fn on_projectile_hit<F>(&mut self, handler: F)
1571 where
1572 F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1573 {
1574 let ud = Self::leak(handler);
1575 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
1576 }
1577
1578 pub fn on_tick<F>(&mut self, listener: F)
1579 where
1580 F: Fn(&dyn Server) + Send + Sync + 'static,
1581 {
1582 let ud = Self::leak(listener);
1583 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
1584 }
1585
1586 pub fn on_server_started<F>(&mut self, listener: F)
1587 where
1588 F: Fn(&dyn Server) + Send + Sync + 'static,
1589 {
1590 let ud = Self::leak(listener);
1591 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
1592 }
1593
1594 pub fn on_server_stopping<F>(&mut self, listener: F)
1595 where
1596 F: Fn(&dyn Server) + Send + Sync + 'static,
1597 {
1598 let ud = Self::leak(listener);
1599 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
1600 }
1601
1602 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
1605 where
1606 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1607 {
1608 let name_ys = YogStr::from_str(name.as_ref());
1609 let ud = Self::leak(handler);
1610 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
1611 }
1612
1613 pub fn on_typed_command<F>(
1620 &mut self,
1621 name: impl AsRef<str>,
1622 schema: impl AsRef<str>,
1623 handler: F,
1624 ) where
1625 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1626 {
1627 let name_ys = YogStr::from_str(name.as_ref());
1628 let schema_ys = YogStr::from_str(schema.as_ref());
1629 let ud = Self::leak(handler);
1630 unsafe {
1631 ((*self.api).register_typed_command)(
1632 self.ctx(),
1633 name_ys,
1634 schema_ys,
1635 ud,
1636 trampoline_command::<F>,
1637 )
1638 }
1639 }
1640
1641 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1644 where
1645 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1646 {
1647 let ch = YogStr::from_str(channel.as_ref());
1648 let ud = Self::leak(handler);
1649 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1650 }
1651
1652 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1653 where
1654 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1655 {
1656 let ch = YogStr::from_str(channel.as_ref());
1657 let ud = Self::leak(handler);
1658 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1659 }
1660
1661 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
1666 where
1667 P: Packet + Send + Sync + 'static,
1668 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
1669 {
1670 self.on_packet(channel, move |ev, srv| {
1671 if let Some(pkt) = P::decode(&ev.payload) {
1672 handler(&pkt, srv);
1673 }
1674 });
1675 }
1676
1677 fn recipe(&mut self, ns: &str, name: &str, json: &str) {
1680 unsafe {
1681 ((*self.api).register_recipe_json)(
1682 self.ctx(),
1683 YogStr::from_str(ns),
1684 YogStr::from_str(name),
1685 YogStr::from_str(json),
1686 )
1687 }
1688 }
1689
1690 pub fn add_shaped_recipe(&mut self, recipe: ShapedRecipe) {
1692 let json = recipe.to_json();
1693 let (ns, name) = recipe.ns_name();
1694 self.recipe(ns, name, &json);
1695 }
1696
1697 pub fn add_shapeless_recipe(&mut self, recipe: ShapelessRecipe) {
1699 let json = recipe.to_json();
1700 let (ns, name) = recipe.ns_name();
1701 self.recipe(ns, name, &json);
1702 }
1703
1704 pub fn add_furnace_recipe(&mut self, recipe: FurnaceRecipe) {
1706 let json = recipe.to_json();
1707 let (ns, name) = recipe.ns_name();
1708 self.recipe(ns, name, &json);
1709 }
1710
1711 pub fn register_item(&mut self, def: ItemDef) {
1714 let food_nutrition = def.food.as_ref().map_or(0, |f| f.nutrition);
1718 let food_saturation = def.food.as_ref().map_or(0.0, |f| f.saturation);
1719 let food_always_eat = def.food.as_ref().map_or(false, |f| f.can_always_eat);
1720 let c = YogItemDef {
1721 id: YogStr::from_str(&def.id),
1722 max_stack: def.max_stack as u32,
1723 name: def
1724 .name
1725 .as_deref()
1726 .map(YogStr::from_str)
1727 .unwrap_or(YogStr::EMPTY),
1728 tooltip: def
1729 .tooltip
1730 .as_deref()
1731 .map(YogStr::from_str)
1732 .unwrap_or(YogStr::EMPTY),
1733 max_damage: def.max_damage,
1734 fire_resistant: def.fire_resistant,
1735 fuel_ticks: def.fuel_ticks,
1736 food_nutrition,
1737 food_saturation,
1738 food_always_eat,
1739 };
1740 unsafe { ((*self.api).register_item)(self.ctx(), &c) }
1741 }
1742
1743 pub fn register_block(&mut self, def: BlockDef) {
1744 let shape = def.shape.unwrap_or([0.0; 6]);
1745 let groups_joined = def.connect_groups.join(",");
1746 let c = YogBlockDef {
1747 id: YogStr::from_str(&def.id),
1748 hardness: def.hardness,
1749 resistance: def.resistance,
1750 name: def
1751 .name
1752 .as_deref()
1753 .map(YogStr::from_str)
1754 .unwrap_or(YogStr::EMPTY),
1755 light_level: def.light_level,
1756 sound: def
1757 .sound
1758 .as_deref()
1759 .map(YogStr::from_str)
1760 .unwrap_or(YogStr::EMPTY),
1761 requires_tool: def.requires_tool,
1762 no_collision: def.no_collision,
1763 slipperiness: def.slipperiness,
1764 shape,
1765 connects: def.connects,
1766 connect_groups: YogStr::from_str(&groups_joined),
1767 inventory_id: def
1768 .inventory_id
1769 .as_deref()
1770 .map(YogStr::from_str)
1771 .unwrap_or(YogStr::EMPTY),
1772 };
1773 unsafe { ((*self.api).register_block)(self.ctx(), &c) }
1774 }
1775
1776 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
1780 let layout = yog_inventory::encode_layout(&def.resolved_layout());
1781 let c = YogInventoryDef {
1782 id: YogStr::from_str(&def.id),
1783 slot_count: def.slot_count as u32,
1784 layout: YogStr::from_str(&layout),
1785 include_player_inventory: def.include_player_inventory,
1786 player_inv_x: def.player_inv_offset.0,
1787 player_inv_y: def.player_inv_offset.1,
1788 background_texture: def
1789 .background_texture
1790 .as_deref()
1791 .map(YogStr::from_str)
1792 .unwrap_or(YogStr::EMPTY),
1793 title: YogStr::from_str(&def.title),
1794 };
1795 unsafe { ((*self.api).register_inventory)(self.ctx(), &c) }
1796 }
1797
1798 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
1802 let items_str = grant.items.join("|");
1803 let c = YogStartupGrantDef {
1804 id: YogStr::from_str(&grant.id),
1805 items: YogStr::from_str(&items_str),
1806 book: grant
1807 .book
1808 .as_deref()
1809 .map(YogStr::from_str)
1810 .unwrap_or(YogStr::EMPTY),
1811 command: grant
1812 .command
1813 .as_deref()
1814 .map(YogStr::from_str)
1815 .unwrap_or(YogStr::EMPTY),
1816 };
1817 unsafe { ((*self.api).register_startup_grant)(self.ctx(), &c) }
1818 }
1819
1820 pub fn register_book(&mut self, book: &Book) {
1822 let json = book.to_json();
1823 let id = YogStr::from_str(&book.id);
1824 let j = YogStr::from_str(&json);
1825 unsafe { ((*self.api).register_book)(self.ctx(), id, j) }
1826 }
1827
1828 pub fn installed_mods(&self) -> Vec<ModInfo> {
1835 installed_mods()
1836 }
1837
1838 pub fn interop(&self) -> crate::Interop {
1843 crate::Interop::new(self.api)
1844 }
1845
1846 pub fn raw_api(&self) -> *const yog_abi::YogApi {
1849 self.api
1850 }
1851
1852 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
1853 let l = YogStr::from_str(label);
1854 let u = YogStr::from_str(ui_id);
1855 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
1856 }
1857
1858 pub fn register_ui<F>(&mut self, ui_id: &str, handler: F)
1862 where
1863 F: Fn(&str, &str) + Send + Sync + 'static,
1864 {
1865 let ud = Self::leak(handler);
1866 let id = YogStr::from_str(ui_id);
1867 let empty = YogStr::from_str("{}");
1868 unsafe { ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F>) }
1869 }
1870
1871 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
1874 where
1875 F: Fn(&dyn Server) + Send + Sync + 'static,
1876 {
1877 let ud = Self::leak(handler);
1878 unsafe {
1879 ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>)
1880 }
1881 }
1882
1883 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
1884 where
1885 F: Fn(&dyn Server) + Send + Sync + 'static,
1886 {
1887 let ud = Self::leak(handler);
1888 unsafe {
1889 ((*self.api).schedule_repeating)(
1890 self.ctx(),
1891 period_ticks,
1892 ud,
1893 trampoline_scheduled::<F>,
1894 )
1895 }
1896 }
1897
1898 pub fn on_client_tick<F>(&mut self, handler: F)
1902 where
1903 F: Fn(&ClientTickEvent) + Send + Sync + 'static,
1904 {
1905 let ud = Self::leak(handler);
1906 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
1907 }
1908
1909 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
1918 where
1919 F: Fn(&GfxContext) + Send + Sync + 'static,
1920 {
1921 let ud = Self::leak(handler);
1922 let id = YogStr::from_str(ui_id);
1923 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
1924 }
1925
1926 pub fn on_hud_render<F>(&mut self, handler: F)
1931 where
1932 F: Fn(&GfxContext) + Send + Sync + 'static,
1933 {
1934 let ud = Self::leak(handler);
1935 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
1936 }
1937
1938 pub fn on_world_render<F>(&mut self, handler: F)
1944 where
1945 F: Fn(&GfxContext) + Send + Sync + 'static,
1946 {
1947 let ud = Self::leak(handler);
1948 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
1949 }
1950
1951 pub fn on_key_press<F>(&mut self, handler: F)
1954 where
1955 F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static,
1956 {
1957 let ud = Self::leak(handler);
1958 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
1959 }
1960
1961 pub fn on_screen_open<F>(&mut self, handler: F)
1963 where
1964 F: Fn(&ScreenEvent) + Send + Sync + 'static,
1965 {
1966 let ud = Self::leak(handler);
1967 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
1968 }
1969
1970 pub fn on_screen_close<F>(&mut self, handler: F)
1972 where
1973 F: Fn(&ScreenEvent) + Send + Sync + 'static,
1974 {
1975 let ud = Self::leak(handler);
1976 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
1977 }
1978}
1979
1980unsafe extern "C" fn trampoline_ui_event<F>(
1981 ud: *mut c_void,
1982 ui_id: yog_abi::YogStr,
1983 event_id: yog_abi::YogStr,
1984) where
1985 F: Fn(&str, &str) + Send + Sync + 'static,
1986{
1987 let f = &*(ud as *const F);
1988 let ui = unsafe { ui_id.as_str() };
1989 let ev = unsafe { event_id.as_str() };
1990 f(ui, ev);
1991}
1992
1993pub trait Mod {
1997 fn register(registry: &mut Registry);
1998}