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 if env
2691 .call_static_method(
2692 "dev/yog/NativeDraw",
2693 "drawTexture",
2694 "(Ljava/lang/String;FFFFFFFF)V",
2698 &[
2699 JValue::Object(&ji),
2700 JValue::Float(x),
2701 JValue::Float(y),
2702 JValue::Float(u0),
2703 JValue::Float(v0),
2704 JValue::Float(w),
2705 JValue::Float(h),
2706 JValue::Float(tw),
2707 JValue::Float(th),
2708 ],
2709 )
2710 .is_err()
2711 {
2712 env.exception_clear().ok();
2713 }
2714 }
2715}
2716
2717unsafe extern "C" fn gfx_draw2d_item(id: YogStr, x: f32, y: f32, size: f32) {
2718 let Some(mut env) = get_env() else { return };
2719 if let Some(ji) = ys_to_java(&mut env, id) {
2720 if env
2721 .call_static_method(
2722 "dev/yog/NativeDraw",
2723 "drawItem",
2724 "(Ljava/lang/String;FFF)V",
2725 &[
2726 JValue::Object(&ji),
2727 JValue::Float(x),
2728 JValue::Float(y),
2729 JValue::Float(size),
2730 ],
2731 )
2732 .is_err()
2733 {
2734 env.exception_clear().ok();
2735 }
2736 }
2737}
2738
2739static GFX_FN_TABLE: YogGfxApi = YogGfxApi {
2742 screen_w: 0,
2744 screen_h: 0,
2745 delta_tick: 0.0,
2746 scale_factor: 1.0,
2747 view_proj: [0.0; 16],
2748 camera_pos: [0.0; 3],
2749 player_pos: [0.0; 3],
2750 _pad1: 0.0,
2751 buf_create: gfx_buf_create,
2752 buf_delete: gfx_buf_delete,
2753 buf_data: gfx_buf_data,
2754 buf_subdata: gfx_buf_subdata,
2755 vao_create: gfx_vao_create,
2756 vao_delete: gfx_vao_delete,
2757 vao_attrib: gfx_vao_attrib,
2758 vao_set_ebo: gfx_vao_set_ebo,
2759 prog_create: gfx_prog_create,
2760 prog_delete: gfx_prog_delete,
2761 prog_uniform_1i: gfx_prog_uniform_1i,
2762 prog_uniform_1f: gfx_prog_uniform_1f,
2763 prog_uniform_2f: gfx_prog_uniform_2f,
2764 prog_uniform_3f: gfx_prog_uniform_3f,
2765 prog_uniform_4f: gfx_prog_uniform_4f,
2766 prog_uniform_mat4: gfx_prog_uniform_mat4,
2767 tex_create: gfx_tex_create,
2768 tex_delete: gfx_tex_delete,
2769 tex_bind: gfx_tex_bind,
2770 tex_from_mc: gfx_tex_from_mc,
2771 draw_arrays: gfx_draw_arrays,
2772 draw_elements: gfx_draw_elements,
2773 set_blend: gfx_set_blend,
2774 set_depth: gfx_set_depth,
2775 set_scissor: gfx_set_scissor,
2776 clear_scissor: gfx_clear_scissor,
2777 set_viewport: gfx_set_viewport,
2778 draw2d_rect: gfx_draw2d_rect,
2779 draw2d_gradient: gfx_draw2d_gradient,
2780 draw2d_text: gfx_draw2d_text,
2781 draw2d_mc_tex: gfx_draw2d_mc_tex,
2782 draw2d_item: gfx_draw2d_item,
2783 draw2d_text_font: gfx_draw2d_text_font,
2784 text_width: gfx_text_width,
2785 draw2d_image: images::gfx_draw2d_image,
2786 image_size: images::gfx_image_size,
2787};
2788
2789macro_rules! api_event {
2796 ($name:ident, $field:ident, $fn_ty:ty) => {
2797 unsafe extern "C" fn $name(ctx: *mut c_void, ud: *mut c_void, h: $fn_ty) {
2798 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2799 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2800 handlers.$field.push((current_registration(), ud, h));
2801 }
2802 };
2803}
2804
2805api_event!(api_on_block_break, block_break, YogBlockBreakFn);
2806api_event!(api_on_chat, chat, YogChatFn);
2807api_event!(api_on_player_join, player_join, YogPlayerFn);
2808api_event!(api_on_player_leave, player_leave, YogPlayerFn);
2809api_event!(api_on_use_item, use_item, YogUseItemFn);
2810api_event!(api_on_use_block, use_block, YogUseBlockFn);
2811api_event!(api_on_attack_entity, attack_entity, YogAttackEntityFn);
2812api_event!(api_on_entity_damage, entity_damage, YogEntityDamageFn);
2813api_event!(api_on_entity_death, entity_death, YogEntityDeathFn);
2814api_event!(api_on_entity_spawn, entity_spawn, YogEntitySpawnFn);
2815api_event!(
2816 api_on_player_place_block,
2817 player_place_block,
2818 YogPlaceBlockFn
2819);
2820api_event!(api_on_player_death, player_death, YogPlayerDeathFn);
2821api_event!(api_on_player_respawn, player_respawn, YogPlayerRespawnFn);
2822api_event!(api_on_advancement, advancement, YogAdvancementFn);
2823api_event!(api_on_entity_interact, entity_interact, YogEntityInteractFn);
2824api_event!(api_on_item_craft, item_craft, YogCraftFn);
2825api_event!(api_on_explosion, explosion, YogExplosionFn);
2826api_event!(api_on_item_pickup, item_pickup, YogItemPickupFn);
2827api_event!(api_on_player_move, player_move, YogPlayerMoveFn);
2828api_event!(api_on_container_open, container_open, YogContainerOpenFn);
2829api_event!(api_on_container_close, container_close, YogContainerCloseFn);
2830api_event!(api_on_projectile_hit, projectile_hit, YogProjectileHitFn);
2831api_event!(api_on_client_tick, client_tick, YogClientFn);
2832api_event!(api_on_hud_render, hud_render, YogHudRenderFn);
2833api_event!(api_on_world_render, world_render, YogWorldRenderFn);
2834api_event!(api_on_key_press, key_press, YogKeyPressFn);
2835api_event!(api_on_screen_open, screen_open, YogScreenFn);
2836api_event!(api_on_screen_close, screen_close, YogScreenFn);
2837api_event!(api_on_server_tick, server_tick, YogServerFn);
2838api_event!(api_on_server_started, server_started, YogServerFn);
2839api_event!(api_on_server_stopping, server_stopping, YogServerFn);
2840
2841unsafe extern "C" fn api_on_packet(
2842 ctx: *mut c_void,
2843 channel: YogStr,
2844 ud: *mut c_void,
2845 h: YogPacketFn,
2846) {
2847 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2848 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2849 handlers
2850 .packets
2851 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2852}
2853
2854unsafe extern "C" fn api_on_client_packet(
2855 ctx: *mut c_void,
2856 channel: YogStr,
2857 ud: *mut c_void,
2858 h: YogPacketFn,
2859) {
2860 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2861 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2862 handlers
2863 .client_packets
2864 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2865}
2866
2867unsafe extern "C" fn api_register_command(
2868 ctx: *mut c_void,
2869 name: YogStr,
2870 ud: *mut c_void,
2871 h: YogCommandFn,
2872) {
2873 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2874 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2875 handlers
2876 .commands
2877 .entry(name.as_str().to_owned())
2878 .or_default()
2879 .push((current_registration(), None, ud, h));
2880}
2881
2882unsafe extern "C" fn api_register_typed_command(
2883 ctx: *mut c_void,
2884 name: YogStr,
2885 schema: YogStr,
2886 ud: *mut c_void,
2887 h: YogCommandFn,
2888) {
2889 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2890 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2891 let n = name.as_str().to_owned();
2892 handlers.commands.entry(n).or_default().push((
2893 current_registration(),
2894 Some(schema.as_str().to_owned()),
2895 ud,
2896 h,
2897 ));
2898}
2899
2900unsafe extern "C" fn api_register_recipe_json(
2901 ctx: *mut c_void,
2902 namespace: YogStr,
2903 name: YogStr,
2904 json: YogStr,
2905) {
2906 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2907 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2908 let ns = namespace.as_str().to_owned();
2909 let n = name.as_str().to_owned();
2910 let j = json.as_str().to_owned();
2911 let full_id = format!("{}:{}", ns, n);
2913 for r in handlers.book_renderers.lock().unwrap().values_mut() {
2914 r.add_recipe(full_id.clone(), &j);
2915 }
2916 handlers.recipes.push((ns, n, j));
2917}
2918
2919unsafe extern "C" fn api_register_item(ctx: *mut c_void, def: *const YogItemDef) {
2920 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2921 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2922 let d = &*def;
2923 let food = if d.food_nutrition > 0 {
2924 Some(FoodDef {
2925 nutrition: d.food_nutrition,
2926 saturation: d.food_saturation,
2927 can_always_eat: d.food_always_eat,
2928 })
2929 } else {
2930 None
2931 };
2932 handlers.items.push(ItemDef {
2933 id: d.id.as_str().to_owned(),
2934 max_stack: d.max_stack as u8,
2935 name: if d.name.is_empty() {
2936 None
2937 } else {
2938 Some(d.name.as_str().to_owned())
2939 },
2940 tooltip: if d.tooltip.is_empty() {
2941 None
2942 } else {
2943 Some(d.tooltip.as_str().to_owned())
2944 },
2945 max_damage: d.max_damage,
2946 fire_resistant: d.fire_resistant,
2947 fuel_ticks: d.fuel_ticks,
2948 food,
2949 });
2950}
2951
2952unsafe extern "C" fn api_register_block(ctx: *mut c_void, def: *const YogBlockDef) {
2953 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
2954 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
2955 let d = &*def;
2956 handlers.blocks.push(BlockDef {
2957 id: d.id.as_str().to_owned(),
2958 hardness: d.hardness,
2959 resistance: d.resistance,
2960 name: if d.name.is_empty() {
2961 None
2962 } else {
2963 Some(d.name.as_str().to_owned())
2964 },
2965 light_level: d.light_level,
2966 sound: if d.sound.is_empty() {
2967 None
2968 } else {
2969 Some(d.sound.as_str().to_owned())
2970 },
2971 requires_tool: d.requires_tool,
2972 no_collision: d.no_collision,
2973 slipperiness: d.slipperiness,
2974 shape: if d.shape == [0.0f32; 6] {
2975 None
2976 } else {
2977 Some(d.shape)
2978 },
2979 connects: d.connects,
2980 connect_groups: if d.connect_groups.is_empty() {
2981 Vec::new()
2982 } else {
2983 d.connect_groups
2984 .as_str()
2985 .split(',')
2986 .map(str::to_owned)
2987 .collect()
2988 },
2989 inventory_id: if d.inventory_id.is_empty() {
2990 None
2991 } else {
2992 Some(d.inventory_id.as_str().to_owned())
2993 },
2994 });
2995}
2996
2997unsafe extern "C" fn api_register_inventory(
2998 ctx: *mut c_void,
2999 def: *const yog_abi::YogInventoryDef,
3000) {
3001 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3002 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3003 let d = &*def;
3004 handlers.inventories.push(yog_inventory::InventoryDef {
3005 id: d.id.as_str().to_owned(),
3006 slot_count: d.slot_count as usize,
3007 layout: yog_inventory::decode_layout(d.layout.as_str()),
3008 include_player_inventory: d.include_player_inventory,
3009 player_inv_offset: (d.player_inv_x, d.player_inv_y),
3010 background_texture: if d.background_texture.is_empty() {
3011 None
3012 } else {
3013 Some(d.background_texture.as_str().to_owned())
3014 },
3015 title: d.title.as_str().to_owned(),
3016 background_size: if d.background_w > 0.0 && d.background_h > 0.0 {
3017 Some((d.background_w, d.background_h))
3018 } else {
3019 None
3020 },
3021 });
3022}
3023
3024unsafe extern "C" fn api_schedule_once(
3025 ctx: *mut c_void,
3026 delay_ticks: u64,
3027 ud: *mut c_void,
3028 h: YogScheduledFn,
3029) {
3030 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3031 let handlers = handlers_lock.write().expect("handlers lock poisoned");
3032 handlers
3033 .scheduler
3034 .lock()
3035 .expect("scheduler poisoned")
3036 .once_tasks
3037 .push(OnceTask {
3038 module: current_registration(),
3039 delay_remaining: delay_ticks,
3040 ud,
3041 f: h,
3042 });
3043}
3044
3045unsafe extern "C" fn api_schedule_repeating(
3046 ctx: *mut c_void,
3047 period_ticks: u64,
3048 ud: *mut c_void,
3049 h: YogScheduledFn,
3050) {
3051 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3052 let handlers = handlers_lock.write().expect("handlers lock poisoned");
3053 handlers
3054 .scheduler
3055 .lock()
3056 .expect("scheduler poisoned")
3057 .repeating_tasks
3058 .push(RepeatingTask {
3059 module: current_registration(),
3060 period: period_ticks,
3061 ticks_left: period_ticks,
3062 ud,
3063 f: h,
3064 });
3065}
3066
3067unsafe extern "C" fn api_register_startup_grant(
3068 ctx: *mut c_void,
3069 grant: *const YogStartupGrantDef,
3070) {
3071 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3072 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3073 let g = &*grant;
3074 let items: Vec<String> = if g.items.is_empty() {
3075 Vec::new()
3076 } else {
3077 unsafe {
3078 g.items
3079 .as_str()
3080 .split('|')
3081 .map(|s: &str| s.to_owned())
3082 .collect()
3083 }
3084 };
3085 let book = if g.book.is_empty() {
3086 None
3087 } else {
3088 Some(unsafe { g.book.as_str().to_owned() })
3089 };
3090 let command = if g.command.is_empty() {
3091 None
3092 } else {
3093 Some(unsafe { g.command.as_str().to_owned() })
3094 };
3095 handlers.startup_grants.push(yog_registry::StartupGrant {
3096 id: unsafe { g.id.as_str().to_owned() },
3097 items,
3098 book,
3099 command,
3100 });
3101}
3102
3103unsafe extern "C" fn api_register_book(ctx: *mut c_void, book_id: YogStr, book_json: YogStr) {
3104 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3105 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3106 let id = unsafe { book_id.as_str().to_owned() };
3107 let json = unsafe { book_json.as_str().to_owned() };
3108 handlers.books.insert(id.clone(), json.clone());
3109 match serde_json::from_str::<yog_book::Book>(&json) {
3111 Ok(book) => {
3112 let mut renderer = yog_book::BookRenderer::new(book);
3113 for (ns, name, j) in &handlers.recipes {
3115 renderer.add_recipe(format!("{}:{}", ns, name), j);
3116 }
3117 handlers.book_renderers.lock().unwrap().insert(id, renderer);
3118 }
3119 Err(e) => {
3120 yog_logging::warn!(
3121 "book '{}': failed to parse JSON for Rust renderer: {}",
3122 id,
3123 e
3124 );
3125 }
3126 }
3127}
3128
3129unsafe extern "C" fn api_register_dimension_json(ctx: *mut c_void, id: YogStr, json: YogStr) {
3130 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3131 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3132 let id = unsafe { id.as_str().to_owned() };
3133 let json = unsafe { json.as_str().to_owned() };
3134 yog_logging::info!("registered dimension type: {}", id);
3135 handlers.dimensions.insert(id, json);
3136}
3137
3138unsafe extern "C" fn api_register_chunk_generator(
3139 ctx: *mut c_void,
3140 id: YogStr,
3141 ud: *mut c_void,
3142 h: yog_abi::YogChunkGeneratorFn,
3143) {
3144 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3145 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3146 let id = unsafe { id.as_str().to_owned() };
3147 yog_logging::info!("registered chunk generator for dimension: {}", id);
3148 handlers
3149 .chunk_generators
3150 .insert(id, (current_registration(), ud, h));
3151}
3152
3153unsafe extern "C" fn api_register_ui(
3154 ctx: *mut c_void,
3155 ui_id: YogStr,
3156 _layout_json: YogStr,
3157 ud: *mut c_void,
3158 h: yog_abi::YogUIEventFn,
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_handlers
3165 .insert(id.clone(), (current_registration(), ud, h));
3166 yog_logging::info!("registered UI handler: {}", id);
3167}
3168
3169unsafe extern "C" fn api_on_ui_render(
3170 ctx: *mut c_void,
3171 ui_id: YogStr,
3172 ud: *mut c_void,
3173 h: YogHudRenderFn,
3174) {
3175 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3176 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3177 let id = unsafe { ui_id.as_str().to_owned() };
3178 handlers
3179 .ui_render_handlers
3180 .entry(id)
3181 .or_default()
3182 .push((current_registration(), ud, h));
3183}
3184
3185fn tsv_clean(v: &str) -> String {
3187 v.replace(['\t', '\n', '\r'], " ")
3188}
3189
3190unsafe extern "C" fn api_ui_open(_ctx: *mut c_void, ui_id: YogStr, modal: bool, pause: bool) {
3191 let id = ui_id.as_str().to_string();
3192 if id.is_empty() {
3193 return;
3194 }
3195 let Some(mut env) = get_env() else { return };
3196 let Ok(jid) = env.new_string(&id) else { return };
3197 let jid_obj: JObject = jid.into();
3198 if env
3200 .call_static_method(
3201 "dev/yog/NativeBridge",
3202 "openUI",
3203 "(Ljava/lang/String;ZZ)V",
3204 &[
3205 JValue::Object(&jid_obj),
3206 JValue::Bool(modal as u8),
3207 JValue::Bool(pause as u8),
3208 ],
3209 )
3210 .is_err()
3211 {
3212 env.exception_clear().ok();
3213 }
3214}
3215
3216unsafe extern "C" fn api_ui_close(_ctx: *mut c_void, ui_id: YogStr) {
3217 let id = ui_id.as_str().to_string();
3218 let Some(mut env) = get_env() else { return };
3219 let Ok(jid) = env.new_string(&id) else { return };
3220 let jid_obj: JObject = jid.into();
3221 if env
3224 .call_static_method(
3225 "dev/yog/NativeBridge",
3226 "closeUI",
3227 "(Ljava/lang/String;)V",
3228 &[JValue::Object(&jid_obj)],
3229 )
3230 .is_err()
3231 {
3232 env.exception_clear().ok();
3233 }
3234}
3235
3236unsafe extern "C" fn api_ui_consume_event(_ctx: *mut c_void) {
3237 ui_jni::consume_event();
3238}
3239
3240unsafe extern "C" fn api_translate(_ctx: *mut c_void, key: YogStr) -> YogOwnedStr {
3241 let key = key.as_str();
3242 if key.is_empty() {
3243 return YogOwnedStr::NONE;
3244 }
3245 let Some(mut env) = get_env() else {
3246 return YogOwnedStr::NONE;
3247 };
3248 let Ok(jkey) = env.new_string(key) else {
3249 return YogOwnedStr::NONE;
3250 };
3251 let jkey: JObject = jkey.into();
3252 let result = env.call_static_method(
3253 "dev/yog/NativeBridge",
3254 "translate",
3255 "(Ljava/lang/String;)Ljava/lang/String;",
3256 &[JValue::Object(&jkey)],
3257 );
3258 let text = match result.and_then(|v| v.l()) {
3259 Ok(obj) if !obj.as_raw().is_null() => env.get_string(&obj.into()).ok().map(String::from),
3260 Ok(_) => None,
3261 Err(_) => {
3262 env.exception_clear().ok();
3263 None
3264 }
3265 };
3266 let _ = env.delete_local_ref(jkey);
3267 text.map_or(YogOwnedStr::NONE, YogOwnedStr::from_string)
3268}
3269
3270unsafe extern "C" fn api_mods_list(_ctx: *mut c_void) -> YogOwnedStr {
3271 let mut lines: Vec<String> = Vec::new();
3272 for m in MOD_INFOS.lock().expect("mod infos lock poisoned").iter() {
3273 lines.push(format!(
3274 "yog\t{}\t{}\t{}\t{}\t{}",
3275 m[0], m[1], m[2], m[3], m[4]
3276 ));
3277 }
3278 if let Some(mut env) = get_env() {
3280 if let Ok(val) = env.call_static_method(
3281 "dev/yog/NativeBridge",
3282 "listPlatformMods",
3283 "()Ljava/lang/String;",
3284 &[],
3285 ) {
3286 if let Ok(obj) = val.l() {
3287 if !obj.as_raw().is_null() {
3288 if let Ok(js) = env.get_string(&obj.into()) {
3289 for line in String::from(js).lines() {
3290 if !line.trim().is_empty() {
3291 lines.push(format!("platform\t{line}"));
3292 }
3293 }
3294 }
3295 }
3296 }
3297 } else {
3298 env.exception_clear().ok();
3299 }
3300 }
3301 YogOwnedStr::from_string(lines.join("\n"))
3302}
3303
3304unsafe extern "C" fn api_register_menu_entry(ctx: *mut c_void, label: YogStr, ui_id: YogStr) {
3305 let handlers_lock = &*(ctx as *const RwLock<RuntimeHandlers>);
3306 let mut handlers = handlers_lock.write().expect("handlers lock poisoned");
3307 let l = unsafe { label.as_str().to_owned() };
3308 let u = unsafe { ui_id.as_str().to_owned() };
3309 handlers.menu_entries.push((l, u));
3310}
3311
3312#[no_mangle]
3313pub extern "system" fn Java_dev_yog_NativeBridge_nativeBookJson<'l>(
3314 mut env: JNIEnv<'l>,
3315 _class: JClass<'l>,
3316 book_id: JString<'l>,
3317) -> jstring {
3318 let id = match env.get_string(&book_id) {
3319 Ok(s) => String::from(s),
3320 Err(_) => return std::ptr::null_mut(),
3321 };
3322 let json = handlers()
3323 .books
3324 .get(&id)
3325 .cloned()
3326 .unwrap_or_else(|| "null".to_string());
3327 env.new_string(json)
3328 .map(|s| s.into_raw())
3329 .unwrap_or(std::ptr::null_mut())
3330}
3331
3332fn build_server_table() -> YogServer {
3335 YogServer {
3336 ctx: std::ptr::null_mut(),
3337 abi_version: ABI_VERSION,
3338 size: std::mem::size_of::<YogServer>() as u32,
3339 free_str: yog_free_str,
3340 broadcast: srv_broadcast,
3341 get_block: srv_get_block,
3342 set_block: srv_set_block,
3343 world_time: srv_world_time,
3344 set_time: srv_set_time,
3345 is_raining: srv_is_raining,
3346 set_weather: srv_set_weather,
3347 give_item: srv_give_item,
3348 player_teleport: srv_player_teleport,
3349 send_to_player: srv_send_to_player,
3350 send_to_server: srv_send_to_server,
3351 kick_player: srv_kick_player,
3352 set_gamemode: srv_set_gamemode,
3353 send_title: srv_send_title,
3354 send_actionbar: srv_send_actionbar,
3355 play_sound: srv_play_sound,
3356 play_sound_player: srv_play_sound_player,
3357 entity_teleport: srv_entity_teleport,
3358 entity_position: srv_entity_position,
3359 entity_rotation: srv_entity_rotation,
3360 entity_health: srv_entity_health,
3361 entity_set_health: srv_entity_set_health,
3362 entity_kill: srv_entity_kill,
3363 spawn_entity: srv_spawn_entity,
3364 entity_add_effect: srv_entity_add_effect,
3365 entity_remove_effect: srv_entity_remove_effect,
3366 entity_clear_effects: srv_entity_clear_effects,
3367 entity_velocity: srv_entity_velocity,
3368 entity_set_velocity: srv_entity_set_velocity,
3369 entity_add_velocity: srv_entity_add_velocity,
3370 has_item_tag: srv_has_item_tag,
3371 has_block_tag: srv_has_block_tag,
3372 drop_loot: srv_drop_loot,
3373 scoreboard_get: srv_scoreboard_get,
3374 scoreboard_set: srv_scoreboard_set,
3375 scoreboard_add: srv_scoreboard_add,
3376 bossbar_create: srv_bossbar_create,
3377 bossbar_remove: srv_bossbar_remove,
3378 bossbar_set_title: srv_bossbar_set_title,
3379 bossbar_set_progress: srv_bossbar_set_progress,
3380 bossbar_set_color: srv_bossbar_set_color,
3381 bossbar_add_player: srv_bossbar_add_player,
3382 bossbar_remove_player: srv_bossbar_remove_player,
3383 bossbar_set_visible: srv_bossbar_set_visible,
3384 game_dir: srv_game_dir,
3385 get_block_nbt: srv_get_block_nbt,
3386 set_block_nbt: srv_set_block_nbt,
3387 get_inventory_slot: srv_get_inventory_slot,
3388 set_inventory_slot: srv_set_inventory_slot,
3389 player_inventory: srv_player_inventory,
3390 player_set_slot: srv_player_set_slot,
3391 player_teleport_dim: srv_player_teleport_dim,
3392 entity_teleport_dim: srv_entity_teleport_dim,
3393 online_players: srv_online_players,
3394 world_entity_count: srv_world_entity_count,
3395 entity_get_nbt: srv_entity_get_nbt,
3396 entity_set_nbt: srv_entity_set_nbt,
3397 spawn_particles: srv_spawn_particles,
3398 entity_attribute_get: srv_entity_attribute_get,
3399 entity_attribute_set: srv_entity_attribute_set,
3400 get_held_item_nbt: srv_get_held_item_nbt,
3401 set_held_item_nbt: srv_set_held_item_nbt,
3402 get_offhand_item_nbt: srv_get_offhand_item_nbt,
3403 set_offhand_item_nbt: srv_set_offhand_item_nbt,
3404 get_slot_item: srv_get_slot_item,
3405 set_slot_item: srv_set_slot_item,
3406 }
3407}
3408
3409unsafe extern "C" fn interop_export_impl(
3412 _ctx: *mut c_void,
3413 mod_id: YogStr,
3414 symbol: YogStr,
3415 ptr: *const c_void,
3416) {
3417 let mod_id = mod_id.as_str();
3418 let sym = symbol.as_str();
3419 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3420 registry
3421 .lock()
3422 .expect("interop symbols lock poisoned")
3423 .entry(mod_id.to_string())
3424 .or_default()
3425 .insert(sym.to_string(), ptr as usize);
3426}
3427
3428unsafe extern "C" fn interop_import_impl(
3429 _ctx: *mut c_void,
3430 mod_id: YogStr,
3431 symbol: YogStr,
3432) -> *const c_void {
3433 let mod_id = mod_id.as_str();
3434 let sym = symbol.as_str();
3435 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3436 registry
3437 .lock()
3438 .expect("interop symbols lock poisoned")
3439 .get(mod_id)
3440 .and_then(|m: &HashMap<String, usize>| m.get(sym))
3441 .map(|&ptr| ptr as *const c_void)
3442 .unwrap_or(std::ptr::null())
3443}
3444
3445fn build_api_table(ctx: *const RwLock<RuntimeHandlers>, server: *const YogServer) -> YogApi {
3446 YogApi {
3447 abi_version: ABI_VERSION,
3448 size: std::mem::size_of::<YogApi>() as u32,
3449 ctx: ctx as *mut c_void,
3450 server,
3451 on_block_break: api_on_block_break,
3452 on_chat: api_on_chat,
3453 on_player_join: api_on_player_join,
3454 on_player_leave: api_on_player_leave,
3455 on_use_item: api_on_use_item,
3456 on_use_block: api_on_use_block,
3457 on_attack_entity: api_on_attack_entity,
3458 on_entity_damage: api_on_entity_damage,
3459 on_entity_death: api_on_entity_death,
3460 on_entity_spawn: api_on_entity_spawn,
3461 on_player_place_block: api_on_player_place_block,
3462 on_player_death: api_on_player_death,
3463 on_player_respawn: api_on_player_respawn,
3464 on_advancement: api_on_advancement,
3465 on_entity_interact: api_on_entity_interact,
3466 on_item_craft: api_on_item_craft,
3467 on_explosion: api_on_explosion,
3468 on_item_pickup: api_on_item_pickup,
3469 on_player_move: api_on_player_move,
3470 on_container_open: api_on_container_open,
3471 on_container_close: api_on_container_close,
3472 on_projectile_hit: api_on_projectile_hit,
3473 on_server_tick: api_on_server_tick,
3474 on_server_started: api_on_server_started,
3475 on_server_stopping: api_on_server_stopping,
3476 on_packet: api_on_packet,
3477 on_client_packet: api_on_client_packet,
3478 register_command: api_register_command,
3479 register_typed_command: api_register_typed_command,
3480 register_recipe_json: api_register_recipe_json,
3481 register_item: api_register_item,
3482 register_block: api_register_block,
3483 schedule_once: api_schedule_once,
3484 schedule_repeating: api_schedule_repeating,
3485 on_client_tick: api_on_client_tick,
3486 on_hud_render: api_on_hud_render,
3487 on_key_press: api_on_key_press,
3488 on_screen_open: api_on_screen_open,
3489 on_screen_close: api_on_screen_close,
3490 on_world_render: api_on_world_render,
3491 register_startup_grant: api_register_startup_grant,
3492 register_book: api_register_book,
3493 register_ui: api_register_ui,
3494 on_ui_render: api_on_ui_render,
3495 register_menu_entry: api_register_menu_entry,
3496 mods_list: api_mods_list,
3497 free_str: yog_free_str,
3498 ui_open: api_ui_open,
3499 register_inventory: api_register_inventory,
3500 interop_export: interop_export_impl,
3501 interop_import: interop_import_impl,
3502 register_dimension_json: api_register_dimension_json,
3503 register_chunk_generator: api_register_chunk_generator,
3504 ui_close: api_ui_close,
3505 ui_consume_event: api_ui_consume_event,
3506 translate: api_translate,
3507 }
3508}
3509
3510fn platform_tag() -> String {
3513 format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH)
3514}
3515
3516type AbiVersionFn = unsafe extern "C" fn() -> u32;
3517type RegisterFn = unsafe extern "C" fn(*const YogApi, *const std::os::raw::c_char);
3519
3520fn load_mods(dir: &Path, api: &YogApi) {
3521 let entries = match std::fs::read_dir(dir) {
3522 Ok(e) => e,
3523 Err(_) => {
3524 yog_logging::info!("no mods directory at {} — none loaded", dir.display());
3525 return;
3526 }
3527 };
3528
3529 struct ModMeta {
3531 id: String,
3532 deps: Vec<String>,
3533 yog_path: PathBuf,
3534 }
3535 let mut metas: Vec<ModMeta> = Vec::new();
3536 for entry in entries.flatten() {
3537 let path = entry.path();
3538 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3539 continue;
3540 }
3541 let info = read_mod_info(&path);
3542 let deps = read_mod_deps(&path);
3543 metas.push(ModMeta {
3544 id: info[0].clone(),
3545 deps,
3546 yog_path: path,
3547 });
3548 }
3549
3550 let ids: Vec<String> = metas.iter().map(|m| m.id.clone()).collect();
3552 let deps_list: Vec<Vec<String>> = metas.iter().map(|m| m.deps.clone()).collect();
3553 let sorted = match topo_sort_mods(&ids, &deps_list) {
3554 Some(v) => v,
3555 None => {
3556 yog_logging::error!("circular dependency detected — loading in file order");
3557 (0..metas.len()).collect()
3558 }
3559 };
3560
3561 let mut loaded: Vec<LoadedModule> = Vec::new();
3562 for idx in sorted {
3563 let meta = &metas[idx];
3564 let lib_path = match extract_yog(&meta.yog_path) {
3565 Some(p) => p,
3566 None => {
3567 yog_logging::error!(
3568 "no native for {} in {}",
3569 platform_tag(),
3570 meta.yog_path.display()
3571 );
3572 continue;
3573 }
3574 };
3575 if let Some(module) = load_mod_lib(&lib_path, api, &meta.id) {
3576 loaded.push(module);
3577 MOD_INFOS
3578 .lock()
3579 .expect("mod infos lock poisoned")
3580 .push(read_mod_info(&meta.yog_path));
3581 }
3582 }
3583 yog_logging::info!("loaded {} mod(s) from {}", loaded.len(), dir.display());
3584
3585 resolve_all_imports(api, &loaded);
3588}
3589
3590fn resolve_all_imports(api: &YogApi, loaded: &[LoadedModule]) {
3593 for module in loaded {
3594 module.with_library(|lib| unsafe {
3595 type ResolveFn = unsafe extern "C" fn(*const YogApi);
3596 if let Ok(resolve) = lib.get::<ResolveFn>(b"__yog_resolve_imports_final") {
3597 resolve(api as *const YogApi);
3598 }
3599 });
3600 }
3601}
3602
3603fn topo_sort_mods(ids: &[String], deps_list: &[Vec<String>]) -> Option<Vec<usize>> {
3605 let n = ids.len();
3606 let name_to_idx: HashMap<&str, usize> = ids
3607 .iter()
3608 .enumerate()
3609 .map(|(i, id)| (id.as_str(), i))
3610 .collect();
3611 let mut in_degree = vec![0u32; n];
3612 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
3613 for (i, deps) in deps_list.iter().enumerate() {
3614 for dep in deps {
3615 if let Some(&j) = name_to_idx.get(dep.as_str()) {
3616 adj[j].push(i);
3617 in_degree[i] += 1;
3618 }
3619 }
3620 }
3621 let mut queue: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
3622 let mut order = Vec::with_capacity(n);
3623 while let Some(u) = queue.pop() {
3624 order.push(u);
3625 for &v in &adj[u] {
3626 in_degree[v] -= 1;
3627 if in_degree[v] == 0 {
3628 queue.push(v);
3629 }
3630 }
3631 }
3632 if order.len() == n {
3633 Some(order)
3634 } else {
3635 None
3636 }
3637}
3638
3639fn load_mod_lib(path: &Path, api: &YogApi, mod_id: &str) -> Option<LoadedModule> {
3647 let module = match unsafe { hot_reloader().load_initial(mod_id, path) } {
3648 Ok(m) => m,
3649 Err(e) => {
3650 yog_logging::error!("failed to load {}: {}", path.display(), e);
3651 return None;
3652 }
3653 };
3654
3655 let abi = module.with_library(|lib| unsafe {
3656 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3657 Some(abi())
3658 });
3659 match abi.flatten() {
3660 Some(v) if v == ABI_VERSION => {}
3661 Some(v) => {
3662 yog_logging::error!(
3663 "{}: ABI {} incompatible with runtime ABI {}",
3664 path.display(),
3665 v,
3666 ABI_VERSION
3667 );
3668 module.retire(|| {});
3669 return None;
3670 }
3671 None => {
3672 yog_logging::error!("{} is not a Yog mod (no yog_abi_version)", path.display());
3673 module.retire(|| {});
3674 return None;
3675 }
3676 }
3677
3678 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3679 yog_logging::error!("{}: {}", path.display(), e);
3680 module.retire(|| {});
3681 return None;
3682 }
3683
3684 Some(module)
3685}
3686
3687fn invoke_mod_register(module: &LoadedModule, api: &YogApi, mod_id: &str) -> Result<(), String> {
3691 module
3692 .with_library(|lib| unsafe {
3693 let register: Symbol<RegisterFn> = lib
3694 .get(b"yog_mod_register")
3695 .map_err(|_| "missing yog_mod_register".to_string())?;
3696 let mod_id_c = std::ffi::CString::new(mod_id).unwrap();
3697 *CURRENT_REGISTRATION.lock().unwrap() = Some(module.clone());
3698 register(api as *const YogApi, mod_id_c.as_ptr());
3699 *CURRENT_REGISTRATION.lock().unwrap() = None;
3700 Ok(())
3701 })
3702 .unwrap_or_else(|| Err("module was unloaded before registration could run".to_string()))
3703}
3704
3705fn read_mod_info(path: &Path) -> [String; 5] {
3709 let stem = path
3710 .file_stem()
3711 .map(|s| s.to_string_lossy().into_owned())
3712 .unwrap_or_default();
3713 let mut info = [
3714 stem.clone(),
3715 stem,
3716 String::new(),
3717 String::new(),
3718 String::new(),
3719 ];
3720 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3721 return info;
3722 }
3723 let Some(text) = (|| {
3724 let file = std::fs::File::open(path).ok()?;
3725 let mut archive = zip::ZipArchive::new(file).ok()?;
3726 let mut entry = archive.by_name("yog.toml").ok()?;
3727 let mut text = String::new();
3728 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3729 Some(text)
3730 })() else {
3731 return info;
3732 };
3733 let mut in_mod = true;
3738 for line in text.lines() {
3739 let line = line.trim();
3740 if line.starts_with('[') {
3741 in_mod = line == "[mod]";
3742 continue;
3743 }
3744 if !in_mod {
3745 continue;
3746 }
3747 let Some((key, value)) = line.split_once('=') else {
3748 continue;
3749 };
3750 let key = key.trim();
3751 let value = value.trim();
3752 let unquote = |v: &str| v.trim().trim_matches('"').to_string();
3753 match key {
3754 "id" => info[0] = tsv_clean(&unquote(value)),
3755 "name" => info[1] = tsv_clean(&unquote(value)),
3756 "version" => info[2] = tsv_clean(&unquote(value)),
3757 "description" => info[4] = tsv_clean(&unquote(value)),
3758 "authors" => {
3759 let list = value.trim_start_matches('[').trim_end_matches(']');
3760 let authors: Vec<String> = list
3761 .split(',')
3762 .map(unquote)
3763 .filter(|a| !a.is_empty())
3764 .collect();
3765 info[3] = tsv_clean(&authors.join(", "));
3766 }
3767 _ => {}
3768 }
3769 }
3770 info
3771}
3772
3773fn read_mod_deps(path: &Path) -> Vec<String> {
3775 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3776 return Vec::new();
3777 }
3778 let text = (|| {
3779 let file = std::fs::File::open(path).ok()?;
3780 let mut archive = zip::ZipArchive::new(file).ok()?;
3781 let mut entry = archive.by_name("yog.toml").ok()?;
3782 let mut text = String::new();
3783 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3784 Some(text)
3785 })();
3786 let Some(text) = text else { return Vec::new() };
3787 let mut deps = Vec::new();
3788 let mut in_deps = false;
3789 for line in text.lines() {
3790 let line = line.trim();
3791 if line.starts_with('[') {
3792 in_deps = line == "[dependencies]";
3793 continue;
3794 }
3795 if !in_deps {
3796 continue;
3797 }
3798 if let Some((key, _)) = line.split_once('=') {
3800 deps.push(key.trim().to_string());
3801 }
3802 }
3803 deps
3804}
3805
3806fn extract_yog(path: &Path) -> Option<PathBuf> {
3807 let file = std::fs::File::open(path).ok()?;
3808 let mut archive = zip::ZipArchive::new(file).ok()?;
3809 let prefix = format!("natives/{}/", platform_tag());
3810 let mut entry_name = None;
3811 for i in 0..archive.len() {
3812 let f = archive.by_index(i).ok()?;
3813 if f.name().starts_with(&prefix) && !f.name().ends_with('/') {
3814 entry_name = Some(f.name().to_string());
3815 break;
3816 }
3817 }
3818 let entry_name = entry_name?;
3819 let ext = Path::new(&entry_name)
3820 .extension()
3821 .and_then(|e| e.to_str())
3822 .unwrap_or("bin");
3823 let stem = path.file_stem()?.to_string_lossy().into_owned();
3824 static EXTRACTIONS: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
3828 let n = EXTRACTIONS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3829 let out =
3830 std::env::temp_dir().join(format!("yog-{}-{}-{}.{}", stem, std::process::id(), n, ext));
3831 let mut entry = archive.by_name(&entry_name).ok()?;
3832 let mut out_file = std::fs::File::create(&out).ok()?;
3833 std::io::copy(&mut entry, &mut out_file).ok()?;
3834 Some(out)
3835}
3836
3837fn srv_ptr() -> *const YogServer {
3840 SERVER.get().expect("yog: SERVER not initialised") as *const YogServer
3841}
3842
3843#[no_mangle]
3846pub extern "system" fn Java_dev_yog_NativeBridge_nativeInit<'l>(
3847 mut env: JNIEnv<'l>,
3848 _class: JClass<'l>,
3849 mods_dir: JString<'l>,
3850) {
3851 if let Ok(vm) = env.get_java_vm() {
3852 let _ = JAVA_VM.set(vm);
3853 }
3854
3855 let dir = env
3856 .get_string(&mods_dir)
3857 .map(String::from)
3858 .unwrap_or_default();
3859
3860 let _ = SERVER.set(build_server_table());
3862 let server_ptr = SERVER.get().unwrap() as *const YogServer;
3863
3864 let handlers_lock = HANDLERS.get_or_init(|| RwLock::new(RuntimeHandlers::new()));
3870 let handlers_ptr = handlers_lock as *const RwLock<RuntimeHandlers>;
3871
3872 let api: &'static YogApi = API_TABLE.get_or_init(|| build_api_table(handlers_ptr, server_ptr));
3873
3874 let _ = HOT_RELOADER.get_or_init(|| HotReloader::new(RuntimeModuleRegistry));
3875
3876 guard("mod loading", || {
3877 load_mods(Path::new(&dir), api);
3878 });
3879
3880 control_socket::start_if_requested();
3881
3882 yog_logging::info!("runtime initialised — the gate is open.");
3883}
3884
3885#[no_mangle]
3886pub extern "system" fn Java_dev_yog_NativeBridge_nativeHotReload<'l>(
3887 mut env: JNIEnv<'l>,
3888 _class: JClass<'l>,
3889 mod_id: JString<'l>,
3890 new_yog_path: JString<'l>,
3891) -> jni::sys::jboolean {
3892 let Ok(mod_id) = env.get_string(&mod_id).map(String::from) else {
3893 return 0;
3894 };
3895 let Ok(yog_path) = env.get_string(&new_yog_path).map(String::from) else {
3896 return 0;
3897 };
3898 let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3899 trigger_hot_reload(&mod_id, Path::new(&yog_path))
3900 }))
3901 .unwrap_or(false);
3902 ok as jni::sys::jboolean
3903}
3904
3905fn trigger_hot_reload(mod_id: &str, new_yog_path: &Path) -> bool {
3915 let Some(native_path) = extract_yog(new_yog_path) else {
3916 yog_logging::error!(
3917 "hot-reload {}: no native for {} in {}",
3918 mod_id,
3919 platform_tag(),
3920 new_yog_path.display()
3921 );
3922 return false;
3923 };
3924
3925 let api = API_TABLE.get().expect("yog: nativeInit not called yet");
3926
3927 let module = match unsafe { hot_reloader().reload(mod_id, &native_path) } {
3928 Ok(m) => m,
3929 Err(e) => {
3930 yog_logging::error!(
3931 "hot-reload {}: failed to load {}: {}",
3932 mod_id,
3933 native_path.display(),
3934 e
3935 );
3936 return false;
3937 }
3938 };
3939
3940 let abi = module.with_library(|lib| unsafe {
3941 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3942 Some(abi())
3943 });
3944 match abi.flatten() {
3945 Some(v) if v == ABI_VERSION => {}
3946 other => {
3947 yog_logging::error!(
3948 "hot-reload {}: ABI {:?} incompatible with runtime ABI {} — mod has no active handlers until a compatible reload succeeds",
3949 mod_id,
3950 other,
3951 ABI_VERSION
3952 );
3953 return false;
3954 }
3955 }
3956
3957 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3958 yog_logging::error!("hot-reload {}: {}", mod_id, e);
3959 return false;
3960 }
3961
3962 yog_logging::info!("hot-reloaded mod {}", mod_id);
3963 true
3964}
3965
3966#[no_mangle]
3967pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreak<'l>(
3968 mut env: JNIEnv<'l>,
3969 _class: JClass<'l>,
3970 player: JString<'l>,
3971 block: JString<'l>,
3972 x: jint,
3973 y: jint,
3974 z: jint,
3975 dimension: JString<'l>,
3976) {
3977 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
3978 let ev = yog_abi::YogBlockBreakEvent {
3979 player: YogStr::from_str(&p),
3980 block: YogStr::from_str(&b),
3981 pos: YogBlockPos { x, y, z },
3982 dimension: YogStr::from_str(&d),
3983 };
3984 let srv = srv_ptr();
3985 guard("on_block_break", || {
3986 for (module, ud, f) in &handlers().block_break {
3987 if module.is_retiring() {
3988 continue;
3989 }
3990 let Some(_call) = module.enter() else {
3991 continue;
3992 };
3993 unsafe { f(*ud, srv, &ev, 1) };
3994 }
3995 });
3996}
3997
3998#[no_mangle]
3999pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChat<'l>(
4000 mut env: JNIEnv<'l>,
4001 _class: JClass<'l>,
4002 player: JString<'l>,
4003 message: JString<'l>,
4004 dimension: JString<'l>,
4005) {
4006 let (p, m, d) = (
4007 jstr!(env, player),
4008 jstr!(env, message),
4009 jstr!(env, dimension),
4010 );
4011 let ev = yog_abi::YogChatEvent {
4012 player: YogStr::from_str(&p),
4013 message: YogStr::from_str(&m),
4014 dimension: YogStr::from_str(&d),
4015 };
4016 let srv = srv_ptr();
4017 guard("on_chat", || {
4018 for (module, ud, f) in &handlers().chat {
4019 if module.is_retiring() {
4020 continue;
4021 }
4022 let Some(_call) = module.enter() else {
4023 continue;
4024 };
4025 unsafe { f(*ud, srv, &ev, 1) };
4026 }
4027 });
4028}
4029
4030#[no_mangle]
4031pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerJoin<'l>(
4032 mut env: JNIEnv<'l>,
4033 _class: JClass<'l>,
4034 player: JString<'l>,
4035 uuid: JString<'l>,
4036 dimension: JString<'l>,
4037) {
4038 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4039 let ev = yog_abi::YogPlayerEvent {
4040 player: YogStr::from_str(&p),
4041 uuid: YogStr::from_str(&u),
4042 dimension: YogStr::from_str(&d),
4043 };
4044 let srv = srv_ptr();
4045 guard("on_player_join", || {
4046 for (module, ud, f) in &handlers().player_join {
4047 if module.is_retiring() {
4048 continue;
4049 }
4050 let Some(_call) = module.enter() else {
4051 continue;
4052 };
4053 unsafe { f(*ud, srv, &ev, 1) };
4054 }
4055 });
4056 let h = handlers();
4060 if !h.startup_grants.is_empty() {
4061 h.pending_grants
4062 .lock()
4063 .expect("pending_grants poisoned")
4064 .push((p, u, 0));
4065 }
4066}
4067
4068fn process_pending_grants() {
4071 let h = handlers();
4072 let mut pending = match h.pending_grants.try_lock() {
4073 Ok(g) => g,
4074 Err(_) => return,
4075 };
4076 if pending.is_empty() {
4077 return;
4078 }
4079 const FIRST_TRY_TICK: u32 = 2;
4081 const MAX_TICKS: u32 = 600;
4083
4084 let mut granted = h.startup_granted.lock().expect("startup_granted poisoned");
4085 let mut still_pending = Vec::new();
4086 let mut any_new = false;
4087
4088 for (p, u, ticks) in pending.drain(..) {
4089 if ticks < FIRST_TRY_TICK {
4090 still_pending.push((p, u, ticks + 1));
4091 continue;
4092 }
4093 let mut all_ok = true;
4094 for sg in &h.startup_grants {
4095 let key = format!("{}::{}", u, sg.id);
4096 if granted.contains_key(&key) {
4097 continue;
4098 }
4099 let mut ok = true;
4100 for item_id in &sg.items {
4101 ok &= unsafe {
4102 srv_give_item(
4103 std::ptr::null_mut(),
4104 YogStr::from_str(&p),
4105 YogStr::from_str(item_id),
4106 1,
4107 )
4108 };
4109 }
4110 if let Some(_book) = &sg.book {
4111 ok &= unsafe {
4112 srv_give_item(
4113 std::ptr::null_mut(),
4114 YogStr::from_str(&p),
4115 YogStr::from_str("minecraft:written_book"),
4116 1,
4117 )
4118 };
4119 }
4120 if ok {
4121 yog_logging::info!("startup grant {} given to {}", sg.id, p);
4122 granted.insert(key, true);
4123 any_new = true;
4124 } else {
4125 all_ok = false;
4126 }
4127 }
4128 if !all_ok && ticks < MAX_TICKS {
4129 still_pending.push((p, u, ticks + 1));
4130 }
4131 }
4132 *pending = still_pending;
4133 if any_new {
4134 save_startup_granted(&granted);
4135 }
4136}
4137
4138#[no_mangle]
4139pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerLeave<'l>(
4140 mut env: JNIEnv<'l>,
4141 _class: JClass<'l>,
4142 player: JString<'l>,
4143 uuid: JString<'l>,
4144 dimension: JString<'l>,
4145) {
4146 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4147 let ev = yog_abi::YogPlayerEvent {
4148 player: YogStr::from_str(&p),
4149 uuid: YogStr::from_str(&u),
4150 dimension: YogStr::from_str(&d),
4151 };
4152 let srv = srv_ptr();
4153 guard("on_player_leave", || {
4154 for (module, ud, f) in &handlers().player_leave {
4155 if module.is_retiring() {
4156 continue;
4157 }
4158 let Some(_call) = module.enter() else {
4159 continue;
4160 };
4161 unsafe { f(*ud, srv, &ev, 1) };
4162 }
4163 });
4164}
4165
4166#[no_mangle]
4167pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItem<'l>(
4168 mut env: JNIEnv<'l>,
4169 _class: JClass<'l>,
4170 player: JString<'l>,
4171 item: JString<'l>,
4172 sneaking: jni::sys::jboolean,
4173 dimension: JString<'l>,
4174) {
4175 let (p, i, d) = (jstr!(env, player), jstr!(env, item), jstr!(env, dimension));
4176 let ev = yog_abi::YogUseItemEvent {
4177 player: YogStr::from_str(&p),
4178 item: YogStr::from_str(&i),
4179 sneaking: sneaking != 0,
4180 dimension: YogStr::from_str(&d),
4181 };
4182 let srv = srv_ptr();
4183 guard("on_use_item", || {
4184 for (module, ud, f) in &handlers().use_item {
4185 if module.is_retiring() {
4186 continue;
4187 }
4188 let Some(_call) = module.enter() else {
4189 continue;
4190 };
4191 unsafe { f(*ud, srv, &ev, 1) };
4192 }
4193 });
4194}
4195
4196#[no_mangle]
4197pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItemPre<'l>(
4198 mut env: JNIEnv<'l>,
4199 _class: JClass<'l>,
4200 player: JString<'l>,
4201 item: JString<'l>,
4202 sneaking: jni::sys::jboolean,
4203 dimension: JString<'l>,
4204) -> jni::sys::jboolean {
4205 let h = handlers();
4206 if h.use_item.is_empty() {
4207 return 1;
4208 }
4209 let p = match env.get_string(&player) {
4210 Ok(s) => String::from(s),
4211 Err(_) => return 1,
4212 };
4213 let i = match env.get_string(&item) {
4214 Ok(s) => String::from(s),
4215 Err(_) => return 1,
4216 };
4217 let d = match env.get_string(&dimension) {
4218 Ok(s) => String::from(s),
4219 Err(_) => return 1,
4220 };
4221 let ev = yog_abi::YogUseItemEvent {
4222 player: YogStr::from_str(&p),
4223 item: YogStr::from_str(&i),
4224 sneaking: sneaking != 0,
4225 dimension: YogStr::from_str(&d),
4226 };
4227 let srv = srv_ptr();
4228 let mut allow = true;
4229 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4230 for (module, ud, f) in &h.use_item {
4231 if module.is_retiring() {
4232 continue;
4233 }
4234 let Some(_call) = module.enter() else {
4235 continue;
4236 };
4237 if !unsafe { f(*ud, srv, &ev, 0) } {
4238 allow = false;
4239 break;
4240 }
4241 }
4242 }))
4243 .ok();
4244 allow as jni::sys::jboolean
4245}
4246
4247#[no_mangle]
4248pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlock<'l>(
4249 mut env: JNIEnv<'l>,
4250 _class: JClass<'l>,
4251 player: JString<'l>,
4252 block: JString<'l>,
4253 x: jint,
4254 y: jint,
4255 z: jint,
4256 dimension: JString<'l>,
4257) {
4258 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4259 let ev = yog_abi::YogUseBlockEvent {
4260 player: YogStr::from_str(&p),
4261 block: YogStr::from_str(&b),
4262 pos: YogBlockPos { x, y, z },
4263 dimension: YogStr::from_str(&d),
4264 };
4265 let srv = srv_ptr();
4266 guard("on_use_block", || {
4267 for (module, ud, f) in &handlers().use_block {
4268 if module.is_retiring() {
4269 continue;
4270 }
4271 let Some(_call) = module.enter() else {
4272 continue;
4273 };
4274 unsafe { f(*ud, srv, &ev, 1) };
4275 }
4276 });
4277}
4278
4279#[no_mangle]
4280pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlockPre<'l>(
4281 mut env: JNIEnv<'l>,
4282 _class: JClass<'l>,
4283 player: JString<'l>,
4284 block: JString<'l>,
4285 x: jint,
4286 y: jint,
4287 z: jint,
4288 dimension: JString<'l>,
4289) -> jni::sys::jboolean {
4290 let h = handlers();
4291 if h.use_block.is_empty() {
4292 return 1;
4293 }
4294 let p = match env.get_string(&player) {
4295 Ok(s) => String::from(s),
4296 Err(_) => return 1,
4297 };
4298 let b = match env.get_string(&block) {
4299 Ok(s) => String::from(s),
4300 Err(_) => return 1,
4301 };
4302 let d = match env.get_string(&dimension) {
4303 Ok(s) => String::from(s),
4304 Err(_) => return 1,
4305 };
4306 let ev = yog_abi::YogUseBlockEvent {
4307 player: YogStr::from_str(&p),
4308 block: YogStr::from_str(&b),
4309 pos: YogBlockPos { x, y, z },
4310 dimension: YogStr::from_str(&d),
4311 };
4312 let srv = srv_ptr();
4313 let mut allow = true;
4314 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4315 for (module, ud, f) in &h.use_block {
4316 if module.is_retiring() {
4317 continue;
4318 }
4319 let Some(_call) = module.enter() else {
4320 continue;
4321 };
4322 if !unsafe { f(*ud, srv, &ev, 0) } {
4323 allow = false;
4324 break;
4325 }
4326 }
4327 }))
4328 .ok();
4329 allow as jni::sys::jboolean
4330}
4331
4332#[no_mangle]
4333pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAttackEntity<'l>(
4334 mut env: JNIEnv<'l>,
4335 _class: JClass<'l>,
4336 player: JString<'l>,
4337 target_type: JString<'l>,
4338 target_uuid: JString<'l>,
4339 dimension: JString<'l>,
4340) {
4341 let (p, tt, tu, d) = (
4342 jstr!(env, player),
4343 jstr!(env, target_type),
4344 jstr!(env, target_uuid),
4345 jstr!(env, dimension),
4346 );
4347 let ev = yog_abi::YogAttackEntityEvent {
4348 player: YogStr::from_str(&p),
4349 target_type: YogStr::from_str(&tt),
4350 target_uuid: YogStr::from_str(&tu),
4351 dimension: YogStr::from_str(&d),
4352 };
4353 let srv = srv_ptr();
4354 guard("on_attack_entity", || {
4355 for (module, ud, f) in &handlers().attack_entity {
4356 if module.is_retiring() {
4357 continue;
4358 }
4359 let Some(_call) = module.enter() else {
4360 continue;
4361 };
4362 unsafe { f(*ud, srv, &ev, 1) };
4363 }
4364 });
4365}
4366
4367#[no_mangle]
4368pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamage<'l>(
4369 mut env: JNIEnv<'l>,
4370 _class: JClass<'l>,
4371 entity_type: JString<'l>,
4372 uuid: JString<'l>,
4373 amount: jfloat,
4374 source: JString<'l>,
4375 dimension: JString<'l>,
4376) {
4377 let (et, u, s, d) = (
4378 jstr!(env, entity_type),
4379 jstr!(env, uuid),
4380 jstr!(env, source),
4381 jstr!(env, dimension),
4382 );
4383 let ev = yog_abi::YogEntityDamageEvent {
4384 entity_type: YogStr::from_str(&et),
4385 uuid: YogStr::from_str(&u),
4386 amount,
4387 source: YogStr::from_str(&s),
4388 dimension: YogStr::from_str(&d),
4389 };
4390 let srv = srv_ptr();
4391 guard("on_entity_damage", || {
4392 for (module, ud, f) in &handlers().entity_damage {
4393 if module.is_retiring() {
4394 continue;
4395 }
4396 let Some(_call) = module.enter() else {
4397 continue;
4398 };
4399 unsafe { f(*ud, srv, &ev, 1) };
4400 }
4401 });
4402}
4403
4404#[no_mangle]
4405pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDeath<'l>(
4406 mut env: JNIEnv<'l>,
4407 _class: JClass<'l>,
4408 entity_type: JString<'l>,
4409 uuid: JString<'l>,
4410 source: JString<'l>,
4411 dimension: JString<'l>,
4412) {
4413 let (et, u, s, d) = (
4414 jstr!(env, entity_type),
4415 jstr!(env, uuid),
4416 jstr!(env, source),
4417 jstr!(env, dimension),
4418 );
4419 let ev = yog_abi::YogEntityDeathEvent {
4420 entity_type: YogStr::from_str(&et),
4421 uuid: YogStr::from_str(&u),
4422 source: YogStr::from_str(&s),
4423 dimension: YogStr::from_str(&d),
4424 };
4425 let srv = srv_ptr();
4426 guard("on_entity_death", || {
4427 for (module, ud, f) in &handlers().entity_death {
4428 if module.is_retiring() {
4429 continue;
4430 }
4431 let Some(_call) = module.enter() else {
4432 continue;
4433 };
4434 unsafe { f(*ud, srv, &ev, 1) };
4435 }
4436 });
4437}
4438
4439#[no_mangle]
4440pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnTick<'l>(
4441 _env: JNIEnv<'l>,
4442 _class: JClass<'l>,
4443) {
4444 let h = handlers();
4445 let srv = srv_ptr();
4446 guard("on_tick", || {
4447 for (module, ud, f) in &h.server_tick {
4448 if module.is_retiring() {
4449 continue;
4450 }
4451 let Some(_call) = module.enter() else {
4452 continue;
4453 };
4454 unsafe { f(*ud, srv) };
4455 }
4456 });
4457
4458 guard("startup_grants", process_pending_grants);
4459
4460 {
4462 let mut sched = h.scheduler.lock().expect("scheduler poisoned");
4463 let mut to_fire: Vec<(LoadedModule, *mut c_void, YogScheduledFn)> = Vec::new();
4464 let mut remaining = Vec::new();
4465 for task in sched.once_tasks.drain(..) {
4466 if task.delay_remaining == 0 {
4467 to_fire.push((task.module.clone(), task.ud, task.f));
4468 } else {
4469 remaining.push(OnceTask {
4470 delay_remaining: task.delay_remaining - 1,
4471 ..task
4472 });
4473 }
4474 }
4475 sched.once_tasks = remaining;
4476 drop(sched);
4477 for (module, ud, f) in to_fire {
4478 if module.is_retiring() {
4479 continue;
4480 }
4481 let Some(_call) = module.enter() else {
4482 continue;
4483 };
4484 guard("schedule_once", || unsafe { f(ud, srv) });
4485 }
4486 }
4487
4488 {
4490 let mut sched = h.scheduler.lock().expect("scheduler poisoned");
4491 let mut to_fire: Vec<(LoadedModule, *mut c_void, YogScheduledFn)> = Vec::new();
4492 for task in &mut sched.repeating_tasks {
4493 if task.ticks_left == 0 {
4494 to_fire.push((task.module.clone(), task.ud, task.f));
4495 task.ticks_left = task.period;
4496 } else {
4497 task.ticks_left -= 1;
4498 }
4499 }
4500 drop(sched);
4501 for (module, ud, f) in to_fire {
4502 if module.is_retiring() {
4503 continue;
4504 }
4505 let Some(_call) = module.enter() else {
4506 continue;
4507 };
4508 guard("schedule_repeating", || unsafe { f(ud, srv) });
4509 }
4510 }
4511}
4512
4513static WORLD_DIR: Mutex<Option<std::path::PathBuf>> = Mutex::new(None);
4515
4516fn startup_grants_path() -> std::path::PathBuf {
4519 let base = WORLD_DIR
4520 .lock()
4521 .ok()
4522 .and_then(|d| d.clone())
4523 .unwrap_or_else(|| std::path::PathBuf::from("."));
4524 base.join("yog-data").join("startup_grants.json")
4525}
4526
4527fn load_startup_granted() {
4528 let h = handlers();
4529 let mut granted = h.startup_granted.lock().expect("startup_granted poisoned");
4530 granted.clear();
4533 h.pending_grants
4534 .lock()
4535 .expect("pending_grants poisoned")
4536 .clear();
4537 let path = startup_grants_path();
4538 let Ok(data) = std::fs::read_to_string(&path) else {
4539 return;
4540 };
4541 let Ok(keys) = serde_json::from_str::<Vec<String>>(&data) else {
4542 yog_logging::warn!("startup_grants.json: invalid JSON, ignoring");
4543 return;
4544 };
4545 for k in keys {
4546 granted.insert(k, true);
4547 }
4548 yog_logging::info!("loaded {} startup grants from disk", granted.len());
4549}
4550
4551fn save_startup_granted(granted: &HashMap<String, bool>) {
4552 let path = startup_grants_path();
4553 if let Some(parent) = path.parent() {
4554 let _ = std::fs::create_dir_all(parent);
4555 }
4556 let keys: Vec<&String> = granted.keys().collect();
4557 match serde_json::to_string(&keys) {
4558 Ok(json) => {
4559 let _ = std::fs::write(&path, json);
4560 }
4561 Err(e) => yog_logging::error!("failed to save startup_grants.json: {}", e),
4562 }
4563}
4564
4565#[no_mangle]
4566pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStarted<'l>(
4567 mut env: JNIEnv<'l>,
4568 _class: JClass<'l>,
4569 world_dir: JString<'l>,
4570) {
4571 let dir = jstr!(env, world_dir);
4572 *WORLD_DIR.lock().expect("WORLD_DIR poisoned") = if dir.is_empty() {
4573 None
4574 } else {
4575 Some(std::path::PathBuf::from(dir))
4576 };
4577 load_startup_granted();
4578 let srv = srv_ptr();
4579 guard("on_server_started", || {
4580 for (module, ud, f) in &handlers().server_started {
4581 if module.is_retiring() {
4582 continue;
4583 }
4584 let Some(_call) = module.enter() else {
4585 continue;
4586 };
4587 unsafe { f(*ud, srv) };
4588 }
4589 });
4590}
4591
4592#[no_mangle]
4593pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStopping<'l>(
4594 _env: JNIEnv<'l>,
4595 _class: JClass<'l>,
4596) {
4597 let srv = srv_ptr();
4598 guard("on_server_stopping", || {
4599 for (module, ud, f) in &handlers().server_stopping {
4600 if module.is_retiring() {
4601 continue;
4602 }
4603 let Some(_call) = module.enter() else {
4604 continue;
4605 };
4606 unsafe { f(*ud, srv) };
4607 }
4608 });
4609}
4610
4611#[no_mangle]
4612pub extern "system" fn Java_dev_yog_NativeBridge_nativeCommandNames<'l>(
4613 env: JNIEnv<'l>,
4614 _class: JClass<'l>,
4615) -> jstring {
4616 let names = handlers()
4617 .commands
4618 .keys()
4619 .cloned()
4620 .collect::<Vec<_>>()
4621 .join("\n");
4622 env.new_string(names)
4623 .map(|s| s.into_raw())
4624 .unwrap_or(std::ptr::null_mut())
4625}
4626
4627#[no_mangle]
4628pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreakPre<'l>(
4629 mut env: JNIEnv<'l>,
4630 _class: JClass<'l>,
4631 player: JString<'l>,
4632 block: JString<'l>,
4633 x: jint,
4634 y: jint,
4635 z: jint,
4636 dimension: JString<'l>,
4637) -> jni::sys::jboolean {
4638 let h = handlers();
4639 if h.block_break.is_empty() {
4640 return 1;
4641 }
4642 let p = match env.get_string(&player) {
4643 Ok(s) => String::from(s),
4644 Err(_) => return 1,
4645 };
4646 let b = match env.get_string(&block) {
4647 Ok(s) => String::from(s),
4648 Err(_) => return 1,
4649 };
4650 let d = match env.get_string(&dimension) {
4651 Ok(s) => String::from(s),
4652 Err(_) => return 1,
4653 };
4654 let ev = yog_abi::YogBlockBreakEvent {
4655 player: YogStr::from_str(&p),
4656 block: YogStr::from_str(&b),
4657 pos: YogBlockPos { x, y, z },
4658 dimension: YogStr::from_str(&d),
4659 };
4660 let srv = srv_ptr();
4661 let mut allow = true;
4662 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4663 for (module, ud, f) in &h.block_break {
4664 if module.is_retiring() {
4665 continue;
4666 }
4667 let Some(_call) = module.enter() else {
4668 continue;
4669 };
4670 if !unsafe { f(*ud, srv, &ev, 0) } {
4671 allow = false;
4672 break;
4673 }
4674 }
4675 }))
4676 .ok();
4677 allow as jni::sys::jboolean
4678}
4679
4680#[no_mangle]
4681pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChatPre<'l>(
4682 mut env: JNIEnv<'l>,
4683 _class: JClass<'l>,
4684 player: JString<'l>,
4685 message: JString<'l>,
4686 dimension: JString<'l>,
4687) -> jni::sys::jboolean {
4688 let h = handlers();
4689 if h.chat.is_empty() {
4690 return 1;
4691 }
4692 let p = match env.get_string(&player) {
4693 Ok(s) => String::from(s),
4694 Err(_) => return 1,
4695 };
4696 let m = match env.get_string(&message) {
4697 Ok(s) => String::from(s),
4698 Err(_) => return 1,
4699 };
4700 let d = match env.get_string(&dimension) {
4701 Ok(s) => String::from(s),
4702 Err(_) => return 1,
4703 };
4704 let ev = yog_abi::YogChatEvent {
4705 player: YogStr::from_str(&p),
4706 message: YogStr::from_str(&m),
4707 dimension: YogStr::from_str(&d),
4708 };
4709 let srv = srv_ptr();
4710 let mut allow = true;
4711 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4712 for (module, ud, f) in &h.chat {
4713 if module.is_retiring() {
4714 continue;
4715 }
4716 let Some(_call) = module.enter() else {
4717 continue;
4718 };
4719 if !unsafe { f(*ud, srv, &ev, 0) } {
4720 allow = false;
4721 break;
4722 }
4723 }
4724 }))
4725 .ok();
4726 allow as jni::sys::jboolean
4727}
4728
4729#[no_mangle]
4730pub extern "system" fn Java_dev_yog_NativeBridge_nativeRecipeJsons<'l>(
4731 env: JNIEnv<'l>,
4732 _class: JClass<'l>,
4733) -> jstring {
4734 let s = handlers()
4735 .recipes
4736 .iter()
4737 .map(|(ns, name, json)| format!("{}\t{}\t{}", ns, name, json))
4738 .collect::<Vec<_>>()
4739 .join("\n");
4740 env.new_string(s)
4741 .map(|s| s.into_raw())
4742 .unwrap_or(std::ptr::null_mut())
4743}
4744
4745#[no_mangle]
4746pub extern "system" fn Java_dev_yog_NativeBridge_nativeDimensionJsons<'l>(
4747 env: JNIEnv<'l>,
4748 _class: JClass<'l>,
4749) -> jstring {
4750 let s = handlers()
4754 .dimensions
4755 .iter()
4756 .map(|(id, json)| format!("{}\t{}", id, json))
4757 .collect::<Vec<_>>()
4758 .join("\n");
4759 env.new_string(s)
4760 .map(|s| s.into_raw())
4761 .unwrap_or(std::ptr::null_mut())
4762}
4763
4764unsafe extern "C" fn chunk_writer_set_block(
4769 _ctx: *mut c_void,
4770 x: i32,
4771 y: i32,
4772 z: i32,
4773 block_id: YogStr,
4774) -> bool {
4775 let Some(mut env) = get_env() else {
4776 return false;
4777 };
4778 let Some(jb) = ys_to_java(&mut env, block_id) else {
4779 return false;
4780 };
4781 env.call_static_method(
4782 "dev/yog/NativeBridge",
4783 "setBlockInGeneratingChunk",
4784 "(IIILjava/lang/String;)Z",
4785 &[
4786 JValue::Int(x),
4787 JValue::Int(y),
4788 JValue::Int(z),
4789 JValue::Object(&jb),
4790 ],
4791 )
4792 .and_then(|v| v.z())
4793 .unwrap_or(false)
4794}
4795
4796#[no_mangle]
4797pub extern "system" fn Java_dev_yog_NativeBridge_nativeGenerateChunk<'l>(
4798 mut env: JNIEnv<'l>,
4799 _class: JClass<'l>,
4800 generator_type_id: JString<'l>,
4801 chunk_x: jint,
4802 chunk_z: jint,
4803 min_y: jint,
4804 height: jint,
4805) {
4806 let id: String = match env.get_string(&generator_type_id) {
4807 Ok(s) => String::from(s),
4808 Err(_) => return,
4809 };
4810 let Some((module, ud, cb)) = handlers().chunk_generators.get(&id).cloned() else {
4811 return;
4812 };
4813 if module.is_retiring() {
4814 return;
4815 }
4816 let Some(_call) = module.enter() else { return };
4817 let writer = yog_abi::YogChunkWriterApi {
4818 chunk_x,
4819 chunk_z,
4820 min_y,
4821 height,
4822 ctx: std::ptr::null_mut(),
4823 set_block: chunk_writer_set_block,
4824 };
4825 guard("chunk_generator", || unsafe {
4826 cb(ud, &writer as *const _);
4827 });
4828}
4829
4830#[no_mangle]
4831pub extern "system" fn Java_dev_yog_NativeBridge_nativeTypedCommandSchemas<'l>(
4832 env: JNIEnv<'l>,
4833 _class: JClass<'l>,
4834) -> jstring {
4835 let mut lines = Vec::new();
4840 for (name, regs) in handlers().commands.iter() {
4841 let mut seen = std::collections::HashSet::new();
4842 for (_module, schema, _, _) in regs {
4843 if let Some(schema) = schema {
4844 if seen.insert(schema.as_str()) {
4845 lines.push(format!("{}\t{}", name, schema));
4846 }
4847 }
4848 }
4849 }
4850 env.new_string(lines.join("\n"))
4851 .map(|s| s.into_raw())
4852 .unwrap_or(std::ptr::null_mut())
4853}
4854
4855#[no_mangle]
4856pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnCommand<'l>(
4857 mut env: JNIEnv<'l>,
4858 _class: JClass<'l>,
4859 name: JString<'l>,
4860 args: JString<'l>,
4861 source: JString<'l>,
4862 uuid: JString<'l>,
4863 dimension: JString<'l>,
4864) -> jstring {
4865 let (n, a, s, u, d) = (
4866 env.get_string(&name).map(String::from).unwrap_or_default(),
4867 env.get_string(&args).map(String::from).unwrap_or_default(),
4868 env.get_string(&source)
4869 .map(String::from)
4870 .unwrap_or_default(),
4871 env.get_string(&uuid).map(String::from).unwrap_or_default(),
4872 env.get_string(&dimension)
4873 .map(String::from)
4874 .unwrap_or_default(),
4875 );
4876 let ev = yog_abi::YogCommandEvent {
4877 name: YogStr::from_str(&n),
4878 args: YogStr::from_str(&a),
4879 source: YogStr::from_str(&s),
4880 uuid: YogStr::from_str(&u),
4881 dimension: YogStr::from_str(&d),
4882 };
4883 let h = handlers();
4884 let srv = srv_ptr();
4885 let actual_argc = if a.is_empty() {
4887 0
4888 } else {
4889 a.split('\t').count()
4890 };
4891 let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4892 let Some(regs) = h.commands.get(&n) else {
4893 return String::new();
4894 };
4895 for (module, schema, ud, f) in regs {
4896 let expected_argc = schema.as_ref().map_or(0, |s| s.split_whitespace().count());
4897 if expected_argc != actual_argc {
4898 continue;
4899 }
4900 if module.is_retiring() {
4901 continue;
4902 }
4903 let Some(_call) = module.enter() else {
4904 continue;
4905 };
4906 let mut buf = [0u8; 4096];
4907 let mut reply_len: u32 = 0;
4908 unsafe {
4909 f(
4910 *ud,
4911 srv,
4912 &ev,
4913 buf.as_mut_ptr(),
4914 buf.len() as u32,
4915 &mut reply_len,
4916 )
4917 };
4918 if reply_len > 0 {
4919 return String::from_utf8_lossy(&buf[..reply_len as usize]).into_owned();
4920 }
4921 }
4922 String::new()
4923 }))
4924 .unwrap_or_else(|_| {
4925 yog_logging::error!("a mod panicked handling command `{}`", n);
4926 String::new()
4927 });
4928
4929 env.new_string(reply)
4930 .map(|s| s.into_raw())
4931 .unwrap_or(std::ptr::null_mut())
4932}
4933
4934#[no_mangle]
4935pub extern "system" fn Java_dev_yog_NativeBridge_nativeItemDefs<'l>(
4936 env: JNIEnv<'l>,
4937 _class: JClass<'l>,
4938) -> jstring {
4939 let s = handlers()
4940 .items
4941 .iter()
4942 .map(|d| {
4943 let mut parts = vec![d.id.clone()];
4944 parts.push(format!("max_stack={}", d.max_stack));
4945 if let Some(n) = &d.name {
4946 parts.push(format!("name={n}"));
4947 }
4948 if let Some(t) = &d.tooltip {
4949 parts.push(format!("tooltip={t}"));
4950 }
4951 if d.max_damage > 0 {
4952 parts.push(format!("max_damage={}", d.max_damage));
4953 }
4954 if d.fire_resistant {
4955 parts.push("fire_resistant=1".into());
4956 }
4957 if d.fuel_ticks > 0 {
4958 parts.push(format!("fuel_ticks={}", d.fuel_ticks));
4959 }
4960 if let Some(f) = &d.food {
4961 parts.push(format!(
4962 "food={}:{}:{}",
4963 f.nutrition,
4964 f.saturation,
4965 if f.can_always_eat { 1 } else { 0 }
4966 ));
4967 }
4968 parts.join("\t")
4969 })
4970 .collect::<Vec<_>>()
4971 .join("\n");
4972 env.new_string(s)
4973 .map(|s| s.into_raw())
4974 .unwrap_or(std::ptr::null_mut())
4975}
4976
4977#[no_mangle]
4978pub extern "system" fn Java_dev_yog_NativeBridge_nativeBlockDefs<'l>(
4979 env: JNIEnv<'l>,
4980 _class: JClass<'l>,
4981) -> jstring {
4982 let s = handlers()
4983 .blocks
4984 .iter()
4985 .map(|d| {
4986 let mut parts = vec![d.id.clone()];
4987 parts.push(format!("hardness={}", d.hardness));
4988 parts.push(format!("resistance={}", d.resistance));
4989 if let Some(n) = &d.name {
4990 parts.push(format!("name={n}"));
4991 }
4992 if let Some(sh) = d.shape {
4993 parts.push(format!(
4994 "shape={}:{}:{}:{}:{}:{}",
4995 sh[0], sh[1], sh[2], sh[3], sh[4], sh[5]
4996 ));
4997 }
4998 if d.light_level > 0 {
4999 parts.push(format!("light={}", d.light_level));
5000 }
5001 if let Some(snd) = &d.sound {
5002 parts.push(format!("sound={snd}"));
5003 }
5004 if d.requires_tool {
5005 parts.push("requires_tool=1".into());
5006 }
5007 if d.no_collision {
5008 parts.push("no_collision=1".into());
5009 }
5010 if d.slipperiness > 0.0 {
5011 parts.push(format!("slipperiness={}", d.slipperiness));
5012 }
5013 if d.connects {
5014 parts.push("connects=1".into());
5015 }
5016 if !d.connect_groups.is_empty() {
5017 parts.push(format!("connect_groups={}", d.connect_groups.join(",")));
5018 }
5019 if let Some(inv) = &d.inventory_id {
5020 parts.push(format!("inventory_id={inv}"));
5021 }
5022 parts.join("\t")
5023 })
5024 .collect::<Vec<_>>()
5025 .join("\n");
5026 env.new_string(s)
5027 .map(|s| s.into_raw())
5028 .unwrap_or(std::ptr::null_mut())
5029}
5030
5031#[no_mangle]
5035pub extern "system" fn Java_dev_yog_NativeBridge_nativeInventoryDefs<'l>(
5036 env: JNIEnv<'l>,
5037 _class: JClass<'l>,
5038) -> jstring {
5039 let s = handlers()
5040 .inventories
5041 .iter()
5042 .map(|d| {
5043 let mut parts = vec![d.id.clone()];
5044 parts.push(format!("slot_count={}", d.slot_count));
5045 parts.push(format!(
5046 "layout={}",
5047 yog_inventory::encode_layout(&d.resolved_layout())
5048 ));
5049 parts.push(format!(
5050 "include_player_inventory={}",
5051 d.include_player_inventory as u8
5052 ));
5053 parts.push(format!(
5054 "player_inv={}:{}",
5055 d.player_inv_offset.0, d.player_inv_offset.1
5056 ));
5057 if let Some(tex) = &d.background_texture {
5058 parts.push(format!("background_texture={tex}"));
5059 }
5060 if !d.title.is_empty() {
5061 parts.push(format!("title={}", d.title));
5062 }
5063 if let Some((w, h)) = d.background_size {
5064 parts.push(format!("background_size={w}:{h}"));
5065 }
5066 parts.join("\t")
5067 })
5068 .collect::<Vec<_>>()
5069 .join("\n");
5070 env.new_string(s)
5071 .map(|s| s.into_raw())
5072 .unwrap_or(std::ptr::null_mut())
5073}
5074
5075#[no_mangle]
5076pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPacket<'l>(
5077 mut env: JNIEnv<'l>,
5078 _class: JClass<'l>,
5079 channel: JString<'l>,
5080 player: JString<'l>,
5081 payload: JByteArray<'l>,
5082) {
5083 let ch = env
5084 .get_string(&channel)
5085 .map(String::from)
5086 .unwrap_or_default();
5087 let pl = env
5088 .get_string(&player)
5089 .map(String::from)
5090 .unwrap_or_default();
5091 let data = env.convert_byte_array(&payload).unwrap_or_default();
5092 let ev = yog_abi::YogPacketEvent {
5093 channel: YogStr::from_str(&ch),
5094 player: YogStr::from_str(&pl),
5095 payload: data.as_ptr(),
5096 payload_len: data.len() as u32,
5097 };
5098 let h = handlers();
5099 let srv = srv_ptr();
5100 guard("on_packet", || {
5101 if let Some((module, ud, f)) = h.packets.get(&ch) {
5102 if module.is_retiring() {
5103 return;
5104 }
5105 let Some(_call) = module.enter() else { return };
5106 unsafe { f(*ud, srv, &ev) };
5107 }
5108 });
5109}
5110
5111#[no_mangle]
5112pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientPacket<'l>(
5113 mut env: JNIEnv<'l>,
5114 _class: JClass<'l>,
5115 channel: JString<'l>,
5116 payload: JByteArray<'l>,
5117) {
5118 let ch = env
5119 .get_string(&channel)
5120 .map(String::from)
5121 .unwrap_or_default();
5122 let data = env.convert_byte_array(&payload).unwrap_or_default();
5123 let ev = yog_abi::YogPacketEvent {
5124 channel: YogStr::from_str(&ch),
5125 player: YogStr::EMPTY,
5126 payload: data.as_ptr(),
5127 payload_len: data.len() as u32,
5128 };
5129 let h = handlers();
5130 let srv = srv_ptr();
5131 guard("on_client_packet", || {
5132 if let Some((module, ud, f)) = h.client_packets.get(&ch) {
5133 if module.is_retiring() {
5134 return;
5135 }
5136 let Some(_call) = module.enter() else { return };
5137 unsafe { f(*ud, srv, &ev) };
5138 }
5139 });
5140}
5141
5142#[no_mangle]
5143pub extern "system" fn Java_dev_yog_NativeBridge_nativePacketChannels<'l>(
5144 env: JNIEnv<'l>,
5145 _class: JClass<'l>,
5146) -> jstring {
5147 let s = handlers()
5148 .packets
5149 .keys()
5150 .cloned()
5151 .collect::<Vec<_>>()
5152 .join("\n");
5153 env.new_string(s)
5154 .map(|s| s.into_raw())
5155 .unwrap_or(std::ptr::null_mut())
5156}
5157
5158#[no_mangle]
5159pub extern "system" fn Java_dev_yog_NativeBridge_nativeClientPacketChannels<'l>(
5160 env: JNIEnv<'l>,
5161 _class: JClass<'l>,
5162) -> jstring {
5163 let s = handlers()
5164 .client_packets
5165 .keys()
5166 .cloned()
5167 .collect::<Vec<_>>()
5168 .join("\n");
5169 env.new_string(s)
5170 .map(|s| s.into_raw())
5171 .unwrap_or(std::ptr::null_mut())
5172}
5173
5174#[no_mangle]
5175pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawn<'l>(
5176 mut env: JNIEnv<'l>,
5177 _class: JClass<'l>,
5178 entity_type: JString<'l>,
5179 uuid: JString<'l>,
5180 dimension: JString<'l>,
5181) {
5182 let h = handlers();
5183 if h.entity_spawn.is_empty() {
5184 return;
5185 }
5186 let (et, u, d) = (
5187 jstr!(env, entity_type),
5188 jstr!(env, uuid),
5189 jstr!(env, dimension),
5190 );
5191 let ev = yog_abi::YogEntitySpawnEvent {
5192 entity_type: YogStr::from_str(&et),
5193 uuid: YogStr::from_str(&u),
5194 dimension: YogStr::from_str(&d),
5195 };
5196 let srv = srv_ptr();
5197 guard("on_entity_spawn", || {
5198 for (module, ud, f) in &h.entity_spawn {
5199 if module.is_retiring() {
5200 continue;
5201 }
5202 let Some(_call) = module.enter() else {
5203 continue;
5204 };
5205 unsafe { f(*ud, srv, &ev, 1) };
5206 }
5207 });
5208}
5209
5210#[no_mangle]
5211pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawnPre<'l>(
5212 mut env: JNIEnv<'l>,
5213 _class: JClass<'l>,
5214 entity_type: JString<'l>,
5215 uuid: JString<'l>,
5216 dimension: JString<'l>,
5217) -> jni::sys::jboolean {
5218 let h = handlers();
5219 if h.entity_spawn.is_empty() {
5220 return 1;
5221 }
5222 let et = match env.get_string(&entity_type) {
5223 Ok(s) => String::from(s),
5224 Err(_) => return 1,
5225 };
5226 let u = match env.get_string(&uuid) {
5227 Ok(s) => String::from(s),
5228 Err(_) => return 1,
5229 };
5230 let d = match env.get_string(&dimension) {
5231 Ok(s) => String::from(s),
5232 Err(_) => return 1,
5233 };
5234 let ev = yog_abi::YogEntitySpawnEvent {
5235 entity_type: YogStr::from_str(&et),
5236 uuid: YogStr::from_str(&u),
5237 dimension: YogStr::from_str(&d),
5238 };
5239 let srv = srv_ptr();
5240 let mut allow = true;
5241 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5242 for (module, ud, f) in &h.entity_spawn {
5243 if module.is_retiring() {
5244 continue;
5245 }
5246 let Some(_call) = module.enter() else {
5247 continue;
5248 };
5249 if !unsafe { f(*ud, srv, &ev, 0) } {
5250 allow = false;
5251 break;
5252 }
5253 }
5254 }))
5255 .ok();
5256 allow as jni::sys::jboolean
5257}
5258
5259#[no_mangle]
5260pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamagePre<'l>(
5261 mut env: JNIEnv<'l>,
5262 _class: JClass<'l>,
5263 entity_type: JString<'l>,
5264 uuid: JString<'l>,
5265 amount: jfloat,
5266 source: JString<'l>,
5267 dimension: JString<'l>,
5268) -> jni::sys::jboolean {
5269 let h = handlers();
5270 if h.entity_damage.is_empty() {
5271 return 1;
5272 }
5273 let et = match env.get_string(&entity_type) {
5274 Ok(s) => String::from(s),
5275 Err(_) => return 1,
5276 };
5277 let u = match env.get_string(&uuid) {
5278 Ok(s) => String::from(s),
5279 Err(_) => return 1,
5280 };
5281 let s = match env.get_string(&source) {
5282 Ok(s) => String::from(s),
5283 Err(_) => return 1,
5284 };
5285 let d = match env.get_string(&dimension) {
5286 Ok(s) => String::from(s),
5287 Err(_) => return 1,
5288 };
5289 let ev = yog_abi::YogEntityDamageEvent {
5290 entity_type: YogStr::from_str(&et),
5291 uuid: YogStr::from_str(&u),
5292 amount,
5293 source: YogStr::from_str(&s),
5294 dimension: YogStr::from_str(&d),
5295 };
5296 let srv = srv_ptr();
5297 let mut allow = true;
5298 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5299 for (module, ud, f) in &h.entity_damage {
5300 if module.is_retiring() {
5301 continue;
5302 }
5303 let Some(_call) = module.enter() else {
5304 continue;
5305 };
5306 if !unsafe { f(*ud, srv, &ev, 0) } {
5307 allow = false;
5308 break;
5309 }
5310 }
5311 }))
5312 .ok();
5313 allow as jni::sys::jboolean
5314}
5315
5316#[no_mangle]
5317pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlockPre<'l>(
5318 mut env: JNIEnv<'l>,
5319 _class: JClass<'l>,
5320 player: JString<'l>,
5321 block: JString<'l>,
5322 x: jint,
5323 y: jint,
5324 z: jint,
5325 dimension: JString<'l>,
5326) -> jni::sys::jboolean {
5327 let h = handlers();
5328 if h.player_place_block.is_empty() {
5329 return 1;
5330 }
5331 let p = match env.get_string(&player) {
5332 Ok(s) => String::from(s),
5333 Err(_) => return 1,
5334 };
5335 let b = match env.get_string(&block) {
5336 Ok(s) => String::from(s),
5337 Err(_) => return 1,
5338 };
5339 let d = match env.get_string(&dimension) {
5340 Ok(s) => String::from(s),
5341 Err(_) => return 1,
5342 };
5343 let ev = YogPlaceBlockEvent {
5344 player: YogStr::from_str(&p),
5345 block: YogStr::from_str(&b),
5346 pos: YogBlockPos { x, y, z },
5347 dimension: YogStr::from_str(&d),
5348 };
5349 let srv = srv_ptr();
5350 let mut allow = true;
5351 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5352 for (module, ud, f) in &h.player_place_block {
5353 if module.is_retiring() {
5354 continue;
5355 }
5356 let Some(_call) = module.enter() else {
5357 continue;
5358 };
5359 if !unsafe { f(*ud, srv, &ev, 0) } {
5360 allow = false;
5361 break;
5362 }
5363 }
5364 }))
5365 .ok();
5366 allow as jni::sys::jboolean
5367}
5368
5369#[no_mangle]
5370pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlock<'l>(
5371 mut env: JNIEnv<'l>,
5372 _class: JClass<'l>,
5373 player: JString<'l>,
5374 block: JString<'l>,
5375 x: jint,
5376 y: jint,
5377 z: jint,
5378 dimension: JString<'l>,
5379) {
5380 let h = handlers();
5381 if h.player_place_block.is_empty() {
5382 return;
5383 }
5384 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
5385 let ev = YogPlaceBlockEvent {
5386 player: YogStr::from_str(&p),
5387 block: YogStr::from_str(&b),
5388 pos: YogBlockPos { x, y, z },
5389 dimension: YogStr::from_str(&d),
5390 };
5391 let srv = srv_ptr();
5392 guard("on_player_place_block", || {
5393 for (module, ud, f) in &h.player_place_block {
5394 if module.is_retiring() {
5395 continue;
5396 }
5397 let Some(_call) = module.enter() else {
5398 continue;
5399 };
5400 unsafe { f(*ud, srv, &ev, 1) };
5401 }
5402 });
5403}
5404
5405#[no_mangle]
5406pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeathPre<'l>(
5407 mut env: JNIEnv<'l>,
5408 _class: JClass<'l>,
5409 player: JString<'l>,
5410 uuid: JString<'l>,
5411 source: JString<'l>,
5412 dimension: JString<'l>,
5413) -> jni::sys::jboolean {
5414 let h = handlers();
5415 if h.player_death.is_empty() {
5416 return 1;
5417 }
5418 let p = match env.get_string(&player) {
5419 Ok(s) => String::from(s),
5420 Err(_) => return 1,
5421 };
5422 let u = match env.get_string(&uuid) {
5423 Ok(s) => String::from(s),
5424 Err(_) => return 1,
5425 };
5426 let s = match env.get_string(&source) {
5427 Ok(s) => String::from(s),
5428 Err(_) => return 1,
5429 };
5430 let d = match env.get_string(&dimension) {
5431 Ok(s) => String::from(s),
5432 Err(_) => return 1,
5433 };
5434 let ev = YogPlayerDeathEvent {
5435 player: YogStr::from_str(&p),
5436 uuid: YogStr::from_str(&u),
5437 source: YogStr::from_str(&s),
5438 dimension: YogStr::from_str(&d),
5439 };
5440 let srv = srv_ptr();
5441 let mut allow = true;
5442 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5443 for (module, ud, f) in &h.player_death {
5444 if module.is_retiring() {
5445 continue;
5446 }
5447 let Some(_call) = module.enter() else {
5448 continue;
5449 };
5450 if !unsafe { f(*ud, srv, &ev, 0) } {
5451 allow = false;
5452 break;
5453 }
5454 }
5455 }))
5456 .ok();
5457 allow as jni::sys::jboolean
5458}
5459
5460#[no_mangle]
5461pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeath<'l>(
5462 mut env: JNIEnv<'l>,
5463 _class: JClass<'l>,
5464 player: JString<'l>,
5465 uuid: JString<'l>,
5466 source: JString<'l>,
5467 dimension: JString<'l>,
5468) {
5469 let h = handlers();
5470 if h.player_death.is_empty() {
5471 return;
5472 }
5473 let (p, u, s, d) = (
5474 jstr!(env, player),
5475 jstr!(env, uuid),
5476 jstr!(env, source),
5477 jstr!(env, dimension),
5478 );
5479 let ev = YogPlayerDeathEvent {
5480 player: YogStr::from_str(&p),
5481 uuid: YogStr::from_str(&u),
5482 source: YogStr::from_str(&s),
5483 dimension: YogStr::from_str(&d),
5484 };
5485 let srv = srv_ptr();
5486 guard("on_player_death", || {
5487 for (module, ud, f) in &h.player_death {
5488 if module.is_retiring() {
5489 continue;
5490 }
5491 let Some(_call) = module.enter() else {
5492 continue;
5493 };
5494 unsafe { f(*ud, srv, &ev, 1) };
5495 }
5496 });
5497}
5498
5499#[no_mangle]
5500pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerRespawn<'l>(
5501 mut env: JNIEnv<'l>,
5502 _class: JClass<'l>,
5503 player: JString<'l>,
5504 uuid: JString<'l>,
5505 at_anchor: jni::sys::jboolean,
5506 dimension: JString<'l>,
5507) {
5508 let h = handlers();
5509 if h.player_respawn.is_empty() {
5510 return;
5511 }
5512 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
5513 let ev = YogPlayerRespawnEvent {
5514 player: YogStr::from_str(&p),
5515 uuid: YogStr::from_str(&u),
5516 at_anchor: at_anchor != 0,
5517 dimension: YogStr::from_str(&d),
5518 };
5519 let srv = srv_ptr();
5520 guard("on_player_respawn", || {
5521 for (module, ud, f) in &h.player_respawn {
5522 if module.is_retiring() {
5523 continue;
5524 }
5525 let Some(_call) = module.enter() else {
5526 continue;
5527 };
5528 unsafe { f(*ud, srv, &ev, 1) };
5529 }
5530 });
5531}
5532
5533#[no_mangle]
5534pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAdvancement<'l>(
5535 mut env: JNIEnv<'l>,
5536 _class: JClass<'l>,
5537 player: JString<'l>,
5538 uuid: JString<'l>,
5539 advancement: JString<'l>,
5540 dimension: JString<'l>,
5541) {
5542 let h = handlers();
5543 if h.advancement.is_empty() {
5544 return;
5545 }
5546 let (p, u, a, d) = (
5547 jstr!(env, player),
5548 jstr!(env, uuid),
5549 jstr!(env, advancement),
5550 jstr!(env, dimension),
5551 );
5552 let ev = YogAdvancementEvent {
5553 player: YogStr::from_str(&p),
5554 uuid: YogStr::from_str(&u),
5555 advancement: YogStr::from_str(&a),
5556 dimension: YogStr::from_str(&d),
5557 };
5558 let srv = srv_ptr();
5559 guard("on_advancement", || {
5560 for (module, ud, f) in &h.advancement {
5561 if module.is_retiring() {
5562 continue;
5563 }
5564 let Some(_call) = module.enter() else {
5565 continue;
5566 };
5567 unsafe { f(*ud, srv, &ev, 1) };
5568 }
5569 });
5570}
5571
5572#[no_mangle]
5573pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteractPre<'l>(
5574 mut env: JNIEnv<'l>,
5575 _class: JClass<'l>,
5576 player: JString<'l>,
5577 player_uuid: JString<'l>,
5578 entity_type: JString<'l>,
5579 entity_uuid: JString<'l>,
5580 hand: JString<'l>,
5581 dimension: JString<'l>,
5582) -> jni::sys::jboolean {
5583 let h = handlers();
5584 if h.entity_interact.is_empty() {
5585 return 1;
5586 }
5587 let p = match env.get_string(&player) {
5588 Ok(s) => String::from(s),
5589 Err(_) => return 1,
5590 };
5591 let pu = match env.get_string(&player_uuid) {
5592 Ok(s) => String::from(s),
5593 Err(_) => return 1,
5594 };
5595 let et = match env.get_string(&entity_type) {
5596 Ok(s) => String::from(s),
5597 Err(_) => return 1,
5598 };
5599 let eu = match env.get_string(&entity_uuid) {
5600 Ok(s) => String::from(s),
5601 Err(_) => return 1,
5602 };
5603 let ha = match env.get_string(&hand) {
5604 Ok(s) => String::from(s),
5605 Err(_) => return 1,
5606 };
5607 let d = match env.get_string(&dimension) {
5608 Ok(s) => String::from(s),
5609 Err(_) => return 1,
5610 };
5611 let ev = YogEntityInteractEvent {
5612 player: YogStr::from_str(&p),
5613 player_uuid: YogStr::from_str(&pu),
5614 entity_type: YogStr::from_str(&et),
5615 entity_uuid: YogStr::from_str(&eu),
5616 hand: YogStr::from_str(&ha),
5617 dimension: YogStr::from_str(&d),
5618 };
5619 let srv = srv_ptr();
5620 let mut allow = true;
5621 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5622 for (module, ud, f) in &h.entity_interact {
5623 if module.is_retiring() {
5624 continue;
5625 }
5626 let Some(_call) = module.enter() else {
5627 continue;
5628 };
5629 if !unsafe { f(*ud, srv, &ev, 0) } {
5630 allow = false;
5631 break;
5632 }
5633 }
5634 }))
5635 .ok();
5636 allow as jni::sys::jboolean
5637}
5638
5639#[no_mangle]
5640pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteract<'l>(
5641 mut env: JNIEnv<'l>,
5642 _class: JClass<'l>,
5643 player: JString<'l>,
5644 player_uuid: JString<'l>,
5645 entity_type: JString<'l>,
5646 entity_uuid: JString<'l>,
5647 hand: JString<'l>,
5648 dimension: JString<'l>,
5649) {
5650 let h = handlers();
5651 if h.entity_interact.is_empty() {
5652 return;
5653 }
5654 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5655 let (et, eu, ha, d) = (
5656 jstr!(env, entity_type),
5657 jstr!(env, entity_uuid),
5658 jstr!(env, hand),
5659 jstr!(env, dimension),
5660 );
5661 let ev = YogEntityInteractEvent {
5662 player: YogStr::from_str(&p),
5663 player_uuid: YogStr::from_str(&pu),
5664 entity_type: YogStr::from_str(&et),
5665 entity_uuid: YogStr::from_str(&eu),
5666 hand: YogStr::from_str(&ha),
5667 dimension: YogStr::from_str(&d),
5668 };
5669 let srv = srv_ptr();
5670 guard("on_entity_interact", || {
5671 for (module, ud, f) in &h.entity_interact {
5672 if module.is_retiring() {
5673 continue;
5674 }
5675 let Some(_call) = module.enter() else {
5676 continue;
5677 };
5678 unsafe { f(*ud, srv, &ev, 1) };
5679 }
5680 });
5681}
5682
5683#[no_mangle]
5684pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemCraft<'l>(
5685 mut env: JNIEnv<'l>,
5686 _class: JClass<'l>,
5687 player: JString<'l>,
5688 player_uuid: JString<'l>,
5689 result_item: JString<'l>,
5690 result_count: jint,
5691 dimension: JString<'l>,
5692) {
5693 let h = handlers();
5694 if h.item_craft.is_empty() {
5695 return;
5696 }
5697 let (p, pu, ri, d) = (
5698 jstr!(env, player),
5699 jstr!(env, player_uuid),
5700 jstr!(env, result_item),
5701 jstr!(env, dimension),
5702 );
5703 let ev = YogCraftEvent {
5704 player: YogStr::from_str(&p),
5705 player_uuid: YogStr::from_str(&pu),
5706 result_item: YogStr::from_str(&ri),
5707 result_count: result_count as u32,
5708 dimension: YogStr::from_str(&d),
5709 };
5710 let srv = srv_ptr();
5711 guard("on_item_craft", || {
5712 for (module, ud, f) in &h.item_craft {
5713 if module.is_retiring() {
5714 continue;
5715 }
5716 let Some(_call) = module.enter() else {
5717 continue;
5718 };
5719 unsafe { f(*ud, srv, &ev, 1) };
5720 }
5721 });
5722}
5723
5724#[no_mangle]
5725pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosionPre<'l>(
5726 mut env: JNIEnv<'l>,
5727 _class: JClass<'l>,
5728 dimension: JString<'l>,
5729 x: jdouble,
5730 y: jdouble,
5731 z: jdouble,
5732 power: jfloat,
5733 cause_uuid: JString<'l>,
5734) -> jni::sys::jboolean {
5735 let h = handlers();
5736 if h.explosion.is_empty() {
5737 return 1;
5738 }
5739 let d = match env.get_string(&dimension) {
5740 Ok(s) => String::from(s),
5741 Err(_) => return 1,
5742 };
5743 let cu = match env.get_string(&cause_uuid) {
5744 Ok(s) => String::from(s),
5745 Err(_) => return 1,
5746 };
5747 let ev = YogExplosionEvent {
5748 dimension: YogStr::from_str(&d),
5749 x,
5750 y,
5751 z,
5752 power,
5753 cause_uuid: YogStr::from_str(&cu),
5754 };
5755 let srv = srv_ptr();
5756 let mut allow = true;
5757 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5758 for (module, ud, f) in &h.explosion {
5759 if module.is_retiring() {
5760 continue;
5761 }
5762 let Some(_call) = module.enter() else {
5763 continue;
5764 };
5765 if !unsafe { f(*ud, srv, &ev, 0) } {
5766 allow = false;
5767 break;
5768 }
5769 }
5770 }))
5771 .ok();
5772 allow as jni::sys::jboolean
5773}
5774
5775#[no_mangle]
5776pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosion<'l>(
5777 mut env: JNIEnv<'l>,
5778 _class: JClass<'l>,
5779 dimension: JString<'l>,
5780 x: jdouble,
5781 y: jdouble,
5782 z: jdouble,
5783 power: jfloat,
5784 cause_uuid: JString<'l>,
5785) {
5786 let h = handlers();
5787 if h.explosion.is_empty() {
5788 return;
5789 }
5790 let (d, cu) = (jstr!(env, dimension), jstr!(env, cause_uuid));
5791 let ev = YogExplosionEvent {
5792 dimension: YogStr::from_str(&d),
5793 x,
5794 y,
5795 z,
5796 power,
5797 cause_uuid: YogStr::from_str(&cu),
5798 };
5799 let srv = srv_ptr();
5800 guard("on_explosion", || {
5801 for (module, ud, f) in &h.explosion {
5802 if module.is_retiring() {
5803 continue;
5804 }
5805 let Some(_call) = module.enter() else {
5806 continue;
5807 };
5808 unsafe { f(*ud, srv, &ev, 1) };
5809 }
5810 });
5811}
5812
5813#[no_mangle]
5816pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickupPre<'l>(
5817 mut env: JNIEnv<'l>,
5818 _class: JClass<'l>,
5819 player: JString<'l>,
5820 player_uuid: JString<'l>,
5821 item_id: JString<'l>,
5822 item_count: jint,
5823 entity_uuid: JString<'l>,
5824 dimension: JString<'l>,
5825) -> jni::sys::jboolean {
5826 let h = handlers();
5827 if h.item_pickup.is_empty() {
5828 return 1;
5829 }
5830 let p = match env.get_string(&player) {
5831 Ok(s) => String::from(s),
5832 Err(_) => return 1,
5833 };
5834 let pu = match env.get_string(&player_uuid) {
5835 Ok(s) => String::from(s),
5836 Err(_) => return 1,
5837 };
5838 let ii = match env.get_string(&item_id) {
5839 Ok(s) => String::from(s),
5840 Err(_) => return 1,
5841 };
5842 let eu = match env.get_string(&entity_uuid) {
5843 Ok(s) => String::from(s),
5844 Err(_) => return 1,
5845 };
5846 let d = match env.get_string(&dimension) {
5847 Ok(s) => String::from(s),
5848 Err(_) => return 1,
5849 };
5850 let ev = YogItemPickupEvent {
5851 player: YogStr::from_str(&p),
5852 player_uuid: YogStr::from_str(&pu),
5853 item_id: YogStr::from_str(&ii),
5854 item_count: item_count as u32,
5855 entity_uuid: YogStr::from_str(&eu),
5856 dimension: YogStr::from_str(&d),
5857 };
5858 let srv = srv_ptr();
5859 let mut allow = true;
5860 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5861 for (module, ud, f) in &h.item_pickup {
5862 if module.is_retiring() {
5863 continue;
5864 }
5865 let Some(_call) = module.enter() else {
5866 continue;
5867 };
5868 if !unsafe { f(*ud, srv, &ev, 0) } {
5869 allow = false;
5870 break;
5871 }
5872 }
5873 }))
5874 .ok();
5875 allow as jni::sys::jboolean
5876}
5877
5878#[no_mangle]
5879pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickup<'l>(
5880 mut env: JNIEnv<'l>,
5881 _class: JClass<'l>,
5882 player: JString<'l>,
5883 player_uuid: JString<'l>,
5884 item_id: JString<'l>,
5885 item_count: jint,
5886 entity_uuid: JString<'l>,
5887 dimension: JString<'l>,
5888) {
5889 let h = handlers();
5890 if h.item_pickup.is_empty() {
5891 return;
5892 }
5893 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5894 let (ii, eu, d) = (
5895 jstr!(env, item_id),
5896 jstr!(env, entity_uuid),
5897 jstr!(env, dimension),
5898 );
5899 let ev = YogItemPickupEvent {
5900 player: YogStr::from_str(&p),
5901 player_uuid: YogStr::from_str(&pu),
5902 item_id: YogStr::from_str(&ii),
5903 item_count: item_count as u32,
5904 entity_uuid: YogStr::from_str(&eu),
5905 dimension: YogStr::from_str(&d),
5906 };
5907 let srv = srv_ptr();
5908 guard("on_item_pickup", || {
5909 for (module, ud, f) in &h.item_pickup {
5910 if module.is_retiring() {
5911 continue;
5912 }
5913 let Some(_call) = module.enter() else {
5914 continue;
5915 };
5916 unsafe { f(*ud, srv, &ev, 1) };
5917 }
5918 });
5919}
5920
5921#[no_mangle]
5922pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerMove<'l>(
5923 mut env: JNIEnv<'l>,
5924 _class: JClass<'l>,
5925 player: JString<'l>,
5926 player_uuid: JString<'l>,
5927 x: jdouble,
5928 y: jdouble,
5929 z: jdouble,
5930 yaw: jfloat,
5931 pitch: jfloat,
5932 dimension: JString<'l>,
5933) {
5934 let h = handlers();
5935 if h.player_move.is_empty() {
5936 return;
5937 }
5938 let (p, pu, d) = (
5939 jstr!(env, player),
5940 jstr!(env, player_uuid),
5941 jstr!(env, dimension),
5942 );
5943 let ev = YogPlayerMoveEvent {
5944 player: YogStr::from_str(&p),
5945 player_uuid: YogStr::from_str(&pu),
5946 x,
5947 y,
5948 z,
5949 yaw,
5950 pitch,
5951 dimension: YogStr::from_str(&d),
5952 };
5953 let srv = srv_ptr();
5954 guard("on_player_move", || {
5955 for (module, ud, f) in &h.player_move {
5956 if module.is_retiring() {
5957 continue;
5958 }
5959 let Some(_call) = module.enter() else {
5960 continue;
5961 };
5962 unsafe { f(*ud, srv, &ev, 1) };
5963 }
5964 });
5965}
5966
5967#[no_mangle]
5968pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpenPre<'l>(
5969 mut env: JNIEnv<'l>,
5970 _class: JClass<'l>,
5971 player: JString<'l>,
5972 player_uuid: JString<'l>,
5973 dimension: JString<'l>,
5974) -> jni::sys::jboolean {
5975 let h = handlers();
5976 if h.container_open.is_empty() {
5977 return 1;
5978 }
5979 let p = match env.get_string(&player) {
5980 Ok(s) => String::from(s),
5981 Err(_) => return 1,
5982 };
5983 let pu = match env.get_string(&player_uuid) {
5984 Ok(s) => String::from(s),
5985 Err(_) => return 1,
5986 };
5987 let d = match env.get_string(&dimension) {
5988 Ok(s) => String::from(s),
5989 Err(_) => return 1,
5990 };
5991 let ev = YogContainerOpenEvent {
5992 player: YogStr::from_str(&p),
5993 player_uuid: YogStr::from_str(&pu),
5994 container_type: YogStr::EMPTY,
5995 dimension: YogStr::from_str(&d),
5996 };
5997 let srv = srv_ptr();
5998 let mut allow = true;
5999 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
6000 for (module, ud, f) in &h.container_open {
6001 if module.is_retiring() {
6002 continue;
6003 }
6004 let Some(_call) = module.enter() else {
6005 continue;
6006 };
6007 if !unsafe { f(*ud, srv, &ev, 0) } {
6008 allow = false;
6009 break;
6010 }
6011 }
6012 }))
6013 .ok();
6014 allow as jni::sys::jboolean
6015}
6016
6017#[no_mangle]
6018pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpen<'l>(
6019 mut env: JNIEnv<'l>,
6020 _class: JClass<'l>,
6021 player: JString<'l>,
6022 player_uuid: JString<'l>,
6023 container_type: JString<'l>,
6024 dimension: JString<'l>,
6025) {
6026 let h = handlers();
6027 if h.container_open.is_empty() {
6028 return;
6029 }
6030 let (p, pu, ct, d) = (
6031 jstr!(env, player),
6032 jstr!(env, player_uuid),
6033 jstr!(env, container_type),
6034 jstr!(env, dimension),
6035 );
6036 let ev = YogContainerOpenEvent {
6037 player: YogStr::from_str(&p),
6038 player_uuid: YogStr::from_str(&pu),
6039 container_type: YogStr::from_str(&ct),
6040 dimension: YogStr::from_str(&d),
6041 };
6042 let srv = srv_ptr();
6043 guard("on_container_open", || {
6044 for (module, ud, f) in &h.container_open {
6045 if module.is_retiring() {
6046 continue;
6047 }
6048 let Some(_call) = module.enter() else {
6049 continue;
6050 };
6051 unsafe { f(*ud, srv, &ev, 1) };
6052 }
6053 });
6054}
6055
6056#[no_mangle]
6057pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerClose<'l>(
6058 mut env: JNIEnv<'l>,
6059 _class: JClass<'l>,
6060 player: JString<'l>,
6061 player_uuid: JString<'l>,
6062 dimension: JString<'l>,
6063) {
6064 let h = handlers();
6065 if h.container_close.is_empty() {
6066 return;
6067 }
6068 let (p, pu, d) = (
6069 jstr!(env, player),
6070 jstr!(env, player_uuid),
6071 jstr!(env, dimension),
6072 );
6073 let ev = YogContainerCloseEvent {
6074 player: YogStr::from_str(&p),
6075 player_uuid: YogStr::from_str(&pu),
6076 dimension: YogStr::from_str(&d),
6077 };
6078 let srv = srv_ptr();
6079 guard("on_container_close", || {
6080 for (module, ud, f) in &h.container_close {
6081 if module.is_retiring() {
6082 continue;
6083 }
6084 let Some(_call) = module.enter() else {
6085 continue;
6086 };
6087 unsafe { f(*ud, srv, &ev, 1) };
6088 }
6089 });
6090}
6091
6092#[no_mangle]
6093pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHitPre<'l>(
6094 mut env: JNIEnv<'l>,
6095 _class: JClass<'l>,
6096 projectile_type: JString<'l>,
6097 projectile_uuid: JString<'l>,
6098 shooter_uuid: JString<'l>,
6099 hit_type: JString<'l>,
6100 hit_entity_uuid: JString<'l>,
6101 x: jdouble,
6102 y: jdouble,
6103 z: jdouble,
6104 dimension: JString<'l>,
6105) -> jni::sys::jboolean {
6106 let h = handlers();
6107 if h.projectile_hit.is_empty() {
6108 return 1;
6109 }
6110 let pt = match env.get_string(&projectile_type) {
6111 Ok(s) => String::from(s),
6112 Err(_) => return 1,
6113 };
6114 let pu = match env.get_string(&projectile_uuid) {
6115 Ok(s) => String::from(s),
6116 Err(_) => return 1,
6117 };
6118 let su = match env.get_string(&shooter_uuid) {
6119 Ok(s) => String::from(s),
6120 Err(_) => return 1,
6121 };
6122 let ht = match env.get_string(&hit_type) {
6123 Ok(s) => String::from(s),
6124 Err(_) => return 1,
6125 };
6126 let heu = match env.get_string(&hit_entity_uuid) {
6127 Ok(s) => String::from(s),
6128 Err(_) => return 1,
6129 };
6130 let dim = match env.get_string(&dimension) {
6131 Ok(s) => String::from(s),
6132 Err(_) => return 1,
6133 };
6134 let ev = YogProjectileHitEvent {
6135 projectile_type: YogStr::from_str(&pt),
6136 projectile_uuid: YogStr::from_str(&pu),
6137 shooter_uuid: YogStr::from_str(&su),
6138 hit_type: YogStr::from_str(&ht),
6139 hit_entity_uuid: YogStr::from_str(&heu),
6140 x,
6141 y,
6142 z,
6143 dimension: YogStr::from_str(&dim),
6144 };
6145 let srv = srv_ptr();
6146 let mut allow = true;
6147 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
6148 for (module, ud, f) in &h.projectile_hit {
6149 if module.is_retiring() {
6150 continue;
6151 }
6152 let Some(_call) = module.enter() else {
6153 continue;
6154 };
6155 if !unsafe { f(*ud, srv, &ev, 0) } {
6156 allow = false;
6157 break;
6158 }
6159 }
6160 }))
6161 .ok();
6162 allow as jni::sys::jboolean
6163}
6164
6165#[no_mangle]
6166pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHit<'l>(
6167 mut env: JNIEnv<'l>,
6168 _class: JClass<'l>,
6169 projectile_type: JString<'l>,
6170 projectile_uuid: JString<'l>,
6171 shooter_uuid: JString<'l>,
6172 hit_type: JString<'l>,
6173 hit_entity_uuid: JString<'l>,
6174 x: jdouble,
6175 y: jdouble,
6176 z: jdouble,
6177 dimension: JString<'l>,
6178) {
6179 let h = handlers();
6180 if h.projectile_hit.is_empty() {
6181 return;
6182 }
6183 let (pt, pu) = (jstr!(env, projectile_type), jstr!(env, projectile_uuid));
6184 let (su, ht) = (jstr!(env, shooter_uuid), jstr!(env, hit_type));
6185 let (heu, dim) = (jstr!(env, hit_entity_uuid), jstr!(env, dimension));
6186 let ev = YogProjectileHitEvent {
6187 projectile_type: YogStr::from_str(&pt),
6188 projectile_uuid: YogStr::from_str(&pu),
6189 shooter_uuid: YogStr::from_str(&su),
6190 hit_type: YogStr::from_str(&ht),
6191 hit_entity_uuid: YogStr::from_str(&heu),
6192 x,
6193 y,
6194 z,
6195 dimension: YogStr::from_str(&dim),
6196 };
6197 let srv = srv_ptr();
6198 guard("on_projectile_hit", || {
6199 for (module, ud, f) in &h.projectile_hit {
6200 if module.is_retiring() {
6201 continue;
6202 }
6203 let Some(_call) = module.enter() else {
6204 continue;
6205 };
6206 unsafe { f(*ud, srv, &ev, 1) };
6207 }
6208 });
6209}
6210
6211#[no_mangle]
6214pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientTick<'l>(
6215 _env: JNIEnv<'l>,
6216 _class: JClass<'l>,
6217) {
6218 let h = handlers();
6219 if h.client_tick.is_empty() {
6220 return;
6221 }
6222 guard("on_client_tick", || {
6223 for (module, ud, f) in &h.client_tick {
6224 if module.is_retiring() {
6225 continue;
6226 }
6227 let Some(_call) = module.enter() else {
6228 continue;
6229 };
6230 unsafe { f(*ud) };
6231 }
6232 });
6233}
6234
6235#[no_mangle]
6236pub extern "system" fn Java_dev_yog_NativeBridge_nativeGlInit<'l>(
6237 _env: JNIEnv<'l>,
6238 _class: JClass<'l>,
6239) {
6240 if GL.get().is_some() {
6241 return;
6242 }
6243 let mut raw_get_binary: usize = 0;
6244 let mut raw_prog_binary: usize = 0;
6245 let gl = unsafe {
6246 glow::Context::from_loader_function(|sym| {
6247 let Some(mut env) = get_env() else {
6248 return std::ptr::null();
6249 };
6250 let jsym = match env.new_string(sym) {
6251 Ok(s) => s,
6252 Err(_) => return std::ptr::null(),
6253 };
6254 let jsym_obj: JObject = jsym.into();
6255 let val = env.call_static_method(
6256 "dev/yog/NativeBridge",
6257 "glProcAddress",
6258 "(Ljava/lang/String;)J",
6259 &[JValue::Object(&jsym_obj)],
6260 );
6261 let ptr = match val.and_then(|v| v.j()) {
6262 Ok(p) if p != 0 => p as usize as *const _,
6263 _ => std::ptr::null(),
6264 };
6265 match sym {
6267 "glGetProgramBinary" => raw_get_binary = ptr as usize,
6268 "glProgramBinary" => raw_prog_binary = ptr as usize,
6269 _ => {}
6270 }
6271 ptr
6272 })
6273 };
6274 let _ = GL.set(GlCtx(gl));
6275 let _ = GL_GET_PROGRAM_BINARY.set(if raw_get_binary != 0 {
6276 Some(raw_get_binary)
6277 } else {
6278 None
6279 });
6280 let _ = GL_PROGRAM_BINARY.set(if raw_prog_binary != 0 {
6281 Some(raw_prog_binary)
6282 } else {
6283 None
6284 });
6285 if let Some(mut env) = get_env() {
6288 if let Ok(jsym) = env.new_string("glGetProgramiv") {
6289 let jsym_obj: JObject = jsym.into();
6290 if let Ok(jv) = env.call_static_method(
6291 "dev/yog/NativeBridge",
6292 "glProcAddress",
6293 "(Ljava/lang/String;)J",
6294 &[JValue::Object(&jsym_obj)],
6295 ) {
6296 if let Ok(ptr) = jv.j() {
6297 let _ = GL_GET_PROGRAM_IV.set(if ptr != 0 { Some(ptr as usize) } else { None });
6298 }
6299 }
6300 }
6301 }
6302}
6303
6304#[no_mangle]
6305pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnHudRender<'l>(
6306 _env: JNIEnv<'l>,
6307 _class: JClass<'l>,
6308 delta_tick: jfloat,
6309 screen_w: jint,
6310 screen_h: jint,
6311 scale_factor: jfloat,
6312 player_x: jfloat,
6313 player_y: jfloat,
6314 player_z: jfloat,
6315) {
6316 let h = handlers();
6317 let mut gfx = GFX_FN_TABLE;
6318 gfx.screen_w = screen_w;
6319 gfx.screen_h = screen_h;
6320 gfx.delta_tick = delta_tick;
6321 gfx.scale_factor = scale_factor;
6322 gfx.player_pos = [player_x, player_y, player_z];
6323
6324 if !h.hud_render.is_empty() {
6326 guard("on_hud_render", || {
6327 for (module, ud, f) in &h.hud_render {
6328 if module.is_retiring() {
6329 continue;
6330 }
6331 let Some(_call) = module.enter() else {
6332 continue;
6333 };
6334 unsafe { f(*ud, &gfx) };
6335 }
6336 });
6337 }
6338
6339 let active_layers: Vec<UiLayer> = h.active_uis.lock().unwrap().clone();
6341 let sw = screen_w as f32;
6342 let sh = screen_h as f32;
6343 let ctx = unsafe { yog_gfx::GfxContext::from_raw(&gfx as *const _) };
6344 let mut renderers = h.book_renderers.lock().unwrap();
6345 for layer in &active_layers {
6346 if !layer.visible {
6347 continue;
6348 }
6349 if let Some(renderer) = renderers.get_mut(&layer.id) {
6350 renderer.render(&ctx, sw, sh);
6351 }
6352 }
6353}
6354
6355#[no_mangle]
6356pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnWorldRender<'l>(
6357 env: JNIEnv<'l>,
6358 _class: JClass<'l>,
6359 delta_tick: jfloat,
6360 screen_w: jint,
6361 screen_h: jint,
6362 scale_factor: jfloat,
6363 view_proj_arr: JFloatArray<'l>,
6364 cam_x: jfloat,
6365 cam_y: jfloat,
6366 cam_z: jfloat,
6367 player_x: jfloat,
6368 player_y: jfloat,
6369 player_z: jfloat,
6370) {
6371 let h = handlers();
6372 if h.world_render.is_empty() {
6373 return;
6374 }
6375 let mut view_proj = [0f32; 16];
6376 if env
6377 .get_float_array_region(&view_proj_arr, 0, &mut view_proj)
6378 .is_err()
6379 {
6380 return;
6381 }
6382 let mut gfx = GFX_FN_TABLE;
6383 gfx.screen_w = screen_w;
6384 gfx.screen_h = screen_h;
6385 gfx.delta_tick = delta_tick;
6386 gfx.scale_factor = scale_factor;
6387 gfx.view_proj = view_proj;
6388 gfx.camera_pos = [cam_x, cam_y, cam_z];
6389 gfx.player_pos = [player_x, player_y, player_z];
6390 guard("on_world_render", || {
6391 for (module, ud, f) in &h.world_render {
6392 if module.is_retiring() {
6393 continue;
6394 }
6395 let Some(_call) = module.enter() else {
6396 continue;
6397 };
6398 unsafe { f(*ud, &gfx) };
6399 }
6400 });
6401}
6402
6403#[no_mangle]
6404pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnKeyPress<'l>(
6405 _env: JNIEnv<'l>,
6406 _class: JClass<'l>,
6407 key_code: jint,
6408 scan_code: jint,
6409 action: jint,
6410 modifiers: jint,
6411) -> jni::sys::jboolean {
6412 let h = handlers();
6413 if h.key_press.is_empty() {
6414 return 1;
6415 }
6416 let ev = YogKeyPressEvent {
6417 key_code,
6418 scan_code,
6419 action,
6420 modifiers,
6421 };
6422 let mut allow = true;
6423 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
6424 for (module, ud, f) in &h.key_press {
6425 if module.is_retiring() {
6426 continue;
6427 }
6428 let Some(_call) = module.enter() else {
6429 continue;
6430 };
6431 if !unsafe { f(*ud, &ev) } {
6432 allow = false;
6433 break;
6434 }
6435 }
6436 }))
6437 .ok();
6438 allow as jni::sys::jboolean
6439}
6440
6441#[no_mangle]
6442pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenOpen<'l>(
6443 mut env: JNIEnv<'l>,
6444 _class: JClass<'l>,
6445 screen_class: JString<'l>,
6446) {
6447 let h = handlers();
6448 if h.screen_open.is_empty() {
6449 return;
6450 }
6451 let sc = match env.get_string(&screen_class) {
6452 Ok(s) => String::from(s),
6453 Err(_) => return,
6454 };
6455 guard("on_screen_open", || {
6456 for (module, ud, f) in &h.screen_open {
6457 if module.is_retiring() {
6458 continue;
6459 }
6460 let Some(_call) = module.enter() else {
6461 continue;
6462 };
6463 unsafe { f(*ud, YogStr::from_str(&sc)) };
6464 }
6465 });
6466}
6467
6468#[no_mangle]
6469pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenClose<'l>(
6470 mut env: JNIEnv<'l>,
6471 _class: JClass<'l>,
6472 screen_class: JString<'l>,
6473) {
6474 let h = handlers();
6475 if h.screen_close.is_empty() {
6476 return;
6477 }
6478 let sc = match env.get_string(&screen_class) {
6479 Ok(s) => String::from(s),
6480 Err(_) => return,
6481 };
6482 guard("on_screen_close", || {
6483 for (module, ud, f) in &h.screen_close {
6484 if module.is_retiring() {
6485 continue;
6486 }
6487 let Some(_call) = module.enter() else {
6488 continue;
6489 };
6490 unsafe { f(*ud, YogStr::from_str(&sc)) };
6491 }
6492 });
6493}
6494
6495mod control_socket;
6496mod images;
6497mod ui_jni;