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 dimension: ev.dimension.as_str().to_owned(),
926 }
927);
928
929trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
930 player_name: ev.player.as_str().to_owned(),
931 message: ev.message.as_str().to_owned(),
932 dimension: ev.dimension.as_str().to_owned(),
933});
934
935trampoline_phased!(
936 trampoline_player_join,
937 YogPlayerEvent,
938 PlayerJoinEvent,
939 |ev| PlayerJoinEvent {
940 player_name: ev.player.as_str().to_owned(),
941 uuid: ev.uuid.as_str().to_owned(),
942 dimension: ev.dimension.as_str().to_owned(),
943 }
944);
945
946trampoline_phased!(
947 trampoline_player_leave,
948 YogPlayerEvent,
949 PlayerLeaveEvent,
950 |ev| PlayerLeaveEvent {
951 player_name: ev.player.as_str().to_owned(),
952 uuid: ev.uuid.as_str().to_owned(),
953 dimension: ev.dimension.as_str().to_owned(),
954 }
955);
956
957trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| {
958 UseItemEvent {
959 player_name: ev.player.as_str().to_owned(),
960 item_id: ev.item.as_str().to_owned(),
961 sneaking: ev.sneaking,
962 dimension: ev.dimension.as_str().to_owned(),
963 }
964});
965
966trampoline_phased!(
967 trampoline_use_block,
968 YogUseBlockEvent,
969 UseBlockEvent,
970 |ev| UseBlockEvent {
971 player_name: ev.player.as_str().to_owned(),
972 block_id: ev.block.as_str().to_owned(),
973 pos: yog_core::BlockPos {
974 x: ev.pos.x,
975 y: ev.pos.y,
976 z: ev.pos.z
977 },
978 dimension: ev.dimension.as_str().to_owned(),
979 }
980);
981
982trampoline_phased!(
983 trampoline_attack_entity,
984 YogAttackEntityEvent,
985 AttackEntityEvent,
986 |ev| AttackEntityEvent {
987 player_name: ev.player.as_str().to_owned(),
988 target_type: ev.target_type.as_str().to_owned(),
989 target_uuid: ev.target_uuid.as_str().to_owned(),
990 dimension: ev.dimension.as_str().to_owned(),
991 }
992);
993
994trampoline_phased!(
995 trampoline_entity_damage,
996 YogEntityDamageEvent,
997 EntityDamageEvent,
998 |ev| EntityDamageEvent {
999 entity_type: ev.entity_type.as_str().to_owned(),
1000 uuid: ev.uuid.as_str().to_owned(),
1001 amount: ev.amount,
1002 source: ev.source.as_str().to_owned(),
1003 dimension: ev.dimension.as_str().to_owned(),
1004 }
1005);
1006
1007trampoline_phased!(
1008 trampoline_entity_death,
1009 YogEntityDeathEvent,
1010 EntityDeathEvent,
1011 |ev| EntityDeathEvent {
1012 entity_type: ev.entity_type.as_str().to_owned(),
1013 uuid: ev.uuid.as_str().to_owned(),
1014 source: ev.source.as_str().to_owned(),
1015 dimension: ev.dimension.as_str().to_owned(),
1016 }
1017);
1018
1019trampoline_phased!(
1020 trampoline_entity_spawn,
1021 YogEntitySpawnEvent,
1022 EntitySpawnEvent,
1023 |ev| EntitySpawnEvent {
1024 entity_type: ev.entity_type.as_str().to_owned(),
1025 uuid: ev.uuid.as_str().to_owned(),
1026 dimension: ev.dimension.as_str().to_owned(),
1027 }
1028);
1029
1030trampoline_phased!(
1031 trampoline_place_block,
1032 YogPlaceBlockEvent,
1033 PlaceBlockEvent,
1034 |ev| PlaceBlockEvent {
1035 player_name: ev.player.as_str().to_owned(),
1036 block_id: ev.block.as_str().to_owned(),
1037 pos: yog_core::BlockPos {
1038 x: ev.pos.x,
1039 y: ev.pos.y,
1040 z: ev.pos.z
1041 },
1042 dimension: ev.dimension.as_str().to_owned(),
1043 }
1044);
1045
1046trampoline_phased!(
1047 trampoline_player_death,
1048 YogPlayerDeathEvent,
1049 PlayerDeathEvent,
1050 |ev| PlayerDeathEvent {
1051 player_name: ev.player.as_str().to_owned(),
1052 uuid: ev.uuid.as_str().to_owned(),
1053 source: ev.source.as_str().to_owned(),
1054 dimension: ev.dimension.as_str().to_owned(),
1055 }
1056);
1057
1058trampoline_phased!(
1059 trampoline_player_respawn,
1060 YogPlayerRespawnEvent,
1061 PlayerRespawnEvent,
1062 |ev| PlayerRespawnEvent {
1063 player_name: ev.player.as_str().to_owned(),
1064 uuid: ev.uuid.as_str().to_owned(),
1065 at_anchor: ev.at_anchor,
1066 dimension: ev.dimension.as_str().to_owned(),
1067 }
1068);
1069
1070trampoline_phased!(
1071 trampoline_advancement,
1072 YogAdvancementEvent,
1073 AdvancementEvent,
1074 |ev| AdvancementEvent {
1075 player_name: ev.player.as_str().to_owned(),
1076 uuid: ev.uuid.as_str().to_owned(),
1077 advancement_id: ev.advancement.as_str().to_owned(),
1078 dimension: ev.dimension.as_str().to_owned(),
1079 }
1080);
1081
1082trampoline_phased!(
1083 trampoline_entity_interact,
1084 YogEntityInteractEvent,
1085 EntityInteractEvent,
1086 |ev| EntityInteractEvent {
1087 player_name: ev.player.as_str().to_owned(),
1088 player_uuid: ev.player_uuid.as_str().to_owned(),
1089 entity_type: ev.entity_type.as_str().to_owned(),
1090 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1091 hand: ev.hand.as_str().to_owned(),
1092 dimension: ev.dimension.as_str().to_owned(),
1093 }
1094);
1095
1096trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| {
1097 CraftEvent {
1098 player_name: ev.player.as_str().to_owned(),
1099 player_uuid: ev.player_uuid.as_str().to_owned(),
1100 result_item: ev.result_item.as_str().to_owned(),
1101 result_count: ev.result_count,
1102 dimension: ev.dimension.as_str().to_owned(),
1103 }
1104});
1105
1106trampoline_phased!(
1107 trampoline_explosion,
1108 YogExplosionEvent,
1109 ExplosionEvent,
1110 |ev| ExplosionEvent {
1111 dimension: ev.dimension.as_str().to_owned(),
1112 x: ev.x,
1113 y: ev.y,
1114 z: ev.z,
1115 power: ev.power,
1116 cause_uuid: ev.cause_uuid.as_str().to_owned(),
1117 }
1118);
1119
1120trampoline_phased!(
1121 trampoline_item_pickup,
1122 YogItemPickupEvent,
1123 ItemPickupEvent,
1124 |ev| ItemPickupEvent {
1125 player_name: ev.player.as_str().to_owned(),
1126 player_uuid: ev.player_uuid.as_str().to_owned(),
1127 item_id: ev.item_id.as_str().to_owned(),
1128 item_count: ev.item_count,
1129 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1130 dimension: ev.dimension.as_str().to_owned(),
1131 }
1132);
1133
1134trampoline_phased!(
1135 trampoline_player_move,
1136 YogPlayerMoveEvent,
1137 PlayerMoveEvent,
1138 |ev| PlayerMoveEvent {
1139 player_name: ev.player.as_str().to_owned(),
1140 player_uuid: ev.player_uuid.as_str().to_owned(),
1141 x: ev.x,
1142 y: ev.y,
1143 z: ev.z,
1144 yaw: ev.yaw,
1145 pitch: ev.pitch,
1146 dimension: ev.dimension.as_str().to_owned(),
1147 }
1148);
1149
1150trampoline_phased!(
1151 trampoline_container_open,
1152 YogContainerOpenEvent,
1153 ContainerOpenEvent,
1154 |ev| ContainerOpenEvent {
1155 player_name: ev.player.as_str().to_owned(),
1156 player_uuid: ev.player_uuid.as_str().to_owned(),
1157 container_type: ev.container_type.as_str().to_owned(),
1158 dimension: ev.dimension.as_str().to_owned(),
1159 }
1160);
1161
1162trampoline_phased!(
1163 trampoline_container_close,
1164 YogContainerCloseEvent,
1165 ContainerCloseEvent,
1166 |ev| ContainerCloseEvent {
1167 player_name: ev.player.as_str().to_owned(),
1168 player_uuid: ev.player_uuid.as_str().to_owned(),
1169 dimension: ev.dimension.as_str().to_owned(),
1170 }
1171);
1172
1173trampoline_phased!(
1174 trampoline_projectile_hit,
1175 YogProjectileHitEvent,
1176 ProjectileHitEvent,
1177 |ev| ProjectileHitEvent {
1178 projectile_type: ev.projectile_type.as_str().to_owned(),
1179 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
1180 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
1181 hit_type: ev.hit_type.as_str().to_owned(),
1182 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
1183 x: ev.x,
1184 y: ev.y,
1185 z: ev.z,
1186 dimension: ev.dimension.as_str().to_owned(),
1187 }
1188);
1189
1190unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
1191where
1192 F: Fn(&dyn Server) + Send + Sync,
1193{
1194 let f = &*(ud as *const F);
1195 f(&CServer(srv));
1196}
1197
1198unsafe extern "C" fn trampoline_packet<F>(
1199 ud: *mut c_void,
1200 srv: *const YogServer,
1201 ev: *const YogPacketEvent,
1202) where
1203 F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
1204{
1205 let f = &*(ud as *const F);
1206 let ev = &*ev;
1207 let rust_ev = PacketEvent {
1208 channel: ev.channel.as_str().to_owned(),
1209 player: ev.player.as_str().to_owned(),
1210 payload: std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec(),
1211 };
1212 f(&rust_ev, &CServer(srv));
1213}
1214
1215unsafe extern "C" fn trampoline_command<F>(
1216 ud: *mut c_void,
1217 srv: *const YogServer,
1218 ev: *const YogCommandEvent,
1219 reply_buf: *mut u8,
1220 reply_cap: u32,
1221 reply_len: *mut u32,
1222) where
1223 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
1224{
1225 let f = &*(ud as *const F);
1226 let ev = &*ev;
1227 let ctx = CommandContext {
1228 name: ev.name.as_str().to_owned(),
1229 args: ev.args.as_str().to_owned(),
1230 source: ev.source.as_str().to_owned(),
1231 uuid: ev.uuid.as_str().to_owned(),
1232 dimension: ev.dimension.as_str().to_owned(),
1233 };
1234 *reply_len = 0;
1235 if let Some(reply) = f(&ctx, &CServer(srv)) {
1236 let bytes = reply.as_bytes();
1237 let n = bytes.len().min(reply_cap as usize);
1238 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
1239 *reply_len = n as u32;
1240 }
1241}
1242
1243unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
1244where
1245 F: Fn(&dyn Server) + Send + Sync,
1246{
1247 let f = &*(ud as *const F);
1248 f(&CServer(srv));
1249}
1250
1251unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
1254where
1255 F: Fn(&ClientTickEvent) + Send + Sync,
1256{
1257 let f = &*(ud as *const F);
1258 f(&ClientTickEvent {});
1259}
1260
1261unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1262where
1263 F: Fn(&GfxContext) + Send + Sync,
1264{
1265 let f = &*(ud as *const F);
1266 f(&GfxContext::from_raw(gfx));
1267}
1268
1269unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1270where
1271 F: Fn(&GfxContext) + Send + Sync,
1272{
1273 let f = &*(ud as *const F);
1274 f(&GfxContext::from_raw(gfx));
1275}
1276
1277unsafe extern "C" fn trampoline_key_press<F>(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool
1278where
1279 F: Fn(&KeyPressEvent) -> bool + Send + Sync,
1280{
1281 let f = &*(ud as *const F);
1282 let e = &*ev;
1283 f(&KeyPressEvent {
1284 key_code: e.key_code,
1285 scan_code: e.scan_code,
1286 action: e.action,
1287 modifiers: e.modifiers,
1288 })
1289}
1290
1291unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
1292where
1293 F: Fn(&ScreenEvent) + Send + Sync,
1294{
1295 let f = &*(ud as *const F);
1296 f(&ScreenEvent {
1297 screen_class: screen_class.as_str().to_owned(),
1298 });
1299 true
1300}
1301
1302static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
1313 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
1314
1315#[derive(Debug, Clone)]
1317pub struct ModInfo {
1318 pub source: String,
1320 pub id: String,
1321 pub name: String,
1322 pub version: String,
1323 pub authors: String,
1325 pub description: String,
1326}
1327
1328pub fn installed_mods() -> Vec<ModInfo> {
1334 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1335 if api.is_null() {
1336 return Vec::new();
1337 }
1338 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
1339 if owned.is_none() {
1340 return Vec::new();
1341 }
1342 let text = unsafe {
1343 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
1344 .unwrap_or_default()
1345 };
1346 unsafe { ((*api).free_str)(owned.ptr, owned.len) };
1347 text.lines()
1348 .filter_map(|line| {
1349 let mut f = line.split('\t');
1350 Some(ModInfo {
1351 source: f.next()?.to_string(),
1352 id: f.next()?.to_string(),
1353 name: f.next().unwrap_or_default().to_string(),
1354 version: f.next().unwrap_or_default().to_string(),
1355 authors: f.next().unwrap_or_default().to_string(),
1356 description: f.next().unwrap_or_default().to_string(),
1357 })
1358 })
1359 .collect()
1360}
1361
1362pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
1366 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1367 if api.is_null() {
1368 return;
1369 }
1370 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
1371}
1372
1373pub fn server() -> Option<CServer> {
1378 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1379 if api.is_null() {
1380 return None;
1381 }
1382 let srv = unsafe { (*api).server };
1383 if srv.is_null() {
1384 return None;
1385 }
1386 Some(CServer(srv))
1387}
1388
1389pub struct Registry {
1390 api: *const YogApi,
1391}
1392
1393unsafe impl Send for Registry {}
1395unsafe impl Sync for Registry {}
1396
1397impl Registry {
1398 pub unsafe fn from_raw(api: *const YogApi) -> Self {
1400 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
1401 Self { api }
1402 }
1403
1404 #[inline]
1405 fn ctx(&self) -> *mut c_void {
1406 unsafe { (*self.api).ctx }
1407 }
1408
1409 fn leak<F: 'static>(f: F) -> *mut c_void {
1412 Box::into_raw(Box::new(f)) as *mut c_void
1413 }
1414
1415 pub fn on_block_break<F>(&mut self, handler: F)
1423 where
1424 F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1425 {
1426 let ud = Self::leak(handler);
1427 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
1428 }
1429
1430 pub fn on_chat<F>(&mut self, handler: F)
1431 where
1432 F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1433 {
1434 let ud = Self::leak(handler);
1435 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
1436 }
1437
1438 pub fn on_player_join<F>(&mut self, handler: F)
1439 where
1440 F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1441 {
1442 let ud = Self::leak(handler);
1443 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
1444 }
1445
1446 pub fn on_player_leave<F>(&mut self, handler: F)
1447 where
1448 F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1449 {
1450 let ud = Self::leak(handler);
1451 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
1452 }
1453
1454 pub fn on_use_item<F>(&mut self, handler: F)
1455 where
1456 F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1457 {
1458 let ud = Self::leak(handler);
1459 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
1460 }
1461
1462 pub fn on_use_block<F>(&mut self, handler: F)
1463 where
1464 F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1465 {
1466 let ud = Self::leak(handler);
1467 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
1468 }
1469
1470 pub fn on_attack_entity<F>(&mut self, handler: F)
1471 where
1472 F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1473 {
1474 let ud = Self::leak(handler);
1475 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
1476 }
1477
1478 pub fn on_entity_damage<F>(&mut self, handler: F)
1479 where
1480 F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1481 {
1482 let ud = Self::leak(handler);
1483 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
1484 }
1485
1486 pub fn on_entity_death<F>(&mut self, handler: F)
1487 where
1488 F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1489 {
1490 let ud = Self::leak(handler);
1491 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
1492 }
1493
1494 pub fn on_entity_spawn<F>(&mut self, handler: F)
1495 where
1496 F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1497 {
1498 let ud = Self::leak(handler);
1499 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
1500 }
1501
1502 pub fn on_player_place_block<F>(&mut self, handler: F)
1503 where
1504 F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1505 {
1506 let ud = Self::leak(handler);
1507 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
1508 }
1509
1510 pub fn on_player_death<F>(&mut self, handler: F)
1511 where
1512 F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1513 {
1514 let ud = Self::leak(handler);
1515 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
1516 }
1517
1518 pub fn on_player_respawn<F>(&mut self, handler: F)
1519 where
1520 F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1521 {
1522 let ud = Self::leak(handler);
1523 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
1524 }
1525
1526 pub fn on_advancement<F>(&mut self, handler: F)
1527 where
1528 F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1529 {
1530 let ud = Self::leak(handler);
1531 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
1532 }
1533
1534 pub fn on_entity_interact<F>(&mut self, handler: F)
1535 where
1536 F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1537 {
1538 let ud = Self::leak(handler);
1539 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
1540 }
1541
1542 pub fn on_item_craft<F>(&mut self, handler: F)
1543 where
1544 F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1545 {
1546 let ud = Self::leak(handler);
1547 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
1548 }
1549
1550 pub fn on_explosion<F>(&mut self, handler: F)
1551 where
1552 F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1553 {
1554 let ud = Self::leak(handler);
1555 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
1556 }
1557
1558 pub fn on_item_pickup<F>(&mut self, handler: F)
1559 where
1560 F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1561 {
1562 let ud = Self::leak(handler);
1563 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
1564 }
1565
1566 pub fn on_player_move<F>(&mut self, handler: F)
1567 where
1568 F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1569 {
1570 let ud = Self::leak(handler);
1571 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
1572 }
1573
1574 pub fn on_container_open<F>(&mut self, handler: F)
1575 where
1576 F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1577 {
1578 let ud = Self::leak(handler);
1579 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
1580 }
1581
1582 pub fn on_container_close<F>(&mut self, handler: F)
1583 where
1584 F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1585 {
1586 let ud = Self::leak(handler);
1587 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
1588 }
1589
1590 pub fn on_projectile_hit<F>(&mut self, handler: F)
1591 where
1592 F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1593 {
1594 let ud = Self::leak(handler);
1595 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
1596 }
1597
1598 pub fn on_tick<F>(&mut self, listener: F)
1599 where
1600 F: Fn(&dyn Server) + Send + Sync + 'static,
1601 {
1602 let ud = Self::leak(listener);
1603 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
1604 }
1605
1606 pub fn on_server_started<F>(&mut self, listener: F)
1607 where
1608 F: Fn(&dyn Server) + Send + Sync + 'static,
1609 {
1610 let ud = Self::leak(listener);
1611 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
1612 }
1613
1614 pub fn on_server_stopping<F>(&mut self, listener: F)
1615 where
1616 F: Fn(&dyn Server) + Send + Sync + 'static,
1617 {
1618 let ud = Self::leak(listener);
1619 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
1620 }
1621
1622 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
1625 where
1626 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1627 {
1628 let name_ys = YogStr::from_str(name.as_ref());
1629 let ud = Self::leak(handler);
1630 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
1631 }
1632
1633 pub fn on_typed_command<F>(
1640 &mut self,
1641 name: impl AsRef<str>,
1642 schema: impl AsRef<str>,
1643 handler: F,
1644 ) where
1645 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1646 {
1647 let name_ys = YogStr::from_str(name.as_ref());
1648 let schema_ys = YogStr::from_str(schema.as_ref());
1649 let ud = Self::leak(handler);
1650 unsafe {
1651 ((*self.api).register_typed_command)(
1652 self.ctx(),
1653 name_ys,
1654 schema_ys,
1655 ud,
1656 trampoline_command::<F>,
1657 )
1658 }
1659 }
1660
1661 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1664 where
1665 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1666 {
1667 let ch = YogStr::from_str(channel.as_ref());
1668 let ud = Self::leak(handler);
1669 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1670 }
1671
1672 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
1673 where
1674 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
1675 {
1676 let ch = YogStr::from_str(channel.as_ref());
1677 let ud = Self::leak(handler);
1678 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
1679 }
1680
1681 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
1686 where
1687 P: Packet + Send + Sync + 'static,
1688 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
1689 {
1690 self.on_packet(channel, move |ev, srv| {
1691 if let Some(pkt) = P::decode(&ev.payload) {
1692 handler(&pkt, srv);
1693 }
1694 });
1695 }
1696
1697 fn recipe(&mut self, ns: &str, name: &str, json: &str) {
1700 unsafe {
1701 ((*self.api).register_recipe_json)(
1702 self.ctx(),
1703 YogStr::from_str(ns),
1704 YogStr::from_str(name),
1705 YogStr::from_str(json),
1706 )
1707 }
1708 }
1709
1710 pub fn add_shaped_recipe(&mut self, recipe: ShapedRecipe) {
1712 let json = recipe.to_json();
1713 let (ns, name) = recipe.ns_name();
1714 self.recipe(ns, name, &json);
1715 }
1716
1717 pub fn add_shapeless_recipe(&mut self, recipe: ShapelessRecipe) {
1719 let json = recipe.to_json();
1720 let (ns, name) = recipe.ns_name();
1721 self.recipe(ns, name, &json);
1722 }
1723
1724 pub fn add_furnace_recipe(&mut self, recipe: FurnaceRecipe) {
1726 let json = recipe.to_json();
1727 let (ns, name) = recipe.ns_name();
1728 self.recipe(ns, name, &json);
1729 }
1730
1731 pub fn register_item(&mut self, def: ItemDef) {
1734 let food_nutrition = def.food.as_ref().map_or(0, |f| f.nutrition);
1738 let food_saturation = def.food.as_ref().map_or(0.0, |f| f.saturation);
1739 let food_always_eat = def.food.as_ref().map_or(false, |f| f.can_always_eat);
1740 let c = YogItemDef {
1741 id: YogStr::from_str(&def.id),
1742 max_stack: def.max_stack as u32,
1743 name: def
1744 .name
1745 .as_deref()
1746 .map(YogStr::from_str)
1747 .unwrap_or(YogStr::EMPTY),
1748 tooltip: def
1749 .tooltip
1750 .as_deref()
1751 .map(YogStr::from_str)
1752 .unwrap_or(YogStr::EMPTY),
1753 max_damage: def.max_damage,
1754 fire_resistant: def.fire_resistant,
1755 fuel_ticks: def.fuel_ticks,
1756 food_nutrition,
1757 food_saturation,
1758 food_always_eat,
1759 };
1760 unsafe { ((*self.api).register_item)(self.ctx(), &c) }
1761 }
1762
1763 pub fn register_block(&mut self, def: BlockDef) {
1764 let shape = def.shape.unwrap_or([0.0; 6]);
1765 let groups_joined = def.connect_groups.join(",");
1766 let c = YogBlockDef {
1767 id: YogStr::from_str(&def.id),
1768 hardness: def.hardness,
1769 resistance: def.resistance,
1770 name: def
1771 .name
1772 .as_deref()
1773 .map(YogStr::from_str)
1774 .unwrap_or(YogStr::EMPTY),
1775 light_level: def.light_level,
1776 sound: def
1777 .sound
1778 .as_deref()
1779 .map(YogStr::from_str)
1780 .unwrap_or(YogStr::EMPTY),
1781 requires_tool: def.requires_tool,
1782 no_collision: def.no_collision,
1783 slipperiness: def.slipperiness,
1784 shape,
1785 connects: def.connects,
1786 connect_groups: YogStr::from_str(&groups_joined),
1787 inventory_id: def
1788 .inventory_id
1789 .as_deref()
1790 .map(YogStr::from_str)
1791 .unwrap_or(YogStr::EMPTY),
1792 };
1793 unsafe { ((*self.api).register_block)(self.ctx(), &c) }
1794 }
1795
1796 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
1800 let layout = yog_inventory::encode_layout(&def.resolved_layout());
1801 let c = YogInventoryDef {
1802 id: YogStr::from_str(&def.id),
1803 slot_count: def.slot_count as u32,
1804 layout: YogStr::from_str(&layout),
1805 include_player_inventory: def.include_player_inventory,
1806 player_inv_x: def.player_inv_offset.0,
1807 player_inv_y: def.player_inv_offset.1,
1808 background_texture: def
1809 .background_texture
1810 .as_deref()
1811 .map(YogStr::from_str)
1812 .unwrap_or(YogStr::EMPTY),
1813 title: YogStr::from_str(&def.title),
1814 };
1815 unsafe { ((*self.api).register_inventory)(self.ctx(), &c) }
1816 }
1817
1818 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
1822 let items_str = grant.items.join("|");
1823 let c = YogStartupGrantDef {
1824 id: YogStr::from_str(&grant.id),
1825 items: YogStr::from_str(&items_str),
1826 book: grant
1827 .book
1828 .as_deref()
1829 .map(YogStr::from_str)
1830 .unwrap_or(YogStr::EMPTY),
1831 command: grant
1832 .command
1833 .as_deref()
1834 .map(YogStr::from_str)
1835 .unwrap_or(YogStr::EMPTY),
1836 };
1837 unsafe { ((*self.api).register_startup_grant)(self.ctx(), &c) }
1838 }
1839
1840 pub fn register_book(&mut self, book: &Book) {
1842 let json = book.to_json();
1843 let id = YogStr::from_str(&book.id);
1844 let j = YogStr::from_str(&json);
1845 unsafe { ((*self.api).register_book)(self.ctx(), id, j) }
1846 }
1847
1848 pub fn installed_mods(&self) -> Vec<ModInfo> {
1855 installed_mods()
1856 }
1857
1858 pub fn interop(&self) -> crate::Interop {
1863 crate::Interop::new(self.api)
1864 }
1865
1866 pub fn raw_api(&self) -> *const yog_abi::YogApi {
1869 self.api
1870 }
1871
1872 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
1873 let l = YogStr::from_str(label);
1874 let u = YogStr::from_str(ui_id);
1875 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
1876 }
1877
1878 pub fn register_ui<F>(&mut self, ui_id: &str, handler: F)
1882 where
1883 F: Fn(&str, &str) + Send + Sync + 'static,
1884 {
1885 let ud = Self::leak(handler);
1886 let id = YogStr::from_str(ui_id);
1887 let empty = YogStr::from_str("{}");
1888 unsafe { ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F>) }
1889 }
1890
1891 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
1894 where
1895 F: Fn(&dyn Server) + Send + Sync + 'static,
1896 {
1897 let ud = Self::leak(handler);
1898 unsafe {
1899 ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>)
1900 }
1901 }
1902
1903 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
1904 where
1905 F: Fn(&dyn Server) + Send + Sync + 'static,
1906 {
1907 let ud = Self::leak(handler);
1908 unsafe {
1909 ((*self.api).schedule_repeating)(
1910 self.ctx(),
1911 period_ticks,
1912 ud,
1913 trampoline_scheduled::<F>,
1914 )
1915 }
1916 }
1917
1918 pub fn on_client_tick<F>(&mut self, handler: F)
1922 where
1923 F: Fn(&ClientTickEvent) + Send + Sync + 'static,
1924 {
1925 let ud = Self::leak(handler);
1926 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
1927 }
1928
1929 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
1938 where
1939 F: Fn(&GfxContext) + Send + Sync + 'static,
1940 {
1941 let ud = Self::leak(handler);
1942 let id = YogStr::from_str(ui_id);
1943 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
1944 }
1945
1946 pub fn on_hud_render<F>(&mut self, handler: F)
1951 where
1952 F: Fn(&GfxContext) + Send + Sync + 'static,
1953 {
1954 let ud = Self::leak(handler);
1955 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
1956 }
1957
1958 pub fn on_world_render<F>(&mut self, handler: F)
1964 where
1965 F: Fn(&GfxContext) + Send + Sync + 'static,
1966 {
1967 let ud = Self::leak(handler);
1968 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
1969 }
1970
1971 pub fn on_key_press<F>(&mut self, handler: F)
1974 where
1975 F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static,
1976 {
1977 let ud = Self::leak(handler);
1978 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
1979 }
1980
1981 pub fn on_screen_open<F>(&mut self, handler: F)
1983 where
1984 F: Fn(&ScreenEvent) + Send + Sync + 'static,
1985 {
1986 let ud = Self::leak(handler);
1987 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
1988 }
1989
1990 pub fn on_screen_close<F>(&mut self, handler: F)
1992 where
1993 F: Fn(&ScreenEvent) + Send + Sync + 'static,
1994 {
1995 let ud = Self::leak(handler);
1996 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
1997 }
1998}
1999
2000unsafe extern "C" fn trampoline_ui_event<F>(
2001 ud: *mut c_void,
2002 ui_id: yog_abi::YogStr,
2003 event_id: yog_abi::YogStr,
2004) where
2005 F: Fn(&str, &str) + Send + Sync + 'static,
2006{
2007 let f = &*(ud as *const F);
2008 let ui = unsafe { ui_id.as_str() };
2009 let ev = unsafe { event_id.as_str() };
2010 f(ui, ev);
2011}
2012
2013pub trait Mod {
2017 fn register(registry: &mut Registry);
2018}