1use std::collections::HashMap;
16use std::num::NonZeroU32;
17use std::os::raw::c_void;
18use std::path::{Path, PathBuf};
19use std::sync::{Arc, Mutex, OnceLock, RwLock};
20
21use glow::HasContext;
22use jcall::JArg;
23use jni::objects::{JByteArray, JClass, JFloatArray, JString};
24use jni::sys::{jdouble, jfloat, jint, jstring};
25use jni::{JNIEnv, JavaVM};
26use libloading::Symbol;
27use yog_hot_reload::{HotReloader, LoadedModule, ModuleGeneration, ModuleRegistry};
28
29use yog_abi::{
30 ABI_VERSION, YogAdvancementEvent, YogAdvancementFn, YogApi, YogAttackEntityFn, YogBlockBreakFn,
31 YogBlockPos, YogChatFn, YogClientFn, YogCommandFn, YogContainerCloseEvent,
32 YogContainerCloseFn, YogContainerOpenEvent, YogContainerOpenFn, YogCraftEvent, YogCraftFn,
33 YogEntityDamageFn, YogEntityDeathFn, YogEntityInteractEvent, YogEntityInteractFn,
34 YogEntitySpawnFn, YogExplosionEvent, YogExplosionFn, YogGfxApi, YogHudRenderFn, YogItemInsertEvent, YogItemInsertFn, YogItemPickupEvent, YogItemPickupFn, YogKeyPressEvent, YogMachineCraftEvent, YogMachineCraftFn, YogKeyPressFn, YogOwnedStr, YogPacketFn,
35 YogPlaceBlockEvent, YogPlaceBlockFn, YogPlayerDeathEvent, YogPlayerDeathFn, YogPlayerFn,
36 YogPlayerMoveEvent, YogPlayerMoveFn, YogPlayerRespawnEvent, YogPlayerRespawnFn,
37 YogProjectileHitEvent, YogProjectileHitFn, YogScheduledFn, YogScreenFn, YogServer, YogServerFn,
38 YogStr, YogUseBlockFn, YogUseItemFn, YogVec3, YogWorldRenderFn,
39};
40use yog_registry::{BlockDef, ItemDef};
41
42static JAVA_VM: OnceLock<JavaVM> = OnceLock::new();
46static HOT_RELOADER: OnceLock<HotReloader<RuntimeModuleRegistry>> = OnceLock::new();
51static CURRENT_REGISTRATION: Mutex<Option<LoadedModule>> = Mutex::new(None);
59static MOD_INFOS: Mutex<Vec<[String; 7]>> = Mutex::new(Vec::new());
63static INTEROP_SYMBOLS: OnceLock<Mutex<HashMap<String, HashMap<String, usize>>>> = OnceLock::new();
66static SERVER: OnceLock<YogServer> = OnceLock::new();
68static API_TABLE: OnceLock<YogApi> = OnceLock::new();
76static HANDLERS: OnceLock<RwLock<RuntimeHandlers>> = OnceLock::new();
84
85struct GlCtx(glow::Context);
88unsafe impl Send for GlCtx {}
89unsafe impl Sync for GlCtx {}
90
91static GL: OnceLock<GlCtx> = OnceLock::new();
93
94static GL_GET_PROGRAM_BINARY: OnceLock<Option<usize>> = OnceLock::new();
98static GL_PROGRAM_BINARY: OnceLock<Option<usize>> = OnceLock::new();
99static GL_GET_PROGRAM_IV: OnceLock<Option<usize>> = OnceLock::new();
100
101#[derive(Debug, Clone)]
104pub struct UiLayer {
105 pub id: String,
106 pub parent: Option<String>,
107 pub modal: bool,
108 pub pause_game: bool,
109 pub visible: bool,
110 pub enabled: bool,
111 pub z_index: i32,
112}
113
114type TooltipEntry = (LoadedModule, *mut c_void, yog_abi::YogTooltipSizeFn, yog_abi::YogTooltipDrawFn);
118type ItemTextEntry = (LoadedModule, *mut c_void, yog_abi::YogItemTextFn);
119type CommandEntry = (LoadedModule, Option<String>, *mut c_void, YogCommandFn);
120
121#[derive(Default)]
132struct RuntimeHandlers {
133 block_break: Vec<(LoadedModule, *mut c_void, YogBlockBreakFn)>,
134 chat: Vec<(LoadedModule, *mut c_void, YogChatFn)>,
135 player_join: Vec<(LoadedModule, *mut c_void, YogPlayerFn)>,
136 player_leave: Vec<(LoadedModule, *mut c_void, YogPlayerFn)>,
137 use_item: Vec<(LoadedModule, *mut c_void, YogUseItemFn)>,
138 use_block: Vec<(LoadedModule, *mut c_void, YogUseBlockFn)>,
139 attack_entity: Vec<(LoadedModule, *mut c_void, YogAttackEntityFn)>,
140 entity_damage: Vec<(LoadedModule, *mut c_void, YogEntityDamageFn)>,
141 entity_death: Vec<(LoadedModule, *mut c_void, YogEntityDeathFn)>,
142 entity_spawn: Vec<(LoadedModule, *mut c_void, YogEntitySpawnFn)>,
143 player_place_block: Vec<(LoadedModule, *mut c_void, YogPlaceBlockFn)>,
144 player_death: Vec<(LoadedModule, *mut c_void, YogPlayerDeathFn)>,
145 player_respawn: Vec<(LoadedModule, *mut c_void, YogPlayerRespawnFn)>,
146 advancement: Vec<(LoadedModule, *mut c_void, YogAdvancementFn)>,
147 entity_interact: Vec<(LoadedModule, *mut c_void, YogEntityInteractFn)>,
148 item_craft: Vec<(LoadedModule, *mut c_void, YogCraftFn)>,
149 machine_craft: Vec<(LoadedModule, *mut c_void, YogMachineCraftFn)>,
150 explosion: Vec<(LoadedModule, *mut c_void, YogExplosionFn)>,
151 item_pickup: Vec<(LoadedModule, *mut c_void, YogItemPickupFn)>,
152 item_insert: Vec<(LoadedModule, *mut c_void, YogItemInsertFn)>,
153 player_move: Vec<(LoadedModule, *mut c_void, YogPlayerMoveFn)>,
154 container_open: Vec<(LoadedModule, *mut c_void, YogContainerOpenFn)>,
155 container_close: Vec<(LoadedModule, *mut c_void, YogContainerCloseFn)>,
156 projectile_hit: Vec<(LoadedModule, *mut c_void, YogProjectileHitFn)>,
157 client_tick: Vec<(LoadedModule, *mut c_void, YogClientFn)>,
158 hud_render: Vec<(LoadedModule, *mut c_void, YogHudRenderFn)>,
159 body: Vec<(LoadedModule, *mut c_void, yog_abi::YogBodyFn)>,
160 camera: Vec<(LoadedModule, *mut c_void, yog_abi::YogCameraFn)>,
161 travel: Vec<(LoadedModule, *mut c_void, yog_abi::YogTravelFn)>,
162 loot: Vec<(LoadedModule, *mut c_void, yog_abi::YogLootFn)>,
163 world_render: Vec<(LoadedModule, *mut c_void, YogWorldRenderFn)>,
164 key_press: Vec<(LoadedModule, *mut c_void, YogKeyPressFn)>,
165 screen_open: Vec<(LoadedModule, *mut c_void, YogScreenFn)>,
166 screen_close: Vec<(LoadedModule, *mut c_void, YogScreenFn)>,
167 server_tick: Vec<(LoadedModule, *mut c_void, YogServerFn)>,
168 server_started: Vec<(LoadedModule, *mut c_void, YogServerFn)>,
169 server_stopping: Vec<(LoadedModule, *mut c_void, YogServerFn)>,
170 commands: HashMap<String, Vec<CommandEntry>>,
180 recipes: Vec<(String, String, yog_data::Value)>,
182 packets: HashMap<String, (LoadedModule, *mut c_void, YogPacketFn)>,
183 client_packets: HashMap<String, (LoadedModule, *mut c_void, YogPacketFn)>,
184 items: Vec<ItemDef>,
185 entities: Vec<yog_data::Value>,
187 attributes: Vec<yog_data::Value>,
189 effects: Vec<yog_data::Value>,
190 potions: Vec<yog_data::Value>,
191 entity_ticks: HashMap<String, Vec<(LoadedModule, *mut c_void, yog_abi::YogEntityTickFn)>>,
193 blocks: Vec<BlockDef>,
194 content_blob: Mutex<Option<Arc<Vec<u8>>>>,
197 sound_groups: HashMap<String, yog_registry::SoundGroup>,
199 tool_materials: HashMap<String, yog_registry::ToolMaterial>,
201 biome_features: Vec<yog_registry::BiomeFeature>,
203 armor_materials: HashMap<String, yog_registry::ArmorMaterial>,
204 inventories: Vec<yog_inventory::InventoryDef>,
205 books: std::collections::HashSet<String>,
207 book_renderers: Mutex<HashMap<String, yog_book::BookRenderer>>,
208 dimensions: HashMap<String, yog_data::Value>,
212 chunk_generators: HashMap<String, (LoadedModule, *mut c_void, yog_abi::YogChunkGeneratorFn)>,
213 terrains: HashMap<String, Arc<yog_worldgen::Generator>>,
216 ui_handlers: HashMap<String, (LoadedModule, *mut c_void, yog_abi::YogUIEventFn)>,
217 ui_render_handlers: HashMap<String, Vec<(LoadedModule, *mut c_void, YogHudRenderFn)>>,
218 page_drawers: HashMap<String, (LoadedModule, *mut c_void, yog_abi::YogPageDrawFn)>,
220 item_tooltips: HashMap<String, Vec<TooltipEntry>>,
222 item_texts: HashMap<String, Vec<ItemTextEntry>>,
224 menu_entries: Vec<(String, String)>, pub active_uis: Mutex<Vec<UiLayer>>,
226 startup_grants: Vec<yog_registry::StartupGrant>,
227 pending_grants: Mutex<Vec<(String, String, u32)>>,
231 scheduler: Mutex<SchedulerState>,
232}
233
234unsafe impl Send for RuntimeHandlers {}
236unsafe impl Sync for RuntimeHandlers {}
237
238#[derive(Default)]
239struct SchedulerState {
240 once_tasks: Vec<OnceTask>,
241 repeating_tasks: Vec<RepeatingTask>,
242}
243
244impl SchedulerState {
245 fn due(&mut self) -> Vec<(LoadedModule, *mut c_void, YogScheduledFn)> {
250 let mut out = Vec::new();
251 self.once_tasks.retain_mut(|t| {
252 if t.delay_remaining <= 1 {
253 out.push((t.module.clone(), t.ud, t.f));
254 false
255 } else {
256 t.delay_remaining -= 1;
257 true
258 }
259 });
260 for t in &mut self.repeating_tasks {
261 t.ticks_left = t.ticks_left.saturating_sub(1);
262 if t.ticks_left == 0 {
263 out.push((t.module.clone(), t.ud, t.f));
264 t.ticks_left = t.period.max(1);
265 }
266 }
267 out
268 }
269}
270
271struct OnceTask {
272 module: LoadedModule,
273 delay_remaining: u64,
274 ud: *mut c_void,
275 f: YogScheduledFn,
276}
277struct RepeatingTask {
278 module: LoadedModule,
279 period: u64,
280 ticks_left: u64,
281 ud: *mut c_void,
282 f: YogScheduledFn,
283}
284
285unsafe impl Send for SchedulerState {}
286unsafe impl Sync for SchedulerState {}
287unsafe impl Send for OnceTask {}
288unsafe impl Send for RepeatingTask {}
289
290type Entry<F> = (LoadedModule, *mut c_void, F);
292
293fn each_while<F: Copy>(entries: &[Entry<F>], mut call: impl FnMut(*mut c_void, F) -> bool) -> bool {
296 for (module, ud, f) in entries {
297 if module.is_retiring() {
298 continue;
299 }
300 let Some(_call) = module.enter() else {
301 continue;
302 };
303 if !call(*ud, *f) {
304 return false;
305 }
306 }
307 true
308}
309
310fn each<F: Copy, R>(entries: &[Entry<F>], mut call: impl FnMut(*mut c_void, F) -> R) {
313 each_while(entries, |ud, f| {
314 call(ud, f);
315 true
316 });
317}
318
319fn allowed<F: Copy>(entries: &[Entry<F>], call: impl FnMut(*mut c_void, F) -> bool) -> bool {
322 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| each_while(entries, call)))
323 .unwrap_or(true)
324}
325
326fn handlers() -> std::sync::RwLockReadGuard<'static, RuntimeHandlers> {
327 HANDLERS
328 .get()
329 .expect("yog: nativeInit not called yet")
330 .read()
331 .expect("handlers lock poisoned")
332}
333
334fn current_registration() -> LoadedModule {
335 CURRENT_REGISTRATION
336 .lock()
337 .unwrap()
338 .clone()
339 .expect("a yog-runtime api_* function was called outside of yog_mod_register")
340}
341
342fn hot_reloader() -> &'static HotReloader<RuntimeModuleRegistry> {
343 HOT_RELOADER.get().expect("yog: nativeInit not called yet")
344}
345
346macro_rules! for_each_handler_list {
355 ($h:expr_2021, |$list:ident| $body:expr_2021) => {{
356 {
357 let $list = &mut $h.block_break;
358 $body;
359 }
360 {
361 let $list = &mut $h.chat;
362 $body;
363 }
364 {
365 let $list = &mut $h.player_join;
366 $body;
367 }
368 {
369 let $list = &mut $h.player_leave;
370 $body;
371 }
372 {
373 let $list = &mut $h.use_item;
374 $body;
375 }
376 {
377 let $list = &mut $h.use_block;
378 $body;
379 }
380 {
381 let $list = &mut $h.attack_entity;
382 $body;
383 }
384 {
385 let $list = &mut $h.entity_damage;
386 $body;
387 }
388 {
389 let $list = &mut $h.entity_death;
390 $body;
391 }
392 {
393 let $list = &mut $h.entity_spawn;
394 $body;
395 }
396 {
397 let $list = &mut $h.player_place_block;
398 $body;
399 }
400 {
401 let $list = &mut $h.player_death;
402 $body;
403 }
404 {
405 let $list = &mut $h.player_respawn;
406 $body;
407 }
408 {
409 let $list = &mut $h.advancement;
410 $body;
411 }
412 {
413 let $list = &mut $h.entity_interact;
414 $body;
415 }
416 {
417 let $list = &mut $h.item_craft;
418 $body;
419 }
420 {
421 let $list = &mut $h.machine_craft;
422 $body;
423 }
424 {
425 let $list = &mut $h.explosion;
426 $body;
427 }
428 {
429 let $list = &mut $h.item_pickup;
430 $body;
431 }
432 {
433 let $list = &mut $h.item_insert;
434 $body;
435 }
436 {
437 let $list = &mut $h.player_move;
438 $body;
439 }
440 {
441 let $list = &mut $h.container_open;
442 $body;
443 }
444 {
445 let $list = &mut $h.container_close;
446 $body;
447 }
448 {
449 let $list = &mut $h.projectile_hit;
450 $body;
451 }
452 {
453 let $list = &mut $h.client_tick;
454 $body;
455 }
456 {
457 let $list = &mut $h.hud_render;
458 $body;
459 }
460 {
461 let $list = &mut $h.body;
462 $body;
463 }
464 {
465 let $list = &mut $h.camera;
466 $body;
467 }
468 {
469 let $list = &mut $h.travel;
470 $body;
471 }
472 {
473 let $list = &mut $h.loot;
474 $body;
475 }
476 {
477 let $list = &mut $h.world_render;
478 $body;
479 }
480 {
481 let $list = &mut $h.key_press;
482 $body;
483 }
484 {
485 let $list = &mut $h.screen_open;
486 $body;
487 }
488 {
489 let $list = &mut $h.screen_close;
490 $body;
491 }
492 {
493 let $list = &mut $h.server_tick;
494 $body;
495 }
496 {
497 let $list = &mut $h.server_started;
498 $body;
499 }
500 {
501 let $list = &mut $h.server_stopping;
502 $body;
503 }
504 }};
505}
506
507struct RuntimeModuleRegistry;
508
509impl ModuleRegistry for RuntimeModuleRegistry {
510 fn retarget(&self, _mod_id: &str, _from: ModuleGeneration, _to: ModuleGeneration) {}
511
512 fn purge(&self, generation: ModuleGeneration) {
513 let Some(lock) = HANDLERS.get() else { return };
514 let mut h = lock.write().expect("handlers lock poisoned");
515 let h = &mut *h;
516 for_each_handler_list!(h, |list| list
517 .retain(|(m, ..)| m.generation() != generation));
518 for entries in h.commands.values_mut() {
519 entries.retain(|(m, ..)| m.generation() != generation);
520 }
521 h.packets.retain(|_, (m, ..)| m.generation() != generation);
522 h.client_packets
523 .retain(|_, (m, ..)| m.generation() != generation);
524 h.chunk_generators
525 .retain(|_, (m, ..)| m.generation() != generation);
526 h.ui_handlers
527 .retain(|_, (m, ..)| m.generation() != generation);
528 for entries in h.ui_render_handlers.values_mut() {
529 entries.retain(|(m, ..)| m.generation() != generation);
530 }
531 h.page_drawers
532 .retain(|_, (m, ..)| m.generation() != generation);
533 for entries in h.item_texts.values_mut() {
534 entries.retain(|(m, ..)| m.generation() != generation);
535 }
536 for entries in h.entity_ticks.values_mut() {
537 entries.retain(|(m, ..)| m.generation() != generation);
538 }
539 for entries in h.item_tooltips.values_mut() {
540 entries.retain(|(m, ..)| m.generation() != generation);
541 }
542 h.scheduler
543 .lock()
544 .expect("scheduler poisoned")
545 .once_tasks
546 .retain(|t| t.module.generation() != generation);
547 h.scheduler
548 .lock()
549 .expect("scheduler poisoned")
550 .repeating_tasks
551 .retain(|t| t.module.generation() != generation);
552 }
553}
554
555fn guard(label: &str, f: impl FnOnce()) {
558 if std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).is_err() {
559 yog_logging::error!("a mod panicked handling `{}` (ignored)", label);
560 }
561}
562
563macro_rules! jstr {
566 ($env:expr_2021, $s:expr_2021) => {
567 jstr!($env, $s, Default::default())
568 };
569 ($env:expr_2021, $s:expr_2021, $or:expr_2021) => {
570 match $env.get_string(&$s) {
571 Ok(s) => String::from(s),
572 Err(_) => return $or,
573 }
574 };
575}
576
577fn get_env() -> Option<jni::AttachGuard<'static>> {
578 JAVA_VM.get()?.attach_current_thread().ok()
579}
580
581macro_rules! host {
584 ($class:literal, $name:literal, [$($arg:expr_2021),* $(,)?]) => {
585 match $crate::get_env() {
586 Some(mut env) => $crate::jcall::jcall!(&mut env, $class, $name, [$($arg),*]),
587 None => None,
588 }
589 };
590}
591
592macro_rules! bridge {
594 ($name:literal, [$($arg:expr_2021),* $(,)?]) => {
595 host!("dev/yog/NativeBridge", $name, [$($arg),*])
596 };
597}
598
599fn owned(s: Option<String>) -> YogOwnedStr {
601 s.map_or(YogOwnedStr::NONE, YogOwnedStr::from_string)
602}
603
604fn parse_tsv<T: std::str::FromStr, const N: usize>(s: &str) -> Option<[T; N]> {
606 let values: Vec<T> = s
607 .split('\t')
608 .take(N)
609 .map(|v| v.parse().ok())
610 .collect::<Option<_>>()?;
611 values.try_into().ok()
612}
613
614unsafe extern "C" fn yog_free_str(ptr: *mut u8, len: u32) {
617 unsafe {
618 if !ptr.is_null() {
619 drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut(
620 ptr,
621 len as usize,
622 )));
623 }
624 }
625}
626
627unsafe extern "C" fn srv_broadcast(_ctx: *mut c_void, msg: YogStr) {
632 unsafe {
633 let _: Option<()> = bridge!("broadcast", [S(msg)]);
634 }
635}
636
637unsafe extern "C" fn srv_get_block(
638 _ctx: *mut c_void,
639 dim: YogStr,
640 pos: YogBlockPos,
641) -> YogOwnedStr {
642 unsafe { owned(bridge!("getBlock", [S(dim), I(pos.x), I(pos.y), I(pos.z)])) }
643}
644
645unsafe extern "C" fn srv_set_block(
646 _ctx: *mut c_void,
647 dim: YogStr,
648 pos: YogBlockPos,
649 block: YogStr,
650) -> bool {
651 unsafe {
652 bridge!("setBlock", [S(dim), I(pos.x), I(pos.y), I(pos.z), S(block)]).unwrap_or(false)
653 }
654}
655
656unsafe extern "C" fn srv_world_time(_ctx: *mut c_void, dim: YogStr, out: *mut i64) -> bool {
657 unsafe {
658 match bridge!("worldTime", [S(dim)]) {
659 Some(v) if v != i64::MIN => {
660 *out = v;
661 true
662 }
663 _ => false,
664 }
665 }
666}
667
668unsafe extern "C" fn srv_set_time(_ctx: *mut c_void, dim: YogStr, time: i64) -> bool {
669 unsafe { bridge!("worldSetTime", [S(dim), J(time)]).unwrap_or(false) }
670}
671
672unsafe extern "C" fn srv_is_raining(_ctx: *mut c_void, dim: YogStr) -> bool {
673 unsafe { bridge!("worldIsRaining", [S(dim)]).unwrap_or(false) }
674}
675
676unsafe extern "C" fn srv_set_weather(
677 _ctx: *mut c_void,
678 dim: YogStr,
679 raining: bool,
680 dur: i32,
681) -> bool {
682 unsafe { bridge!("worldSetWeather", [S(dim), Z(raining), I(dur)]).unwrap_or(false) }
683}
684
685unsafe extern "C" fn srv_give_item(
686 _ctx: *mut c_void,
687 player: YogStr,
688 item: YogStr,
689 count: u32,
690) -> bool {
691 unsafe { bridge!("giveItem", [S(player), S(item), I(count as i32)]).unwrap_or(false) }
692}
693
694unsafe extern "C" fn srv_player_teleport(_ctx: *mut c_void, player: YogStr, pos: YogVec3) -> bool {
695 unsafe { bridge!("teleport", [S(player), D(pos.x), D(pos.y), D(pos.z)]).unwrap_or(false) }
696}
697
698unsafe extern "C" fn srv_send_to_player(
699 _ctx: *mut c_void,
700 player: YogStr,
701 channel: YogStr,
702 data: *const u8,
703 len: u32,
704) -> bool {
705 unsafe {
706 bridge!(
707 "sendToPlayer",
708 [
709 S(player),
710 S(channel),
711 Bytes(std::slice::from_raw_parts(data, len as usize))
712 ]
713 )
714 .unwrap_or(false)
715 }
716}
717
718unsafe extern "C" fn srv_send_to_server(
719 _ctx: *mut c_void,
720 channel: YogStr,
721 data: *const u8,
722 len: u32,
723) -> bool {
724 unsafe {
725 host!(
726 "dev/yog/YogClient",
727 "sendToServer",
728 [
729 S(channel),
730 Bytes(std::slice::from_raw_parts(data, len as usize))
731 ]
732 )
733 .unwrap_or(false)
734 }
735}
736
737unsafe extern "C" fn srv_kick_player(_ctx: *mut c_void, player: YogStr, reason: YogStr) -> bool {
738 unsafe { bridge!("kickPlayer", [S(player), S(reason)]).unwrap_or(false) }
739}
740
741unsafe extern "C" fn srv_set_gamemode(_ctx: *mut c_void, player: YogStr, mode: YogStr) -> bool {
742 unsafe { bridge!("setGamemode", [S(player), S(mode)]).unwrap_or(false) }
743}
744
745unsafe extern "C" fn srv_send_title(
746 _ctx: *mut c_void,
747 player: YogStr,
748 title: YogStr,
749 sub: YogStr,
750 fi: i32,
751 stay: i32,
752 fo: i32,
753) -> bool {
754 unsafe {
755 bridge!(
756 "sendTitle",
757 [S(player), S(title), S(sub), I(fi), I(stay), I(fo)]
758 )
759 .unwrap_or(false)
760 }
761}
762
763unsafe extern "C" fn srv_send_actionbar(_ctx: *mut c_void, player: YogStr, msg: YogStr) -> bool {
764 unsafe { bridge!("sendActionbar", [S(player), S(msg)]).unwrap_or(false) }
765}
766
767unsafe extern "C" fn srv_play_sound(
768 _ctx: *mut c_void,
769 dim: YogStr,
770 pos: YogVec3,
771 sound: YogStr,
772 vol: f32,
773 pitch: f32,
774) -> bool {
775 unsafe {
776 bridge!(
777 "playSound",
778 [
779 S(dim),
780 D(pos.x),
781 D(pos.y),
782 D(pos.z),
783 S(sound),
784 F(vol),
785 F(pitch)
786 ]
787 )
788 .unwrap_or(false)
789 }
790}
791
792unsafe extern "C" fn srv_play_sound_player(
793 _ctx: *mut c_void,
794 player: YogStr,
795 sound: YogStr,
796 vol: f32,
797 pitch: f32,
798) -> bool {
799 unsafe {
800 bridge!("playSoundToPlayer", [S(player), S(sound), F(vol), F(pitch)]).unwrap_or(false)
801 }
802}
803
804unsafe extern "C" fn srv_entity_teleport(_ctx: *mut c_void, uuid: YogStr, pos: YogVec3) -> bool {
805 unsafe { bridge!("entityTeleport", [S(uuid), D(pos.x), D(pos.y), D(pos.z)]).unwrap_or(false) }
806}
807
808unsafe extern "C" fn srv_entity_rotation(
809 _ctx: *mut c_void,
810 uuid: YogStr,
811 out: *mut YogVec3,
812) -> bool {
813 unsafe {
814 let Some(s): Option<String> = bridge!("entityRotation", [S(uuid)]) else {
815 return false;
816 };
817 match parse_tsv::<f64, 2>(&s) {
818 Some([yaw, pitch]) => {
819 *out = YogVec3 {
820 x: yaw,
821 y: pitch,
822 z: 0.0,
823 };
824 true
825 }
826 None => false,
827 }
828 }
829}
830
831unsafe extern "C" fn srv_entity_position(
832 _ctx: *mut c_void,
833 uuid: YogStr,
834 out: *mut YogVec3,
835) -> bool {
836 unsafe {
837 let Some(s): Option<String> = bridge!("entityPosition", [S(uuid)]) else {
838 return false;
839 };
840 match parse_tsv::<f64, 3>(&s) {
841 Some([x, y, z]) => {
842 *out = YogVec3 { x, y, z };
843 true
844 }
845 None => false,
846 }
847 }
848}
849
850unsafe extern "C" fn srv_entity_health(_ctx: *mut c_void, uuid: YogStr, out: *mut f32) -> bool {
851 unsafe {
852 match bridge!("entityHealth", [S(uuid)]) {
853 Some(v) if !f64::is_nan(v) => {
854 *out = v as f32;
855 true
856 }
857 _ => false,
858 }
859 }
860}
861
862unsafe extern "C" fn srv_entity_set_health(_ctx: *mut c_void, uuid: YogStr, hp: f32) -> bool {
863 unsafe { bridge!("entitySetHealth", [S(uuid), D(hp as f64)]).unwrap_or(false) }
864}
865
866unsafe extern "C" fn srv_entity_kill(_ctx: *mut c_void, uuid: YogStr) -> bool {
867 unsafe { bridge!("entityKill", [S(uuid)]).unwrap_or(false) }
868}
869
870unsafe extern "C" fn srv_spawn_entity(
871 _ctx: *mut c_void,
872 type_id: YogStr,
873 dim: YogStr,
874 pos: YogVec3,
875) -> YogOwnedStr {
876 unsafe {
877 owned(bridge!(
878 "spawnEntity",
879 [S(type_id), S(dim), D(pos.x), D(pos.y), D(pos.z)]
880 ))
881 }
882}
883
884unsafe extern "C" fn srv_entity_add_effect(
885 _ctx: *mut c_void,
886 uuid: YogStr,
887 fx: YogStr,
888 dur: i32,
889 amp: u8,
890 particles: bool,
891) -> bool {
892 unsafe {
893 bridge!(
894 "entityAddEffect",
895 [S(uuid), S(fx), I(dur), I(amp as i32), Z(particles)]
896 )
897 .unwrap_or(false)
898 }
899}
900
901unsafe extern "C" fn srv_entity_remove_effect(_ctx: *mut c_void, uuid: YogStr, fx: YogStr) -> bool {
902 unsafe { bridge!("entityRemoveEffect", [S(uuid), S(fx)]).unwrap_or(false) }
903}
904
905unsafe extern "C" fn srv_entity_clear_effects(_ctx: *mut c_void, uuid: YogStr) -> bool {
906 unsafe { bridge!("entityClearEffects", [S(uuid)]).unwrap_or(false) }
907}
908
909unsafe extern "C" fn srv_entity_velocity(
910 _ctx: *mut c_void,
911 uuid: YogStr,
912 out: *mut YogVec3,
913) -> bool {
914 unsafe {
915 let Some(s): Option<String> = bridge!("entityVelocity", [S(uuid)]) else {
916 return false;
917 };
918 match parse_tsv::<f64, 3>(&s) {
919 Some([x, y, z]) => {
920 *out = YogVec3 { x, y, z };
921 true
922 }
923 None => false,
924 }
925 }
926}
927
928unsafe extern "C" fn srv_entity_set_velocity(
929 _ctx: *mut c_void,
930 uuid: YogStr,
931 vel: YogVec3,
932) -> bool {
933 unsafe {
934 bridge!("entitySetVelocity", [S(uuid), D(vel.x), D(vel.y), D(vel.z)]).unwrap_or(false)
935 }
936}
937
938unsafe extern "C" fn srv_entity_add_velocity(
939 _ctx: *mut c_void,
940 uuid: YogStr,
941 vel: YogVec3,
942) -> bool {
943 unsafe {
944 bridge!("entityAddVelocity", [S(uuid), D(vel.x), D(vel.y), D(vel.z)]).unwrap_or(false)
945 }
946}
947
948unsafe extern "C" fn srv_has_item_tag(_ctx: *mut c_void, item: YogStr, tag: YogStr) -> bool {
949 unsafe { bridge!("hasItemTag", [S(item), S(tag)]).unwrap_or(false) }
950}
951
952unsafe extern "C" fn srv_has_block_tag(_ctx: *mut c_void, block: YogStr, tag: YogStr) -> bool {
953 unsafe { bridge!("hasBlockTag", [S(block), S(tag)]).unwrap_or(false) }
954}
955
956unsafe extern "C" fn srv_drop_loot(
957 _ctx: *mut c_void,
958 table: YogStr,
959 dim: YogStr,
960 pos: YogVec3,
961) -> bool {
962 unsafe {
963 bridge!("dropLoot", [S(table), S(dim), D(pos.x), D(pos.y), D(pos.z)]).unwrap_or(false)
964 }
965}
966
967unsafe extern "C" fn srv_scoreboard_get(
968 _ctx: *mut c_void,
969 obj: YogStr,
970 player: YogStr,
971 out: *mut i32,
972) -> bool {
973 unsafe {
974 match bridge!("scoreboardGet", [S(obj), S(player)]) {
975 Some(v) if v != i32::MIN => {
976 *out = v;
977 true
978 }
979 _ => false,
980 }
981 }
982}
983
984unsafe extern "C" fn srv_scoreboard_set(
985 _ctx: *mut c_void,
986 obj: YogStr,
987 player: YogStr,
988 score: i32,
989) -> bool {
990 unsafe { bridge!("scoreboardSet", [S(obj), S(player), I(score)]).unwrap_or(false) }
991}
992
993unsafe extern "C" fn srv_scoreboard_add(
994 _ctx: *mut c_void,
995 obj: YogStr,
996 player: YogStr,
997 delta: i32,
998 out: *mut i32,
999) -> bool {
1000 unsafe {
1001 match bridge!("scoreboardAdd", [S(obj), S(player), I(delta)]) {
1002 Some(v) if v != i32::MIN => {
1003 *out = v;
1004 true
1005 }
1006 _ => false,
1007 }
1008 }
1009}
1010
1011unsafe extern "C" fn srv_bossbar_create(
1012 _ctx: *mut c_void,
1013 id: YogStr,
1014 title: YogStr,
1015 color: YogStr,
1016 style: YogStr,
1017) -> bool {
1018 unsafe { bridge!("bossbarCreate", [S(id), S(title), S(color), S(style)]).unwrap_or(false) }
1019}
1020
1021unsafe extern "C" fn srv_bossbar_remove(_ctx: *mut c_void, id: YogStr) -> bool {
1022 unsafe { bridge!("bossbarRemove", [S(id)]).unwrap_or(false) }
1023}
1024
1025unsafe extern "C" fn srv_bossbar_set_title(_ctx: *mut c_void, id: YogStr, title: YogStr) -> bool {
1026 unsafe { bridge!("bossbarSetTitle", [S(id), S(title)]).unwrap_or(false) }
1027}
1028
1029unsafe extern "C" fn srv_bossbar_set_progress(
1030 _ctx: *mut c_void,
1031 id: YogStr,
1032 progress: f32,
1033) -> bool {
1034 unsafe { bridge!("bossbarSetProgress", [S(id), F(progress)]).unwrap_or(false) }
1035}
1036
1037unsafe extern "C" fn srv_bossbar_set_color(_ctx: *mut c_void, id: YogStr, color: YogStr) -> bool {
1038 unsafe { bridge!("bossbarSetColor", [S(id), S(color)]).unwrap_or(false) }
1039}
1040
1041unsafe extern "C" fn srv_bossbar_add_player(_ctx: *mut c_void, id: YogStr, player: YogStr) -> bool {
1042 unsafe { bridge!("bossbarAddPlayer", [S(id), S(player)]).unwrap_or(false) }
1043}
1044
1045unsafe extern "C" fn srv_bossbar_remove_player(
1046 _ctx: *mut c_void,
1047 id: YogStr,
1048 player: YogStr,
1049) -> bool {
1050 unsafe { bridge!("bossbarRemovePlayer", [S(id), S(player)]).unwrap_or(false) }
1051}
1052
1053unsafe extern "C" fn srv_bossbar_set_visible(_ctx: *mut c_void, id: YogStr, visible: bool) -> bool {
1054 unsafe { bridge!("bossbarSetVisible", [S(id), Z(visible)]).unwrap_or(false) }
1055}
1056
1057unsafe extern "C" fn srv_get_block_nbt(
1058 _ctx: *mut c_void,
1059 dim: YogStr,
1060 pos: YogBlockPos,
1061) -> YogOwnedStr {
1062 unsafe {
1063 owned(bridge!(
1064 "getBlockNbt",
1065 [S(dim), I(pos.x), I(pos.y), I(pos.z)]
1066 ))
1067 }
1068}
1069
1070unsafe extern "C" fn srv_set_block_nbt(
1071 _ctx: *mut c_void,
1072 dim: YogStr,
1073 pos: YogBlockPos,
1074 snbt: YogStr,
1075) -> bool {
1076 unsafe {
1077 bridge!(
1078 "setBlockNbt",
1079 [S(dim), I(pos.x), I(pos.y), I(pos.z), S(snbt)]
1080 )
1081 .unwrap_or(false)
1082 }
1083}
1084
1085unsafe extern "C" fn srv_get_inventory_slot(
1086 _ctx: *mut c_void,
1087 dim: YogStr,
1088 pos: yog_abi::YogBlockPos,
1089 slot: u32,
1090) -> YogOwnedStr {
1091 unsafe {
1092 owned(bridge!(
1093 "getInventorySlot",
1094 [S(dim), I(pos.x), I(pos.y), I(pos.z), I(slot as i32)]
1095 ))
1096 }
1097}
1098
1099unsafe extern "C" fn srv_set_inventory_slot(
1100 _ctx: *mut c_void,
1101 dim: YogStr,
1102 pos: yog_abi::YogBlockPos,
1103 slot: u32,
1104 item_id: YogStr,
1105 count: u32,
1106) -> bool {
1107 unsafe {
1108 bridge!(
1109 "setInventorySlot",
1110 [
1111 S(dim),
1112 I(pos.x),
1113 I(pos.y),
1114 I(pos.z),
1115 I(slot as i32),
1116 S(item_id),
1117 I(count as i32)
1118 ]
1119 )
1120 .unwrap_or(false)
1121 }
1122}
1123
1124unsafe extern "C" fn srv_player_inventory(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
1125 unsafe { owned(bridge!("playerInventory", [S(player)])) }
1126}
1127
1128unsafe extern "C" fn srv_player_set_slot(
1129 _ctx: *mut c_void,
1130 player: YogStr,
1131 slot: u32,
1132 item_id: YogStr,
1133 count: u32,
1134) -> bool {
1135 unsafe {
1136 bridge!(
1137 "playerSetSlot",
1138 [S(player), I(slot as i32), S(item_id), I(count as i32)]
1139 )
1140 .unwrap_or(false)
1141 }
1142}
1143
1144unsafe extern "C" fn srv_player_teleport_dim(
1145 _ctx: *mut c_void,
1146 player: YogStr,
1147 dim: YogStr,
1148 pos: YogVec3,
1149) -> bool {
1150 unsafe {
1151 bridge!(
1152 "teleportToDim",
1153 [S(player), S(dim), D(pos.x), D(pos.y), D(pos.z)]
1154 )
1155 .unwrap_or(false)
1156 }
1157}
1158
1159unsafe extern "C" fn srv_entity_teleport_dim(
1160 _ctx: *mut c_void,
1161 uuid: YogStr,
1162 dim: YogStr,
1163 pos: YogVec3,
1164) -> bool {
1165 unsafe {
1166 bridge!(
1167 "entityTeleportToDim",
1168 [S(uuid), S(dim), D(pos.x), D(pos.y), D(pos.z)]
1169 )
1170 .unwrap_or(false)
1171 }
1172}
1173
1174unsafe extern "C" fn srv_online_players(_ctx: *mut c_void) -> YogOwnedStr {
1175 unsafe { owned(bridge!("onlinePlayers", [])) }
1176}
1177
1178unsafe extern "C" fn srv_world_entity_count(
1179 _ctx: *mut c_void,
1180 dim: YogStr,
1181 entity_type: YogStr,
1182) -> i32 {
1183 unsafe { bridge!("worldEntityCount", [S(dim), S(entity_type)]).unwrap_or(-1) }
1184}
1185
1186unsafe extern "C" fn srv_game_dir(_ctx: *mut c_void) -> YogOwnedStr {
1187 unsafe { owned(bridge!("gameDir", [])) }
1188}
1189
1190unsafe extern "C" fn srv_world_dir(_ctx: *mut c_void) -> YogOwnedStr {
1191 unsafe { owned(bridge!("worldDir", [])) }
1192}
1193
1194unsafe extern "C" fn srv_entity_get_nbt(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
1195 unsafe { owned(bridge!("entityGetNbt", [S(uuid)])) }
1196}
1197
1198unsafe extern "C" fn srv_entity_set_nbt(_ctx: *mut c_void, uuid: YogStr, snbt: YogStr) -> bool {
1199 unsafe { bridge!("entitySetNbt", [S(uuid), S(snbt)]).unwrap_or(false) }
1200}
1201
1202unsafe extern "C" fn srv_spawn_particles(
1203 _ctx: *mut c_void,
1204 dim: YogStr,
1205 pos: YogVec3,
1206 particle_type: YogStr,
1207 count: i32,
1208 dx: f64,
1209 dy: f64,
1210 dz: f64,
1211 speed: f64,
1212) -> bool {
1213 unsafe {
1214 bridge!(
1215 "spawnParticles",
1216 [
1217 S(dim),
1218 D(pos.x),
1219 D(pos.y),
1220 D(pos.z),
1221 S(particle_type),
1222 I(count),
1223 D(dx),
1224 D(dy),
1225 D(dz),
1226 D(speed)
1227 ]
1228 )
1229 .unwrap_or(false)
1230 }
1231}
1232
1233unsafe extern "C" fn srv_entity_attribute_value(_ctx: *mut c_void, uuid: YogStr, attr: YogStr) -> f64 {
1234 unsafe { bridge!("entityAttributeValue", [S(uuid), S(attr)]).unwrap_or(f64::NAN) }
1235}
1236
1237unsafe extern "C" fn api_client_attribute_value(_ctx: *mut c_void, attr: YogStr) -> f64 {
1238 unsafe { host!("dev/yog/YogClientApi", "attributeValue", [S(attr)]).unwrap_or(f64::NAN) }
1239}
1240
1241unsafe extern "C" fn srv_advancement_grant(_ctx: *mut c_void, uuid: YogStr, id: YogStr) -> bool {
1242 unsafe { bridge!("advancementGrant", [S(uuid), S(id)]).unwrap_or(false) }
1243}
1244
1245unsafe extern "C" fn srv_advancement_done(_ctx: *mut c_void, uuid: YogStr, id: YogStr) -> bool {
1246 unsafe { bridge!("advancementDone", [S(uuid), S(id)]).unwrap_or(false) }
1247}
1248
1249fn client_advancement_done(id: &str) -> bool {
1252 unsafe { host!("dev/yog/YogClientApi", "advancementDone", [JArg::S(YogStr::from_str(id))]).unwrap_or(false) }
1253}
1254
1255thread_local! {
1258 static LOGGING: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
1261}
1262
1263fn host_log(level: yog_logging::Level, message: &str) {
1266 let sent = !LOGGING.with(|busy| busy.replace(true)) && {
1267 let r: Option<()> = unsafe { host!("dev/yog/YogLog", "log", [JArg::I(level as i32), JArg::S(YogStr::from_str(message))]) };
1268 LOGGING.with(|busy| busy.set(false));
1269 r.is_some()
1270 };
1271 if !sent {
1272 eprintln!("[yog] [{level:?}] {message}");
1273 }
1274}
1275
1276unsafe extern "C" fn api_log(level: u32, message: YogStr) {
1278 host_log(yog_logging::Level::from_u32(level), unsafe { message.as_str() });
1279}
1280
1281unsafe extern "C" fn api_client_advancement_done(_ctx: *mut c_void, id: YogStr) -> bool {
1282 client_advancement_done(unsafe { id.as_str() })
1283}
1284
1285unsafe extern "C" fn srv_entity_attribute_get(
1286 _ctx: *mut c_void,
1287 uuid: YogStr,
1288 attr: YogStr,
1289) -> f64 {
1290 unsafe { bridge!("entityAttributeGet", [S(uuid), S(attr)]).unwrap_or(f64::NAN) }
1291}
1292
1293unsafe extern "C" fn srv_entity_attribute_set(
1294 _ctx: *mut c_void,
1295 uuid: YogStr,
1296 attr: YogStr,
1297 value: f64,
1298) -> bool {
1299 unsafe { bridge!("entityAttributeSet", [S(uuid), S(attr), D(value)]).unwrap_or(false) }
1300}
1301
1302unsafe extern "C" fn srv_get_held_item_nbt(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
1303 unsafe { owned(bridge!("getHeldItemNbt", [S(player)])) }
1304}
1305
1306unsafe extern "C" fn srv_set_held_item_nbt(
1307 _ctx: *mut c_void,
1308 player: YogStr,
1309 snbt: YogStr,
1310) -> bool {
1311 unsafe { bridge!("setHeldItemNbt", [S(player), S(snbt)]).unwrap_or(false) }
1312}
1313
1314unsafe extern "C" fn srv_get_offhand_item_nbt(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
1315 unsafe { owned(bridge!("getOffhandItemNbt", [S(player)])) }
1316}
1317
1318unsafe extern "C" fn srv_set_offhand_item_nbt(
1319 _ctx: *mut c_void,
1320 player: YogStr,
1321 snbt: YogStr,
1322) -> bool {
1323 unsafe { bridge!("setOffhandItemNbt", [S(player), S(snbt)]).unwrap_or(false) }
1324}
1325
1326unsafe extern "C" fn srv_get_slot_item(
1327 _ctx: *mut c_void,
1328 player: YogStr,
1329 slot: u32,
1330) -> YogOwnedStr {
1331 unsafe { owned(bridge!("getSlotItem", [S(player), I(slot as i32)])) }
1332}
1333
1334unsafe extern "C" fn srv_set_slot_item(
1335 _ctx: *mut c_void,
1336 player: YogStr,
1337 slot: u32,
1338 item_id: YogStr,
1339 count: u32,
1340 snbt: YogStr,
1341) -> bool {
1342 unsafe {
1343 bridge!(
1344 "setSlotItem",
1345 [
1346 S(player),
1347 I(slot as i32),
1348 S(item_id),
1349 I(count as i32),
1350 S(snbt)
1351 ]
1352 )
1353 .unwrap_or(false)
1354 }
1355}
1356
1357fn gl_draw_mode(mode: u8) -> u32 {
1366 match mode {
1367 1 => glow::LINES,
1368 2 => glow::LINE_STRIP,
1369 3 => glow::TRIANGLE_STRIP,
1370 4 => glow::TRIANGLE_FAN,
1371 _ => glow::TRIANGLES,
1372 }
1373}
1374
1375fn gl_attr_type(dtype: u8) -> u32 {
1376 match dtype {
1377 1 => glow::UNSIGNED_BYTE,
1378 2 => glow::INT,
1379 3 => glow::UNSIGNED_INT,
1380 _ => glow::FLOAT,
1381 }
1382}
1383
1384unsafe fn compile_shader(gl: &glow::Context, stage: u32, src: &str) -> Option<glow::NativeShader> {
1385 unsafe {
1386 let sh = gl.create_shader(stage).ok()?;
1387 gl.shader_source(sh, src);
1388 gl.compile_shader(sh);
1389 if !gl.get_shader_compile_status(sh) {
1390 yog_logging::error!("yog-gfx shader compile: {}", gl.get_shader_info_log(sh));
1391 gl.delete_shader(sh);
1392 return None;
1393 }
1394 Some(sh)
1395 }
1396}
1397
1398unsafe extern "C" fn gfx_buf_create() -> u32 {
1401 unsafe {
1402 let Some(g) = GL.get() else { return 0 };
1403 g.0.create_buffer().map(|b| b.0.get()).unwrap_or(0)
1404 }
1405}
1406
1407unsafe extern "C" fn gfx_buf_delete(handle: u32) {
1408 unsafe {
1409 let Some(g) = GL.get() else { return };
1410 let Some(n) = NonZeroU32::new(handle) else {
1411 return;
1412 };
1413 g.0.delete_buffer(glow::NativeBuffer(n));
1414 }
1415}
1416
1417unsafe extern "C" fn gfx_buf_data(handle: u32, bytes: *const u8, len: u32, dynamic: bool) {
1418 unsafe {
1419 let Some(g) = GL.get() else { return };
1420 let Some(n) = NonZeroU32::new(handle) else {
1421 return;
1422 };
1423 let gl = &g.0;
1424 let data = std::slice::from_raw_parts(bytes, len as usize);
1425 let usage = if dynamic {
1426 glow::DYNAMIC_DRAW
1427 } else {
1428 glow::STATIC_DRAW
1429 };
1430 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(n)));
1431 gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, data, usage);
1432 gl.bind_buffer(glow::ARRAY_BUFFER, None);
1433 }
1434}
1435
1436unsafe extern "C" fn gfx_buf_subdata(handle: u32, offset: u32, bytes: *const u8, len: u32) {
1437 unsafe {
1438 let Some(g) = GL.get() else { return };
1439 let Some(n) = NonZeroU32::new(handle) else {
1440 return;
1441 };
1442 let gl = &g.0;
1443 let data = std::slice::from_raw_parts(bytes, len as usize);
1444 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(n)));
1445 gl.buffer_sub_data_u8_slice(glow::ARRAY_BUFFER, offset as i32, data);
1446 gl.bind_buffer(glow::ARRAY_BUFFER, None);
1447 }
1448}
1449
1450unsafe extern "C" fn gfx_vao_create() -> u32 {
1453 unsafe {
1454 let Some(g) = GL.get() else { return 0 };
1455 g.0.create_vertex_array().map(|v| v.0.get()).unwrap_or(0)
1456 }
1457}
1458
1459unsafe extern "C" fn gfx_vao_delete(handle: u32) {
1460 unsafe {
1461 let Some(g) = GL.get() else { return };
1462 let Some(n) = NonZeroU32::new(handle) else {
1463 return;
1464 };
1465 g.0.delete_vertex_array(glow::NativeVertexArray(n));
1466 }
1467}
1468
1469unsafe extern "C" fn gfx_vao_attrib(
1470 vao: u32,
1471 vbo: u32,
1472 index: u32,
1473 components: u8,
1474 dtype: u8,
1475 normalized: bool,
1476 stride: u32,
1477 offset: u32,
1478) {
1479 unsafe {
1480 let Some(g) = GL.get() else { return };
1481 let (Some(vn), Some(bn)) = (NonZeroU32::new(vao), NonZeroU32::new(vbo)) else {
1482 return;
1483 };
1484 let gl = &g.0;
1485 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1486 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(bn)));
1487 let gl_type = gl_attr_type(dtype);
1488 if dtype == 2 || dtype == 3 {
1489 gl.vertex_attrib_pointer_i32(
1490 index,
1491 components as i32,
1492 gl_type,
1493 stride as i32,
1494 offset as i32,
1495 );
1496 } else {
1497 gl.vertex_attrib_pointer_f32(
1498 index,
1499 components as i32,
1500 gl_type,
1501 normalized,
1502 stride as i32,
1503 offset as i32,
1504 );
1505 }
1506 gl.enable_vertex_attrib_array(index);
1507 gl.bind_vertex_array(None);
1508 }
1509}
1510
1511unsafe extern "C" fn gfx_vao_set_ebo(vao: u32, ebo: u32) {
1512 unsafe {
1513 let Some(g) = GL.get() else { return };
1514 let (Some(vn), Some(en)) = (NonZeroU32::new(vao), NonZeroU32::new(ebo)) else {
1515 return;
1516 };
1517 let gl = &g.0;
1518 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1519 gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(glow::NativeBuffer(en)));
1520 gl.bind_vertex_array(None);
1521 }
1522}
1523
1524fn shader_cache_path(vert: &str, frag: &str) -> Option<PathBuf> {
1529 use std::hash::{Hash, Hasher};
1530 let mut h = std::collections::hash_map::DefaultHasher::new();
1531 vert.hash(&mut h);
1532 frag.hash(&mut h);
1533 let hash = h.finish();
1534 let dir = std::env::var("HOME")
1535 .map(|home| {
1536 PathBuf::from(home)
1537 .join(".cache")
1538 .join("yog")
1539 .join("shaders")
1540 })
1541 .ok()?;
1542 std::fs::create_dir_all(&dir).ok()?;
1543 Some(dir.join(format!("{hash:016x}.ysc")))
1544}
1545
1546unsafe fn load_shader_binary(gl: &glow::Context, data: &[u8]) -> Option<glow::NativeProgram> {
1552 unsafe {
1553 if data.len() < 4 {
1554 return None;
1555 }
1556 type ProgramBinaryFn = unsafe extern "system" fn(u32, u32, *const c_void, i32);
1557 let fn_ptr = (*GL_PROGRAM_BINARY.get()?)?;
1558 let program_binary: ProgramBinaryFn = std::mem::transmute(fn_ptr);
1559
1560 let fmt = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1561 let prog = gl.create_program().ok()?;
1562 program_binary(
1563 prog.0.get(),
1564 fmt,
1565 data[4..].as_ptr() as *const c_void,
1566 (data.len() - 4) as i32,
1567 );
1568 if gl.get_program_link_status(prog) {
1569 Some(prog)
1570 } else {
1571 gl.delete_program(prog);
1572 None
1573 }
1574 }
1575}
1576
1577unsafe fn get_shader_binary(prog: glow::NativeProgram) -> (Vec<u8>, u32) {
1579 unsafe {
1580 type GetProgramivFn = unsafe extern "system" fn(u32, u32, *mut i32);
1581 type GetProgramBinaryFn =
1582 unsafe extern "system" fn(u32, i32, *mut i32, *mut u32, *mut c_void);
1583
1584 let Some(get_iv_raw) = GL_GET_PROGRAM_IV.get().and_then(|v| *v) else {
1585 return (vec![], 0);
1586 };
1587 let Some(get_bin_raw) = GL_GET_PROGRAM_BINARY.get().and_then(|v| *v) else {
1588 return (vec![], 0);
1589 };
1590 let get_program_iv: GetProgramivFn = std::mem::transmute(get_iv_raw);
1591 let get_program_binary: GetProgramBinaryFn = std::mem::transmute(get_bin_raw);
1592
1593 const PROGRAM_BINARY_LENGTH: u32 = 0x8741;
1595 let mut size: i32 = 0;
1596 get_program_iv(prog.0.get(), PROGRAM_BINARY_LENGTH, &mut size);
1597 if size <= 0 {
1598 return (vec![], 0);
1599 }
1600
1601 let mut buf = vec![0u8; size as usize];
1602 let mut actual_len: i32 = 0;
1603 let mut fmt: u32 = 0;
1604 get_program_binary(
1605 prog.0.get(),
1606 size,
1607 &mut actual_len,
1608 &mut fmt,
1609 buf.as_mut_ptr() as *mut c_void,
1610 );
1611 buf.truncate(actual_len.max(0) as usize);
1612 (buf, fmt)
1613 }
1614}
1615
1616unsafe extern "C" fn gfx_prog_create(vert: YogStr, frag: YogStr, out: *mut u32) -> bool {
1617 unsafe {
1618 let Some(g) = GL.get() else { return false };
1619 let gl = &g.0;
1620 let vert_s = vert.as_str();
1621 let frag_s = frag.as_str();
1622
1623 let cache_path = shader_cache_path(vert_s, frag_s);
1625 if let Some(ref path) = cache_path
1626 && let Ok(data) = std::fs::read(path)
1627 {
1628 if let Some(prog) = load_shader_binary(gl, &data) {
1629 *out = prog.0.get();
1630 return true;
1631 }
1632 let _ = std::fs::remove_file(path);
1634 }
1635
1636 let vs = match compile_shader(gl, glow::VERTEX_SHADER, vert_s) {
1638 Some(s) => s,
1639 None => return false,
1640 };
1641 let fs = match compile_shader(gl, glow::FRAGMENT_SHADER, frag_s) {
1642 Some(s) => s,
1643 None => {
1644 gl.delete_shader(vs);
1645 return false;
1646 }
1647 };
1648 let prog = match gl.create_program() {
1649 Ok(p) => p,
1650 Err(e) => {
1651 yog_logging::error!("yog-gfx: create_program: {}", e);
1652 gl.delete_shader(vs);
1653 gl.delete_shader(fs);
1654 return false;
1655 }
1656 };
1657 gl.attach_shader(prog, vs);
1658 gl.attach_shader(prog, fs);
1659 gl.link_program(prog);
1660 gl.detach_shader(prog, vs);
1661 gl.detach_shader(prog, fs);
1662 gl.delete_shader(vs);
1663 gl.delete_shader(fs);
1664 if !gl.get_program_link_status(prog) {
1665 yog_logging::error!("yog-gfx: shader link: {}", gl.get_program_info_log(prog));
1666 gl.delete_program(prog);
1667 return false;
1668 }
1669
1670 if let Some(ref path) = cache_path {
1672 let (binary, fmt) = get_shader_binary(prog);
1673 if !binary.is_empty() {
1674 let mut blob = fmt.to_le_bytes().to_vec();
1675 blob.extend_from_slice(&binary);
1676 let _ = std::fs::write(path, &blob);
1677 }
1678 }
1679
1680 *out = prog.0.get();
1681 true
1682 }
1683}
1684
1685unsafe extern "C" fn gfx_prog_delete(handle: u32) {
1686 unsafe {
1687 let Some(g) = GL.get() else { return };
1688 let Some(n) = NonZeroU32::new(handle) else {
1689 return;
1690 };
1691 g.0.delete_program(glow::NativeProgram(n));
1692 }
1693}
1694
1695macro_rules! with_prog {
1696 ($handle:expr_2021, |$gl:ident, $prog:ident, $loc:ident ($name:expr_2021)| $body:expr_2021) => {{
1697 let Some(g) = GL.get() else { return };
1698 let Some(n) = NonZeroU32::new($handle) else {
1699 return;
1700 };
1701 let $gl = &g.0;
1702 let $prog = glow::NativeProgram(n);
1703 $gl.use_program(Some($prog));
1704 let $loc = $gl.get_uniform_location($prog, $name.as_str());
1705 $body
1706 }};
1707}
1708
1709unsafe extern "C" fn gfx_prog_uniform_1i(prog: u32, name: YogStr, v: i32) {
1710 unsafe {
1711 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_1_i32(loc.as_ref(), v));
1712 }
1713}
1714unsafe extern "C" fn gfx_prog_uniform_1f(prog: u32, name: YogStr, v: f32) {
1715 unsafe {
1716 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_1_f32(loc.as_ref(), v));
1717 }
1718}
1719unsafe extern "C" fn gfx_prog_uniform_2f(prog: u32, name: YogStr, x: f32, y: f32) {
1720 unsafe {
1721 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_2_f32(
1722 loc.as_ref(),
1723 x,
1724 y
1725 ));
1726 }
1727}
1728unsafe extern "C" fn gfx_prog_uniform_3f(prog: u32, name: YogStr, x: f32, y: f32, z: f32) {
1729 unsafe {
1730 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_3_f32(
1731 loc.as_ref(),
1732 x,
1733 y,
1734 z
1735 ));
1736 }
1737}
1738unsafe extern "C" fn gfx_prog_uniform_4f(prog: u32, name: YogStr, x: f32, y: f32, z: f32, w: f32) {
1739 unsafe {
1740 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_4_f32(
1741 loc.as_ref(),
1742 x,
1743 y,
1744 z,
1745 w
1746 ));
1747 }
1748}
1749unsafe extern "C" fn gfx_prog_uniform_mat4(prog: u32, name: YogStr, col_major: *const f32) {
1750 unsafe {
1751 with_prog!(prog, |gl, _p, loc(name)| {
1752 let data = std::slice::from_raw_parts(col_major, 16);
1753 gl.uniform_matrix_4_f32_slice(loc.as_ref(), false, data);
1754 });
1755 }
1756}
1757
1758unsafe extern "C" fn gfx_tex_create(w: u32, h: u32, rgba: *const u8, linear: bool) -> u32 {
1761 unsafe {
1762 let Some(g) = GL.get() else { return 0 };
1763 let gl = &g.0;
1764 let tex = match gl.create_texture() {
1765 Ok(t) => t,
1766 Err(_) => return 0,
1767 };
1768 gl.bind_texture(glow::TEXTURE_2D, Some(tex));
1769 let filter = if linear { glow::LINEAR } else { glow::NEAREST };
1770 gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter as i32);
1771 gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter as i32);
1772 gl.tex_parameter_i32(
1773 glow::TEXTURE_2D,
1774 glow::TEXTURE_WRAP_S,
1775 glow::CLAMP_TO_EDGE as i32,
1776 );
1777 gl.tex_parameter_i32(
1778 glow::TEXTURE_2D,
1779 glow::TEXTURE_WRAP_T,
1780 glow::CLAMP_TO_EDGE as i32,
1781 );
1782 let pixels = std::slice::from_raw_parts(rgba, (w * h * 4) as usize);
1783 unpack_tightly(gl);
1786 gl.tex_image_2d(
1787 glow::TEXTURE_2D,
1788 0,
1789 glow::RGBA8 as i32,
1790 w as i32,
1791 h as i32,
1792 0,
1793 glow::RGBA,
1794 glow::UNSIGNED_BYTE,
1795 glow::PixelUnpackData::Slice(Some(pixels)),
1796 );
1797 gl.bind_texture(glow::TEXTURE_2D, None);
1798 tex.0.get()
1799 }
1800}
1801
1802unsafe fn unpack_tightly(gl: &glow::Context) {
1804 unsafe {
1805 gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
1806 gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
1807 gl.pixel_store_i32(glow::UNPACK_SKIP_ROWS, 0);
1808 gl.pixel_store_i32(glow::UNPACK_SKIP_PIXELS, 0);
1809 gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
1810 }
1811}
1812
1813unsafe extern "C" fn gfx_tex_delete(handle: u32) {
1814 unsafe {
1815 let Some(g) = GL.get() else { return };
1816 let Some(n) = NonZeroU32::new(handle) else {
1817 return;
1818 };
1819 g.0.delete_texture(glow::NativeTexture(n));
1820 }
1821}
1822
1823unsafe extern "C" fn gfx_tex_bind(unit: u32, handle: u32) {
1824 unsafe {
1825 let Some(g) = GL.get() else { return };
1826 let gl = &g.0;
1827 gl.active_texture(glow::TEXTURE0 + unit);
1828 match NonZeroU32::new(handle) {
1829 Some(n) => gl.bind_texture(glow::TEXTURE_2D, Some(glow::NativeTexture(n))),
1830 None => gl.bind_texture(glow::TEXTURE_2D, None),
1831 }
1832 }
1833}
1834
1835unsafe extern "C" fn gfx_tex_from_mc(id: YogStr) -> u32 {
1836 unsafe { host!("dev/yog/NativeDraw", "getMcTextureId", [S(id)]).map_or(0, |id: i32| id as u32) }
1837}
1838
1839unsafe extern "C" fn gfx_draw_arrays(vao: u32, prog: u32, mode: u8, first: u32, count: u32) {
1842 unsafe {
1843 let Some(g) = GL.get() else { return };
1844 let (Some(vn), Some(pn)) = (NonZeroU32::new(vao), NonZeroU32::new(prog)) else {
1845 return;
1846 };
1847 let gl = &g.0;
1848 gl.use_program(Some(glow::NativeProgram(pn)));
1849 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1850 gl.draw_arrays(gl_draw_mode(mode), first as i32, count as i32);
1851 gl.bind_vertex_array(None);
1852 }
1853}
1854
1855unsafe extern "C" fn gfx_draw_elements(
1856 vao: u32,
1857 ebo: u32,
1858 prog: u32,
1859 mode: u8,
1860 count: u32,
1861 u32_idx: bool,
1862) {
1863 unsafe {
1864 let Some(g) = GL.get() else { return };
1865 let (Some(vn), Some(pn)) = (NonZeroU32::new(vao), NonZeroU32::new(prog)) else {
1866 return;
1867 };
1868 let gl = &g.0;
1869 gl.use_program(Some(glow::NativeProgram(pn)));
1870 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1871 let _ = ebo;
1873 let idx_type = if u32_idx {
1874 glow::UNSIGNED_INT
1875 } else {
1876 glow::UNSIGNED_SHORT
1877 };
1878 gl.draw_elements(gl_draw_mode(mode), count as i32, idx_type, 0);
1879 gl.bind_vertex_array(None);
1880 }
1881}
1882
1883unsafe extern "C" fn gfx_set_blend(enabled: bool, src: u32, dst: u32) {
1886 unsafe {
1887 let Some(g) = GL.get() else { return };
1888 let gl = &g.0;
1889 if enabled {
1890 gl.enable(glow::BLEND);
1891 gl.blend_func(src, dst);
1892 } else {
1893 gl.disable(glow::BLEND);
1894 }
1895 }
1896}
1897
1898unsafe extern "C" fn gfx_set_depth(test: bool, write: bool) {
1899 unsafe {
1900 let Some(g) = GL.get() else { return };
1901 let gl = &g.0;
1902 if test {
1903 gl.enable(glow::DEPTH_TEST);
1904 } else {
1905 gl.disable(glow::DEPTH_TEST);
1906 }
1907 gl.depth_mask(write);
1908 if !test {
1910 gl.disable(glow::CULL_FACE);
1911 }
1912 }
1913}
1914
1915unsafe extern "C" fn gfx_set_scissor(x: i32, y: i32, w: i32, h: i32) {
1916 unsafe {
1917 let Some(g) = GL.get() else { return };
1918 let gl = &g.0;
1919 gl.enable(glow::SCISSOR_TEST);
1920 gl.scissor(x, y, w, h);
1921 }
1922}
1923
1924unsafe extern "C" fn gfx_clear_scissor() {
1925 unsafe {
1926 let Some(g) = GL.get() else { return };
1927 g.0.disable(glow::SCISSOR_TEST);
1928 }
1929}
1930
1931unsafe extern "C" fn gfx_set_viewport(x: i32, y: i32, w: i32, h: i32) {
1932 unsafe {
1933 let Some(g) = GL.get() else { return };
1934 g.0.viewport(x, y, w, h);
1935 }
1936}
1937
1938unsafe extern "C" fn gfx_draw2d_rect(x1: f32, y1: f32, x2: f32, y2: f32, color: u32) {
1941 unsafe {
1942 let _: Option<()> = host!(
1943 "dev/yog/NativeDraw",
1944 "drawRect",
1945 [F(x1), F(y1), F(x2), F(y2), I(color as i32)]
1946 );
1947 }
1948}
1949
1950unsafe extern "C" fn gfx_draw2d_gradient(
1951 x1: f32,
1952 y1: f32,
1953 x2: f32,
1954 y2: f32,
1955 top: u32,
1956 bottom: u32,
1957) {
1958 unsafe {
1959 let _: Option<()> = host!(
1960 "dev/yog/NativeDraw",
1961 "drawGradientRect",
1962 [F(x1), F(y1), F(x2), F(y2), I(top as i32), I(bottom as i32)]
1963 );
1964 }
1965}
1966
1967unsafe extern "C" fn gfx_draw2d_text(text: YogStr, x: f32, y: f32, color: u32, shadow: bool) {
1968 unsafe {
1969 let _: Option<()> = host!(
1970 "dev/yog/NativeDraw",
1971 "drawText",
1972 [S(text), F(x), F(y), I(color as i32), Z(shadow)]
1973 );
1974 }
1975}
1976
1977unsafe extern "C" fn gfx_draw2d_text_font(
1978 text: YogStr,
1979 font: YogStr,
1980 x: f32,
1981 y: f32,
1982 color: u32,
1983 shadow: bool,
1984) {
1985 unsafe {
1986 let _: Option<()> = host!(
1987 "dev/yog/NativeDraw",
1988 "drawTextFont",
1989 [S(text), S(font), F(x), F(y), I(color as i32), Z(shadow)]
1990 );
1991 }
1992}
1993
1994unsafe extern "C" fn gfx_draw2d_text_scaled(
1995 text: YogStr,
1996 font: YogStr,
1997 x: f32,
1998 y: f32,
1999 scale: f32,
2000 color: u32,
2001 shadow: bool,
2002) {
2003 unsafe {
2004 let _: Option<()> = host!(
2005 "dev/yog/NativeDraw",
2006 "drawTextScaled",
2007 [S(text), S(font), F(x), F(y), F(scale), I(color as i32), Z(shadow)]
2008 );
2009 }
2010}
2011
2012unsafe extern "C" fn gfx_draw2d_blocks(blocks: YogStr, x: f32, y: f32, w: f32, h: f32, yaw: f32, pitch: f32) {
2013 if w <= 0.0 || h <= 0.0 {
2014 return;
2015 }
2016 unsafe {
2017 let _: Option<()> = host!(
2018 "dev/yog/NativeDraw",
2019 "drawBlocks",
2020 [S(blocks), F(x), F(y), F(w), F(h), F(yaw), F(pitch)]
2021 );
2022 }
2023}
2024
2025unsafe extern "C" fn gfx_text_width(text: YogStr, font: YogStr) -> f32 {
2026 unsafe { host!("dev/yog/NativeDraw", "textWidth", [S(text), S(font)]).unwrap_or(0.0) }
2027}
2028
2029unsafe extern "C" fn gfx_draw2d_item(id: YogStr, x: f32, y: f32, size: f32) {
2030 unsafe {
2031 let _: Option<()> = host!(
2032 "dev/yog/NativeDraw",
2033 "drawItem",
2034 [S(id), F(x), F(y), F(size)]
2035 );
2036 }
2037}
2038
2039static GFX_FN_TABLE: YogGfxApi = YogGfxApi {
2042 screen_w: 0,
2044 screen_h: 0,
2045 delta_tick: 0.0,
2046 scale_factor: 1.0,
2047 view_proj: [0.0; 16],
2048 camera_pos: [0.0; 3],
2049 player_pos: [0.0; 3],
2050 _pad1: 0.0,
2051 buf_create: gfx_buf_create,
2052 buf_delete: gfx_buf_delete,
2053 buf_data: gfx_buf_data,
2054 buf_subdata: gfx_buf_subdata,
2055 vao_create: gfx_vao_create,
2056 vao_delete: gfx_vao_delete,
2057 vao_attrib: gfx_vao_attrib,
2058 vao_set_ebo: gfx_vao_set_ebo,
2059 prog_create: gfx_prog_create,
2060 prog_delete: gfx_prog_delete,
2061 prog_uniform_1i: gfx_prog_uniform_1i,
2062 prog_uniform_1f: gfx_prog_uniform_1f,
2063 prog_uniform_2f: gfx_prog_uniform_2f,
2064 prog_uniform_3f: gfx_prog_uniform_3f,
2065 prog_uniform_4f: gfx_prog_uniform_4f,
2066 prog_uniform_mat4: gfx_prog_uniform_mat4,
2067 tex_create: gfx_tex_create,
2068 tex_delete: gfx_tex_delete,
2069 tex_bind: gfx_tex_bind,
2070 tex_from_mc: gfx_tex_from_mc,
2071 draw_arrays: gfx_draw_arrays,
2072 draw_elements: gfx_draw_elements,
2073 set_blend: gfx_set_blend,
2074 set_depth: gfx_set_depth,
2075 set_scissor: gfx_set_scissor,
2076 clear_scissor: gfx_clear_scissor,
2077 set_viewport: gfx_set_viewport,
2078 draw2d_rect: gfx_draw2d_rect,
2079 draw2d_gradient: gfx_draw2d_gradient,
2080 draw2d_text: gfx_draw2d_text,
2081 draw2d_item: gfx_draw2d_item,
2082 draw2d_text_font: gfx_draw2d_text_font,
2083 text_width: gfx_text_width,
2084 draw2d_image: images::gfx_draw2d_image,
2085 image_size: images::gfx_image_size,
2086 draw2d_svg: svg::gfx_draw2d_svg,
2087 draw2d_entity: svg::gfx_draw2d_entity,
2088 draw2d_text_scaled: gfx_draw2d_text_scaled,
2089 draw2d_blocks: gfx_draw2d_blocks,
2090};
2091
2092macro_rules! api_event {
2099 ($name:ident, $field:ident, $fn_ty:ty) => {
2100 unsafe extern "C" fn $name(ctx: *mut c_void, ud: *mut c_void, h: $fn_ty) {
2101 unsafe {
2102 let mut handlers = handlers_mut(ctx);
2103 handlers.$field.push((current_registration(), ud, h));
2104 }
2105 }
2106 };
2107}
2108
2109api_event!(api_on_block_break, block_break, YogBlockBreakFn);
2110api_event!(api_on_chat, chat, YogChatFn);
2111api_event!(api_on_player_join, player_join, YogPlayerFn);
2112api_event!(api_on_player_leave, player_leave, YogPlayerFn);
2113api_event!(api_on_use_item, use_item, YogUseItemFn);
2114api_event!(api_on_use_block, use_block, YogUseBlockFn);
2115api_event!(api_on_attack_entity, attack_entity, YogAttackEntityFn);
2116api_event!(api_on_entity_damage, entity_damage, YogEntityDamageFn);
2117api_event!(api_on_entity_death, entity_death, YogEntityDeathFn);
2118api_event!(api_on_entity_spawn, entity_spawn, YogEntitySpawnFn);
2119api_event!(
2120 api_on_player_place_block,
2121 player_place_block,
2122 YogPlaceBlockFn
2123);
2124api_event!(api_on_player_death, player_death, YogPlayerDeathFn);
2125api_event!(api_on_player_respawn, player_respawn, YogPlayerRespawnFn);
2126api_event!(api_on_advancement, advancement, YogAdvancementFn);
2127api_event!(api_on_entity_interact, entity_interact, YogEntityInteractFn);
2128api_event!(api_on_item_craft, item_craft, YogCraftFn);
2129api_event!(api_on_machine_craft, machine_craft, YogMachineCraftFn);
2130api_event!(api_on_explosion, explosion, YogExplosionFn);
2131api_event!(api_on_item_pickup, item_pickup, YogItemPickupFn);
2132api_event!(api_on_item_insert, item_insert, YogItemInsertFn);
2133api_event!(api_on_player_move, player_move, YogPlayerMoveFn);
2134api_event!(api_on_container_open, container_open, YogContainerOpenFn);
2135api_event!(api_on_container_close, container_close, YogContainerCloseFn);
2136api_event!(api_on_projectile_hit, projectile_hit, YogProjectileHitFn);
2137api_event!(api_on_client_tick, client_tick, YogClientFn);
2138api_event!(api_on_hud_render, hud_render, YogHudRenderFn);
2139api_event!(api_on_world_render, world_render, YogWorldRenderFn);
2140api_event!(api_on_key_press, key_press, YogKeyPressFn);
2141api_event!(api_on_screen_open, screen_open, YogScreenFn);
2142api_event!(api_on_screen_close, screen_close, YogScreenFn);
2143api_event!(api_on_server_tick, server_tick, YogServerFn);
2144api_event!(api_on_server_started, server_started, YogServerFn);
2145api_event!(api_on_server_stopping, server_stopping, YogServerFn);
2146
2147unsafe extern "C" fn api_on_packet(
2148 ctx: *mut c_void,
2149 channel: YogStr,
2150 ud: *mut c_void,
2151 h: YogPacketFn,
2152) {
2153 unsafe {
2154 let mut handlers = handlers_mut(ctx);
2155 handlers
2156 .packets
2157 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2158 }
2159}
2160
2161unsafe extern "C" fn api_on_client_packet(
2162 ctx: *mut c_void,
2163 channel: YogStr,
2164 ud: *mut c_void,
2165 h: YogPacketFn,
2166) {
2167 unsafe {
2168 let mut handlers = handlers_mut(ctx);
2169 handlers
2170 .client_packets
2171 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2172 }
2173}
2174
2175unsafe extern "C" fn api_register_command(
2176 ctx: *mut c_void,
2177 name: YogStr,
2178 ud: *mut c_void,
2179 h: YogCommandFn,
2180) {
2181 unsafe {
2182 let mut handlers = handlers_mut(ctx);
2183 handlers
2184 .commands
2185 .entry(name.as_str().to_owned())
2186 .or_default()
2187 .push((current_registration(), None, ud, h));
2188 }
2189}
2190
2191unsafe extern "C" fn api_register_typed_command(
2192 ctx: *mut c_void,
2193 name: YogStr,
2194 schema: YogStr,
2195 ud: *mut c_void,
2196 h: YogCommandFn,
2197) {
2198 unsafe {
2199 let mut handlers = handlers_mut(ctx);
2200 let n = name.as_str().to_owned();
2201 handlers.commands.entry(n).or_default().push((
2202 current_registration(),
2203 Some(schema.as_str().to_owned()),
2204 ud,
2205 h,
2206 ));
2207 }
2208}
2209
2210unsafe fn handlers_mut<'a>(ctx: *mut c_void) -> std::sync::RwLockWriteGuard<'a, RuntimeHandlers> {
2218 unsafe { (*(ctx as *const RwLock<RuntimeHandlers>)).write().unwrap_or_else(|e| e.into_inner()) }
2219}
2220
2221unsafe extern "C" fn api_register_content(ctx: *mut c_void, kind: YogStr, id: YogStr, data: *const u8, len: usize) {
2224 unsafe {
2225 let kind = kind.as_str();
2226 let id = id.as_str().to_owned();
2227 let bytes = if data.is_null() || len == 0 { &[][..] } else { std::slice::from_raw_parts(data, len) };
2228 let arena = yog_data::Bump::new();
2230 let value = match yog_data::binary::decode_ref(bytes, &arena) {
2231 Ok(v) => v,
2232 Err(e) => {
2233 yog_logging::warn!("{kind} {id}: not Yog Data: {e}");
2234 return;
2235 }
2236 };
2237 register_content(&mut handlers_mut(ctx), kind, id, &value);
2238 }
2239}
2240
2241fn register_content(h: &mut RuntimeHandlers, kind: &str, id: String, value: &yog_data::ValueRef) {
2243 *h.content_blob.get_mut().unwrap_or_else(|e| e.into_inner()) = None;
2244 fn read<T: yog_data::Schema>(kind: &str, id: &str, v: &yog_data::ValueRef) -> Option<T> {
2245 match yog_data::read_ref_value::<T>(v) {
2246 Ok(value) => Some(value),
2247 Err(problems) => {
2248 let list: Vec<String> = problems.iter().map(|p| format!("{}: {}", p.path, p.message)).collect();
2249 yog_logging::warn!("{kind} {id}: {}", list.join("; "));
2250 None
2251 }
2252 }
2253 }
2254 let none_if_empty = |s: &mut Option<String>| {
2255 if s.as_deref() == Some("") {
2256 *s = None;
2257 }
2258 };
2259 match kind {
2260 "item" => {
2261 if let Some(mut d) = read::<ItemDef>(kind, &id, value) {
2262 none_if_empty(&mut d.name);
2263 none_if_empty(&mut d.tooltip);
2264 h.items.push(d);
2265 }
2266 }
2267 "block" => {
2268 if let Some(mut d) = read::<BlockDef>(kind, &id, value) {
2269 none_if_empty(&mut d.name);
2270 none_if_empty(&mut d.sound);
2271 none_if_empty(&mut d.inventory_id);
2272 none_if_empty(&mut d.base);
2273 none_if_empty(&mut d.set);
2274 h.blocks.push(d);
2275 }
2276 }
2277 "entity" => {
2278 if read::<yog_registry::EntityDef>(kind, &id, value).is_some() {
2279 h.entities.push(value.to_value());
2280 }
2281 }
2282 "attribute" => {
2283 if read::<yog_registry::AttributeDef>(kind, &id, value).is_some() {
2284 h.attributes.push(value.to_value());
2285 }
2286 }
2287 "effect" => {
2288 if read::<yog_registry::EffectDef>(kind, &id, value).is_some() {
2289 h.effects.push(value.to_value());
2290 }
2291 }
2292 "potion" => {
2293 if read::<yog_registry::PotionDef>(kind, &id, value).is_some() {
2294 h.potions.push(value.to_value());
2295 }
2296 }
2297 "biome_feature" => {
2298 if let Some(f) = read::<yog_registry::BiomeFeature>(kind, &id, value) {
2299 h.biome_features.push(f);
2300 }
2301 }
2302 "tool_material" => {
2303 if let Some(m) = read::<yog_registry::ToolMaterial>(kind, &id, value) {
2304 h.tool_materials.insert(m.id.clone(), m);
2305 }
2306 }
2307 "armor_material" => {
2308 if let Some(m) = read::<yog_registry::ArmorMaterial>(kind, &id, value) {
2309 h.armor_materials.insert(m.id.clone(), m);
2310 }
2311 }
2312 "sound_group" => {
2313 if let Some(g) = read::<yog_registry::SoundGroup>(kind, &id, value) {
2314 h.sound_groups.insert(g.id.clone(), g);
2315 }
2316 }
2317 "inventory" => {
2318 if let Some(d) = read::<yog_inventory::InventoryDef>(kind, &id, value) {
2319 h.inventories.push(d);
2320 }
2321 }
2322 "startup_grant" => {
2323 if let Some(mut g) = read::<yog_registry::StartupGrant>(kind, &id, value) {
2324 none_if_empty(&mut g.book);
2325 none_if_empty(&mut g.command);
2326 h.startup_grants.push(g);
2327 }
2328 }
2329 "recipe" => {
2330 let (ns, name) = id.split_once(':').unwrap_or(("minecraft", &id));
2331 let recipe = value.to_value();
2332 for r in h.book_renderers.lock().unwrap().values_mut() {
2334 r.add_recipe(format!("{ns}:{name}"), &recipe);
2335 }
2336 h.recipes.push((ns.to_string(), name.to_string(), recipe));
2337 }
2338 "book" => {
2339 if let Some(mut book) = read::<yog_book::Book>(kind, &id, value) {
2341 book.entries.retain(|e| e.requires.iter().all(|m| mod_loaded(m)));
2343 h.books.insert(id.clone());
2344 let mut renderer = yog_book::BookRenderer::new(book);
2345 renderer.set_translator(translate);
2347 for (ns, name, recipe) in &h.recipes {
2349 renderer.add_recipe(format!("{ns}:{name}"), recipe);
2350 }
2351 h.book_renderers.get_mut().unwrap_or_else(|e| e.into_inner()).insert(id, renderer);
2352 }
2353 }
2354 "dimension" => {
2355 yog_logging::info!("registered dimension type: {}", id);
2356 let mut def = value.to_value();
2357 if let Some(t) = value.get("terrain") {
2359 let Some(terrain) = read::<yog_worldgen::Terrain>(kind, &id, t) else { return };
2360 if let yog_data::Value::Map(m) = &mut def {
2361 m.remove("terrain");
2362 m.insert("generator_type", yog_data::Value::from(id.as_str()));
2363 }
2364 h.terrains.insert(id.clone(), Arc::new(yog_worldgen::Generator::new(terrain)));
2365 }
2366 h.dimensions.insert(id, def);
2367 }
2368 other => yog_logging::warn!("unknown kind of content `{other}` ({id}) — this runtime doesn't know it"),
2369 }
2370}
2371
2372
2373
2374
2375unsafe extern "C" fn api_schedule_once(
2376 ctx: *mut c_void,
2377 delay_ticks: u64,
2378 ud: *mut c_void,
2379 h: YogScheduledFn,
2380) {
2381 unsafe {
2382 let handlers = handlers_mut(ctx);
2383 handlers
2384 .scheduler
2385 .lock()
2386 .expect("scheduler poisoned")
2387 .once_tasks
2388 .push(OnceTask {
2389 module: current_registration(),
2390 delay_remaining: delay_ticks,
2391 ud,
2392 f: h,
2393 });
2394 }
2395}
2396
2397unsafe extern "C" fn api_schedule_repeating(
2398 ctx: *mut c_void,
2399 period_ticks: u64,
2400 ud: *mut c_void,
2401 h: YogScheduledFn,
2402) {
2403 unsafe {
2404 let handlers = handlers_mut(ctx);
2405 handlers
2406 .scheduler
2407 .lock()
2408 .expect("scheduler poisoned")
2409 .repeating_tasks
2410 .push(RepeatingTask {
2411 module: current_registration(),
2412 period: period_ticks,
2413 ticks_left: period_ticks.max(1),
2414 ud,
2415 f: h,
2416 });
2417 }
2418}
2419
2420
2421
2422
2423unsafe extern "C" fn api_register_chunk_generator(
2424 ctx: *mut c_void,
2425 id: YogStr,
2426 ud: *mut c_void,
2427 h: yog_abi::YogChunkGeneratorFn,
2428) {
2429 unsafe {
2430 let mut handlers = handlers_mut(ctx);
2431 let id = id.as_str().to_owned();
2432 yog_logging::info!("registered chunk generator for dimension: {}", id);
2433 handlers
2434 .chunk_generators
2435 .insert(id, (current_registration(), ud, h));
2436 }
2437}
2438
2439unsafe extern "C" fn api_register_ui(
2440 ctx: *mut c_void,
2441 ui_id: YogStr,
2442 _layout_json: YogStr,
2443 ud: *mut c_void,
2444 h: yog_abi::YogUIEventFn,
2445) {
2446 unsafe {
2447 let mut handlers = handlers_mut(ctx);
2448 let id = ui_id.as_str().to_owned();
2449 handlers
2450 .ui_handlers
2451 .insert(id.clone(), (current_registration(), ud, h));
2452 yog_logging::info!("registered UI handler: {}", id);
2453 }
2454}
2455
2456unsafe extern "C" fn api_on_ui_render(
2457 ctx: *mut c_void,
2458 ui_id: YogStr,
2459 ud: *mut c_void,
2460 h: YogHudRenderFn,
2461) {
2462 unsafe {
2463 let mut handlers = handlers_mut(ctx);
2464 let id = ui_id.as_str().to_owned();
2465 handlers
2466 .ui_render_handlers
2467 .entry(id)
2468 .or_default()
2469 .push((current_registration(), ud, h));
2470 }
2471}
2472
2473unsafe extern "C" fn api_on_book_page(
2474 ctx: *mut c_void,
2475 drawer: YogStr,
2476 ud: *mut c_void,
2477 f: yog_abi::YogPageDrawFn,
2478) {
2479 unsafe {
2480 let mut handlers = handlers_mut(ctx);
2481 handlers
2482 .page_drawers
2483 .insert(drawer.as_str().to_owned(), (current_registration(), ud, f));
2484 }
2485}
2486
2487unsafe extern "C" fn api_on_item_tooltip(
2488 ctx: *mut c_void,
2489 item: YogStr,
2490 ud: *mut c_void,
2491 size: yog_abi::YogTooltipSizeFn,
2492 draw: yog_abi::YogTooltipDrawFn,
2493) {
2494 unsafe {
2495 let mut handlers = handlers_mut(ctx);
2496 handlers
2497 .item_tooltips
2498 .entry(item.as_str().to_owned())
2499 .or_default()
2500 .push((current_registration(), ud, size, draw));
2501 }
2502}
2503
2504unsafe extern "C" fn api_on_entity_tick(ctx: *mut c_void, entity_type: YogStr, ud: *mut c_void, f: yog_abi::YogEntityTickFn) {
2505 unsafe {
2506 handlers_mut(ctx).entity_ticks.entry(entity_type.as_str().to_owned()).or_default().push((current_registration(), ud, f));
2507 }
2508}
2509
2510#[unsafe(no_mangle)]
2512pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityTick<'l>(
2513 mut env: JNIEnv<'l>,
2514 _class: JClass<'l>,
2515 entity_type: JString<'l>,
2516 uuid: JString<'l>,
2517) {
2518 let (t, u) = (jstr!(env, entity_type), jstr!(env, uuid));
2519 let entries = handlers().entity_ticks.get(&t).cloned().unwrap_or_default();
2520 let srv = srv_ptr();
2521 guard("on_entity_tick", || each(&entries, |ud, f| unsafe { f(ud, srv, YogStr::from_str(&u)) }));
2522}
2523
2524unsafe extern "C" fn api_on_item_text(ctx: *mut c_void, item: YogStr, ud: *mut c_void, f: yog_abi::YogItemTextFn) {
2525 unsafe {
2526 handlers_mut(ctx).item_texts.entry(item.as_str().to_owned()).or_default().push((current_registration(), ud, f));
2527 }
2528}
2529
2530unsafe extern "C" fn copy_text(sink: *mut c_void, text: YogStr) {
2531 unsafe { (*(sink as *mut String)).push_str(text.as_str()) }
2533}
2534
2535fn item_text(item: &str, snbt: &str) -> Option<String> {
2538 let entries = handlers().item_texts.get(item).cloned().unwrap_or_default();
2539 for (module, ud, f) in entries {
2540 let Some(_call) = module.enter() else { continue };
2541 let mut out = String::new();
2542 let mut given = false;
2543 guard("item_text", || unsafe {
2544 given = f(ud, YogStr::from_str(item), YogStr::from_str(snbt), &mut out as *mut String as *mut c_void, copy_text);
2545 });
2546 if given {
2547 return Some(out);
2548 }
2549 }
2550 None
2551}
2552
2553#[unsafe(no_mangle)]
2554pub extern "system" fn Java_dev_yog_NativeBridge_nativeItemText<'l>(
2555 mut env: JNIEnv<'l>,
2556 _class: JClass<'l>,
2557 item: JString<'l>,
2558 snbt: JString<'l>,
2559) -> jstring {
2560 let item: String = env.get_string(&item).map(String::from).unwrap_or_default();
2561 let snbt: String = env.get_string(&snbt).map(String::from).unwrap_or_default();
2562 match item_text(&item, &snbt) {
2563 Some(t) => env.new_string(t).map(JString::into_raw).unwrap_or(std::ptr::null_mut()),
2564 None => std::ptr::null_mut(),
2565 }
2566}
2567
2568fn machine_event<'a>(
2572 m: &'a yog_inventory::Machine,
2573 def: &'a str,
2574 found: &'a machine::Found,
2575 dim: &'a str,
2576 pos: YogBlockPos,
2577 player: &'a str,
2578) -> YogMachineCraftEvent {
2579 YogMachineCraftEvent {
2580 machine: YogStr::from_str(def),
2581 kind: YogStr::from_str(&m.kind),
2582 recipe: YogStr::from_str(&found.recipe),
2583 result_item: YogStr::from_str(&found.result),
2584 result_count: found.count,
2585 values: YogStr::from_str(&found.values),
2586 pos,
2587 dimension: YogStr::from_str(dim),
2588 player: YogStr::from_str(player),
2589 }
2590}
2591
2592fn machine_recipe(def: &str, dim: &str, pos: YogBlockPos, items: &[String], recipe: Option<&str>) -> Option<machine::Found> {
2595 let h = handlers();
2596 let m = h.inventories.iter().find(|d| d.id == def)?.machine.as_ref()?;
2597 let has_tag = |item: &str, tag: &str| unsafe { srv_has_item_tag(std::ptr::null_mut(), YogStr::from_str(item), YogStr::from_str(tag)) };
2598 let recipes = h.recipes.iter().map(|(ns, name, r)| (format!("{ns}:{name}"), r)).filter(|(id, _)| recipe.is_none_or(|r| r == id));
2599 let srv = srv_ptr();
2600 machine::find(m, recipes, items, &has_tag).find(|found| {
2601 let ev = machine_event(m, def, found, dim, pos, "");
2602 allowed(&h.machine_craft, |ud, f| unsafe { f(ud, srv, &ev, 0) })
2603 })
2604}
2605
2606#[unsafe(no_mangle)]
2609pub extern "system" fn Java_dev_yog_NativeBridge_nativeMachineResult<'l>(
2610 mut env: JNIEnv<'l>,
2611 _class: JClass<'l>,
2612 def: JString<'l>,
2613 dimension: JString<'l>,
2614 x: jint,
2615 y: jint,
2616 z: jint,
2617 items: JString<'l>,
2618) -> jstring {
2619 let (def, d, items) = (jstr!(env, def, std::ptr::null_mut()), jstr!(env, dimension, std::ptr::null_mut()), jstr!(env, items, std::ptr::null_mut()));
2620 let items: Vec<String> = items.split('\n').map(str::to_string).collect();
2621 let mut found = None;
2622 guard("machine_result", || found = machine_recipe(&def, &d, YogBlockPos { x, y, z }, &items, None));
2623 match found {
2624 Some(f) => env.new_string(format!("{}\n{}\n{}", f.recipe, f.result, f.count)).map(JString::into_raw).unwrap_or(std::ptr::null_mut()),
2625 None => std::ptr::null_mut(),
2626 }
2627}
2628
2629#[unsafe(no_mangle)]
2632#[allow(clippy::too_many_arguments)]
2633pub extern "system" fn Java_dev_yog_NativeBridge_nativeMachineCraft<'l>(
2634 mut env: JNIEnv<'l>,
2635 _class: JClass<'l>,
2636 def: JString<'l>,
2637 recipe: JString<'l>,
2638 dimension: JString<'l>,
2639 x: jint,
2640 y: jint,
2641 z: jint,
2642 items: JString<'l>,
2643 player: JString<'l>,
2644) -> jni::sys::jboolean {
2645 let (def, recipe, d, items, player) =
2646 (jstr!(env, def, 0), jstr!(env, recipe, 0), jstr!(env, dimension, 0), jstr!(env, items, 0), jstr!(env, player, 0));
2647 let items: Vec<String> = items.split('\n').map(str::to_string).collect();
2648 let pos = YogBlockPos { x, y, z };
2649 let mut made = false;
2650 guard("machine_craft", || {
2651 let Some(found) = machine_recipe(&def, &d, pos, &items, Some(&recipe)) else { return };
2652 let h = handlers();
2653 let Some(m) = h.inventories.iter().find(|i| i.id == def).and_then(|i| i.machine.as_ref()) else { return };
2654 let ev = machine_event(m, &def, &found, &d, pos, &player);
2655 let srv = srv_ptr();
2656 each(&h.machine_craft, |ud, f| unsafe { f(ud, srv, &ev, 1) });
2657 made = true;
2658 });
2659 made as jni::sys::jboolean
2660}
2661
2662static YOG_MOD_IDS: RwLock<std::collections::BTreeSet<String>> = RwLock::new(std::collections::BTreeSet::new());
2664
2665fn mod_loaded(id: &str) -> bool {
2668 YOG_MOD_IDS.read().unwrap_or_else(|e| e.into_inner()).contains(id)
2669 || HOT_RELOADER.get().is_some_and(|r| r.all_active().iter().any(|(m, _)| m == id))
2670 || unsafe { bridge!("isModLoaded", [JArg::S(YogStr::from_str(id))]) }.unwrap_or(false)
2672}
2673
2674unsafe extern "C" fn api_is_mod_loaded(_ctx: *mut c_void, id: YogStr) -> bool {
2675 unsafe { mod_loaded(id.as_str()) }
2676}
2677
2678unsafe extern "C" fn api_java_call(_ctx: *mut c_void, expr: YogStr, args: YogStr) -> YogOwnedStr {
2679 unsafe {
2680 let args = match yog_data::text::parse_value(args.as_str()) {
2681 Ok(v) => v,
2682 Err(e) => {
2683 yog_logging::warn!("java_call {}: its arguments aren't Yog Data: {}", expr.as_str(), e.message);
2684 return YogOwnedStr::NONE;
2685 }
2686 };
2687 let bytes = yog_data::binary::encode(&args);
2688 let out: Option<Vec<u8>> = host!("dev/yog/data/YogJava", "call", [JArg::S(expr), JArg::Bytes(&bytes)]);
2689 match out.map(|b| yog_data::binary::decode(&b)) {
2690 Some(Ok(v)) => YogOwnedStr::from_string(yog_data::text::to_string(&v)),
2691 _ => YogOwnedStr::NONE,
2692 }
2693 }
2694}
2695
2696fn tsv_clean(v: &str) -> String {
2698 v.replace(['\t', '\n', '\r'], " ")
2699}
2700
2701unsafe extern "C" fn api_ui_open(_ctx: *mut c_void, ui_id: YogStr, modal: bool, pause: bool) {
2702 unsafe {
2703 if ui_id.as_str().is_empty() {
2704 return;
2705 }
2706 let _: Option<()> = bridge!("openUI", [S(ui_id), Z(modal), Z(pause)]);
2708 }
2709}
2710
2711unsafe extern "C" fn api_ui_close(_ctx: *mut c_void, ui_id: YogStr) {
2712 unsafe {
2713 let _: Option<()> = bridge!("closeUI", [S(ui_id)]);
2716 }
2717}
2718
2719unsafe extern "C" fn api_ui_consume_event(_ctx: *mut c_void) {
2720 ui_jni::consume_event();
2721}
2722
2723unsafe extern "C" fn api_translate(_ctx: *mut c_void, key: YogStr) -> YogOwnedStr {
2724 unsafe { translate(key.as_str()).map_or(YogOwnedStr::NONE, YogOwnedStr::from_string) }
2725}
2726
2727fn translate(key: &str) -> Option<String> {
2730 if key.is_empty() {
2731 return None;
2732 }
2733 unsafe { bridge!("translate", [S(YogStr::from_str(key))]) }
2734}
2735
2736unsafe extern "C" fn api_mods_list(_ctx: *mut c_void) -> YogOwnedStr {
2737 unsafe {
2738 let mut lines: Vec<String> = Vec::new();
2739 let infos = MOD_INFOS.lock().expect("mod infos lock poisoned").clone();
2740 for m in &infos {
2741 let localized = |text: &str, key: &str| {
2743 translate(key).map_or_else(|| text.to_string(), |t| tsv_clean(&t))
2744 };
2745 lines.push(format!(
2746 "yog\t{}\t{}\t{}\t{}\t{}",
2747 m[0],
2748 localized(&m[1], &m[5]),
2749 m[2],
2750 m[3],
2751 localized(&m[4], &m[6])
2752 ));
2753 }
2754 let platform: Option<String> = bridge!("listPlatformMods", []);
2756 for line in platform.iter().flat_map(|s| s.lines()) {
2757 if !line.trim().is_empty() {
2758 lines.push(format!("platform\t{line}"));
2759 }
2760 }
2761 YogOwnedStr::from_string(lines.join("\n"))
2762 }
2763}
2764
2765unsafe extern "C" fn api_register_menu_entry(ctx: *mut c_void, label: YogStr, ui_id: YogStr) {
2766 unsafe {
2767 let mut handlers = handlers_mut(ctx);
2768 let l = label.as_str().to_owned();
2769 let u = ui_id.as_str().to_owned();
2770 handlers.menu_entries.push((l, u));
2771 }
2772}
2773
2774fn vec_args(v: YogVec3) -> [JArg<'static>; 3] {
2780 [JArg::D(v.x), JArg::D(v.y), JArg::D(v.z)]
2781}
2782
2783fn pos_args(p: YogBlockPos) -> [JArg<'static>; 3] {
2784 [JArg::I(p.x), JArg::I(p.y), JArg::I(p.z)]
2785}
2786
2787unsafe extern "C" fn srv_entity_info(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
2788 unsafe { owned(bridge!("entityInfo", [JArg::S(uuid)])) }
2789}
2790
2791unsafe extern "C" fn srv_entities_in_box(
2792 _ctx: *mut c_void,
2793 dim: YogStr,
2794 min: YogVec3,
2795 max: YogVec3,
2796) -> YogOwnedStr {
2797 unsafe {
2798 let [a, b, c] = vec_args(min);
2799 let [d, e, f] = vec_args(max);
2800 owned(bridge!("entitiesInBox", [JArg::S(dim), a, b, c, d, e, f]))
2801 }
2802}
2803
2804unsafe extern "C" fn srv_raycast_block(
2805 _ctx: *mut c_void,
2806 dim: YogStr,
2807 from: YogVec3,
2808 to: YogVec3,
2809) -> YogOwnedStr {
2810 unsafe {
2811 let [a, b, c] = vec_args(from);
2812 let [d, e, f] = vec_args(to);
2813 owned(bridge!("raycastBlock", [JArg::S(dim), a, b, c, d, e, f]))
2814 }
2815}
2816
2817unsafe extern "C" fn srv_raycast_entity(
2818 _ctx: *mut c_void,
2819 dim: YogStr,
2820 from: YogVec3,
2821 to: YogVec3,
2822 exclude: YogStr,
2823) -> YogOwnedStr {
2824 unsafe {
2825 let [a, b, c] = vec_args(from);
2826 let [d, e, f] = vec_args(to);
2827 owned(bridge!(
2828 "raycastEntity",
2829 [JArg::S(dim), a, b, c, d, e, f, JArg::S(exclude)]
2830 ))
2831 }
2832}
2833
2834unsafe extern "C" fn srv_block_info(
2835 _ctx: *mut c_void,
2836 dim: YogStr,
2837 pos: YogBlockPos,
2838) -> YogOwnedStr {
2839 unsafe {
2840 let [x, y, z] = pos_args(pos);
2841 owned(bridge!("blockInfo", [JArg::S(dim), x, y, z]))
2842 }
2843}
2844
2845unsafe extern "C" fn srv_break_block(
2846 _ctx: *mut c_void,
2847 dim: YogStr,
2848 pos: YogBlockPos,
2849 drop: bool,
2850 breaker: YogStr,
2851) -> bool {
2852 unsafe {
2853 let [x, y, z] = pos_args(pos);
2854 bridge!(
2855 "breakBlock",
2856 [JArg::S(dim), x, y, z, JArg::Z(drop), JArg::S(breaker)]
2857 )
2858 .unwrap_or(false)
2859 }
2860}
2861
2862unsafe extern "C" fn srv_place_block_from_slot(
2863 _ctx: *mut c_void,
2864 player: YogStr,
2865 slot: u32,
2866 dim: YogStr,
2867 pos: YogBlockPos,
2868) -> bool {
2869 unsafe {
2870 let [x, y, z] = pos_args(pos);
2871 bridge!(
2872 "placeBlockFromSlot",
2873 [JArg::S(player), JArg::I(slot as i32), JArg::S(dim), x, y, z]
2874 )
2875 .unwrap_or(false)
2876 }
2877}
2878
2879unsafe extern "C" fn srv_bonemeal(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> bool {
2880 unsafe {
2881 let [x, y, z] = pos_args(pos);
2882 bridge!("bonemeal", [JArg::S(dim), x, y, z]).unwrap_or(false)
2883 }
2884}
2885
2886unsafe extern "C" fn srv_explode(
2887 _ctx: *mut c_void,
2888 dim: YogStr,
2889 pos: YogVec3,
2890 power: f32,
2891 fire: bool,
2892 source: YogStr,
2893) -> bool {
2894 unsafe {
2895 let [x, y, z] = vec_args(pos);
2896 bridge!(
2897 "explode",
2898 [
2899 JArg::S(dim),
2900 x,
2901 y,
2902 z,
2903 JArg::F(power),
2904 JArg::Z(fire),
2905 JArg::S(source),
2906 ]
2907 )
2908 .unwrap_or(false)
2909 }
2910}
2911
2912unsafe extern "C" fn srv_tellraw(_ctx: *mut c_void, player: YogStr, json: YogStr) -> bool {
2913 unsafe { bridge!("tellraw", [JArg::S(player), JArg::S(json)]).unwrap_or(false) }
2914}
2915
2916unsafe extern "C" fn srv_entity_damage(
2917 _ctx: *mut c_void,
2918 uuid: YogStr,
2919 amount: f32,
2920 damage_type: YogStr,
2921 attacker: YogStr,
2922) -> bool {
2923 unsafe {
2924 bridge!(
2925 "entityDamage",
2926 [
2927 JArg::S(uuid),
2928 JArg::F(amount),
2929 JArg::S(damage_type),
2930 JArg::S(attacker),
2931 ]
2932 )
2933 .unwrap_or(false)
2934 }
2935}
2936
2937unsafe extern "C" fn srv_entity_set_fire_ticks(
2938 _ctx: *mut c_void,
2939 uuid: YogStr,
2940 ticks: i32,
2941) -> bool {
2942 unsafe { bridge!("entitySetFireTicks", [JArg::S(uuid), JArg::I(ticks)]).unwrap_or(false) }
2943}
2944
2945unsafe extern "C" fn srv_player_add_xp(_ctx: *mut c_void, player: YogStr, amount: i32) -> bool {
2946 unsafe { bridge!("playerAddXp", [JArg::S(player), JArg::I(amount)]).unwrap_or(false) }
2947}
2948
2949unsafe extern "C" fn srv_player_set_may_fly(
2950 _ctx: *mut c_void,
2951 player: YogStr,
2952 may_fly: bool,
2953) -> bool {
2954 unsafe { bridge!("playerSetMayFly", [JArg::S(player), JArg::Z(may_fly)]).unwrap_or(false) }
2955}
2956
2957unsafe extern "C" fn srv_player_info(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
2958 unsafe { owned(bridge!("playerInfo", [JArg::S(player)])) }
2959}
2960
2961unsafe extern "C" fn srv_spawn_item(
2962 _ctx: *mut c_void,
2963 dim: YogStr,
2964 pos: YogVec3,
2965 vel: YogVec3,
2966 item_id: YogStr,
2967 count: u32,
2968 snbt: YogStr,
2969) -> YogOwnedStr {
2970 unsafe {
2971 let [a, b, c] = vec_args(pos);
2972 let [d, e, f] = vec_args(vel);
2973 owned(bridge!(
2974 "spawnItem",
2975 [
2976 JArg::S(dim),
2977 a,
2978 b,
2979 c,
2980 d,
2981 e,
2982 f,
2983 JArg::S(item_id),
2984 JArg::I(count as i32),
2985 JArg::S(snbt),
2986 ]
2987 ))
2988 }
2989}
2990
2991unsafe extern "C" fn srv_item_entity_stack(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
2992 unsafe { owned(bridge!("itemEntityStack", [JArg::S(uuid)])) }
2993}
2994
2995unsafe extern "C" fn srv_actionbar_json(_ctx: *mut c_void, player: YogStr, json: YogStr) -> bool {
2996 unsafe { bridge!("actionbarJson", [JArg::S(player), JArg::S(json)]).unwrap_or(false) }
2997}
2998
2999unsafe extern "C" fn srv_use_item_on_block(
3000 _ctx: *mut c_void,
3001 dim: YogStr,
3002 pos: YogBlockPos,
3003 face: u8,
3004 item_id: YogStr,
3005 player: YogStr,
3006) -> bool {
3007 unsafe {
3008 let [x, y, z] = pos_args(pos);
3009 bridge!(
3010 "useItemOnBlock",
3011 [
3012 JArg::S(dim),
3013 x,
3014 y,
3015 z,
3016 JArg::I(face as i32),
3017 JArg::S(item_id),
3018 JArg::S(player),
3019 ]
3020 )
3021 .unwrap_or(false)
3022 }
3023}
3024
3025unsafe extern "C" fn srv_pour_fluid(
3026 _ctx: *mut c_void,
3027 dim: YogStr,
3028 pos: YogBlockPos,
3029 bucket_item_id: YogStr,
3030) -> bool {
3031 unsafe {
3032 let [x, y, z] = pos_args(pos);
3033 bridge!(
3034 "pourFluid",
3035 [JArg::S(dim), x, y, z, JArg::S(bucket_item_id)]
3036 )
3037 .unwrap_or(false)
3038 }
3039}
3040
3041unsafe extern "C" fn srv_has_entity_tag(
3042 _ctx: *mut c_void,
3043 entity_type: YogStr,
3044 tag: YogStr,
3045) -> bool {
3046 unsafe { bridge!("hasEntityTag", [JArg::S(entity_type), JArg::S(tag)]).unwrap_or(false) }
3047}
3048
3049unsafe extern "C" fn srv_item_block(_ctx: *mut c_void, item_id: YogStr) -> YogOwnedStr {
3050 unsafe { owned(bridge!("itemBlock", [JArg::S(item_id)])) }
3051}
3052
3053unsafe extern "C" fn srv_place_feature(
3054 _ctx: *mut c_void,
3055 dim: YogStr,
3056 pos: YogBlockPos,
3057 feature_id: YogStr,
3058) -> bool {
3059 unsafe {
3060 let [x, y, z] = pos_args(pos);
3061 bridge!("placeFeature", [JArg::S(dim), x, y, z, JArg::S(feature_id)]).unwrap_or(false)
3062 }
3063}
3064
3065unsafe extern "C" fn srv_item_entity_set_stack(
3066 _ctx: *mut c_void,
3067 uuid: YogStr,
3068 item_id: YogStr,
3069 count: u32,
3070 snbt: YogStr,
3071) -> bool {
3072 unsafe {
3073 bridge!(
3074 "itemEntitySetStack",
3075 [
3076 JArg::S(uuid),
3077 JArg::S(item_id),
3078 JArg::I(count as i32),
3079 JArg::S(snbt),
3080 ]
3081 )
3082 .unwrap_or(false)
3083 }
3084}
3085
3086fn build_server_table() -> YogServer {
3089 YogServer {
3090 ctx: std::ptr::null_mut(),
3091 abi_version: ABI_VERSION,
3092 size: std::mem::size_of::<YogServer>() as u32,
3093 free_str: yog_free_str,
3094 broadcast: srv_broadcast,
3095 get_block: srv_get_block,
3096 set_block: srv_set_block,
3097 world_time: srv_world_time,
3098 set_time: srv_set_time,
3099 is_raining: srv_is_raining,
3100 set_weather: srv_set_weather,
3101 give_item: srv_give_item,
3102 player_teleport: srv_player_teleport,
3103 send_to_player: srv_send_to_player,
3104 send_to_server: srv_send_to_server,
3105 kick_player: srv_kick_player,
3106 set_gamemode: srv_set_gamemode,
3107 send_title: srv_send_title,
3108 send_actionbar: srv_send_actionbar,
3109 play_sound: srv_play_sound,
3110 play_sound_player: srv_play_sound_player,
3111 entity_teleport: srv_entity_teleport,
3112 entity_position: srv_entity_position,
3113 entity_rotation: srv_entity_rotation,
3114 entity_health: srv_entity_health,
3115 entity_set_health: srv_entity_set_health,
3116 entity_kill: srv_entity_kill,
3117 spawn_entity: srv_spawn_entity,
3118 entity_add_effect: srv_entity_add_effect,
3119 entity_remove_effect: srv_entity_remove_effect,
3120 entity_clear_effects: srv_entity_clear_effects,
3121 entity_velocity: srv_entity_velocity,
3122 entity_set_velocity: srv_entity_set_velocity,
3123 entity_add_velocity: srv_entity_add_velocity,
3124 has_item_tag: srv_has_item_tag,
3125 has_block_tag: srv_has_block_tag,
3126 drop_loot: srv_drop_loot,
3127 scoreboard_get: srv_scoreboard_get,
3128 scoreboard_set: srv_scoreboard_set,
3129 scoreboard_add: srv_scoreboard_add,
3130 bossbar_create: srv_bossbar_create,
3131 bossbar_remove: srv_bossbar_remove,
3132 bossbar_set_title: srv_bossbar_set_title,
3133 bossbar_set_progress: srv_bossbar_set_progress,
3134 bossbar_set_color: srv_bossbar_set_color,
3135 bossbar_add_player: srv_bossbar_add_player,
3136 bossbar_remove_player: srv_bossbar_remove_player,
3137 bossbar_set_visible: srv_bossbar_set_visible,
3138 game_dir: srv_game_dir,
3139 get_block_nbt: srv_get_block_nbt,
3140 set_block_nbt: srv_set_block_nbt,
3141 get_inventory_slot: srv_get_inventory_slot,
3142 set_inventory_slot: srv_set_inventory_slot,
3143 player_inventory: srv_player_inventory,
3144 player_set_slot: srv_player_set_slot,
3145 player_teleport_dim: srv_player_teleport_dim,
3146 entity_teleport_dim: srv_entity_teleport_dim,
3147 online_players: srv_online_players,
3148 world_entity_count: srv_world_entity_count,
3149 entity_get_nbt: srv_entity_get_nbt,
3150 entity_set_nbt: srv_entity_set_nbt,
3151 spawn_particles: srv_spawn_particles,
3152 entity_attribute_get: srv_entity_attribute_get,
3153 entity_attribute_set: srv_entity_attribute_set,
3154 get_held_item_nbt: srv_get_held_item_nbt,
3155 set_held_item_nbt: srv_set_held_item_nbt,
3156 get_offhand_item_nbt: srv_get_offhand_item_nbt,
3157 set_offhand_item_nbt: srv_set_offhand_item_nbt,
3158 get_slot_item: srv_get_slot_item,
3159 set_slot_item: srv_set_slot_item,
3160 entity_info: srv_entity_info,
3161 entities_in_box: srv_entities_in_box,
3162 raycast_block: srv_raycast_block,
3163 raycast_entity: srv_raycast_entity,
3164 block_info: srv_block_info,
3165 break_block: srv_break_block,
3166 place_block_from_slot: srv_place_block_from_slot,
3167 bonemeal: srv_bonemeal,
3168 explode: srv_explode,
3169 tellraw: srv_tellraw,
3170 entity_damage: srv_entity_damage,
3171 entity_set_fire_ticks: srv_entity_set_fire_ticks,
3172 player_add_xp: srv_player_add_xp,
3173 player_set_may_fly: srv_player_set_may_fly,
3174 player_info: srv_player_info,
3175 spawn_item: srv_spawn_item,
3176 item_entity_stack: srv_item_entity_stack,
3177 item_entity_set_stack: srv_item_entity_set_stack,
3178 actionbar_json: srv_actionbar_json,
3179 use_item_on_block: srv_use_item_on_block,
3180 pour_fluid: srv_pour_fluid,
3181 has_entity_tag: srv_has_entity_tag,
3182 item_block: srv_item_block,
3183 place_feature: srv_place_feature,
3184 set_item_cooldown: srv_set_item_cooldown,
3185 entity_vehicle: srv_entity_vehicle,
3186 entity_passengers: srv_entity_passengers,
3187 entity_mount: srv_entity_mount,
3188 entity_dismount: srv_entity_dismount,
3189 redstone_power: srv_redstone_power,
3190 entity_set_dimensions: srv_entity_set_dimensions,
3191 entity_data_set: entity_data::srv_entity_data_set,
3192 entity_data_get: entity_data::srv_entity_data_get,
3193 world_dir: srv_world_dir,
3194 entity_attribute_value: srv_entity_attribute_value,
3195 advancement_grant: srv_advancement_grant,
3196 advancement_done: srv_advancement_done,
3197 entity_set_fall_distance: srv_entity_set_fall_distance,
3198 redstone_signal: srv_redstone_signal,
3199 set_redstone_output: srv_set_redstone_output,
3200 get_block_state: srv_get_block_state,
3201 set_block_state: srv_set_block_state,
3202 world_is_thundering: srv_world_is_thundering,
3203 world_set_thunder: srv_world_set_thunder,
3204 fill_region: srv_fill_region,
3205 scan_region: srv_scan_region,
3206 }
3207}
3208
3209unsafe extern "C" fn srv_set_item_cooldown(_ctx: *mut c_void, player: YogStr, item_id: YogStr, ticks: i32) -> bool {
3212 unsafe { bridge!("setItemCooldown", [S(player), S(item_id), I(ticks)]).unwrap_or(false) }
3213}
3214
3215unsafe extern "C" fn srv_entity_vehicle(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
3216 unsafe { owned(bridge!("entityVehicle", [S(uuid)])) }
3217}
3218
3219unsafe extern "C" fn srv_entity_passengers(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
3220 unsafe { owned(bridge!("entityPassengers", [S(uuid)])) }
3221}
3222
3223unsafe extern "C" fn srv_entity_mount(_ctx: *mut c_void, rider: YogStr, vehicle: YogStr, force: bool) -> bool {
3224 unsafe { bridge!("entityMount", [S(rider), S(vehicle), Z(force)]).unwrap_or(false) }
3225}
3226
3227unsafe extern "C" fn srv_entity_dismount(_ctx: *mut c_void, uuid: YogStr) -> bool {
3228 unsafe { bridge!("entityDismount", [S(uuid)]).unwrap_or(false) }
3229}
3230
3231unsafe extern "C" fn srv_entity_set_dimensions(_ctx: *mut c_void, uuid: YogStr, w: f32, h: f32, eye: f32) -> bool {
3233 let found: bool = unsafe { bridge!("entitySetDimensions", [S(uuid), F(w), F(h), F(eye)]).unwrap_or(false) };
3234 if found {
3235 let value = (w >= 0.0).then(|| entity_data::encode_dims(w, h, eye));
3236 entity_data::set(unsafe { uuid.as_str() }, entity_data::DIMENSIONS, value.as_deref(), false);
3237 }
3238 found
3239}
3240
3241unsafe extern "C" fn srv_entity_set_fall_distance(_ctx: *mut c_void, uuid: YogStr, distance: f32) -> bool {
3242 unsafe { bridge!("entitySetFallDistance", [S(uuid), F(distance)]).unwrap_or(false) }
3243}
3244
3245unsafe extern "C" fn srv_redstone_signal(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32 {
3246 unsafe { bridge!("redstoneSignal", [S(dim), I(pos.x), I(pos.y), I(pos.z)]).unwrap_or(-1) }
3247}
3248
3249unsafe extern "C" fn srv_redstone_power(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32 {
3250 unsafe { bridge!("redstonePower", [S(dim), I(pos.x), I(pos.y), I(pos.z)]).unwrap_or(-1) }
3251}
3252
3253unsafe extern "C" fn srv_set_redstone_output(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, level: i32) -> bool {
3254 unsafe { bridge!("setRedstoneOutput", [S(dim), I(pos.x), I(pos.y), I(pos.z), I(level)]).unwrap_or(false) }
3255}
3256
3257unsafe extern "C" fn srv_get_block_state(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr {
3258 unsafe { owned(bridge!("getBlockState", [S(dim), I(pos.x), I(pos.y), I(pos.z)])) }
3259}
3260
3261unsafe extern "C" fn srv_set_block_state(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, state: YogStr) -> bool {
3262 unsafe { bridge!("setBlockState", [S(dim), I(pos.x), I(pos.y), I(pos.z), S(state)]).unwrap_or(false) }
3263}
3264
3265unsafe extern "C" fn srv_world_is_thundering(_ctx: *mut c_void, dim: YogStr) -> bool {
3266 unsafe { bridge!("worldIsThundering", [S(dim)]).unwrap_or(false) }
3267}
3268
3269unsafe extern "C" fn srv_world_set_thunder(_ctx: *mut c_void, dim: YogStr, thundering: bool, duration_ticks: i32) -> bool {
3270 unsafe { bridge!("worldSetThunder", [S(dim), Z(thundering), I(duration_ticks)]).unwrap_or(false) }
3271}
3272
3273unsafe extern "C" fn srv_fill_region(
3274 _ctx: *mut c_void,
3275 dim: YogStr,
3276 from: YogBlockPos,
3277 to: YogBlockPos,
3278 state: YogStr,
3279) -> i32 {
3280 unsafe {
3281 bridge!(
3282 "fillRegion",
3283 [S(dim), I(from.x), I(from.y), I(from.z), I(to.x), I(to.y), I(to.z), S(state)]
3284 )
3285 .unwrap_or(-1)
3286 }
3287}
3288
3289unsafe extern "C" fn srv_scan_region(_ctx: *mut c_void, dim: YogStr, from: YogBlockPos, to: YogBlockPos) -> YogOwnedStr {
3290 unsafe { owned(bridge!("scanRegion", [S(dim), I(from.x), I(from.y), I(from.z), I(to.x), I(to.y), I(to.z)])) }
3291}
3292
3293unsafe extern "C" fn interop_export_impl(
3296 _ctx: *mut c_void,
3297 mod_id: YogStr,
3298 symbol: YogStr,
3299 ptr: *const c_void,
3300) {
3301 unsafe {
3302 let mod_id = mod_id.as_str();
3303 let sym = symbol.as_str();
3304 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3305 registry
3306 .lock()
3307 .expect("interop symbols lock poisoned")
3308 .entry(mod_id.to_string())
3309 .or_default()
3310 .insert(sym.to_string(), ptr as usize);
3311 }
3312}
3313
3314unsafe extern "C" fn interop_import_impl(
3315 _ctx: *mut c_void,
3316 mod_id: YogStr,
3317 symbol: YogStr,
3318) -> *const c_void {
3319 unsafe {
3320 let mod_id = mod_id.as_str();
3321 let sym = symbol.as_str();
3322 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3323 registry
3324 .lock()
3325 .expect("interop symbols lock poisoned")
3326 .get(mod_id)
3327 .and_then(|m: &HashMap<String, usize>| m.get(sym))
3328 .map(|&ptr| ptr as *const c_void)
3329 .unwrap_or(std::ptr::null())
3330 }
3331}
3332
3333fn build_api_table(ctx: *const RwLock<RuntimeHandlers>, server: *const YogServer) -> YogApi {
3334 YogApi {
3335 abi_version: ABI_VERSION,
3336 size: std::mem::size_of::<YogApi>() as u32,
3337 ctx: ctx as *mut c_void,
3338 server,
3339 on_block_break: api_on_block_break,
3340 on_chat: api_on_chat,
3341 on_player_join: api_on_player_join,
3342 on_player_leave: api_on_player_leave,
3343 on_use_item: api_on_use_item,
3344 on_use_block: api_on_use_block,
3345 on_attack_entity: api_on_attack_entity,
3346 on_entity_damage: api_on_entity_damage,
3347 on_entity_death: api_on_entity_death,
3348 on_entity_spawn: api_on_entity_spawn,
3349 on_player_place_block: api_on_player_place_block,
3350 on_player_death: api_on_player_death,
3351 on_player_respawn: api_on_player_respawn,
3352 on_advancement: api_on_advancement,
3353 on_entity_interact: api_on_entity_interact,
3354 on_item_craft: api_on_item_craft,
3355 on_explosion: api_on_explosion,
3356 on_item_pickup: api_on_item_pickup,
3357 on_item_insert: api_on_item_insert,
3358 on_player_move: api_on_player_move,
3359 on_container_open: api_on_container_open,
3360 on_container_close: api_on_container_close,
3361 on_projectile_hit: api_on_projectile_hit,
3362 on_server_tick: api_on_server_tick,
3363 on_server_started: api_on_server_started,
3364 on_server_stopping: api_on_server_stopping,
3365 on_packet: api_on_packet,
3366 on_client_packet: api_on_client_packet,
3367 register_command: api_register_command,
3368 register_typed_command: api_register_typed_command,
3369 register_content: api_register_content,
3370 schedule_once: api_schedule_once,
3371 schedule_repeating: api_schedule_repeating,
3372 on_client_tick: api_on_client_tick,
3373 on_hud_render: api_on_hud_render,
3374 on_key_press: api_on_key_press,
3375 on_screen_open: api_on_screen_open,
3376 on_screen_close: api_on_screen_close,
3377 on_world_render: api_on_world_render,
3378 register_ui: api_register_ui,
3379 on_ui_render: api_on_ui_render,
3380 register_menu_entry: api_register_menu_entry,
3381 mods_list: api_mods_list,
3382 free_str: yog_free_str,
3383 ui_open: api_ui_open,
3384 interop_export: interop_export_impl,
3385 interop_import: interop_import_impl,
3386 register_chunk_generator: api_register_chunk_generator,
3387 ui_close: api_ui_close,
3388 ui_consume_event: api_ui_consume_event,
3389 translate: api_translate,
3390 ui_open_flags: client_api::api_ui_open_flags,
3391 client_play_sound: client_api::api_client_play_sound,
3392 client_stop_sound: client_api::api_client_stop_sound,
3393 client_held_item: client_api::api_client_held_item,
3394 client_set_block_tint: client_api::api_client_set_block_tint,
3395 client_key_down: client_api::api_client_key_down,
3396 on_book_page: api_on_book_page,
3397 on_item_tooltip: api_on_item_tooltip,
3398 is_mod_loaded: api_is_mod_loaded,
3399 java_call: api_java_call,
3400 on_item_text: api_on_item_text,
3401 client_body_track: body::api_client_body_track,
3402 client_body_track_type: body::api_client_body_track_type,
3403 client_set_dimensions: body::api_client_set_dimensions,
3404 client_entity_data: entity_data::api_client_entity_data,
3405 on_body: body::api_on_body,
3406 on_camera: body::api_on_camera,
3407 on_travel: body::api_on_travel,
3408 on_machine_craft: api_on_machine_craft,
3409 on_entity_tick: api_on_entity_tick,
3410 on_loot: loot::api_on_loot,
3411 client_attribute_value: api_client_attribute_value,
3412 client_advancement_done: api_client_advancement_done,
3413 log: api_log,
3414 }
3415}
3416
3417fn platform_tag() -> String {
3420 format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH)
3421}
3422
3423type AbiVersionFn = unsafe extern "C" fn() -> u32;
3424type RegisterFn = unsafe extern "C" fn(*const YogApi, *const std::os::raw::c_char);
3426
3427fn load_mods(dir: &Path, api: &YogApi) {
3428 let entries = match std::fs::read_dir(dir) {
3429 Ok(e) => e,
3430 Err(_) => {
3431 yog_logging::info!("no mods directory at {} — none loaded", dir.display());
3432 return;
3433 }
3434 };
3435
3436 struct ModMeta {
3438 id: String,
3439 deps: Vec<String>,
3440 yog_path: PathBuf,
3441 }
3442 let mut metas: Vec<ModMeta> = Vec::new();
3443 for entry in entries.flatten() {
3444 let path = entry.path();
3445 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3446 continue;
3447 }
3448 let info = read_mod_info(&path);
3449 let deps = read_mod_deps(&path);
3450 metas.push(ModMeta {
3451 id: info[0].clone(),
3452 deps,
3453 yog_path: path,
3454 });
3455 }
3456
3457 let ids: Vec<String> = metas.iter().map(|m| m.id.clone()).collect();
3459 YOG_MOD_IDS.write().unwrap_or_else(|e| e.into_inner()).extend(ids.iter().cloned());
3462 let deps_list: Vec<Vec<String>> = metas.iter().map(|m| m.deps.clone()).collect();
3463 let sorted = match topo_sort_mods(&ids, &deps_list) {
3464 Some(v) => v,
3465 None => {
3466 yog_logging::error!("circular dependency detected — loading in file order");
3467 (0..metas.len()).collect()
3468 }
3469 };
3470
3471 let mut loaded: Vec<LoadedModule> = Vec::new();
3472 for idx in sorted {
3473 let meta = &metas[idx];
3474 let lib_path = match extract_yog(&meta.yog_path) {
3475 Some(p) => p,
3476 None => {
3477 yog_logging::error!(
3478 "no native for {} in {}",
3479 platform_tag(),
3480 meta.yog_path.display()
3481 );
3482 continue;
3483 }
3484 };
3485 if let Some(module) = load_mod_lib(&lib_path, api, &meta.id) {
3486 loaded.push(module);
3487 #[cfg(feature = "dev")]
3488 registrations::record(&meta.id, &meta.yog_path);
3489 MOD_INFOS
3490 .lock()
3491 .expect("mod infos lock poisoned")
3492 .push(read_mod_info(&meta.yog_path));
3493 }
3494 }
3495 yog_logging::info!("loaded {} mod(s) from {}", loaded.len(), dir.display());
3496
3497 resolve_all_imports(api, &loaded);
3500}
3501
3502fn resolve_all_imports(api: &YogApi, loaded: &[LoadedModule]) {
3505 for module in loaded {
3506 module.with_library(|lib| unsafe {
3507 type ResolveFn = unsafe extern "C" fn(*const YogApi);
3508 if let Ok(resolve) = lib.get::<ResolveFn>(b"__yog_resolve_imports_final") {
3509 resolve(api as *const YogApi);
3510 }
3511 });
3512 }
3513}
3514
3515fn topo_sort_mods(ids: &[String], deps_list: &[Vec<String>]) -> Option<Vec<usize>> {
3517 let n = ids.len();
3518 let name_to_idx: HashMap<&str, usize> = ids
3519 .iter()
3520 .enumerate()
3521 .map(|(i, id)| (id.as_str(), i))
3522 .collect();
3523 let mut in_degree = vec![0u32; n];
3524 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
3525 for (i, deps) in deps_list.iter().enumerate() {
3526 for dep in deps {
3527 if let Some(&j) = name_to_idx.get(dep.as_str()) {
3528 adj[j].push(i);
3529 in_degree[i] += 1;
3530 }
3531 }
3532 }
3533 let mut queue: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
3534 let mut order = Vec::with_capacity(n);
3535 while let Some(u) = queue.pop() {
3536 order.push(u);
3537 for &v in &adj[u] {
3538 in_degree[v] -= 1;
3539 if in_degree[v] == 0 {
3540 queue.push(v);
3541 }
3542 }
3543 }
3544 if order.len() == n { Some(order) } else { None }
3545}
3546
3547fn load_mod_lib(path: &Path, api: &YogApi, mod_id: &str) -> Option<LoadedModule> {
3555 let module = match unsafe { hot_reloader().load_initial(mod_id, path) } {
3556 Ok(m) => m,
3557 Err(e) => {
3558 yog_logging::error!("failed to load {}: {}", path.display(), e);
3559 return None;
3560 }
3561 };
3562
3563 let abi = module.with_library(|lib| unsafe {
3564 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3565 Some(abi())
3566 });
3567 match abi.flatten() {
3568 Some(v) if yog_abi::compatible(v) => {}
3569 Some(v) => {
3570 yog_logging::error!(
3571 "{}: ABI {} incompatible with runtime ABI {}",
3572 path.display(),
3573 v,
3574 ABI_VERSION
3575 );
3576 module.retire(|| {});
3577 return None;
3578 }
3579 None => {
3580 yog_logging::error!("{} is not a Yog mod (no yog_abi_version)", path.display());
3581 module.retire(|| {});
3582 return None;
3583 }
3584 }
3585
3586 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3587 yog_logging::error!("{}: {}", path.display(), e);
3588 module.retire(|| {});
3589 return None;
3590 }
3591
3592 Some(module)
3593}
3594
3595fn invoke_mod_register(module: &LoadedModule, api: &YogApi, mod_id: &str) -> Result<(), String> {
3599 module
3600 .with_library(|lib| unsafe {
3601 let register: Symbol<RegisterFn> = lib
3602 .get(b"yog_mod_register")
3603 .map_err(|_| "missing yog_mod_register".to_string())?;
3604 let mod_id_c = std::ffi::CString::new(mod_id).unwrap();
3605 *CURRENT_REGISTRATION.lock().unwrap() = Some(module.clone());
3606 register(api as *const YogApi, mod_id_c.as_ptr());
3607 *CURRENT_REGISTRATION.lock().unwrap() = None;
3608 Ok(())
3609 })
3610 .unwrap_or_else(|| Err("module was unloaded before registration could run".to_string()))
3611}
3612
3613fn read_mod_info(path: &Path) -> [String; 7] {
3618 let stem = path
3619 .file_stem()
3620 .map(|s| s.to_string_lossy().into_owned())
3621 .unwrap_or_default();
3622 let mut info = [
3623 stem.clone(),
3624 stem,
3625 String::new(),
3626 String::new(),
3627 String::new(),
3628 String::new(),
3629 String::new(),
3630 ];
3631 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3632 return info;
3633 }
3634 let Some(text) = (|| {
3635 let file = std::fs::File::open(path).ok()?;
3636 let mut archive = zip::ZipArchive::new(file).ok()?;
3637 let mut entry = archive.by_name("yog.toml").ok()?;
3638 let mut text = String::new();
3639 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3640 Some(text)
3641 })() else {
3642 return info;
3643 };
3644 let mut in_mod = true;
3649 for line in text.lines() {
3650 let line = line.trim();
3651 if line.starts_with('[') {
3652 in_mod = line == "[mod]";
3653 continue;
3654 }
3655 if !in_mod {
3656 continue;
3657 }
3658 let Some((key, value)) = line.split_once('=') else {
3659 continue;
3660 };
3661 let key = key.trim();
3662 let value = value.trim();
3663 let unquote = |v: &str| v.trim().trim_matches('"').to_string();
3664 match key {
3665 "id" => info[0] = tsv_clean(&unquote(value)),
3666 "name" => info[1] = tsv_clean(&unquote(value)),
3667 "version" => info[2] = tsv_clean(&unquote(value)),
3668 "description" => info[4] = tsv_clean(&unquote(value)),
3669 "name_key" => info[5] = unquote(value),
3670 "description_key" => info[6] = unquote(value),
3671 "authors" => {
3672 let list = value.trim_start_matches('[').trim_end_matches(']');
3673 let authors: Vec<String> = list
3674 .split(',')
3675 .map(unquote)
3676 .filter(|a| !a.is_empty())
3677 .collect();
3678 info[3] = tsv_clean(&authors.join(", "));
3679 }
3680 _ => {}
3681 }
3682 }
3683 info
3684}
3685
3686fn read_mod_deps(path: &Path) -> Vec<String> {
3688 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3689 return Vec::new();
3690 }
3691 let text = (|| {
3692 let file = std::fs::File::open(path).ok()?;
3693 let mut archive = zip::ZipArchive::new(file).ok()?;
3694 let mut entry = archive.by_name("yog.toml").ok()?;
3695 let mut text = String::new();
3696 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3697 Some(text)
3698 })();
3699 let Some(text) = text else { return Vec::new() };
3700 let mut deps = Vec::new();
3701 let mut in_deps = false;
3702 for line in text.lines() {
3703 let line = line.trim();
3704 if line.starts_with('[') {
3705 in_deps = line == "[dependencies]";
3706 continue;
3707 }
3708 if !in_deps {
3709 continue;
3710 }
3711 if let Some((key, _)) = line.split_once('=') {
3713 deps.push(key.trim().to_string());
3714 }
3715 }
3716 deps
3717}
3718
3719fn extract_yog(path: &Path) -> Option<PathBuf> {
3720 let file = std::fs::File::open(path).ok()?;
3721 let mut archive = zip::ZipArchive::new(file).ok()?;
3722 let prefix = format!("natives/{}/", platform_tag());
3723 let mut entry_name = None;
3724 for i in 0..archive.len() {
3725 let f = archive.by_index(i).ok()?;
3726 let name = f.name().ok()?;
3727 if name.starts_with(&prefix) && !name.ends_with('/') {
3728 entry_name = Some(name.to_string());
3729 break;
3730 }
3731 }
3732 let entry_name = entry_name?;
3733 let ext = Path::new(&entry_name)
3734 .extension()
3735 .and_then(|e| e.to_str())
3736 .unwrap_or("bin");
3737 let stem = path.file_stem()?.to_string_lossy().into_owned();
3738 static EXTRACTIONS: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
3742 let n = EXTRACTIONS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3743 let out =
3744 std::env::temp_dir().join(format!("yog-{}-{}-{}.{}", stem, std::process::id(), n, ext));
3745 let mut entry = archive.by_name(&entry_name).ok()?;
3746 let mut out_file = std::fs::File::create(&out).ok()?;
3747 std::io::copy(&mut entry, &mut out_file).ok()?;
3748 Some(out)
3749}
3750
3751fn srv_ptr() -> *const YogServer {
3754 SERVER.get().expect("yog: SERVER not initialised") as *const YogServer
3755}
3756
3757#[unsafe(no_mangle)]
3760pub extern "system" fn Java_dev_yog_NativeBridge_nativeInit<'l>(
3761 mut env: JNIEnv<'l>,
3762 _class: JClass<'l>,
3763 mods_dir: JString<'l>,
3764) {
3765 if let Ok(vm) = env.get_java_vm() {
3766 let _ = JAVA_VM.set(vm);
3767 }
3768 yog_logging::set_sink(host_log);
3770
3771 let dir = env
3772 .get_string(&mods_dir)
3773 .map(String::from)
3774 .unwrap_or_default();
3775
3776 let _ = SERVER.set(build_server_table());
3778 let server_ptr = SERVER.get().unwrap() as *const YogServer;
3779
3780 let handlers_lock = HANDLERS.get_or_init(|| RwLock::new(RuntimeHandlers::default()));
3786 let handlers_ptr = handlers_lock as *const RwLock<RuntimeHandlers>;
3787
3788 let api: &'static YogApi = API_TABLE.get_or_init(|| build_api_table(handlers_ptr, server_ptr));
3789
3790 let _ = HOT_RELOADER.get_or_init(|| HotReloader::new(RuntimeModuleRegistry));
3791 yog_book::set_unlocked(client_advancement_done);
3793
3794 guard("mod loading", || {
3795 load_mods(Path::new(&dir), api);
3796 });
3797
3798 #[cfg(feature = "dev")]
3799 control_socket::start_if_requested();
3800
3801 yog_logging::info!("runtime initialised — the gate is open.");
3802}
3803
3804#[cfg(feature = "dev")]
3814pub(crate) fn trigger_hot_reload(mod_id: &str, new_yog_path: &Path) -> bool {
3815 let Some(native_path) = extract_yog(new_yog_path) else {
3816 yog_logging::error!(
3817 "hot-reload {}: no native for {} in {}",
3818 mod_id,
3819 platform_tag(),
3820 new_yog_path.display()
3821 );
3822 return false;
3823 };
3824
3825 let api = API_TABLE.get().expect("yog: nativeInit not called yet");
3826
3827 let module = match unsafe { hot_reloader().reload(mod_id, &native_path) } {
3828 Ok(m) => m,
3829 Err(e) => {
3830 yog_logging::error!(
3831 "hot-reload {}: failed to load {}: {}",
3832 mod_id,
3833 native_path.display(),
3834 e
3835 );
3836 return false;
3837 }
3838 };
3839
3840 let abi = module.with_library(|lib| unsafe {
3841 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3842 Some(abi())
3843 });
3844 match abi.flatten() {
3845 Some(v) if yog_abi::compatible(v) => {}
3846 other => {
3847 yog_logging::error!(
3848 "hot-reload {}: ABI {:?} incompatible with runtime ABI {} — mod has no active handlers until a compatible reload succeeds",
3849 mod_id,
3850 other,
3851 ABI_VERSION
3852 );
3853 return false;
3854 }
3855 }
3856
3857 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3858 yog_logging::error!("hot-reload {}: {}", mod_id, e);
3859 return false;
3860 }
3861
3862 yog_logging::info!("hot-reloaded mod {}", mod_id);
3863 true
3864}
3865
3866#[unsafe(no_mangle)]
3867pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreak<'l>(
3868 mut env: JNIEnv<'l>,
3869 _class: JClass<'l>,
3870 player: JString<'l>,
3871 block: JString<'l>,
3872 x: jint,
3873 y: jint,
3874 z: jint,
3875 dimension: JString<'l>,
3876) {
3877 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
3878 let ev = yog_abi::YogBlockBreakEvent {
3879 player: YogStr::from_str(&p),
3880 block: YogStr::from_str(&b),
3881 pos: YogBlockPos { x, y, z },
3882 dimension: YogStr::from_str(&d),
3883 };
3884 let srv = srv_ptr();
3885 guard("on_block_break", || {
3886 each(&handlers().block_break, |ud, f| unsafe {
3887 f(ud, srv, &ev, 1)
3888 });
3889 });
3890}
3891
3892#[unsafe(no_mangle)]
3893pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChat<'l>(
3894 mut env: JNIEnv<'l>,
3895 _class: JClass<'l>,
3896 player: JString<'l>,
3897 message: JString<'l>,
3898 dimension: JString<'l>,
3899) {
3900 let (p, m, d) = (
3901 jstr!(env, player),
3902 jstr!(env, message),
3903 jstr!(env, dimension),
3904 );
3905 let ev = yog_abi::YogChatEvent {
3906 player: YogStr::from_str(&p),
3907 message: YogStr::from_str(&m),
3908 dimension: YogStr::from_str(&d),
3909 };
3910 let srv = srv_ptr();
3911 guard("on_chat", || {
3912 each(&handlers().chat, |ud, f| unsafe { f(ud, srv, &ev, 1) });
3913 });
3914}
3915
3916#[unsafe(no_mangle)]
3917pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerJoin<'l>(
3918 mut env: JNIEnv<'l>,
3919 _class: JClass<'l>,
3920 player: JString<'l>,
3921 uuid: JString<'l>,
3922 dimension: JString<'l>,
3923) {
3924 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
3925 let ev = yog_abi::YogPlayerEvent {
3926 player: YogStr::from_str(&p),
3927 uuid: YogStr::from_str(&u),
3928 dimension: YogStr::from_str(&d),
3929 };
3930 let srv = srv_ptr();
3931 guard("on_player_join", || {
3932 each(&handlers().player_join, |ud, f| unsafe {
3933 f(ud, srv, &ev, 1)
3934 });
3935 });
3936 let h = handlers();
3940 if !h.startup_grants.is_empty() {
3941 h.pending_grants
3942 .lock()
3943 .expect("pending_grants poisoned")
3944 .push((p, u, 0));
3945 }
3946}
3947
3948fn grant_tag(id: &str) -> String {
3953 format!("yog.grant.{id}")
3954}
3955
3956fn process_pending_grants() {
3959 let h = handlers();
3960 let mut pending = match h.pending_grants.try_lock() {
3961 Ok(g) => g,
3962 Err(_) => return,
3963 };
3964 if pending.is_empty() {
3965 return;
3966 }
3967 const FIRST_TRY_TICK: u32 = 2;
3969 const MAX_TICKS: u32 = 600;
3971
3972 let mut still_pending = Vec::new();
3973 for (p, u, ticks) in pending.drain(..) {
3974 if ticks < FIRST_TRY_TICK {
3975 still_pending.push((p, u, ticks + 1));
3976 continue;
3977 }
3978 let player = YogStr::from_str(&p);
3979 let mut all_ok = true;
3980 for sg in &h.startup_grants {
3981 let tag = grant_tag(&sg.id);
3982 let tag = YogStr::from_str(&tag);
3983 if unsafe { bridge!("playerHasTag", [S(player), S(tag)]) }.unwrap_or(false) {
3984 continue;
3985 }
3986 let book = sg.book.as_ref().map(|_| "minecraft:written_book");
3987 let mut ok = true;
3988 for item_id in sg.items.iter().map(String::as_str).chain(book) {
3989 ok &= unsafe { srv_give_item(std::ptr::null_mut(), player, YogStr::from_str(item_id), 1) };
3990 }
3991 if ok && unsafe { bridge!("addPlayerTag", [S(player), S(tag)]) }.unwrap_or(false) {
3992 yog_logging::info!("startup grant {} given to {}", sg.id, p);
3993 } else {
3994 all_ok = false;
3995 }
3996 }
3997 if !all_ok && ticks < MAX_TICKS {
3998 still_pending.push((p, u, ticks + 1));
3999 }
4000 }
4001 *pending = still_pending;
4002}
4003
4004#[unsafe(no_mangle)]
4005pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerLeave<'l>(
4006 mut env: JNIEnv<'l>,
4007 _class: JClass<'l>,
4008 player: JString<'l>,
4009 uuid: JString<'l>,
4010 dimension: JString<'l>,
4011) {
4012 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4013 let ev = yog_abi::YogPlayerEvent {
4014 player: YogStr::from_str(&p),
4015 uuid: YogStr::from_str(&u),
4016 dimension: YogStr::from_str(&d),
4017 };
4018 let srv = srv_ptr();
4019 guard("on_player_leave", || {
4020 each(&handlers().player_leave, |ud, f| unsafe {
4021 f(ud, srv, &ev, 1)
4022 });
4023 });
4024}
4025
4026#[unsafe(no_mangle)]
4027pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItem<'l>(
4028 mut env: JNIEnv<'l>,
4029 _class: JClass<'l>,
4030 player: JString<'l>,
4031 item: JString<'l>,
4032 sneaking: jni::sys::jboolean,
4033 dimension: JString<'l>,
4034) {
4035 let (p, i, d) = (jstr!(env, player), jstr!(env, item), jstr!(env, dimension));
4036 let ev = yog_abi::YogUseItemEvent {
4037 player: YogStr::from_str(&p),
4038 item: YogStr::from_str(&i),
4039 sneaking: sneaking != 0,
4040 dimension: YogStr::from_str(&d),
4041 };
4042 let srv = srv_ptr();
4043 guard("on_use_item", || {
4044 each(&handlers().use_item, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4045 });
4046}
4047
4048#[unsafe(no_mangle)]
4049pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItemPre<'l>(
4050 mut env: JNIEnv<'l>,
4051 _class: JClass<'l>,
4052 player: JString<'l>,
4053 item: JString<'l>,
4054 sneaking: jni::sys::jboolean,
4055 dimension: JString<'l>,
4056) -> jni::sys::jboolean {
4057 let h = handlers();
4058 if h.use_item.is_empty() {
4059 return 1;
4060 }
4061 let p = jstr!(env, player, 1);
4062 let i = jstr!(env, item, 1);
4063 let d = jstr!(env, dimension, 1);
4064 let ev = yog_abi::YogUseItemEvent {
4065 player: YogStr::from_str(&p),
4066 item: YogStr::from_str(&i),
4067 sneaking: sneaking != 0,
4068 dimension: YogStr::from_str(&d),
4069 };
4070 let srv = srv_ptr();
4071 allowed(&h.use_item, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4072}
4073
4074#[unsafe(no_mangle)]
4075pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlock<'l>(
4076 mut env: JNIEnv<'l>,
4077 _class: JClass<'l>,
4078 player: JString<'l>,
4079 block: JString<'l>,
4080 x: jint,
4081 y: jint,
4082 z: jint,
4083 dimension: JString<'l>,
4084 sneaking: jni::sys::jboolean,
4085) {
4086 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4087 let ev = yog_abi::YogUseBlockEvent {
4088 player: YogStr::from_str(&p),
4089 block: YogStr::from_str(&b),
4090 pos: YogBlockPos { x, y, z },
4091 dimension: YogStr::from_str(&d),
4092 sneaking: sneaking != 0,
4093 };
4094 let srv = srv_ptr();
4095 guard("on_use_block", || {
4096 each(&handlers().use_block, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4097 });
4098}
4099
4100#[unsafe(no_mangle)]
4101pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlockPre<'l>(
4102 mut env: JNIEnv<'l>,
4103 _class: JClass<'l>,
4104 player: JString<'l>,
4105 block: JString<'l>,
4106 x: jint,
4107 y: jint,
4108 z: jint,
4109 dimension: JString<'l>,
4110 sneaking: jni::sys::jboolean,
4111) -> jni::sys::jboolean {
4112 let h = handlers();
4113 if h.use_block.is_empty() {
4114 return 1;
4115 }
4116 let p = jstr!(env, player, 1);
4117 let b = jstr!(env, block, 1);
4118 let d = jstr!(env, dimension, 1);
4119 let ev = yog_abi::YogUseBlockEvent {
4120 player: YogStr::from_str(&p),
4121 block: YogStr::from_str(&b),
4122 pos: YogBlockPos { x, y, z },
4123 dimension: YogStr::from_str(&d),
4124 sneaking: sneaking != 0,
4125 };
4126 let srv = srv_ptr();
4127 allowed(&h.use_block, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4128}
4129
4130#[unsafe(no_mangle)]
4131pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAttackEntity<'l>(
4132 mut env: JNIEnv<'l>,
4133 _class: JClass<'l>,
4134 player: JString<'l>,
4135 target_type: JString<'l>,
4136 target_uuid: JString<'l>,
4137 dimension: JString<'l>,
4138) {
4139 let (p, tt, tu, d) = (
4140 jstr!(env, player),
4141 jstr!(env, target_type),
4142 jstr!(env, target_uuid),
4143 jstr!(env, dimension),
4144 );
4145 let ev = yog_abi::YogAttackEntityEvent {
4146 player: YogStr::from_str(&p),
4147 target_type: YogStr::from_str(&tt),
4148 target_uuid: YogStr::from_str(&tu),
4149 dimension: YogStr::from_str(&d),
4150 };
4151 let srv = srv_ptr();
4152 guard("on_attack_entity", || {
4153 each(&handlers().attack_entity, |ud, f| unsafe {
4154 f(ud, srv, &ev, 1)
4155 });
4156 });
4157}
4158
4159#[unsafe(no_mangle)]
4160pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamage<'l>(
4161 mut env: JNIEnv<'l>,
4162 _class: JClass<'l>,
4163 entity_type: JString<'l>,
4164 uuid: JString<'l>,
4165 amount: jfloat,
4166 source: JString<'l>,
4167 dimension: JString<'l>,
4168) {
4169 let (et, u, s, d) = (
4170 jstr!(env, entity_type),
4171 jstr!(env, uuid),
4172 jstr!(env, source),
4173 jstr!(env, dimension),
4174 );
4175 let ev = yog_abi::YogEntityDamageEvent {
4176 entity_type: YogStr::from_str(&et),
4177 uuid: YogStr::from_str(&u),
4178 amount,
4179 source: YogStr::from_str(&s),
4180 dimension: YogStr::from_str(&d),
4181 };
4182 let srv = srv_ptr();
4183 guard("on_entity_damage", || {
4184 each(&handlers().entity_damage, |ud, f| unsafe {
4185 f(ud, srv, &ev, 1)
4186 });
4187 });
4188}
4189
4190#[unsafe(no_mangle)]
4191pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDeath<'l>(
4192 mut env: JNIEnv<'l>,
4193 _class: JClass<'l>,
4194 entity_type: JString<'l>,
4195 uuid: JString<'l>,
4196 source: JString<'l>,
4197 dimension: JString<'l>,
4198) {
4199 let (et, u, s, d) = (
4200 jstr!(env, entity_type),
4201 jstr!(env, uuid),
4202 jstr!(env, source),
4203 jstr!(env, dimension),
4204 );
4205 let ev = yog_abi::YogEntityDeathEvent {
4206 entity_type: YogStr::from_str(&et),
4207 uuid: YogStr::from_str(&u),
4208 source: YogStr::from_str(&s),
4209 dimension: YogStr::from_str(&d),
4210 };
4211 let srv = srv_ptr();
4212 guard("on_entity_death", || {
4213 each(&handlers().entity_death, |ud, f| unsafe {
4214 f(ud, srv, &ev, 1)
4215 });
4216 });
4217}
4218
4219#[unsafe(no_mangle)]
4220pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnTick<'l>(
4221 _env: JNIEnv<'l>,
4222 _class: JClass<'l>,
4223) {
4224 let h = handlers();
4225 let srv = srv_ptr();
4226 guard("on_tick", || {
4227 each(&h.server_tick, |ud, f| unsafe { f(ud, srv) });
4228 });
4229
4230 guard("startup_grants", process_pending_grants);
4231
4232 guard("entity data", entity_data::flush);
4234
4235 let due = h.scheduler.lock().expect("scheduler poisoned").due();
4237 for (module, ud, f) in due {
4238 if module.is_retiring() {
4239 continue;
4240 }
4241 let Some(_call) = module.enter() else {
4242 continue;
4243 };
4244 guard("scheduled task", || unsafe { f(ud, srv) });
4245 }
4246}
4247
4248#[unsafe(no_mangle)]
4249pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStarted<'l>(
4250 _env: JNIEnv<'l>,
4251 _class: JClass<'l>,
4252 _world_dir: JString<'l>,
4253) {
4254 handlers().pending_grants.lock().expect("pending_grants poisoned").clear();
4256 let srv = srv_ptr();
4257 guard("on_server_started", || {
4258 each(&handlers().server_started, |ud, f| unsafe { f(ud, srv) });
4259 });
4260}
4261
4262#[unsafe(no_mangle)]
4263pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStopping<'l>(
4264 _env: JNIEnv<'l>,
4265 _class: JClass<'l>,
4266) {
4267 let srv = srv_ptr();
4268 guard("on_server_stopping", || {
4269 each(&handlers().server_stopping, |ud, f| unsafe { f(ud, srv) });
4270 });
4271 entity_data::server_stopped();
4272}
4273
4274#[unsafe(no_mangle)]
4275pub extern "system" fn Java_dev_yog_NativeBridge_nativeCommandNames<'l>(
4276 env: JNIEnv<'l>,
4277 _class: JClass<'l>,
4278) -> jstring {
4279 let names = handlers()
4280 .commands
4281 .keys()
4282 .cloned()
4283 .collect::<Vec<_>>()
4284 .join("\n");
4285 env.new_string(names)
4286 .map(jni::objects::JString::into_raw)
4287 .unwrap_or(std::ptr::null_mut())
4288}
4289
4290#[unsafe(no_mangle)]
4291pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreakPre<'l>(
4292 mut env: JNIEnv<'l>,
4293 _class: JClass<'l>,
4294 player: JString<'l>,
4295 block: JString<'l>,
4296 x: jint,
4297 y: jint,
4298 z: jint,
4299 dimension: JString<'l>,
4300) -> jni::sys::jboolean {
4301 let h = handlers();
4302 if h.block_break.is_empty() {
4303 return 1;
4304 }
4305 let p = jstr!(env, player, 1);
4306 let b = jstr!(env, block, 1);
4307 let d = jstr!(env, dimension, 1);
4308 let ev = yog_abi::YogBlockBreakEvent {
4309 player: YogStr::from_str(&p),
4310 block: YogStr::from_str(&b),
4311 pos: YogBlockPos { x, y, z },
4312 dimension: YogStr::from_str(&d),
4313 };
4314 let srv = srv_ptr();
4315 allowed(&h.block_break, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4316}
4317
4318#[unsafe(no_mangle)]
4319pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChatPre<'l>(
4320 mut env: JNIEnv<'l>,
4321 _class: JClass<'l>,
4322 player: JString<'l>,
4323 message: JString<'l>,
4324 dimension: JString<'l>,
4325) -> jni::sys::jboolean {
4326 let h = handlers();
4327 if h.chat.is_empty() {
4328 return 1;
4329 }
4330 let p = jstr!(env, player, 1);
4331 let m = jstr!(env, message, 1);
4332 let d = jstr!(env, dimension, 1);
4333 let ev = yog_abi::YogChatEvent {
4334 player: YogStr::from_str(&p),
4335 message: YogStr::from_str(&m),
4336 dimension: YogStr::from_str(&d),
4337 };
4338 let srv = srv_ptr();
4339 allowed(&h.chat, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4340}
4341
4342unsafe extern "C" fn chunk_writer_set_block(
4347 _ctx: *mut c_void,
4348 x: i32,
4349 y: i32,
4350 z: i32,
4351 block_id: YogStr,
4352) -> bool {
4353 unsafe {
4354 host!(
4355 "dev/yog/YogCallbackChunkGenerator",
4356 "setBlockInGeneratingChunk",
4357 [I(x), I(y), I(z), S(block_id)]
4358 )
4359 .unwrap_or(false)
4360 }
4361}
4362
4363#[unsafe(no_mangle)]
4364pub extern "system" fn Java_dev_yog_NativeBridge_nativeGenerateChunk<'l>(
4365 mut env: JNIEnv<'l>,
4366 _class: JClass<'l>,
4367 generator_type_id: JString<'l>,
4368 chunk_x: jint,
4369 chunk_z: jint,
4370 min_y: jint,
4371 height: jint,
4372) {
4373 let id: String = match env.get_string(&generator_type_id) {
4374 Ok(s) => String::from(s),
4375 Err(_) => return,
4376 };
4377 let terrain = handlers().terrains.get(&id).cloned();
4378 if let Some(g) = terrain {
4379 guard("chunk_generator", || {
4380 g.generate(chunk_x, chunk_z, min_y, height, |x, y, z, block| unsafe {
4381 chunk_writer_set_block(std::ptr::null_mut(), x, y, z, YogStr::from_str(block));
4382 });
4383 });
4384 return;
4385 }
4386 let Some((module, ud, cb)) = handlers().chunk_generators.get(&id).cloned() else {
4387 return;
4388 };
4389 if module.is_retiring() {
4390 return;
4391 }
4392 let Some(_call) = module.enter() else { return };
4393 let writer = yog_abi::YogChunkWriterApi {
4394 chunk_x,
4395 chunk_z,
4396 min_y,
4397 height,
4398 ctx: std::ptr::null_mut(),
4399 set_block: chunk_writer_set_block,
4400 };
4401 guard("chunk_generator", || unsafe {
4402 cb(ud, &writer as *const _);
4403 });
4404}
4405
4406#[unsafe(no_mangle)]
4407pub extern "system" fn Java_dev_yog_NativeBridge_nativeTypedCommandSchemas<'l>(
4408 env: JNIEnv<'l>,
4409 _class: JClass<'l>,
4410) -> jstring {
4411 let mut lines = Vec::new();
4416 for (name, regs) in handlers().commands.iter() {
4417 let mut seen = std::collections::HashSet::new();
4418 for (_module, schema, _, _) in regs {
4419 if let Some(schema) = schema
4420 && seen.insert(schema.as_str())
4421 {
4422 lines.push(format!("{name}\t{schema}"));
4423 }
4424 }
4425 }
4426 env.new_string(lines.join("\n"))
4427 .map(jni::objects::JString::into_raw)
4428 .unwrap_or(std::ptr::null_mut())
4429}
4430
4431#[unsafe(no_mangle)]
4432pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnCommand<'l>(
4433 mut env: JNIEnv<'l>,
4434 _class: JClass<'l>,
4435 name: JString<'l>,
4436 args: JString<'l>,
4437 source: JString<'l>,
4438 uuid: JString<'l>,
4439 dimension: JString<'l>,
4440) -> jstring {
4441 let (n, a, s, u, d) = (
4442 env.get_string(&name).map(String::from).unwrap_or_default(),
4443 env.get_string(&args).map(String::from).unwrap_or_default(),
4444 env.get_string(&source)
4445 .map(String::from)
4446 .unwrap_or_default(),
4447 env.get_string(&uuid).map(String::from).unwrap_or_default(),
4448 env.get_string(&dimension)
4449 .map(String::from)
4450 .unwrap_or_default(),
4451 );
4452 let h = handlers();
4453 let srv = srv_ptr();
4454 let args: Vec<&str> = if a.is_empty() { Vec::new() } else { a.split('\t').collect() };
4455 let call = |ud: &*mut c_void, f: &YogCommandFn, args: &str| -> Option<String> {
4457 let ev = yog_abi::YogCommandEvent {
4458 name: YogStr::from_str(&n),
4459 args: YogStr::from_str(args),
4460 source: YogStr::from_str(&s),
4461 uuid: YogStr::from_str(&u),
4462 dimension: YogStr::from_str(&d),
4463 };
4464 let mut buf = [0u8; 4096];
4465 let mut reply_len: u32 = 0;
4466 unsafe { f(*ud, srv, &ev, buf.as_mut_ptr(), buf.len() as u32, &mut reply_len) };
4467 (reply_len > 0).then(|| String::from_utf8_lossy(&buf[..reply_len as usize]).into_owned())
4468 };
4469 let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4470 let Some(regs) = h.commands.get(&n) else {
4471 return String::new();
4472 };
4473 let live = regs.iter().filter(|(module, ..)| !module.is_retiring());
4474 for pass in 0..3 {
4479 for (module, schema, ud, f) in live.clone() {
4480 let joined;
4481 let these = match (pass, schema) {
4482 (0, Some(schema)) if schema.split_whitespace().count() == args.len() => a.as_str(),
4483 (1, Some(schema)) => {
4484 let types: Vec<&str> = schema.split_whitespace().collect();
4485 let k = types.len();
4486 if types.last() != Some(&"string") || k == 0 || args.len() <= k {
4487 continue;
4488 }
4489 let mut parts: Vec<String> = args[..k - 1].iter().map(|x| x.to_string()).collect();
4490 parts.push(args[k - 1..].join(" "));
4491 joined = parts.join("\t");
4492 joined.as_str()
4493 }
4494 (2, None) => a.as_str(),
4495 _ => continue,
4496 };
4497 let Some(_call) = module.enter() else {
4498 continue;
4499 };
4500 if let Some(r) = call(ud, f, these) {
4501 return r;
4502 }
4503 }
4504 }
4505 String::new()
4506 }))
4507 .unwrap_or_else(|_| {
4508 yog_logging::error!("a mod panicked handling command `{}`", n);
4509 String::new()
4510 });
4511
4512 env.new_string(reply)
4513 .map(jni::objects::JString::into_raw)
4514 .unwrap_or(std::ptr::null_mut())
4515}
4516
4517#[unsafe(no_mangle)]
4520pub extern "system" fn Java_dev_yog_NativeBridge_nativeContent<'l>(
4521 env: JNIEnv<'l>,
4522 _class: JClass<'l>,
4523) -> jni::sys::jbyteArray {
4524 let h = handlers();
4525 let bytes = {
4526 let mut cached = h.content_blob.lock().unwrap_or_else(|e| e.into_inner());
4527 cached.get_or_insert_with(|| std::sync::Arc::new(content_blob::blob(&h))).clone()
4528 };
4529 env.byte_array_from_slice(&bytes)
4530 .map(|a| a.into_raw())
4531 .unwrap_or(std::ptr::null_mut())
4532}
4533
4534#[unsafe(no_mangle)]
4535pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPacket<'l>(
4536 mut env: JNIEnv<'l>,
4537 _class: JClass<'l>,
4538 channel: JString<'l>,
4539 player: JString<'l>,
4540 payload: JByteArray<'l>,
4541) {
4542 let ch = env
4543 .get_string(&channel)
4544 .map(String::from)
4545 .unwrap_or_default();
4546 let pl = env
4547 .get_string(&player)
4548 .map(String::from)
4549 .unwrap_or_default();
4550 let data = env.convert_byte_array(&payload).unwrap_or_default();
4551 let ev = yog_abi::YogPacketEvent {
4552 channel: YogStr::from_str(&ch),
4553 player: YogStr::from_str(&pl),
4554 payload: data.as_ptr(),
4555 payload_len: data.len() as u32,
4556 };
4557 let h = handlers();
4558 let srv = srv_ptr();
4559 guard("on_packet", || {
4560 if let Some((module, ud, f)) = h.packets.get(&ch) {
4561 if module.is_retiring() {
4562 return;
4563 }
4564 let Some(_call) = module.enter() else { return };
4565 unsafe { f(*ud, srv, &ev) };
4566 }
4567 });
4568}
4569
4570#[unsafe(no_mangle)]
4571pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientPacket<'l>(
4572 mut env: JNIEnv<'l>,
4573 _class: JClass<'l>,
4574 channel: JString<'l>,
4575 payload: JByteArray<'l>,
4576) {
4577 let ch = env
4578 .get_string(&channel)
4579 .map(String::from)
4580 .unwrap_or_default();
4581 let data = env.convert_byte_array(&payload).unwrap_or_default();
4582 if ch == entity_data::CHANNEL {
4583 guard("entity data", || entity_data::receive(&data));
4584 return;
4585 }
4586 let ev = yog_abi::YogPacketEvent {
4587 channel: YogStr::from_str(&ch),
4588 player: YogStr::EMPTY,
4589 payload: data.as_ptr(),
4590 payload_len: data.len() as u32,
4591 };
4592 let h = handlers();
4593 let srv = srv_ptr();
4594 guard("on_client_packet", || {
4595 if let Some((module, ud, f)) = h.client_packets.get(&ch) {
4596 if module.is_retiring() {
4597 return;
4598 }
4599 let Some(_call) = module.enter() else { return };
4600 unsafe { f(*ud, srv, &ev) };
4601 }
4602 });
4603}
4604
4605#[unsafe(no_mangle)]
4606pub extern "system" fn Java_dev_yog_NativeBridge_nativePacketChannels<'l>(
4607 env: JNIEnv<'l>,
4608 _class: JClass<'l>,
4609) -> jstring {
4610 let s = handlers()
4611 .packets
4612 .keys()
4613 .cloned()
4614 .collect::<Vec<_>>()
4615 .join("\n");
4616 env.new_string(s)
4617 .map(jni::objects::JString::into_raw)
4618 .unwrap_or(std::ptr::null_mut())
4619}
4620
4621#[unsafe(no_mangle)]
4622pub extern "system" fn Java_dev_yog_NativeBridge_nativeClientPacketChannels<'l>(
4623 env: JNIEnv<'l>,
4624 _class: JClass<'l>,
4625) -> jstring {
4626 let s = handlers()
4627 .client_packets
4628 .keys()
4629 .cloned()
4630 .chain([entity_data::CHANNEL.to_owned()])
4631 .collect::<Vec<_>>()
4632 .join("\n");
4633 env.new_string(s)
4634 .map(jni::objects::JString::into_raw)
4635 .unwrap_or(std::ptr::null_mut())
4636}
4637
4638#[unsafe(no_mangle)]
4639pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawn<'l>(
4640 mut env: JNIEnv<'l>,
4641 _class: JClass<'l>,
4642 entity_type: JString<'l>,
4643 uuid: JString<'l>,
4644 dimension: JString<'l>,
4645) {
4646 let h = handlers();
4647 if h.entity_spawn.is_empty() {
4648 return;
4649 }
4650 let (et, u, d) = (
4651 jstr!(env, entity_type),
4652 jstr!(env, uuid),
4653 jstr!(env, dimension),
4654 );
4655 let ev = yog_abi::YogEntitySpawnEvent {
4656 entity_type: YogStr::from_str(&et),
4657 uuid: YogStr::from_str(&u),
4658 dimension: YogStr::from_str(&d),
4659 };
4660 let srv = srv_ptr();
4661 guard("on_entity_spawn", || {
4662 each(&h.entity_spawn, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4663 });
4664}
4665
4666#[unsafe(no_mangle)]
4667pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawnPre<'l>(
4668 mut env: JNIEnv<'l>,
4669 _class: JClass<'l>,
4670 entity_type: JString<'l>,
4671 uuid: JString<'l>,
4672 dimension: JString<'l>,
4673) -> jni::sys::jboolean {
4674 let h = handlers();
4675 if h.entity_spawn.is_empty() {
4676 return 1;
4677 }
4678 let et = jstr!(env, entity_type, 1);
4679 let u = jstr!(env, uuid, 1);
4680 let d = jstr!(env, dimension, 1);
4681 let ev = yog_abi::YogEntitySpawnEvent {
4682 entity_type: YogStr::from_str(&et),
4683 uuid: YogStr::from_str(&u),
4684 dimension: YogStr::from_str(&d),
4685 };
4686 let srv = srv_ptr();
4687 allowed(&h.entity_spawn, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4688}
4689
4690#[unsafe(no_mangle)]
4691pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamagePre<'l>(
4692 mut env: JNIEnv<'l>,
4693 _class: JClass<'l>,
4694 entity_type: JString<'l>,
4695 uuid: JString<'l>,
4696 amount: jfloat,
4697 source: JString<'l>,
4698 dimension: JString<'l>,
4699) -> jni::sys::jboolean {
4700 let h = handlers();
4701 if h.entity_damage.is_empty() {
4702 return 1;
4703 }
4704 let et = jstr!(env, entity_type, 1);
4705 let u = jstr!(env, uuid, 1);
4706 let s = jstr!(env, source, 1);
4707 let d = jstr!(env, dimension, 1);
4708 let ev = yog_abi::YogEntityDamageEvent {
4709 entity_type: YogStr::from_str(&et),
4710 uuid: YogStr::from_str(&u),
4711 amount,
4712 source: YogStr::from_str(&s),
4713 dimension: YogStr::from_str(&d),
4714 };
4715 let srv = srv_ptr();
4716 allowed(&h.entity_damage, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4717}
4718
4719#[unsafe(no_mangle)]
4720pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlockPre<'l>(
4721 mut env: JNIEnv<'l>,
4722 _class: JClass<'l>,
4723 player: JString<'l>,
4724 block: JString<'l>,
4725 x: jint,
4726 y: jint,
4727 z: jint,
4728 dimension: JString<'l>,
4729 sneaking: jni::sys::jboolean,
4730) -> jni::sys::jboolean {
4731 let h = handlers();
4732 if h.player_place_block.is_empty() {
4733 return 1;
4734 }
4735 let p = jstr!(env, player, 1);
4736 let b = jstr!(env, block, 1);
4737 let d = jstr!(env, dimension, 1);
4738 let ev = YogPlaceBlockEvent {
4739 player: YogStr::from_str(&p),
4740 block: YogStr::from_str(&b),
4741 pos: YogBlockPos { x, y, z },
4742 dimension: YogStr::from_str(&d),
4743 sneaking: sneaking != 0,
4744 };
4745 let srv = srv_ptr();
4746 allowed(&h.player_place_block, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4747}
4748
4749#[unsafe(no_mangle)]
4750pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlock<'l>(
4751 mut env: JNIEnv<'l>,
4752 _class: JClass<'l>,
4753 player: JString<'l>,
4754 block: JString<'l>,
4755 x: jint,
4756 y: jint,
4757 z: jint,
4758 dimension: JString<'l>,
4759 sneaking: jni::sys::jboolean,
4760) {
4761 let h = handlers();
4762 if h.player_place_block.is_empty() {
4763 return;
4764 }
4765 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4766 let ev = YogPlaceBlockEvent {
4767 player: YogStr::from_str(&p),
4768 block: YogStr::from_str(&b),
4769 pos: YogBlockPos { x, y, z },
4770 dimension: YogStr::from_str(&d),
4771 sneaking: sneaking != 0,
4772 };
4773 let srv = srv_ptr();
4774 guard("on_player_place_block", || {
4775 each(&h.player_place_block, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4776 });
4777}
4778
4779#[unsafe(no_mangle)]
4780pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeathPre<'l>(
4781 mut env: JNIEnv<'l>,
4782 _class: JClass<'l>,
4783 player: JString<'l>,
4784 uuid: JString<'l>,
4785 source: JString<'l>,
4786 dimension: JString<'l>,
4787) -> jni::sys::jboolean {
4788 let h = handlers();
4789 if h.player_death.is_empty() {
4790 return 1;
4791 }
4792 let p = jstr!(env, player, 1);
4793 let u = jstr!(env, uuid, 1);
4794 let s = jstr!(env, source, 1);
4795 let d = jstr!(env, dimension, 1);
4796 let ev = YogPlayerDeathEvent {
4797 player: YogStr::from_str(&p),
4798 uuid: YogStr::from_str(&u),
4799 source: YogStr::from_str(&s),
4800 dimension: YogStr::from_str(&d),
4801 };
4802 let srv = srv_ptr();
4803 allowed(&h.player_death, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4804}
4805
4806#[unsafe(no_mangle)]
4807pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeath<'l>(
4808 mut env: JNIEnv<'l>,
4809 _class: JClass<'l>,
4810 player: JString<'l>,
4811 uuid: JString<'l>,
4812 source: JString<'l>,
4813 dimension: JString<'l>,
4814) {
4815 let h = handlers();
4816 if h.player_death.is_empty() {
4817 return;
4818 }
4819 let (p, u, s, d) = (
4820 jstr!(env, player),
4821 jstr!(env, uuid),
4822 jstr!(env, source),
4823 jstr!(env, dimension),
4824 );
4825 let ev = YogPlayerDeathEvent {
4826 player: YogStr::from_str(&p),
4827 uuid: YogStr::from_str(&u),
4828 source: YogStr::from_str(&s),
4829 dimension: YogStr::from_str(&d),
4830 };
4831 let srv = srv_ptr();
4832 guard("on_player_death", || {
4833 each(&h.player_death, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4834 });
4835}
4836
4837#[unsafe(no_mangle)]
4838pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerRespawn<'l>(
4839 mut env: JNIEnv<'l>,
4840 _class: JClass<'l>,
4841 player: JString<'l>,
4842 uuid: JString<'l>,
4843 at_anchor: jni::sys::jboolean,
4844 dimension: JString<'l>,
4845) {
4846 let h = handlers();
4847 if h.player_respawn.is_empty() {
4848 return;
4849 }
4850 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4851 let ev = YogPlayerRespawnEvent {
4852 player: YogStr::from_str(&p),
4853 uuid: YogStr::from_str(&u),
4854 at_anchor: at_anchor != 0,
4855 dimension: YogStr::from_str(&d),
4856 };
4857 let srv = srv_ptr();
4858 guard("on_player_respawn", || {
4859 each(&h.player_respawn, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4860 });
4861}
4862
4863#[unsafe(no_mangle)]
4864pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAdvancement<'l>(
4865 mut env: JNIEnv<'l>,
4866 _class: JClass<'l>,
4867 player: JString<'l>,
4868 uuid: JString<'l>,
4869 advancement: JString<'l>,
4870 dimension: JString<'l>,
4871) {
4872 let h = handlers();
4873 if h.advancement.is_empty() {
4874 return;
4875 }
4876 let (p, u, a, d) = (
4877 jstr!(env, player),
4878 jstr!(env, uuid),
4879 jstr!(env, advancement),
4880 jstr!(env, dimension),
4881 );
4882 let ev = YogAdvancementEvent {
4883 player: YogStr::from_str(&p),
4884 uuid: YogStr::from_str(&u),
4885 advancement: YogStr::from_str(&a),
4886 dimension: YogStr::from_str(&d),
4887 };
4888 let srv = srv_ptr();
4889 guard("on_advancement", || {
4890 each(&h.advancement, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4891 });
4892}
4893
4894#[unsafe(no_mangle)]
4895pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteractPre<'l>(
4896 mut env: JNIEnv<'l>,
4897 _class: JClass<'l>,
4898 player: JString<'l>,
4899 player_uuid: JString<'l>,
4900 entity_type: JString<'l>,
4901 entity_uuid: JString<'l>,
4902 hand: JString<'l>,
4903 dimension: JString<'l>,
4904) -> jni::sys::jboolean {
4905 let h = handlers();
4906 if h.entity_interact.is_empty() {
4907 return 1;
4908 }
4909 let p = jstr!(env, player, 1);
4910 let pu = jstr!(env, player_uuid, 1);
4911 let et = jstr!(env, entity_type, 1);
4912 let eu = jstr!(env, entity_uuid, 1);
4913 let ha = jstr!(env, hand, 1);
4914 let d = jstr!(env, dimension, 1);
4915 let ev = YogEntityInteractEvent {
4916 player: YogStr::from_str(&p),
4917 player_uuid: YogStr::from_str(&pu),
4918 entity_type: YogStr::from_str(&et),
4919 entity_uuid: YogStr::from_str(&eu),
4920 hand: YogStr::from_str(&ha),
4921 dimension: YogStr::from_str(&d),
4922 };
4923 let srv = srv_ptr();
4924 allowed(&h.entity_interact, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4925}
4926
4927#[unsafe(no_mangle)]
4928pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteract<'l>(
4929 mut env: JNIEnv<'l>,
4930 _class: JClass<'l>,
4931 player: JString<'l>,
4932 player_uuid: JString<'l>,
4933 entity_type: JString<'l>,
4934 entity_uuid: JString<'l>,
4935 hand: JString<'l>,
4936 dimension: JString<'l>,
4937) {
4938 let h = handlers();
4939 if h.entity_interact.is_empty() {
4940 return;
4941 }
4942 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
4943 let (et, eu, ha, d) = (
4944 jstr!(env, entity_type),
4945 jstr!(env, entity_uuid),
4946 jstr!(env, hand),
4947 jstr!(env, dimension),
4948 );
4949 let ev = YogEntityInteractEvent {
4950 player: YogStr::from_str(&p),
4951 player_uuid: YogStr::from_str(&pu),
4952 entity_type: YogStr::from_str(&et),
4953 entity_uuid: YogStr::from_str(&eu),
4954 hand: YogStr::from_str(&ha),
4955 dimension: YogStr::from_str(&d),
4956 };
4957 let srv = srv_ptr();
4958 guard("on_entity_interact", || {
4959 each(&h.entity_interact, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4960 });
4961}
4962
4963#[unsafe(no_mangle)]
4964pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemCraft<'l>(
4965 mut env: JNIEnv<'l>,
4966 _class: JClass<'l>,
4967 player: JString<'l>,
4968 player_uuid: JString<'l>,
4969 result_item: JString<'l>,
4970 result_count: jint,
4971 dimension: JString<'l>,
4972) {
4973 let h = handlers();
4974 if h.item_craft.is_empty() {
4975 return;
4976 }
4977 let (p, pu, ri, d) = (
4978 jstr!(env, player),
4979 jstr!(env, player_uuid),
4980 jstr!(env, result_item),
4981 jstr!(env, dimension),
4982 );
4983 let ev = YogCraftEvent {
4984 player: YogStr::from_str(&p),
4985 player_uuid: YogStr::from_str(&pu),
4986 result_item: YogStr::from_str(&ri),
4987 result_count: result_count as u32,
4988 dimension: YogStr::from_str(&d),
4989 };
4990 let srv = srv_ptr();
4991 guard("on_item_craft", || {
4992 each(&h.item_craft, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4993 });
4994}
4995
4996#[unsafe(no_mangle)]
4997pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosionPre<'l>(
4998 mut env: JNIEnv<'l>,
4999 _class: JClass<'l>,
5000 dimension: JString<'l>,
5001 x: jdouble,
5002 y: jdouble,
5003 z: jdouble,
5004 power: jfloat,
5005 cause_uuid: JString<'l>,
5006) -> jni::sys::jboolean {
5007 let h = handlers();
5008 if h.explosion.is_empty() {
5009 return 1;
5010 }
5011 let d = jstr!(env, dimension, 1);
5012 let cu = jstr!(env, cause_uuid, 1);
5013 let ev = YogExplosionEvent {
5014 dimension: YogStr::from_str(&d),
5015 x,
5016 y,
5017 z,
5018 power,
5019 cause_uuid: YogStr::from_str(&cu),
5020 };
5021 let srv = srv_ptr();
5022 allowed(&h.explosion, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5023}
5024
5025#[unsafe(no_mangle)]
5026pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosion<'l>(
5027 mut env: JNIEnv<'l>,
5028 _class: JClass<'l>,
5029 dimension: JString<'l>,
5030 x: jdouble,
5031 y: jdouble,
5032 z: jdouble,
5033 power: jfloat,
5034 cause_uuid: JString<'l>,
5035) {
5036 let h = handlers();
5037 if h.explosion.is_empty() {
5038 return;
5039 }
5040 let (d, cu) = (jstr!(env, dimension), jstr!(env, cause_uuid));
5041 let ev = YogExplosionEvent {
5042 dimension: YogStr::from_str(&d),
5043 x,
5044 y,
5045 z,
5046 power,
5047 cause_uuid: YogStr::from_str(&cu),
5048 };
5049 let srv = srv_ptr();
5050 guard("on_explosion", || {
5051 each(&h.explosion, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5052 });
5053}
5054
5055#[unsafe(no_mangle)]
5060pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemInsert<'l>(
5061 mut env: JNIEnv<'l>,
5062 _class: JClass<'l>,
5063 block: JString<'l>,
5064 x: jint,
5065 y: jint,
5066 z: jint,
5067 dimension: JString<'l>,
5068 item_id: JString<'l>,
5069 count: jint,
5070 nbt: JString<'l>,
5071 simulate: jni::sys::jboolean,
5072) -> jni::sys::jboolean {
5073 let h = handlers();
5074 if h.item_insert.is_empty() {
5075 return 0;
5076 }
5077 let (b, d, i, n) = (jstr!(env, block, 0), jstr!(env, dimension, 0), jstr!(env, item_id, 0), jstr!(env, nbt, 0));
5078 let ev = YogItemInsertEvent {
5079 block: YogStr::from_str(&b),
5080 pos: YogBlockPos { x, y, z },
5081 dimension: YogStr::from_str(&d),
5082 item_id: YogStr::from_str(&i),
5083 count: count.max(0) as u32,
5084 nbt: YogStr::from_str(&n),
5085 };
5086 let srv = srv_ptr();
5087 if simulate != 0 {
5088 !allowed(&h.item_insert, |ud, f| unsafe { !f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5090 } else {
5091 let mut taken = false;
5092 guard("on_item_insert", || {
5093 each(&h.item_insert, |ud, f| taken |= unsafe { f(ud, srv, &ev, 1) });
5094 });
5095 taken as jni::sys::jboolean
5096 }
5097}
5098
5099#[unsafe(no_mangle)]
5102pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickupPre<'l>(
5103 mut env: JNIEnv<'l>,
5104 _class: JClass<'l>,
5105 player: JString<'l>,
5106 player_uuid: JString<'l>,
5107 item_id: JString<'l>,
5108 item_count: jint,
5109 entity_uuid: JString<'l>,
5110 dimension: JString<'l>,
5111) -> jni::sys::jboolean {
5112 let h = handlers();
5113 if h.item_pickup.is_empty() {
5114 return 1;
5115 }
5116 let p = jstr!(env, player, 1);
5117 let pu = jstr!(env, player_uuid, 1);
5118 let ii = jstr!(env, item_id, 1);
5119 let eu = jstr!(env, entity_uuid, 1);
5120 let d = jstr!(env, dimension, 1);
5121 let ev = YogItemPickupEvent {
5122 player: YogStr::from_str(&p),
5123 player_uuid: YogStr::from_str(&pu),
5124 item_id: YogStr::from_str(&ii),
5125 item_count: item_count as u32,
5126 entity_uuid: YogStr::from_str(&eu),
5127 dimension: YogStr::from_str(&d),
5128 };
5129 let srv = srv_ptr();
5130 allowed(&h.item_pickup, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5131}
5132
5133#[unsafe(no_mangle)]
5134pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickup<'l>(
5135 mut env: JNIEnv<'l>,
5136 _class: JClass<'l>,
5137 player: JString<'l>,
5138 player_uuid: JString<'l>,
5139 item_id: JString<'l>,
5140 item_count: jint,
5141 entity_uuid: JString<'l>,
5142 dimension: JString<'l>,
5143) {
5144 let h = handlers();
5145 if h.item_pickup.is_empty() {
5146 return;
5147 }
5148 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5149 let (ii, eu, d) = (
5150 jstr!(env, item_id),
5151 jstr!(env, entity_uuid),
5152 jstr!(env, dimension),
5153 );
5154 let ev = YogItemPickupEvent {
5155 player: YogStr::from_str(&p),
5156 player_uuid: YogStr::from_str(&pu),
5157 item_id: YogStr::from_str(&ii),
5158 item_count: item_count as u32,
5159 entity_uuid: YogStr::from_str(&eu),
5160 dimension: YogStr::from_str(&d),
5161 };
5162 let srv = srv_ptr();
5163 guard("on_item_pickup", || {
5164 each(&h.item_pickup, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5165 });
5166}
5167
5168#[unsafe(no_mangle)]
5169pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerMove<'l>(
5170 mut env: JNIEnv<'l>,
5171 _class: JClass<'l>,
5172 player: JString<'l>,
5173 player_uuid: JString<'l>,
5174 x: jdouble,
5175 y: jdouble,
5176 z: jdouble,
5177 yaw: jfloat,
5178 pitch: jfloat,
5179 dimension: JString<'l>,
5180) {
5181 let h = handlers();
5182 if h.player_move.is_empty() {
5183 return;
5184 }
5185 let (p, pu, d) = (
5186 jstr!(env, player),
5187 jstr!(env, player_uuid),
5188 jstr!(env, dimension),
5189 );
5190 let ev = YogPlayerMoveEvent {
5191 player: YogStr::from_str(&p),
5192 player_uuid: YogStr::from_str(&pu),
5193 x,
5194 y,
5195 z,
5196 yaw,
5197 pitch,
5198 dimension: YogStr::from_str(&d),
5199 };
5200 let srv = srv_ptr();
5201 guard("on_player_move", || {
5202 each(&h.player_move, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5203 });
5204}
5205
5206#[unsafe(no_mangle)]
5207pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpenPre<'l>(
5208 mut env: JNIEnv<'l>,
5209 _class: JClass<'l>,
5210 player: JString<'l>,
5211 player_uuid: JString<'l>,
5212 dimension: JString<'l>,
5213) -> jni::sys::jboolean {
5214 let h = handlers();
5215 if h.container_open.is_empty() {
5216 return 1;
5217 }
5218 let p = jstr!(env, player, 1);
5219 let pu = jstr!(env, player_uuid, 1);
5220 let d = jstr!(env, dimension, 1);
5221 let ev = YogContainerOpenEvent {
5222 player: YogStr::from_str(&p),
5223 player_uuid: YogStr::from_str(&pu),
5224 container_type: YogStr::EMPTY,
5225 dimension: YogStr::from_str(&d),
5226 };
5227 let srv = srv_ptr();
5228 allowed(&h.container_open, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5229}
5230
5231#[unsafe(no_mangle)]
5232pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpen<'l>(
5233 mut env: JNIEnv<'l>,
5234 _class: JClass<'l>,
5235 player: JString<'l>,
5236 player_uuid: JString<'l>,
5237 container_type: JString<'l>,
5238 dimension: JString<'l>,
5239) {
5240 let h = handlers();
5241 if h.container_open.is_empty() {
5242 return;
5243 }
5244 let (p, pu, ct, d) = (
5245 jstr!(env, player),
5246 jstr!(env, player_uuid),
5247 jstr!(env, container_type),
5248 jstr!(env, dimension),
5249 );
5250 let ev = YogContainerOpenEvent {
5251 player: YogStr::from_str(&p),
5252 player_uuid: YogStr::from_str(&pu),
5253 container_type: YogStr::from_str(&ct),
5254 dimension: YogStr::from_str(&d),
5255 };
5256 let srv = srv_ptr();
5257 guard("on_container_open", || {
5258 each(&h.container_open, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5259 });
5260}
5261
5262#[unsafe(no_mangle)]
5263pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerClose<'l>(
5264 mut env: JNIEnv<'l>,
5265 _class: JClass<'l>,
5266 player: JString<'l>,
5267 player_uuid: JString<'l>,
5268 dimension: JString<'l>,
5269) {
5270 let h = handlers();
5271 if h.container_close.is_empty() {
5272 return;
5273 }
5274 let (p, pu, d) = (
5275 jstr!(env, player),
5276 jstr!(env, player_uuid),
5277 jstr!(env, dimension),
5278 );
5279 let ev = YogContainerCloseEvent {
5280 player: YogStr::from_str(&p),
5281 player_uuid: YogStr::from_str(&pu),
5282 dimension: YogStr::from_str(&d),
5283 };
5284 let srv = srv_ptr();
5285 guard("on_container_close", || {
5286 each(&h.container_close, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5287 });
5288}
5289
5290#[unsafe(no_mangle)]
5291pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHitPre<'l>(
5292 mut env: JNIEnv<'l>,
5293 _class: JClass<'l>,
5294 projectile_type: JString<'l>,
5295 projectile_uuid: JString<'l>,
5296 shooter_uuid: JString<'l>,
5297 hit_type: JString<'l>,
5298 hit_entity_uuid: JString<'l>,
5299 x: jdouble,
5300 y: jdouble,
5301 z: jdouble,
5302 dimension: JString<'l>,
5303) -> jni::sys::jboolean {
5304 let h = handlers();
5305 if h.projectile_hit.is_empty() {
5306 return 1;
5307 }
5308 let pt = jstr!(env, projectile_type, 1);
5309 let pu = jstr!(env, projectile_uuid, 1);
5310 let su = jstr!(env, shooter_uuid, 1);
5311 let ht = jstr!(env, hit_type, 1);
5312 let heu = jstr!(env, hit_entity_uuid, 1);
5313 let dim = jstr!(env, dimension, 1);
5314 let ev = YogProjectileHitEvent {
5315 projectile_type: YogStr::from_str(&pt),
5316 projectile_uuid: YogStr::from_str(&pu),
5317 shooter_uuid: YogStr::from_str(&su),
5318 hit_type: YogStr::from_str(&ht),
5319 hit_entity_uuid: YogStr::from_str(&heu),
5320 x,
5321 y,
5322 z,
5323 dimension: YogStr::from_str(&dim),
5324 };
5325 let srv = srv_ptr();
5326 allowed(&h.projectile_hit, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5327}
5328
5329#[unsafe(no_mangle)]
5330pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHit<'l>(
5331 mut env: JNIEnv<'l>,
5332 _class: JClass<'l>,
5333 projectile_type: JString<'l>,
5334 projectile_uuid: JString<'l>,
5335 shooter_uuid: JString<'l>,
5336 hit_type: JString<'l>,
5337 hit_entity_uuid: JString<'l>,
5338 x: jdouble,
5339 y: jdouble,
5340 z: jdouble,
5341 dimension: JString<'l>,
5342) {
5343 let h = handlers();
5344 if h.projectile_hit.is_empty() {
5345 return;
5346 }
5347 let (pt, pu) = (jstr!(env, projectile_type), jstr!(env, projectile_uuid));
5348 let (su, ht) = (jstr!(env, shooter_uuid), jstr!(env, hit_type));
5349 let (heu, dim) = (jstr!(env, hit_entity_uuid), jstr!(env, dimension));
5350 let ev = YogProjectileHitEvent {
5351 projectile_type: YogStr::from_str(&pt),
5352 projectile_uuid: YogStr::from_str(&pu),
5353 shooter_uuid: YogStr::from_str(&su),
5354 hit_type: YogStr::from_str(&ht),
5355 hit_entity_uuid: YogStr::from_str(&heu),
5356 x,
5357 y,
5358 z,
5359 dimension: YogStr::from_str(&dim),
5360 };
5361 let srv = srv_ptr();
5362 guard("on_projectile_hit", || {
5363 each(&h.projectile_hit, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5364 });
5365}
5366
5367#[unsafe(no_mangle)]
5370pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientTick<'l>(
5371 _env: JNIEnv<'l>,
5372 _class: JClass<'l>,
5373) {
5374 let h = handlers();
5375 if h.client_tick.is_empty() {
5376 return;
5377 }
5378 guard("on_client_tick", || {
5379 each(&h.client_tick, |ud, f| unsafe { f(ud) });
5380 });
5381}
5382
5383fn gl_proc_address(sym: &str) -> usize {
5385 let ptr: Option<i64> = unsafe { bridge!("glProcAddress", [S(YogStr::from_str(sym))]) };
5386 ptr.unwrap_or(0) as usize
5387}
5388
5389#[unsafe(no_mangle)]
5390pub extern "system" fn Java_dev_yog_NativeBridge_nativeGlInit<'l>(
5391 _env: JNIEnv<'l>,
5392 _class: JClass<'l>,
5393) {
5394 if GL.get().is_some() {
5395 return;
5396 }
5397 let mut raw_get_binary: usize = 0;
5398 let mut raw_prog_binary: usize = 0;
5399 let gl = unsafe {
5400 glow::Context::from_loader_function(|sym| {
5401 let ptr = gl_proc_address(sym);
5402 match sym {
5404 "glGetProgramBinary" => raw_get_binary = ptr,
5405 "glProgramBinary" => raw_prog_binary = ptr,
5406 _ => {}
5407 }
5408 ptr as *const _
5409 })
5410 };
5411 let _ = GL.set(GlCtx(gl));
5412 let _ = GL_GET_PROGRAM_BINARY.set((raw_get_binary != 0).then_some(raw_get_binary));
5413 let _ = GL_PROGRAM_BINARY.set((raw_prog_binary != 0).then_some(raw_prog_binary));
5414 let get_program_iv = gl_proc_address("glGetProgramiv");
5417 let _ = GL_GET_PROGRAM_IV.set((get_program_iv != 0).then_some(get_program_iv));
5418}
5419
5420#[unsafe(no_mangle)]
5421pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnHudRender<'l>(
5422 _env: JNIEnv<'l>,
5423 _class: JClass<'l>,
5424 delta_tick: jfloat,
5425 screen_w: jint,
5426 screen_h: jint,
5427 scale_factor: jfloat,
5428 player_x: jfloat,
5429 player_y: jfloat,
5430 player_z: jfloat,
5431) {
5432 let h = handlers();
5433 let mut gfx = GFX_FN_TABLE;
5434 gfx.screen_w = screen_w;
5435 gfx.screen_h = screen_h;
5436 gfx.delta_tick = delta_tick;
5437 gfx.scale_factor = scale_factor;
5438 gfx.player_pos = [player_x, player_y, player_z];
5439
5440 if !h.hud_render.is_empty() {
5442 guard("on_hud_render", || {
5443 each(&h.hud_render, |ud, f| unsafe { f(ud, &gfx) });
5444 });
5445 }
5446
5447 let active_layers: Vec<UiLayer> = h.active_uis.lock().unwrap().clone();
5449 let sw = screen_w as f32;
5450 let sh = screen_h as f32;
5451 let ctx = unsafe { yog_gfx::GfxContext::from_raw(&gfx as *const _) };
5452 let mut renderers = h.book_renderers.lock().unwrap();
5453 for layer in &active_layers {
5454 if !layer.visible {
5455 continue;
5456 }
5457 if let Some(renderer) = renderers.get_mut(&layer.id) {
5458 renderer.render(&ctx, sw, sh);
5459 }
5460 }
5461}
5462
5463#[unsafe(no_mangle)]
5464pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnWorldRender<'l>(
5465 env: JNIEnv<'l>,
5466 _class: JClass<'l>,
5467 delta_tick: jfloat,
5468 screen_w: jint,
5469 screen_h: jint,
5470 scale_factor: jfloat,
5471 view_proj_arr: JFloatArray<'l>,
5472 cam_x: jfloat,
5473 cam_y: jfloat,
5474 cam_z: jfloat,
5475 player_x: jfloat,
5476 player_y: jfloat,
5477 player_z: jfloat,
5478) {
5479 let h = handlers();
5480 if h.world_render.is_empty() {
5481 return;
5482 }
5483 let mut view_proj = [0f32; 16];
5484 if env
5485 .get_float_array_region(&view_proj_arr, 0, &mut view_proj)
5486 .is_err()
5487 {
5488 return;
5489 }
5490 let mut gfx = GFX_FN_TABLE;
5491 gfx.screen_w = screen_w;
5492 gfx.screen_h = screen_h;
5493 gfx.delta_tick = delta_tick;
5494 gfx.scale_factor = scale_factor;
5495 gfx.view_proj = view_proj;
5496 gfx.camera_pos = [cam_x, cam_y, cam_z];
5497 gfx.player_pos = [player_x, player_y, player_z];
5498 guard("on_world_render", || {
5499 each(&h.world_render, |ud, f| unsafe { f(ud, &gfx) });
5500 });
5501}
5502
5503#[unsafe(no_mangle)]
5504pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnKeyPress<'l>(
5505 _env: JNIEnv<'l>,
5506 _class: JClass<'l>,
5507 key_code: jint,
5508 scan_code: jint,
5509 action: jint,
5510 modifiers: jint,
5511) -> jni::sys::jboolean {
5512 let h = handlers();
5513 if h.key_press.is_empty() {
5514 return 1;
5515 }
5516 let ev = YogKeyPressEvent {
5517 key_code,
5518 scan_code,
5519 action,
5520 modifiers,
5521 };
5522 allowed(&h.key_press, |ud, f| unsafe { f(ud, &ev) }) as jni::sys::jboolean
5523}
5524
5525#[unsafe(no_mangle)]
5526pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenOpen<'l>(
5527 mut env: JNIEnv<'l>,
5528 _class: JClass<'l>,
5529 screen_class: JString<'l>,
5530) {
5531 let h = handlers();
5532 if h.screen_open.is_empty() {
5533 return;
5534 }
5535 let sc = match env.get_string(&screen_class) {
5536 Ok(s) => String::from(s),
5537 Err(_) => return,
5538 };
5539 guard("on_screen_open", || {
5540 each(&h.screen_open, |ud, f| unsafe {
5541 f(ud, YogStr::from_str(&sc))
5542 });
5543 });
5544}
5545
5546#[unsafe(no_mangle)]
5547pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenClose<'l>(
5548 mut env: JNIEnv<'l>,
5549 _class: JClass<'l>,
5550 screen_class: JString<'l>,
5551) {
5552 let h = handlers();
5553 if h.screen_close.is_empty() {
5554 return;
5555 }
5556 let sc = match env.get_string(&screen_class) {
5557 Ok(s) => String::from(s),
5558 Err(_) => return,
5559 };
5560 guard("on_screen_close", || {
5561 each(&h.screen_close, |ud, f| unsafe {
5562 f(ud, YogStr::from_str(&sc))
5563 });
5564 });
5565}
5566
5567mod body;
5568mod loot;
5569#[cfg(feature = "dev")]
5571mod registrations;
5572mod entity_data;
5573mod client_api;
5574mod content_blob;
5575mod machine;
5576#[cfg(feature = "dev")]
5577mod control_socket;
5578mod images;
5579mod svg;
5580mod jcall;
5581mod ui_jni;
5582#[cfg(feature = "bench")]
5584#[doc(hidden)]
5585pub mod bench_support;
5586#[cfg(test)]
5587mod tests;