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