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