1use std::os::raw::c_void;
11
12use yog_abi::{
13 YogAdvancementEvent, YogApi, YogAttackEntityEvent, YogBlockBreakEvent, YogBlockDef,
14 YogChatEvent, YogCommandEvent, YogContainerCloseEvent, YogContainerOpenEvent, YogCraftEvent,
15 YogEntityDamageEvent, YogEntityDeathEvent, YogEntityInteractEvent, YogEntitySpawnEvent,
16 YogExplosionEvent, YogGfxApi, YogInventoryDef, YogItemDef, YogItemPickupEvent,
17 YogKeyPressEvent, YogPacketEvent, YogPlaceBlockEvent, YogPlayerDeathEvent, YogPlayerEvent,
18 YogPlayerMoveEvent, YogPlayerRespawnEvent, YogProjectileHitEvent, YogServer,
19 YogStartupGrantDef, YogStr, YogUseBlockEvent, YogUseItemEvent,
20};
21use yog_book::Book;
22use yog_command::CommandContext;
23use yog_core::Server;
24use yog_dimensions::{ChunkWriter, YogDimensionDef};
25use yog_event::{
26 AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
27 ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
28 EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent, ItemPickupEvent,
29 KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent, PlayerLeaveEvent,
30 PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent, UseBlockEvent,
31 UseItemEvent,
32};
33use yog_gfx::GfxContext;
34use yog_network::{Packet, PacketEvent};
35use yog_registry::{BlockDef, FurnaceRecipe, ItemDef, ShapedRecipe, ShapelessRecipe, StartupGrant};
36
37pub struct CServer(pub *const YogServer);
42
43unsafe impl Send for CServer {}
44unsafe impl Sync for CServer {}
45
46macro_rules! srv {
47 ($self:ident) => {
48 unsafe { &*$self.0 }
49 };
50}
51
52impl Server for CServer {
53 fn broadcast(&self, message: &str) {
54 let s = srv!(self);
55 unsafe { (s.broadcast)(s.ctx, YogStr::from_str(message)) }
56 }
57
58 fn get_block(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
59 let s = srv!(self);
60 let owned = unsafe {
61 (s.get_block)(
62 s.ctx,
63 YogStr::from_str(dimension),
64 yog_abi::YogBlockPos {
65 x: pos.x,
66 y: pos.y,
67 z: pos.z,
68 },
69 )
70 };
71 if owned.is_none() {
72 return None;
73 }
74 let result = unsafe {
75 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
76 .ok()
77 };
78 unsafe { (s.free_str)(owned.ptr, owned.len) };
79 result
80 }
81
82 fn set_block(&self, dimension: &str, pos: yog_core::BlockPos, block_id: &str) -> bool {
83 let s = srv!(self);
84 unsafe {
85 (s.set_block)(
86 s.ctx,
87 YogStr::from_str(dimension),
88 yog_abi::YogBlockPos {
89 x: pos.x,
90 y: pos.y,
91 z: pos.z,
92 },
93 YogStr::from_str(block_id),
94 )
95 }
96 }
97
98 fn give_item(&self, player: &str, item_id: &str, count: u32) -> bool {
99 let s = srv!(self);
100 unsafe {
101 (s.give_item)(
102 s.ctx,
103 YogStr::from_str(player),
104 YogStr::from_str(item_id),
105 count,
106 )
107 }
108 }
109
110 fn teleport(&self, player: &str, x: f64, y: f64, z: f64) -> bool {
111 let s = srv!(self);
112 unsafe {
113 (s.player_teleport)(
114 s.ctx,
115 YogStr::from_str(player),
116 yog_abi::YogVec3 { x, y, z },
117 )
118 }
119 }
120
121 fn send_to_player(&self, player: &str, channel: &str, payload: &[u8]) -> bool {
122 let s = srv!(self);
123 unsafe {
124 (s.send_to_player)(
125 s.ctx,
126 YogStr::from_str(player),
127 YogStr::from_str(channel),
128 payload.as_ptr(),
129 payload.len() as u32,
130 )
131 }
132 }
133
134 fn send_to_server(&self, channel: &str, payload: &[u8]) -> bool {
135 let s = srv!(self);
136 unsafe {
137 (s.send_to_server)(
138 s.ctx,
139 YogStr::from_str(channel),
140 payload.as_ptr(),
141 payload.len() as u32,
142 )
143 }
144 }
145
146 fn entity_teleport(&self, uuid: &str, x: f64, y: f64, z: f64) -> bool {
147 let s = srv!(self);
148 unsafe { (s.entity_teleport)(s.ctx, YogStr::from_str(uuid), yog_abi::YogVec3 { x, y, z }) }
149 }
150
151 fn entity_position(&self, uuid: &str) -> Option<(f64, f64, f64)> {
152 let s = srv!(self);
153 let mut out = yog_abi::YogVec3 {
154 x: 0.0,
155 y: 0.0,
156 z: 0.0,
157 };
158 if unsafe { (s.entity_position)(s.ctx, YogStr::from_str(uuid), &mut out) } {
159 Some((out.x, out.y, out.z))
160 } else {
161 None
162 }
163 }
164
165 fn entity_health(&self, uuid: &str) -> Option<f32> {
166 let s = srv!(self);
167 let mut hp = 0f32;
168 if unsafe { (s.entity_health)(s.ctx, YogStr::from_str(uuid), &mut hp) } {
169 Some(hp)
170 } else {
171 None
172 }
173 }
174
175 fn entity_set_health(&self, uuid: &str, health: f32) -> bool {
176 let s = srv!(self);
177 unsafe { (s.entity_set_health)(s.ctx, YogStr::from_str(uuid), health) }
178 }
179
180 fn entity_kill(&self, uuid: &str) -> bool {
181 let s = srv!(self);
182 unsafe { (s.entity_kill)(s.ctx, YogStr::from_str(uuid)) }
183 }
184
185 fn spawn_entity(
186 &self,
187 entity_type: &str,
188 dimension: &str,
189 x: f64,
190 y: f64,
191 z: f64,
192 ) -> Option<String> {
193 let s = srv!(self);
194 let owned = unsafe {
195 (s.spawn_entity)(
196 s.ctx,
197 YogStr::from_str(entity_type),
198 YogStr::from_str(dimension),
199 yog_abi::YogVec3 { x, y, z },
200 )
201 };
202 if owned.is_none() {
203 return None;
204 }
205 let result = unsafe {
206 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
207 .ok()
208 };
209 unsafe { (s.free_str)(owned.ptr, owned.len) };
210 result
211 }
212
213 fn entity_add_effect(
214 &self,
215 uuid: &str,
216 effect_id: &str,
217 duration_ticks: i32,
218 amplifier: u8,
219 show_particles: bool,
220 ) -> bool {
221 let s = srv!(self);
222 unsafe {
223 (s.entity_add_effect)(
224 s.ctx,
225 YogStr::from_str(uuid),
226 YogStr::from_str(effect_id),
227 duration_ticks,
228 amplifier,
229 show_particles,
230 )
231 }
232 }
233
234 fn entity_remove_effect(&self, uuid: &str, effect_id: &str) -> bool {
235 let s = srv!(self);
236 unsafe {
237 (s.entity_remove_effect)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(effect_id))
238 }
239 }
240
241 fn entity_clear_effects(&self, uuid: &str) -> bool {
242 let s = srv!(self);
243 unsafe { (s.entity_clear_effects)(s.ctx, YogStr::from_str(uuid)) }
244 }
245
246 fn drop_loot(&self, table_id: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
247 let s = srv!(self);
248 unsafe {
249 (s.drop_loot)(
250 s.ctx,
251 YogStr::from_str(table_id),
252 YogStr::from_str(dimension),
253 yog_abi::YogVec3 { x, y, z },
254 )
255 }
256 }
257
258 fn has_item_tag(&self, item_id: &str, tag_id: &str) -> bool {
259 let s = srv!(self);
260 unsafe { (s.has_item_tag)(s.ctx, YogStr::from_str(item_id), YogStr::from_str(tag_id)) }
261 }
262
263 fn has_block_tag(&self, block_id: &str, tag_id: &str) -> bool {
264 let s = srv!(self);
265 unsafe { (s.has_block_tag)(s.ctx, YogStr::from_str(block_id), YogStr::from_str(tag_id)) }
266 }
267
268 fn world_time(&self, dimension: &str) -> Option<i64> {
269 let s = srv!(self);
270 let mut t = 0i64;
271 if unsafe { (s.world_time)(s.ctx, YogStr::from_str(dimension), &mut t) } {
272 Some(t)
273 } else {
274 None
275 }
276 }
277
278 fn world_set_time(&self, dimension: &str, time: i64) -> bool {
279 let s = srv!(self);
280 unsafe { (s.set_time)(s.ctx, YogStr::from_str(dimension), time) }
281 }
282
283 fn world_is_raining(&self, dimension: &str) -> bool {
284 let s = srv!(self);
285 unsafe { (s.is_raining)(s.ctx, YogStr::from_str(dimension)) }
286 }
287
288 fn world_set_weather(&self, dimension: &str, raining: bool, duration_ticks: i32) -> bool {
289 let s = srv!(self);
290 unsafe { (s.set_weather)(s.ctx, YogStr::from_str(dimension), raining, duration_ticks) }
291 }
292
293 fn entity_velocity(&self, uuid: &str) -> Option<(f64, f64, f64)> {
294 let s = srv!(self);
295 let mut v = yog_abi::YogVec3 {
296 x: 0.0,
297 y: 0.0,
298 z: 0.0,
299 };
300 if unsafe { (s.entity_velocity)(s.ctx, YogStr::from_str(uuid), &mut v) } {
301 Some((v.x, v.y, v.z))
302 } else {
303 None
304 }
305 }
306
307 fn entity_set_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
308 let s = srv!(self);
309 unsafe {
310 (s.entity_set_velocity)(
311 s.ctx,
312 YogStr::from_str(uuid),
313 yog_abi::YogVec3 {
314 x: vx,
315 y: vy,
316 z: vz,
317 },
318 )
319 }
320 }
321
322 fn entity_add_velocity(&self, uuid: &str, vx: f64, vy: f64, vz: f64) -> bool {
323 let s = srv!(self);
324 unsafe {
325 (s.entity_add_velocity)(
326 s.ctx,
327 YogStr::from_str(uuid),
328 yog_abi::YogVec3 {
329 x: vx,
330 y: vy,
331 z: vz,
332 },
333 )
334 }
335 }
336
337 fn scoreboard_get(&self, objective: &str, player: &str) -> Option<i32> {
338 let s = srv!(self);
339 let mut score = 0i32;
340 if unsafe {
341 (s.scoreboard_get)(
342 s.ctx,
343 YogStr::from_str(objective),
344 YogStr::from_str(player),
345 &mut score,
346 )
347 } {
348 Some(score)
349 } else {
350 None
351 }
352 }
353
354 fn scoreboard_set(&self, objective: &str, player: &str, score: i32) -> bool {
355 let s = srv!(self);
356 unsafe {
357 (s.scoreboard_set)(
358 s.ctx,
359 YogStr::from_str(objective),
360 YogStr::from_str(player),
361 score,
362 )
363 }
364 }
365
366 fn scoreboard_add(&self, objective: &str, player: &str, delta: i32) -> Option<i32> {
367 let s = srv!(self);
368 let mut new_score = 0i32;
369 if unsafe {
370 (s.scoreboard_add)(
371 s.ctx,
372 YogStr::from_str(objective),
373 YogStr::from_str(player),
374 delta,
375 &mut new_score,
376 )
377 } {
378 Some(new_score)
379 } else {
380 None
381 }
382 }
383
384 fn game_dir(&self) -> String {
385 let s = srv!(self);
386 let owned = unsafe { (s.game_dir)(s.ctx) };
387 if owned.is_none() {
388 return String::new();
389 }
390 let result = unsafe {
391 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
392 .unwrap_or_default()
393 };
394 unsafe { (s.free_str)(owned.ptr, owned.len) };
395 result
396 }
397
398 fn play_sound(
399 &self,
400 dimension: &str,
401 x: f64,
402 y: f64,
403 z: f64,
404 sound_id: &str,
405 volume: f32,
406 pitch: f32,
407 ) -> bool {
408 let s = srv!(self);
409 unsafe {
410 (s.play_sound)(
411 s.ctx,
412 YogStr::from_str(dimension),
413 yog_abi::YogVec3 { x, y, z },
414 YogStr::from_str(sound_id),
415 volume,
416 pitch,
417 )
418 }
419 }
420
421 fn play_sound_to_player(&self, player: &str, sound_id: &str, volume: f32, pitch: f32) -> bool {
422 let s = srv!(self);
423 unsafe {
424 (s.play_sound_player)(
425 s.ctx,
426 YogStr::from_str(player),
427 YogStr::from_str(sound_id),
428 volume,
429 pitch,
430 )
431 }
432 }
433
434 fn send_title(
435 &self,
436 player: &str,
437 title: &str,
438 subtitle: &str,
439 fadein: i32,
440 stay: i32,
441 fadeout: i32,
442 ) -> bool {
443 let s = srv!(self);
444 unsafe {
445 (s.send_title)(
446 s.ctx,
447 YogStr::from_str(player),
448 YogStr::from_str(title),
449 YogStr::from_str(subtitle),
450 fadein,
451 stay,
452 fadeout,
453 )
454 }
455 }
456
457 fn entity_rotation(&self, uuid: &str) -> Option<(f32, f32)> {
458 let s = srv!(self);
459 let mut out = yog_abi::YogVec3 {
460 x: 0.0,
461 y: 0.0,
462 z: 0.0,
463 };
464 let ok = unsafe { (s.entity_rotation)(s.ctx, YogStr::from_str(uuid), &mut out) };
465 if ok {
466 Some((out.x as f32, out.y as f32))
467 } else {
468 None
469 }
470 }
471
472 fn send_actionbar(&self, player: &str, message: &str) -> bool {
473 let s = srv!(self);
474 unsafe { (s.send_actionbar)(s.ctx, YogStr::from_str(player), YogStr::from_str(message)) }
475 }
476
477 fn kick_player(&self, player: &str, reason: &str) -> bool {
478 let s = srv!(self);
479 unsafe { (s.kick_player)(s.ctx, YogStr::from_str(player), YogStr::from_str(reason)) }
480 }
481
482 fn set_gamemode(&self, player: &str, gamemode: &str) -> bool {
483 let s = srv!(self);
484 unsafe { (s.set_gamemode)(s.ctx, YogStr::from_str(player), YogStr::from_str(gamemode)) }
485 }
486
487 fn bossbar_create(&self, id: &str, title: &str, color: &str, style: &str) -> bool {
488 let s = srv!(self);
489 unsafe {
490 (s.bossbar_create)(
491 s.ctx,
492 YogStr::from_str(id),
493 YogStr::from_str(title),
494 YogStr::from_str(color),
495 YogStr::from_str(style),
496 )
497 }
498 }
499
500 fn bossbar_remove(&self, id: &str) -> bool {
501 let s = srv!(self);
502 unsafe { (s.bossbar_remove)(s.ctx, YogStr::from_str(id)) }
503 }
504
505 fn bossbar_set_title(&self, id: &str, title: &str) -> bool {
506 let s = srv!(self);
507 unsafe { (s.bossbar_set_title)(s.ctx, YogStr::from_str(id), YogStr::from_str(title)) }
508 }
509
510 fn bossbar_set_progress(&self, id: &str, progress: f32) -> bool {
511 let s = srv!(self);
512 unsafe { (s.bossbar_set_progress)(s.ctx, YogStr::from_str(id), progress) }
513 }
514
515 fn bossbar_set_color(&self, id: &str, color: &str) -> bool {
516 let s = srv!(self);
517 unsafe { (s.bossbar_set_color)(s.ctx, YogStr::from_str(id), YogStr::from_str(color)) }
518 }
519
520 fn bossbar_add_player(&self, id: &str, player: &str) -> bool {
521 let s = srv!(self);
522 unsafe { (s.bossbar_add_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
523 }
524
525 fn bossbar_remove_player(&self, id: &str, player: &str) -> bool {
526 let s = srv!(self);
527 unsafe { (s.bossbar_remove_player)(s.ctx, YogStr::from_str(id), YogStr::from_str(player)) }
528 }
529
530 fn bossbar_set_visible(&self, id: &str, visible: bool) -> bool {
531 let s = srv!(self);
532 unsafe { (s.bossbar_set_visible)(s.ctx, YogStr::from_str(id), visible) }
533 }
534
535 fn online_players(&self) -> Vec<String> {
536 let s = srv!(self);
537 let owned = unsafe { (s.online_players)(s.ctx) };
538 if owned.is_none() {
539 return Vec::new();
540 }
541 let text = unsafe {
542 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
543 .unwrap_or_default()
544 };
545 unsafe { (s.free_str)(owned.ptr, owned.len) };
546 if text.is_empty() {
547 Vec::new()
548 } else {
549 text.lines().map(str::to_owned).collect()
550 }
551 }
552
553 fn get_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<String> {
554 let s = srv!(self);
555 let owned = unsafe {
556 (s.get_block_nbt)(
557 s.ctx,
558 YogStr::from_str(dimension),
559 yog_abi::YogBlockPos {
560 x: pos.x,
561 y: pos.y,
562 z: pos.z,
563 },
564 )
565 };
566 if owned.is_none() {
567 return None;
568 }
569 let result = unsafe {
570 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
571 .ok()
572 };
573 unsafe { (s.free_str)(owned.ptr, owned.len) };
574 result
575 }
576
577 fn set_block_nbt(&self, dimension: &str, pos: yog_core::BlockPos, snbt: &str) -> bool {
578 let s = srv!(self);
579 unsafe {
580 (s.set_block_nbt)(
581 s.ctx,
582 YogStr::from_str(dimension),
583 yog_abi::YogBlockPos {
584 x: pos.x,
585 y: pos.y,
586 z: pos.z,
587 },
588 YogStr::from_str(snbt),
589 )
590 }
591 }
592
593 fn get_inventory_slot(
594 &self,
595 dimension: &str,
596 pos: yog_core::BlockPos,
597 slot: u32,
598 ) -> Option<(String, u32)> {
599 let s = srv!(self);
600 let owned = unsafe {
601 (s.get_inventory_slot)(
602 s.ctx,
603 YogStr::from_str(dimension),
604 yog_abi::YogBlockPos {
605 x: pos.x,
606 y: pos.y,
607 z: pos.z,
608 },
609 slot,
610 )
611 };
612 if owned.is_none() {
613 return None;
614 }
615 let text = unsafe {
616 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
617 .unwrap_or_default()
618 };
619 unsafe { (s.free_str)(owned.ptr, owned.len) };
620 let (item_id, count) = text.split_once('\t')?;
621 Some((item_id.to_owned(), count.parse().ok()?))
622 }
623
624 fn set_inventory_slot(
625 &self,
626 dimension: &str,
627 pos: yog_core::BlockPos,
628 slot: u32,
629 item_id: &str,
630 count: u32,
631 ) -> bool {
632 let s = srv!(self);
633 unsafe {
634 (s.set_inventory_slot)(
635 s.ctx,
636 YogStr::from_str(dimension),
637 yog_abi::YogBlockPos {
638 x: pos.x,
639 y: pos.y,
640 z: pos.z,
641 },
642 slot,
643 YogStr::from_str(item_id),
644 count,
645 )
646 }
647 }
648
649 fn player_inventory(&self, player: &str) -> Vec<(u32, String, u32)> {
650 let s = srv!(self);
651 let owned = unsafe { (s.player_inventory)(s.ctx, YogStr::from_str(player)) };
652 if owned.is_none() {
653 return Vec::new();
654 }
655 let text = unsafe {
656 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
657 .unwrap_or_default()
658 };
659 unsafe { (s.free_str)(owned.ptr, owned.len) };
660 text.lines()
661 .filter_map(|line| {
662 let mut it = line.split('\t');
663 let slot: u32 = it.next()?.parse().ok()?;
664 let item_id = it.next()?.to_owned();
665 let count: u32 = it.next()?.parse().ok()?;
666 Some((slot, item_id, count))
667 })
668 .collect()
669 }
670
671 fn player_set_slot(&self, player: &str, slot: u32, item_id: &str, count: u32) -> bool {
672 let s = srv!(self);
673 unsafe {
674 (s.player_set_slot)(
675 s.ctx,
676 YogStr::from_str(player),
677 slot,
678 YogStr::from_str(item_id),
679 count,
680 )
681 }
682 }
683
684 fn teleport_to_dim(&self, player: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
685 let s = srv!(self);
686 unsafe {
687 (s.player_teleport_dim)(
688 s.ctx,
689 YogStr::from_str(player),
690 YogStr::from_str(dimension),
691 yog_abi::YogVec3 { x, y, z },
692 )
693 }
694 }
695
696 fn entity_teleport_to_dim(&self, uuid: &str, dimension: &str, x: f64, y: f64, z: f64) -> bool {
697 let s = srv!(self);
698 unsafe {
699 (s.entity_teleport_dim)(
700 s.ctx,
701 YogStr::from_str(uuid),
702 YogStr::from_str(dimension),
703 yog_abi::YogVec3 { x, y, z },
704 )
705 }
706 }
707
708 fn world_entity_count(&self, dimension: &str, entity_type: &str) -> i32 {
709 let s = srv!(self);
710 unsafe {
711 (s.world_entity_count)(
712 s.ctx,
713 YogStr::from_str(dimension),
714 YogStr::from_str(entity_type),
715 )
716 }
717 }
718
719 fn entity_get_nbt(&self, uuid: &str) -> Option<String> {
720 let s = srv!(self);
721 let owned = unsafe { (s.entity_get_nbt)(s.ctx, YogStr::from_str(uuid)) };
722 if owned.is_none() {
723 return None;
724 }
725 let result = unsafe {
726 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
727 .ok()
728 };
729 unsafe { (s.free_str)(owned.ptr, owned.len) };
730 result
731 }
732
733 fn entity_set_nbt(&self, uuid: &str, snbt: &str) -> bool {
734 let s = srv!(self);
735 unsafe { (s.entity_set_nbt)(s.ctx, YogStr::from_str(uuid), YogStr::from_str(snbt)) }
736 }
737
738 fn spawn_particles(
739 &self,
740 dimension: &str,
741 x: f64,
742 y: f64,
743 z: f64,
744 particle_type: &str,
745 count: i32,
746 dx: f64,
747 dy: f64,
748 dz: f64,
749 speed: f64,
750 ) -> bool {
751 let s = srv!(self);
752 unsafe {
753 (s.spawn_particles)(
754 s.ctx,
755 YogStr::from_str(dimension),
756 yog_abi::YogVec3 { x, y, z },
757 YogStr::from_str(particle_type),
758 count,
759 dx,
760 dy,
761 dz,
762 speed,
763 )
764 }
765 }
766
767 fn entity_attribute_get(&self, uuid: &str, attribute_id: &str) -> Option<f64> {
768 let s = srv!(self);
769 let v = unsafe {
770 (s.entity_attribute_get)(
771 s.ctx,
772 YogStr::from_str(uuid),
773 YogStr::from_str(attribute_id),
774 )
775 };
776 if v.is_nan() {
777 None
778 } else {
779 Some(v)
780 }
781 }
782
783 fn entity_attribute_set(&self, uuid: &str, attribute_id: &str, value: f64) -> bool {
784 let s = srv!(self);
785 unsafe {
786 (s.entity_attribute_set)(
787 s.ctx,
788 YogStr::from_str(uuid),
789 YogStr::from_str(attribute_id),
790 value,
791 )
792 }
793 }
794
795 fn get_held_item_nbt(&self, player: &str) -> Option<String> {
796 let s = srv!(self);
797 let owned = unsafe { (s.get_held_item_nbt)(s.ctx, YogStr::from_str(player)) };
798 if owned.is_none() {
799 return None;
800 }
801 let result = unsafe {
802 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
803 .ok()
804 };
805 unsafe { (s.free_str)(owned.ptr, owned.len) };
806 result
807 }
808
809 fn set_held_item_nbt(&self, player: &str, snbt: &str) -> bool {
810 let s = srv!(self);
811 unsafe { (s.set_held_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
812 }
813
814 fn get_offhand_item_nbt(&self, player: &str) -> Option<String> {
815 let s = srv!(self);
816 let owned = unsafe { (s.get_offhand_item_nbt)(s.ctx, YogStr::from_str(player)) };
817 if owned.is_none() {
818 return None;
819 }
820 let result = unsafe {
821 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
822 .ok()
823 };
824 unsafe { (s.free_str)(owned.ptr, owned.len) };
825 result
826 }
827
828 fn set_offhand_item_nbt(&self, player: &str, snbt: &str) -> bool {
829 let s = srv!(self);
830 unsafe { (s.set_offhand_item_nbt)(s.ctx, YogStr::from_str(player), YogStr::from_str(snbt)) }
831 }
832
833 fn get_slot_item(&self, player: &str, slot: u32) -> Option<(String, u32, String)> {
834 let s = srv!(self);
835 let owned = unsafe { (s.get_slot_item)(s.ctx, YogStr::from_str(player), slot) };
836 if owned.is_none() {
837 return None;
838 }
839 let text = unsafe {
840 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
841 .unwrap_or_default()
842 };
843 unsafe { (s.free_str)(owned.ptr, owned.len) };
844 let mut it = text.splitn(3, '\t');
845 let item_id = it.next()?.to_owned();
846 let count: u32 = it.next()?.parse().ok()?;
847 let nbt = it.next().unwrap_or("{}").to_owned();
848 Some((item_id, count, nbt))
849 }
850
851 fn set_slot_item(
852 &self,
853 player: &str,
854 slot: u32,
855 item_id: &str,
856 count: u32,
857 snbt: &str,
858 ) -> bool {
859 let s = srv!(self);
860 unsafe {
861 (s.set_slot_item)(
862 s.ctx,
863 YogStr::from_str(player),
864 slot,
865 YogStr::from_str(item_id),
866 count,
867 YogStr::from_str(snbt),
868 )
869 }
870 }
871
872 fn entity_info(&self, uuid: &str) -> Option<yog_core::EntityInfo> {
875 let s = srv!(self);
876 let owned = unsafe { (s.entity_info)(s.ctx, YogStr::from_str(uuid)) };
877 take_owned(s, owned).and_then(|l| yog_core::EntityInfo::decode(&l))
878 }
879
880 fn entities_in_box(
881 &self,
882 dimension: &str,
883 min: (f64, f64, f64),
884 max: (f64, f64, f64),
885 ) -> Vec<String> {
886 let s = srv!(self);
887 let owned =
888 unsafe { (s.entities_in_box)(s.ctx, YogStr::from_str(dimension), v3(min), v3(max)) };
889 take_owned(s, owned)
890 .map(|t| {
891 t.lines()
892 .filter(|l| !l.is_empty())
893 .map(str::to_owned)
894 .collect()
895 })
896 .unwrap_or_default()
897 }
898
899 fn raycast_block(
900 &self,
901 dimension: &str,
902 from: (f64, f64, f64),
903 to: (f64, f64, f64),
904 ) -> Option<yog_core::BlockHit> {
905 let s = srv!(self);
906 let owned =
907 unsafe { (s.raycast_block)(s.ctx, YogStr::from_str(dimension), v3(from), v3(to)) };
908 take_owned(s, owned).and_then(|l| yog_core::BlockHit::decode(&l))
909 }
910
911 fn raycast_entity(
912 &self,
913 dimension: &str,
914 from: (f64, f64, f64),
915 to: (f64, f64, f64),
916 exclude: &str,
917 ) -> Option<String> {
918 let s = srv!(self);
919 let owned = unsafe {
920 (s.raycast_entity)(
921 s.ctx,
922 YogStr::from_str(dimension),
923 v3(from),
924 v3(to),
925 YogStr::from_str(exclude),
926 )
927 };
928 take_owned(s, owned).filter(|u| !u.is_empty())
929 }
930
931 fn block_info(&self, dimension: &str, pos: yog_core::BlockPos) -> Option<yog_core::BlockInfo> {
932 let s = srv!(self);
933 let owned = unsafe { (s.block_info)(s.ctx, YogStr::from_str(dimension), bp(pos)) };
934 take_owned(s, owned).and_then(|l| yog_core::BlockInfo::decode(&l))
935 }
936
937 fn break_block(
938 &self,
939 dimension: &str,
940 pos: yog_core::BlockPos,
941 drop: bool,
942 breaker: &str,
943 ) -> bool {
944 let s = srv!(self);
945 unsafe {
946 (s.break_block)(
947 s.ctx,
948 YogStr::from_str(dimension),
949 bp(pos),
950 drop,
951 YogStr::from_str(breaker),
952 )
953 }
954 }
955
956 fn place_block_from_slot(
957 &self,
958 player: &str,
959 slot: u32,
960 dimension: &str,
961 pos: yog_core::BlockPos,
962 ) -> bool {
963 let s = srv!(self);
964 unsafe {
965 (s.place_block_from_slot)(
966 s.ctx,
967 YogStr::from_str(player),
968 slot,
969 YogStr::from_str(dimension),
970 bp(pos),
971 )
972 }
973 }
974
975 fn bonemeal(&self, dimension: &str, pos: yog_core::BlockPos) -> bool {
976 let s = srv!(self);
977 unsafe { (s.bonemeal)(s.ctx, YogStr::from_str(dimension), bp(pos)) }
978 }
979
980 fn explode(
981 &self,
982 dimension: &str,
983 x: f64,
984 y: f64,
985 z: f64,
986 power: f32,
987 fire: bool,
988 source: &str,
989 ) -> bool {
990 let s = srv!(self);
991 unsafe {
992 (s.explode)(
993 s.ctx,
994 YogStr::from_str(dimension),
995 v3((x, y, z)),
996 power,
997 fire,
998 YogStr::from_str(source),
999 )
1000 }
1001 }
1002
1003 fn tellraw(&self, player: &str, json: &str) -> bool {
1004 let s = srv!(self);
1005 unsafe { (s.tellraw)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
1006 }
1007
1008 fn entity_damage(&self, uuid: &str, amount: f32, damage_type: &str, attacker: &str) -> bool {
1009 let s = srv!(self);
1010 unsafe {
1011 (s.entity_damage)(
1012 s.ctx,
1013 YogStr::from_str(uuid),
1014 amount,
1015 YogStr::from_str(damage_type),
1016 YogStr::from_str(attacker),
1017 )
1018 }
1019 }
1020
1021 fn entity_set_fire_ticks(&self, uuid: &str, ticks: i32) -> bool {
1022 let s = srv!(self);
1023 unsafe { (s.entity_set_fire_ticks)(s.ctx, YogStr::from_str(uuid), ticks) }
1024 }
1025
1026 fn player_add_xp(&self, player: &str, amount: i32) -> bool {
1027 let s = srv!(self);
1028 unsafe { (s.player_add_xp)(s.ctx, YogStr::from_str(player), amount) }
1029 }
1030
1031 fn player_set_may_fly(&self, player: &str, may_fly: bool) -> bool {
1032 let s = srv!(self);
1033 unsafe { (s.player_set_may_fly)(s.ctx, YogStr::from_str(player), may_fly) }
1034 }
1035
1036 fn player_info(&self, player: &str) -> Option<yog_core::PlayerInfo> {
1037 let s = srv!(self);
1038 let owned = unsafe { (s.player_info)(s.ctx, YogStr::from_str(player)) };
1039 take_owned(s, owned).and_then(|l| yog_core::PlayerInfo::decode(&l))
1040 }
1041
1042 fn spawn_item(
1043 &self,
1044 dimension: &str,
1045 pos: (f64, f64, f64),
1046 vel: (f64, f64, f64),
1047 item_id: &str,
1048 count: u32,
1049 snbt: &str,
1050 ) -> Option<String> {
1051 let s = srv!(self);
1052 let owned = unsafe {
1053 (s.spawn_item)(
1054 s.ctx,
1055 YogStr::from_str(dimension),
1056 v3(pos),
1057 v3(vel),
1058 YogStr::from_str(item_id),
1059 count,
1060 YogStr::from_str(snbt),
1061 )
1062 };
1063 take_owned(s, owned).filter(|u| !u.is_empty())
1064 }
1065
1066 fn item_entity_stack(&self, uuid: &str) -> Option<(String, u32, String)> {
1067 let s = srv!(self);
1068 let owned = unsafe { (s.item_entity_stack)(s.ctx, YogStr::from_str(uuid)) };
1069 let text = take_owned(s, owned)?;
1070 let mut it = text.splitn(3, '\t');
1071 let item_id = it.next()?.to_owned();
1072 let count = it.next()?.parse().ok()?;
1073 let nbt = it.next().unwrap_or("{}").to_owned();
1074 Some((item_id, count, nbt))
1075 }
1076
1077 fn item_entity_set_stack(&self, uuid: &str, item_id: &str, count: u32, snbt: &str) -> bool {
1078 let s = srv!(self);
1079 unsafe {
1080 (s.item_entity_set_stack)(
1081 s.ctx,
1082 YogStr::from_str(uuid),
1083 YogStr::from_str(item_id),
1084 count,
1085 YogStr::from_str(snbt),
1086 )
1087 }
1088 }
1089
1090 fn actionbar_json(&self, player: &str, json: &str) -> bool {
1091 let s = srv!(self);
1092 unsafe { (s.actionbar_json)(s.ctx, YogStr::from_str(player), YogStr::from_str(json)) }
1093 }
1094
1095 fn use_item_on_block(
1096 &self,
1097 dimension: &str,
1098 pos: yog_core::BlockPos,
1099 face: yog_core::Face,
1100 item_id: &str,
1101 player: &str,
1102 ) -> bool {
1103 let s = srv!(self);
1104 unsafe {
1105 (s.use_item_on_block)(
1106 s.ctx,
1107 YogStr::from_str(dimension),
1108 bp(pos),
1109 face.index(),
1110 YogStr::from_str(item_id),
1111 YogStr::from_str(player),
1112 )
1113 }
1114 }
1115
1116 fn pour_fluid(&self, dimension: &str, pos: yog_core::BlockPos, bucket_item_id: &str) -> bool {
1117 let s = srv!(self);
1118 unsafe {
1119 (s.pour_fluid)(
1120 s.ctx,
1121 YogStr::from_str(dimension),
1122 bp(pos),
1123 YogStr::from_str(bucket_item_id),
1124 )
1125 }
1126 }
1127
1128 fn has_entity_tag(&self, entity_type: &str, tag_id: &str) -> bool {
1129 let s = srv!(self);
1130 unsafe {
1131 (s.has_entity_tag)(
1132 s.ctx,
1133 YogStr::from_str(entity_type),
1134 YogStr::from_str(tag_id),
1135 )
1136 }
1137 }
1138
1139 fn item_block(&self, item_id: &str) -> Option<String> {
1140 let s = srv!(self);
1141 let owned = unsafe { (s.item_block)(s.ctx, YogStr::from_str(item_id)) };
1142 take_owned(s, owned).filter(|b| !b.is_empty())
1143 }
1144
1145 fn place_feature(&self, dimension: &str, pos: yog_core::BlockPos, feature_id: &str) -> bool {
1146 let s = srv!(self);
1147 unsafe {
1148 (s.place_feature)(
1149 s.ctx,
1150 YogStr::from_str(dimension),
1151 bp(pos),
1152 YogStr::from_str(feature_id),
1153 )
1154 }
1155 }
1156}
1157
1158fn take_owned(s: &YogServer, owned: yog_abi::YogOwnedStr) -> Option<String> {
1160 if owned.is_none() {
1161 return None;
1162 }
1163 let text = unsafe {
1164 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec()).ok()
1165 };
1166 unsafe { (s.free_str)(owned.ptr, owned.len) };
1167 text
1168}
1169
1170fn v3((x, y, z): (f64, f64, f64)) -> yog_abi::YogVec3 {
1171 yog_abi::YogVec3 { x, y, z }
1172}
1173
1174fn bp(pos: yog_core::BlockPos) -> yog_abi::YogBlockPos {
1175 yog_abi::YogBlockPos {
1176 x: pos.x,
1177 y: pos.y,
1178 z: pos.z,
1179 }
1180}
1181
1182macro_rules! trampoline_phased {
1200 ($name:ident, $abi_ev:ty, $rust_ev:ty, |$ev:ident| $build:expr) => {
1201 unsafe extern "C" fn $name<F>(
1202 ud: *mut c_void,
1203 srv: *const YogServer,
1204 ev: *const $abi_ev,
1205 phase: u8,
1206 ) -> bool
1207 where
1208 F: Fn(&$rust_ev, EventPhase, &dyn Server) -> bool + Send + Sync,
1209 {
1210 let f = &*(ud as *const F);
1211 let $ev = &*ev;
1212 let rust_ev = $build;
1213 let p = if phase == 0 {
1214 EventPhase::Pre
1215 } else {
1216 EventPhase::Post
1217 };
1218 f(&rust_ev, p, &CServer(srv))
1219 }
1220 };
1221}
1222
1223trampoline_phased!(
1224 trampoline_block_break,
1225 YogBlockBreakEvent,
1226 BlockBreakEvent,
1227 |ev| BlockBreakEvent {
1228 player_name: ev.player.as_str().to_owned(),
1229 block_id: ev.block.as_str().to_owned(),
1230 pos: yog_core::BlockPos {
1231 x: ev.pos.x,
1232 y: ev.pos.y,
1233 z: ev.pos.z
1234 },
1235 dimension: ev.dimension.as_str().to_owned(),
1236 }
1237);
1238
1239trampoline_phased!(trampoline_chat, YogChatEvent, ChatEvent, |ev| ChatEvent {
1240 player_name: ev.player.as_str().to_owned(),
1241 message: ev.message.as_str().to_owned(),
1242 dimension: ev.dimension.as_str().to_owned(),
1243});
1244
1245trampoline_phased!(
1246 trampoline_player_join,
1247 YogPlayerEvent,
1248 PlayerJoinEvent,
1249 |ev| PlayerJoinEvent {
1250 player_name: ev.player.as_str().to_owned(),
1251 uuid: ev.uuid.as_str().to_owned(),
1252 dimension: ev.dimension.as_str().to_owned(),
1253 }
1254);
1255
1256trampoline_phased!(
1257 trampoline_player_leave,
1258 YogPlayerEvent,
1259 PlayerLeaveEvent,
1260 |ev| PlayerLeaveEvent {
1261 player_name: ev.player.as_str().to_owned(),
1262 uuid: ev.uuid.as_str().to_owned(),
1263 dimension: ev.dimension.as_str().to_owned(),
1264 }
1265);
1266
1267trampoline_phased!(trampoline_use_item, YogUseItemEvent, UseItemEvent, |ev| {
1268 UseItemEvent {
1269 player_name: ev.player.as_str().to_owned(),
1270 item_id: ev.item.as_str().to_owned(),
1271 sneaking: ev.sneaking,
1272 dimension: ev.dimension.as_str().to_owned(),
1273 }
1274});
1275
1276trampoline_phased!(
1277 trampoline_use_block,
1278 YogUseBlockEvent,
1279 UseBlockEvent,
1280 |ev| UseBlockEvent {
1281 player_name: ev.player.as_str().to_owned(),
1282 block_id: ev.block.as_str().to_owned(),
1283 pos: yog_core::BlockPos {
1284 x: ev.pos.x,
1285 y: ev.pos.y,
1286 z: ev.pos.z
1287 },
1288 dimension: ev.dimension.as_str().to_owned(),
1289 }
1290);
1291
1292trampoline_phased!(
1293 trampoline_attack_entity,
1294 YogAttackEntityEvent,
1295 AttackEntityEvent,
1296 |ev| AttackEntityEvent {
1297 player_name: ev.player.as_str().to_owned(),
1298 target_type: ev.target_type.as_str().to_owned(),
1299 target_uuid: ev.target_uuid.as_str().to_owned(),
1300 dimension: ev.dimension.as_str().to_owned(),
1301 }
1302);
1303
1304trampoline_phased!(
1305 trampoline_entity_damage,
1306 YogEntityDamageEvent,
1307 EntityDamageEvent,
1308 |ev| EntityDamageEvent {
1309 entity_type: ev.entity_type.as_str().to_owned(),
1310 uuid: ev.uuid.as_str().to_owned(),
1311 amount: ev.amount,
1312 source: ev.source.as_str().to_owned(),
1313 dimension: ev.dimension.as_str().to_owned(),
1314 }
1315);
1316
1317trampoline_phased!(
1318 trampoline_entity_death,
1319 YogEntityDeathEvent,
1320 EntityDeathEvent,
1321 |ev| EntityDeathEvent {
1322 entity_type: ev.entity_type.as_str().to_owned(),
1323 uuid: ev.uuid.as_str().to_owned(),
1324 source: ev.source.as_str().to_owned(),
1325 dimension: ev.dimension.as_str().to_owned(),
1326 }
1327);
1328
1329trampoline_phased!(
1330 trampoline_entity_spawn,
1331 YogEntitySpawnEvent,
1332 EntitySpawnEvent,
1333 |ev| EntitySpawnEvent {
1334 entity_type: ev.entity_type.as_str().to_owned(),
1335 uuid: ev.uuid.as_str().to_owned(),
1336 dimension: ev.dimension.as_str().to_owned(),
1337 }
1338);
1339
1340trampoline_phased!(
1341 trampoline_place_block,
1342 YogPlaceBlockEvent,
1343 PlaceBlockEvent,
1344 |ev| PlaceBlockEvent {
1345 player_name: ev.player.as_str().to_owned(),
1346 block_id: ev.block.as_str().to_owned(),
1347 pos: yog_core::BlockPos {
1348 x: ev.pos.x,
1349 y: ev.pos.y,
1350 z: ev.pos.z
1351 },
1352 dimension: ev.dimension.as_str().to_owned(),
1353 }
1354);
1355
1356trampoline_phased!(
1357 trampoline_player_death,
1358 YogPlayerDeathEvent,
1359 PlayerDeathEvent,
1360 |ev| PlayerDeathEvent {
1361 player_name: ev.player.as_str().to_owned(),
1362 uuid: ev.uuid.as_str().to_owned(),
1363 source: ev.source.as_str().to_owned(),
1364 dimension: ev.dimension.as_str().to_owned(),
1365 }
1366);
1367
1368trampoline_phased!(
1369 trampoline_player_respawn,
1370 YogPlayerRespawnEvent,
1371 PlayerRespawnEvent,
1372 |ev| PlayerRespawnEvent {
1373 player_name: ev.player.as_str().to_owned(),
1374 uuid: ev.uuid.as_str().to_owned(),
1375 at_anchor: ev.at_anchor,
1376 dimension: ev.dimension.as_str().to_owned(),
1377 }
1378);
1379
1380trampoline_phased!(
1381 trampoline_advancement,
1382 YogAdvancementEvent,
1383 AdvancementEvent,
1384 |ev| AdvancementEvent {
1385 player_name: ev.player.as_str().to_owned(),
1386 uuid: ev.uuid.as_str().to_owned(),
1387 advancement_id: ev.advancement.as_str().to_owned(),
1388 dimension: ev.dimension.as_str().to_owned(),
1389 }
1390);
1391
1392trampoline_phased!(
1393 trampoline_entity_interact,
1394 YogEntityInteractEvent,
1395 EntityInteractEvent,
1396 |ev| EntityInteractEvent {
1397 player_name: ev.player.as_str().to_owned(),
1398 player_uuid: ev.player_uuid.as_str().to_owned(),
1399 entity_type: ev.entity_type.as_str().to_owned(),
1400 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1401 hand: ev.hand.as_str().to_owned(),
1402 dimension: ev.dimension.as_str().to_owned(),
1403 }
1404);
1405
1406trampoline_phased!(trampoline_craft, YogCraftEvent, CraftEvent, |ev| {
1407 CraftEvent {
1408 player_name: ev.player.as_str().to_owned(),
1409 player_uuid: ev.player_uuid.as_str().to_owned(),
1410 result_item: ev.result_item.as_str().to_owned(),
1411 result_count: ev.result_count,
1412 dimension: ev.dimension.as_str().to_owned(),
1413 }
1414});
1415
1416trampoline_phased!(
1417 trampoline_explosion,
1418 YogExplosionEvent,
1419 ExplosionEvent,
1420 |ev| ExplosionEvent {
1421 dimension: ev.dimension.as_str().to_owned(),
1422 x: ev.x,
1423 y: ev.y,
1424 z: ev.z,
1425 power: ev.power,
1426 cause_uuid: ev.cause_uuid.as_str().to_owned(),
1427 }
1428);
1429
1430trampoline_phased!(
1431 trampoline_item_pickup,
1432 YogItemPickupEvent,
1433 ItemPickupEvent,
1434 |ev| ItemPickupEvent {
1435 player_name: ev.player.as_str().to_owned(),
1436 player_uuid: ev.player_uuid.as_str().to_owned(),
1437 item_id: ev.item_id.as_str().to_owned(),
1438 item_count: ev.item_count,
1439 entity_uuid: ev.entity_uuid.as_str().to_owned(),
1440 dimension: ev.dimension.as_str().to_owned(),
1441 }
1442);
1443
1444trampoline_phased!(
1445 trampoline_player_move,
1446 YogPlayerMoveEvent,
1447 PlayerMoveEvent,
1448 |ev| PlayerMoveEvent {
1449 player_name: ev.player.as_str().to_owned(),
1450 player_uuid: ev.player_uuid.as_str().to_owned(),
1451 x: ev.x,
1452 y: ev.y,
1453 z: ev.z,
1454 yaw: ev.yaw,
1455 pitch: ev.pitch,
1456 dimension: ev.dimension.as_str().to_owned(),
1457 }
1458);
1459
1460trampoline_phased!(
1461 trampoline_container_open,
1462 YogContainerOpenEvent,
1463 ContainerOpenEvent,
1464 |ev| ContainerOpenEvent {
1465 player_name: ev.player.as_str().to_owned(),
1466 player_uuid: ev.player_uuid.as_str().to_owned(),
1467 container_type: ev.container_type.as_str().to_owned(),
1468 dimension: ev.dimension.as_str().to_owned(),
1469 }
1470);
1471
1472trampoline_phased!(
1473 trampoline_container_close,
1474 YogContainerCloseEvent,
1475 ContainerCloseEvent,
1476 |ev| ContainerCloseEvent {
1477 player_name: ev.player.as_str().to_owned(),
1478 player_uuid: ev.player_uuid.as_str().to_owned(),
1479 dimension: ev.dimension.as_str().to_owned(),
1480 }
1481);
1482
1483trampoline_phased!(
1484 trampoline_projectile_hit,
1485 YogProjectileHitEvent,
1486 ProjectileHitEvent,
1487 |ev| ProjectileHitEvent {
1488 projectile_type: ev.projectile_type.as_str().to_owned(),
1489 projectile_uuid: ev.projectile_uuid.as_str().to_owned(),
1490 shooter_uuid: ev.shooter_uuid.as_str().to_owned(),
1491 hit_type: ev.hit_type.as_str().to_owned(),
1492 hit_entity_uuid: ev.hit_entity_uuid.as_str().to_owned(),
1493 x: ev.x,
1494 y: ev.y,
1495 z: ev.z,
1496 dimension: ev.dimension.as_str().to_owned(),
1497 }
1498);
1499
1500unsafe extern "C" fn trampoline_server_fn<F>(ud: *mut c_void, srv: *const YogServer)
1501where
1502 F: Fn(&dyn Server) + Send + Sync,
1503{
1504 let f = &*(ud as *const F);
1505 f(&CServer(srv));
1506}
1507
1508unsafe extern "C" fn trampoline_packet<F>(
1509 ud: *mut c_void,
1510 srv: *const YogServer,
1511 ev: *const YogPacketEvent,
1512) where
1513 F: Fn(&PacketEvent, &dyn Server) + Send + Sync,
1514{
1515 let f = &*(ud as *const F);
1516 let ev = &*ev;
1517 let rust_ev = PacketEvent {
1518 channel: ev.channel.as_str().to_owned(),
1519 player: ev.player.as_str().to_owned(),
1520 payload: std::slice::from_raw_parts(ev.payload, ev.payload_len as usize).to_vec(),
1521 };
1522 f(&rust_ev, &CServer(srv));
1523}
1524
1525unsafe extern "C" fn trampoline_command<F>(
1526 ud: *mut c_void,
1527 srv: *const YogServer,
1528 ev: *const YogCommandEvent,
1529 reply_buf: *mut u8,
1530 reply_cap: u32,
1531 reply_len: *mut u32,
1532) where
1533 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync,
1534{
1535 let f = &*(ud as *const F);
1536 let ev = &*ev;
1537 let ctx = CommandContext {
1538 name: ev.name.as_str().to_owned(),
1539 args: ev.args.as_str().to_owned(),
1540 source: ev.source.as_str().to_owned(),
1541 uuid: ev.uuid.as_str().to_owned(),
1542 dimension: ev.dimension.as_str().to_owned(),
1543 };
1544 *reply_len = 0;
1545 if let Some(reply) = f(&ctx, &CServer(srv)) {
1546 let bytes = reply.as_bytes();
1547 let n = bytes.len().min(reply_cap as usize);
1548 std::ptr::copy_nonoverlapping(bytes.as_ptr(), reply_buf, n);
1549 *reply_len = n as u32;
1550 }
1551}
1552
1553unsafe extern "C" fn trampoline_scheduled<F>(ud: *mut c_void, srv: *const YogServer)
1554where
1555 F: Fn(&dyn Server) + Send + Sync,
1556{
1557 let f = &*(ud as *const F);
1558 f(&CServer(srv));
1559}
1560
1561unsafe extern "C" fn trampoline_client_tick<F>(ud: *mut c_void)
1564where
1565 F: Fn(&ClientTickEvent) + Send + Sync,
1566{
1567 let f = &*(ud as *const F);
1568 f(&ClientTickEvent {});
1569}
1570
1571unsafe extern "C" fn trampoline_hud_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1572where
1573 F: Fn(&GfxContext) + Send + Sync,
1574{
1575 let f = &*(ud as *const F);
1576 f(&GfxContext::from_raw(gfx));
1577}
1578
1579unsafe extern "C" fn trampoline_world_render<F>(ud: *mut c_void, gfx: *const YogGfxApi)
1580where
1581 F: Fn(&GfxContext) + Send + Sync,
1582{
1583 let f = &*(ud as *const F);
1584 f(&GfxContext::from_raw(gfx));
1585}
1586
1587unsafe extern "C" fn trampoline_key_press<F>(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool
1588where
1589 F: Fn(&KeyPressEvent) -> bool + Send + Sync,
1590{
1591 let f = &*(ud as *const F);
1592 let e = &*ev;
1593 f(&KeyPressEvent {
1594 key_code: e.key_code,
1595 scan_code: e.scan_code,
1596 action: e.action,
1597 modifiers: e.modifiers,
1598 })
1599}
1600
1601unsafe extern "C" fn trampoline_screen<F>(ud: *mut c_void, screen_class: YogStr) -> bool
1602where
1603 F: Fn(&ScreenEvent) + Send + Sync,
1604{
1605 let f = &*(ud as *const F);
1606 f(&ScreenEvent {
1607 screen_class: screen_class.as_str().to_owned(),
1608 });
1609 true
1610}
1611
1612static GLOBAL_API: std::sync::atomic::AtomicPtr<YogApi> =
1623 std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
1624
1625pub(crate) fn global_api() -> *mut YogApi {
1627 GLOBAL_API.load(std::sync::atomic::Ordering::Acquire)
1628}
1629
1630#[derive(Debug, Clone)]
1632pub struct ModInfo {
1633 pub source: String,
1635 pub id: String,
1636 pub name: String,
1637 pub version: String,
1638 pub authors: String,
1640 pub description: String,
1641}
1642
1643pub fn installed_mods() -> Vec<ModInfo> {
1649 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1650 if api.is_null() {
1651 return Vec::new();
1652 }
1653 let owned = unsafe { ((*api).mods_list)((*api).ctx) };
1654 if owned.is_none() {
1655 return Vec::new();
1656 }
1657 let text = unsafe {
1658 String::from_utf8(std::slice::from_raw_parts(owned.ptr, owned.len as usize).to_vec())
1659 .unwrap_or_default()
1660 };
1661 unsafe { ((*api).free_str)(owned.ptr, owned.len) };
1662 text.lines()
1663 .filter_map(|line| {
1664 let mut f = line.split('\t');
1665 Some(ModInfo {
1666 source: f.next()?.to_string(),
1667 id: f.next()?.to_string(),
1668 name: f.next().unwrap_or_default().to_string(),
1669 version: f.next().unwrap_or_default().to_string(),
1670 authors: f.next().unwrap_or_default().to_string(),
1671 description: f.next().unwrap_or_default().to_string(),
1672 })
1673 })
1674 .collect()
1675}
1676
1677pub fn open_ui(ui_id: &str, modal: bool, pause: bool) {
1681 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1682 if api.is_null() {
1683 return;
1684 }
1685 unsafe { ((*api).ui_open)((*api).ctx, YogStr::from_str(ui_id), modal, pause) }
1686}
1687
1688pub fn close_ui(ui_id: &str) {
1693 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1694 if api.is_null() {
1695 return;
1696 }
1697 unsafe { ((*api).ui_close)((*api).ctx, YogStr::from_str(ui_id)) }
1698}
1699
1700pub trait UiHandled {
1704 fn handled(self) -> bool;
1705}
1706
1707impl UiHandled for () {
1708 fn handled(self) -> bool {
1709 false
1710 }
1711}
1712
1713impl UiHandled for bool {
1714 fn handled(self) -> bool {
1715 self
1716 }
1717}
1718
1719pub fn server() -> Option<CServer> {
1724 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
1725 if api.is_null() {
1726 return None;
1727 }
1728 let srv = unsafe { (*api).server };
1729 if srv.is_null() {
1730 return None;
1731 }
1732 Some(CServer(srv))
1733}
1734
1735pub struct Registry {
1736 api: *const YogApi,
1737}
1738
1739unsafe impl Send for Registry {}
1741unsafe impl Sync for Registry {}
1742
1743impl Registry {
1744 pub unsafe fn from_raw(api: *const YogApi) -> Self {
1751 GLOBAL_API.store(api as *mut YogApi, std::sync::atomic::Ordering::Release);
1752 Self { api }
1753 }
1754
1755 #[inline]
1756 fn ctx(&self) -> *mut c_void {
1757 unsafe { (*self.api).ctx }
1758 }
1759
1760 fn leak<F: 'static>(f: F) -> *mut c_void {
1763 Box::into_raw(Box::new(f)) as *mut c_void
1764 }
1765
1766 pub fn on_block_break<F>(&mut self, handler: F)
1774 where
1775 F: Fn(&BlockBreakEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1776 {
1777 let ud = Self::leak(handler);
1778 unsafe { ((*self.api).on_block_break)(self.ctx(), ud, trampoline_block_break::<F>) }
1779 }
1780
1781 pub fn on_chat<F>(&mut self, handler: F)
1782 where
1783 F: Fn(&ChatEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1784 {
1785 let ud = Self::leak(handler);
1786 unsafe { ((*self.api).on_chat)(self.ctx(), ud, trampoline_chat::<F>) }
1787 }
1788
1789 pub fn on_player_join<F>(&mut self, handler: F)
1790 where
1791 F: Fn(&PlayerJoinEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1792 {
1793 let ud = Self::leak(handler);
1794 unsafe { ((*self.api).on_player_join)(self.ctx(), ud, trampoline_player_join::<F>) }
1795 }
1796
1797 pub fn on_player_leave<F>(&mut self, handler: F)
1798 where
1799 F: Fn(&PlayerLeaveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1800 {
1801 let ud = Self::leak(handler);
1802 unsafe { ((*self.api).on_player_leave)(self.ctx(), ud, trampoline_player_leave::<F>) }
1803 }
1804
1805 pub fn on_use_item<F>(&mut self, handler: F)
1806 where
1807 F: Fn(&UseItemEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1808 {
1809 let ud = Self::leak(handler);
1810 unsafe { ((*self.api).on_use_item)(self.ctx(), ud, trampoline_use_item::<F>) }
1811 }
1812
1813 pub fn on_use_block<F>(&mut self, handler: F)
1814 where
1815 F: Fn(&UseBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1816 {
1817 let ud = Self::leak(handler);
1818 unsafe { ((*self.api).on_use_block)(self.ctx(), ud, trampoline_use_block::<F>) }
1819 }
1820
1821 pub fn on_attack_entity<F>(&mut self, handler: F)
1822 where
1823 F: Fn(&AttackEntityEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1824 {
1825 let ud = Self::leak(handler);
1826 unsafe { ((*self.api).on_attack_entity)(self.ctx(), ud, trampoline_attack_entity::<F>) }
1827 }
1828
1829 pub fn on_entity_damage<F>(&mut self, handler: F)
1830 where
1831 F: Fn(&EntityDamageEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1832 {
1833 let ud = Self::leak(handler);
1834 unsafe { ((*self.api).on_entity_damage)(self.ctx(), ud, trampoline_entity_damage::<F>) }
1835 }
1836
1837 pub fn on_entity_death<F>(&mut self, handler: F)
1838 where
1839 F: Fn(&EntityDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1840 {
1841 let ud = Self::leak(handler);
1842 unsafe { ((*self.api).on_entity_death)(self.ctx(), ud, trampoline_entity_death::<F>) }
1843 }
1844
1845 pub fn on_entity_spawn<F>(&mut self, handler: F)
1846 where
1847 F: Fn(&EntitySpawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1848 {
1849 let ud = Self::leak(handler);
1850 unsafe { ((*self.api).on_entity_spawn)(self.ctx(), ud, trampoline_entity_spawn::<F>) }
1851 }
1852
1853 pub fn on_player_place_block<F>(&mut self, handler: F)
1854 where
1855 F: Fn(&PlaceBlockEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1856 {
1857 let ud = Self::leak(handler);
1858 unsafe { ((*self.api).on_player_place_block)(self.ctx(), ud, trampoline_place_block::<F>) }
1859 }
1860
1861 pub fn on_player_death<F>(&mut self, handler: F)
1862 where
1863 F: Fn(&PlayerDeathEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1864 {
1865 let ud = Self::leak(handler);
1866 unsafe { ((*self.api).on_player_death)(self.ctx(), ud, trampoline_player_death::<F>) }
1867 }
1868
1869 pub fn on_player_respawn<F>(&mut self, handler: F)
1870 where
1871 F: Fn(&PlayerRespawnEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1872 {
1873 let ud = Self::leak(handler);
1874 unsafe { ((*self.api).on_player_respawn)(self.ctx(), ud, trampoline_player_respawn::<F>) }
1875 }
1876
1877 pub fn on_advancement<F>(&mut self, handler: F)
1878 where
1879 F: Fn(&AdvancementEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1880 {
1881 let ud = Self::leak(handler);
1882 unsafe { ((*self.api).on_advancement)(self.ctx(), ud, trampoline_advancement::<F>) }
1883 }
1884
1885 pub fn on_entity_interact<F>(&mut self, handler: F)
1886 where
1887 F: Fn(&EntityInteractEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1888 {
1889 let ud = Self::leak(handler);
1890 unsafe { ((*self.api).on_entity_interact)(self.ctx(), ud, trampoline_entity_interact::<F>) }
1891 }
1892
1893 pub fn on_item_craft<F>(&mut self, handler: F)
1894 where
1895 F: Fn(&CraftEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1896 {
1897 let ud = Self::leak(handler);
1898 unsafe { ((*self.api).on_item_craft)(self.ctx(), ud, trampoline_craft::<F>) }
1899 }
1900
1901 pub fn on_explosion<F>(&mut self, handler: F)
1902 where
1903 F: Fn(&ExplosionEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1904 {
1905 let ud = Self::leak(handler);
1906 unsafe { ((*self.api).on_explosion)(self.ctx(), ud, trampoline_explosion::<F>) }
1907 }
1908
1909 pub fn on_item_pickup<F>(&mut self, handler: F)
1910 where
1911 F: Fn(&ItemPickupEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1912 {
1913 let ud = Self::leak(handler);
1914 unsafe { ((*self.api).on_item_pickup)(self.ctx(), ud, trampoline_item_pickup::<F>) }
1915 }
1916
1917 pub fn on_player_move<F>(&mut self, handler: F)
1918 where
1919 F: Fn(&PlayerMoveEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1920 {
1921 let ud = Self::leak(handler);
1922 unsafe { ((*self.api).on_player_move)(self.ctx(), ud, trampoline_player_move::<F>) }
1923 }
1924
1925 pub fn on_container_open<F>(&mut self, handler: F)
1926 where
1927 F: Fn(&ContainerOpenEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1928 {
1929 let ud = Self::leak(handler);
1930 unsafe { ((*self.api).on_container_open)(self.ctx(), ud, trampoline_container_open::<F>) }
1931 }
1932
1933 pub fn on_container_close<F>(&mut self, handler: F)
1934 where
1935 F: Fn(&ContainerCloseEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1936 {
1937 let ud = Self::leak(handler);
1938 unsafe { ((*self.api).on_container_close)(self.ctx(), ud, trampoline_container_close::<F>) }
1939 }
1940
1941 pub fn on_projectile_hit<F>(&mut self, handler: F)
1942 where
1943 F: Fn(&ProjectileHitEvent, EventPhase, &dyn Server) -> bool + Send + Sync + 'static,
1944 {
1945 let ud = Self::leak(handler);
1946 unsafe { ((*self.api).on_projectile_hit)(self.ctx(), ud, trampoline_projectile_hit::<F>) }
1947 }
1948
1949 pub fn on_tick<F>(&mut self, listener: F)
1950 where
1951 F: Fn(&dyn Server) + Send + Sync + 'static,
1952 {
1953 let ud = Self::leak(listener);
1954 unsafe { ((*self.api).on_server_tick)(self.ctx(), ud, trampoline_server_fn::<F>) }
1955 }
1956
1957 pub fn on_server_started<F>(&mut self, listener: F)
1958 where
1959 F: Fn(&dyn Server) + Send + Sync + 'static,
1960 {
1961 let ud = Self::leak(listener);
1962 unsafe { ((*self.api).on_server_started)(self.ctx(), ud, trampoline_server_fn::<F>) }
1963 }
1964
1965 pub fn on_server_stopping<F>(&mut self, listener: F)
1966 where
1967 F: Fn(&dyn Server) + Send + Sync + 'static,
1968 {
1969 let ud = Self::leak(listener);
1970 unsafe { ((*self.api).on_server_stopping)(self.ctx(), ud, trampoline_server_fn::<F>) }
1971 }
1972
1973 pub fn on_command<F>(&mut self, name: impl AsRef<str>, handler: F)
1976 where
1977 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1978 {
1979 let name_ys = YogStr::from_str(name.as_ref());
1980 let ud = Self::leak(handler);
1981 unsafe { ((*self.api).register_command)(self.ctx(), name_ys, ud, trampoline_command::<F>) }
1982 }
1983
1984 pub fn on_typed_command<F>(
1991 &mut self,
1992 name: impl AsRef<str>,
1993 schema: impl AsRef<str>,
1994 handler: F,
1995 ) where
1996 F: Fn(&CommandContext, &dyn Server) -> Option<String> + Send + Sync + 'static,
1997 {
1998 let name_ys = YogStr::from_str(name.as_ref());
1999 let schema_ys = YogStr::from_str(schema.as_ref());
2000 let ud = Self::leak(handler);
2001 unsafe {
2002 ((*self.api).register_typed_command)(
2003 self.ctx(),
2004 name_ys,
2005 schema_ys,
2006 ud,
2007 trampoline_command::<F>,
2008 )
2009 }
2010 }
2011
2012 pub fn on_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
2015 where
2016 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
2017 {
2018 let ch = YogStr::from_str(channel.as_ref());
2019 let ud = Self::leak(handler);
2020 unsafe { ((*self.api).on_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
2021 }
2022
2023 pub fn on_client_packet<F>(&mut self, channel: impl AsRef<str>, handler: F)
2024 where
2025 F: Fn(&PacketEvent, &dyn Server) + Send + Sync + 'static,
2026 {
2027 let ch = YogStr::from_str(channel.as_ref());
2028 let ud = Self::leak(handler);
2029 unsafe { ((*self.api).on_client_packet)(self.ctx(), ch, ud, trampoline_packet::<F>) }
2030 }
2031
2032 pub fn on_typed_packet<P, F>(&mut self, channel: impl AsRef<str>, handler: F)
2037 where
2038 P: Packet + Send + Sync + 'static,
2039 F: Fn(&P, &dyn Server) + Send + Sync + 'static,
2040 {
2041 self.on_packet(channel, move |ev, srv| {
2042 if let Some(pkt) = P::decode(&ev.payload) {
2043 handler(&pkt, srv);
2044 }
2045 });
2046 }
2047
2048 fn recipe(&mut self, ns: &str, name: &str, json: &str) {
2051 unsafe {
2052 ((*self.api).register_recipe_json)(
2053 self.ctx(),
2054 YogStr::from_str(ns),
2055 YogStr::from_str(name),
2056 YogStr::from_str(json),
2057 )
2058 }
2059 }
2060
2061 pub fn add_shaped_recipe(&mut self, recipe: ShapedRecipe) {
2063 let json = recipe.to_json();
2064 let (ns, name) = recipe.ns_name();
2065 self.recipe(ns, name, &json);
2066 }
2067
2068 pub fn add_shapeless_recipe(&mut self, recipe: ShapelessRecipe) {
2070 let json = recipe.to_json();
2071 let (ns, name) = recipe.ns_name();
2072 self.recipe(ns, name, &json);
2073 }
2074
2075 pub fn add_furnace_recipe(&mut self, recipe: FurnaceRecipe) {
2077 let json = recipe.to_json();
2078 let (ns, name) = recipe.ns_name();
2079 self.recipe(ns, name, &json);
2080 }
2081
2082 pub fn register_item(&mut self, def: ItemDef) {
2085 let food_nutrition = def.food.as_ref().map_or(0, |f| f.nutrition);
2089 let food_saturation = def.food.as_ref().map_or(0.0, |f| f.saturation);
2090 let food_always_eat = def.food.as_ref().is_some_and(|f| f.can_always_eat);
2091 let c = YogItemDef {
2092 id: YogStr::from_str(&def.id),
2093 max_stack: def.max_stack as u32,
2094 name: def
2095 .name
2096 .as_deref()
2097 .map(YogStr::from_str)
2098 .unwrap_or(YogStr::EMPTY),
2099 tooltip: def
2100 .tooltip
2101 .as_deref()
2102 .map(YogStr::from_str)
2103 .unwrap_or(YogStr::EMPTY),
2104 max_damage: def.max_damage,
2105 fire_resistant: def.fire_resistant,
2106 fuel_ticks: def.fuel_ticks,
2107 food_nutrition,
2108 food_saturation,
2109 food_always_eat,
2110 };
2111 unsafe { ((*self.api).register_item)(self.ctx(), &c) }
2112 }
2113
2114 pub fn register_block(&mut self, def: BlockDef) {
2115 let shape = def.shape.unwrap_or([0.0; 6]);
2116 let groups_joined = def.connect_groups.join(",");
2117 let c = YogBlockDef {
2118 id: YogStr::from_str(&def.id),
2119 hardness: def.hardness,
2120 resistance: def.resistance,
2121 name: def
2122 .name
2123 .as_deref()
2124 .map(YogStr::from_str)
2125 .unwrap_or(YogStr::EMPTY),
2126 light_level: def.light_level,
2127 sound: def
2128 .sound
2129 .as_deref()
2130 .map(YogStr::from_str)
2131 .unwrap_or(YogStr::EMPTY),
2132 requires_tool: def.requires_tool,
2133 no_collision: def.no_collision,
2134 slipperiness: def.slipperiness,
2135 shape,
2136 connects: def.connects,
2137 connect_groups: YogStr::from_str(&groups_joined),
2138 inventory_id: def
2139 .inventory_id
2140 .as_deref()
2141 .map(YogStr::from_str)
2142 .unwrap_or(YogStr::EMPTY),
2143 };
2144 unsafe { ((*self.api).register_block)(self.ctx(), &c) }
2145 }
2146
2147 pub fn register_inventory(&mut self, def: yog_inventory::InventoryDef) {
2151 let layout = yog_inventory::encode_layout(&def.resolved_layout());
2152 let c = YogInventoryDef {
2153 id: YogStr::from_str(&def.id),
2154 slot_count: def.slot_count as u32,
2155 layout: YogStr::from_str(&layout),
2156 include_player_inventory: def.include_player_inventory,
2157 player_inv_x: def.player_inv_offset.0,
2158 player_inv_y: def.player_inv_offset.1,
2159 background_texture: def
2160 .background_texture
2161 .as_deref()
2162 .map(YogStr::from_str)
2163 .unwrap_or(YogStr::EMPTY),
2164 title: YogStr::from_str(&def.title),
2165 background_w: def.background_size.map(|(w, _)| w).unwrap_or(0.0),
2166 background_h: def.background_size.map(|(_, h)| h).unwrap_or(0.0),
2167 };
2168 unsafe { ((*self.api).register_inventory)(self.ctx(), &c) }
2169 }
2170
2171 pub fn register_startup_grant(&mut self, grant: StartupGrant) {
2175 let items_str = grant.items.join("|");
2176 let c = YogStartupGrantDef {
2177 id: YogStr::from_str(&grant.id),
2178 items: YogStr::from_str(&items_str),
2179 book: grant
2180 .book
2181 .as_deref()
2182 .map(YogStr::from_str)
2183 .unwrap_or(YogStr::EMPTY),
2184 command: grant
2185 .command
2186 .as_deref()
2187 .map(YogStr::from_str)
2188 .unwrap_or(YogStr::EMPTY),
2189 };
2190 unsafe { ((*self.api).register_startup_grant)(self.ctx(), &c) }
2191 }
2192
2193 pub fn register_book(&mut self, book: &Book) {
2195 let json = book.to_json();
2196 let id = YogStr::from_str(&book.id);
2197 let j = YogStr::from_str(&json);
2198 unsafe { ((*self.api).register_book)(self.ctx(), id, j) }
2199 }
2200
2201 pub fn register_dimension(&mut self, def: &YogDimensionDef) {
2216 let json = def.to_json();
2217 let id = YogStr::from_str(&def.id);
2218 let j = YogStr::from_str(&json);
2219 unsafe { ((*self.api).register_dimension_json)(self.ctx(), id, j) }
2220 }
2221
2222 pub fn register_chunk_generator<F>(&mut self, generator_type_id: &str, generator: F)
2234 where
2235 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2236 {
2237 let ud = Self::leak(generator);
2238 let id = YogStr::from_str(generator_type_id);
2239 unsafe {
2240 ((*self.api).register_chunk_generator)(
2241 self.ctx(),
2242 id,
2243 ud,
2244 trampoline_chunk_generator::<F>,
2245 )
2246 }
2247 }
2248
2249 pub fn installed_mods(&self) -> Vec<ModInfo> {
2256 installed_mods()
2257 }
2258
2259 pub fn interop(&self) -> crate::Interop {
2264 crate::Interop::new(self.api)
2265 }
2266
2267 pub fn raw_api(&self) -> *const yog_abi::YogApi {
2270 self.api
2271 }
2272
2273 pub fn register_menu_entry(&mut self, label: &str, ui_id: &str) {
2274 let l = YogStr::from_str(label);
2275 let u = YogStr::from_str(ui_id);
2276 unsafe { ((*self.api).register_menu_entry)(self.ctx(), l, u) }
2277 }
2278
2279 pub fn register_ui<F, R>(&mut self, ui_id: &str, handler: F)
2288 where
2289 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2290 R: UiHandled,
2291 {
2292 let ud = Self::leak(handler);
2293 let id = YogStr::from_str(ui_id);
2294 let empty = YogStr::from_str("{}");
2295 unsafe { ((*self.api).register_ui)(self.ctx(), id, empty, ud, trampoline_ui_event::<F, R>) }
2296 }
2297
2298 pub fn schedule_once<F>(&self, delay_ticks: u64, handler: F)
2301 where
2302 F: Fn(&dyn Server) + Send + Sync + 'static,
2303 {
2304 let ud = Self::leak(handler);
2305 unsafe {
2306 ((*self.api).schedule_once)(self.ctx(), delay_ticks, ud, trampoline_scheduled::<F>)
2307 }
2308 }
2309
2310 pub fn schedule_repeating<F>(&self, period_ticks: u64, handler: F)
2311 where
2312 F: Fn(&dyn Server) + Send + Sync + 'static,
2313 {
2314 let ud = Self::leak(handler);
2315 unsafe {
2316 ((*self.api).schedule_repeating)(
2317 self.ctx(),
2318 period_ticks,
2319 ud,
2320 trampoline_scheduled::<F>,
2321 )
2322 }
2323 }
2324
2325 pub fn on_client_tick<F>(&mut self, handler: F)
2329 where
2330 F: Fn(&ClientTickEvent) + Send + Sync + 'static,
2331 {
2332 let ud = Self::leak(handler);
2333 unsafe { ((*self.api).on_client_tick)(self.ctx(), ud, trampoline_client_tick::<F>) }
2334 }
2335
2336 pub fn on_ui_render<F>(&mut self, ui_id: &str, handler: F)
2345 where
2346 F: Fn(&GfxContext) + Send + Sync + 'static,
2347 {
2348 let ud = Self::leak(handler);
2349 let id = YogStr::from_str(ui_id);
2350 unsafe { ((*self.api).on_ui_render)(self.ctx(), id, ud, trampoline_hud_render::<F>) }
2351 }
2352
2353 pub fn on_hud_render<F>(&mut self, handler: F)
2358 where
2359 F: Fn(&GfxContext) + Send + Sync + 'static,
2360 {
2361 let ud = Self::leak(handler);
2362 unsafe { ((*self.api).on_hud_render)(self.ctx(), ud, trampoline_hud_render::<F>) }
2363 }
2364
2365 pub fn on_world_render<F>(&mut self, handler: F)
2371 where
2372 F: Fn(&GfxContext) + Send + Sync + 'static,
2373 {
2374 let ud = Self::leak(handler);
2375 unsafe { ((*self.api).on_world_render)(self.ctx(), ud, trampoline_world_render::<F>) }
2376 }
2377
2378 pub fn on_key_press<F>(&mut self, handler: F)
2381 where
2382 F: Fn(&KeyPressEvent) -> bool + Send + Sync + 'static,
2383 {
2384 let ud = Self::leak(handler);
2385 unsafe { ((*self.api).on_key_press)(self.ctx(), ud, trampoline_key_press::<F>) }
2386 }
2387
2388 pub fn on_screen_open<F>(&mut self, handler: F)
2390 where
2391 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2392 {
2393 let ud = Self::leak(handler);
2394 unsafe { ((*self.api).on_screen_open)(self.ctx(), ud, trampoline_screen::<F>) }
2395 }
2396
2397 pub fn on_screen_close<F>(&mut self, handler: F)
2399 where
2400 F: Fn(&ScreenEvent) + Send + Sync + 'static,
2401 {
2402 let ud = Self::leak(handler);
2403 unsafe { ((*self.api).on_screen_close)(self.ctx(), ud, trampoline_screen::<F>) }
2404 }
2405}
2406
2407unsafe extern "C" fn trampoline_ui_event<F, R>(
2408 ud: *mut c_void,
2409 ui_id: yog_abi::YogStr,
2410 event_id: yog_abi::YogStr,
2411) where
2412 F: Fn(&str, &str) -> R + Send + Sync + 'static,
2413 R: UiHandled,
2414{
2415 let f = &*(ud as *const F);
2416 let ui = unsafe { ui_id.as_str() };
2417 let ev = unsafe { event_id.as_str() };
2418 if f(ui, ev).handled() {
2419 let api = GLOBAL_API.load(std::sync::atomic::Ordering::Acquire);
2420 if !api.is_null() {
2421 unsafe { ((*api).ui_consume_event)((*api).ctx) }
2422 }
2423 }
2424}
2425
2426unsafe extern "C" fn trampoline_chunk_generator<F>(
2427 ud: *mut c_void,
2428 writer: *const yog_abi::YogChunkWriterApi,
2429) where
2430 F: Fn(&ChunkWriter) + Send + Sync + 'static,
2431{
2432 let f = &*(ud as *const F);
2433 let writer = unsafe { ChunkWriter::from_raw(writer) };
2434 f(&writer);
2435}
2436
2437pub trait Mod {
2441 fn register(registry: &mut Registry);
2442}