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