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 control_socket::start_if_requested();
3707
3708 yog_logging::info!("runtime initialised — the gate is open.");
3709}
3710
3711#[no_mangle]
3712pub extern "system" fn Java_dev_yog_NativeBridge_nativeHotReload<'l>(
3713 mut env: JNIEnv<'l>,
3714 _class: JClass<'l>,
3715 mod_id: JString<'l>,
3716 new_yog_path: JString<'l>,
3717) -> jni::sys::jboolean {
3718 let Ok(mod_id) = env.get_string(&mod_id).map(String::from) else { return 0 };
3719 let Ok(yog_path) = env.get_string(&new_yog_path).map(String::from) else { return 0 };
3720 let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| trigger_hot_reload(&mod_id, Path::new(&yog_path)))).unwrap_or(false);
3721 ok as jni::sys::jboolean
3722}
3723
3724fn trigger_hot_reload(mod_id: &str, new_yog_path: &Path) -> bool {
3734 let Some(native_path) = extract_yog(new_yog_path) else {
3735 yog_logging::error!("hot-reload {}: no native for {} in {}", mod_id, platform_tag(), new_yog_path.display());
3736 return false;
3737 };
3738
3739 let api = API_TABLE.get().expect("yog: nativeInit not called yet");
3740
3741 let module = match unsafe { hot_reloader().reload(mod_id, &native_path) } {
3742 Ok(m) => m,
3743 Err(e) => {
3744 yog_logging::error!("hot-reload {}: failed to load {}: {}", mod_id, native_path.display(), e);
3745 return false;
3746 }
3747 };
3748
3749 let abi = module.with_library(|lib| unsafe {
3750 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3751 Some(abi())
3752 });
3753 match abi.flatten() {
3754 Some(v) if v == ABI_VERSION => {}
3755 other => {
3756 yog_logging::error!(
3757 "hot-reload {}: ABI {:?} incompatible with runtime ABI {} — mod has no active handlers until a compatible reload succeeds",
3758 mod_id,
3759 other,
3760 ABI_VERSION
3761 );
3762 return false;
3763 }
3764 }
3765
3766 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3767 yog_logging::error!("hot-reload {}: {}", mod_id, e);
3768 return false;
3769 }
3770
3771 yog_logging::info!("hot-reloaded mod {}", mod_id);
3772 true
3773}
3774
3775#[no_mangle]
3776pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreak<'l>(
3777 mut env: JNIEnv<'l>,
3778 _class: JClass<'l>,
3779 player: JString<'l>,
3780 block: JString<'l>,
3781 x: jint,
3782 y: jint,
3783 z: jint,
3784 dimension: JString<'l>,
3785) {
3786 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
3787 let ev = yog_abi::YogBlockBreakEvent {
3788 player: YogStr::from_str(&p),
3789 block: YogStr::from_str(&b),
3790 pos: YogBlockPos { x, y, z },
3791 dimension: YogStr::from_str(&d),
3792 };
3793 let srv = srv_ptr();
3794 guard("on_block_break", || {
3795 for (module, ud, f) in &handlers().block_break {
3796 if module.is_retiring() { continue; }
3797 let Some(_call) = module.enter() else { continue };
3798 unsafe { f(*ud, srv, &ev, 1) };
3799 }
3800 });
3801}
3802
3803#[no_mangle]
3804pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChat<'l>(
3805 mut env: JNIEnv<'l>,
3806 _class: JClass<'l>,
3807 player: JString<'l>,
3808 message: JString<'l>,
3809 dimension: JString<'l>,
3810) {
3811 let (p, m, d) = (
3812 jstr!(env, player),
3813 jstr!(env, message),
3814 jstr!(env, dimension),
3815 );
3816 let ev = yog_abi::YogChatEvent {
3817 player: YogStr::from_str(&p),
3818 message: YogStr::from_str(&m),
3819 dimension: YogStr::from_str(&d),
3820 };
3821 let srv = srv_ptr();
3822 guard("on_chat", || {
3823 for (module, ud, f) in &handlers().chat {
3824 if module.is_retiring() { continue; }
3825 let Some(_call) = module.enter() else { continue };
3826 unsafe { f(*ud, srv, &ev, 1) };
3827 }
3828 });
3829}
3830
3831#[no_mangle]
3832pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerJoin<'l>(
3833 mut env: JNIEnv<'l>,
3834 _class: JClass<'l>,
3835 player: JString<'l>,
3836 uuid: JString<'l>,
3837 dimension: JString<'l>,
3838) {
3839 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
3840 let ev = yog_abi::YogPlayerEvent {
3841 player: YogStr::from_str(&p),
3842 uuid: YogStr::from_str(&u),
3843 dimension: YogStr::from_str(&d),
3844 };
3845 let srv = srv_ptr();
3846 guard("on_player_join", || {
3847 for (module, ud, f) in &handlers().player_join {
3848 if module.is_retiring() { continue; }
3849 let Some(_call) = module.enter() else { continue };
3850 unsafe { f(*ud, srv, &ev, 1) };
3851 }
3852 });
3853 let h = handlers();
3857 if !h.startup_grants.is_empty() {
3858 h.pending_grants
3859 .lock()
3860 .expect("pending_grants poisoned")
3861 .push((p, u, 0));
3862 }
3863}
3864
3865fn process_pending_grants() {
3868 let h = handlers();
3869 let mut pending = match h.pending_grants.try_lock() {
3870 Ok(g) => g,
3871 Err(_) => return,
3872 };
3873 if pending.is_empty() {
3874 return;
3875 }
3876 const FIRST_TRY_TICK: u32 = 2;
3878 const MAX_TICKS: u32 = 600;
3880
3881 let mut granted = h.startup_granted.lock().expect("startup_granted poisoned");
3882 let mut still_pending = Vec::new();
3883 let mut any_new = false;
3884
3885 for (p, u, ticks) in pending.drain(..) {
3886 if ticks < FIRST_TRY_TICK {
3887 still_pending.push((p, u, ticks + 1));
3888 continue;
3889 }
3890 let mut all_ok = true;
3891 for sg in &h.startup_grants {
3892 let key = format!("{}::{}", u, sg.id);
3893 if granted.contains_key(&key) {
3894 continue;
3895 }
3896 let mut ok = true;
3897 for item_id in &sg.items {
3898 ok &= unsafe {
3899 srv_give_item(
3900 std::ptr::null_mut(),
3901 YogStr::from_str(&p),
3902 YogStr::from_str(item_id),
3903 1,
3904 )
3905 };
3906 }
3907 if let Some(_book) = &sg.book {
3908 ok &= unsafe {
3909 srv_give_item(
3910 std::ptr::null_mut(),
3911 YogStr::from_str(&p),
3912 YogStr::from_str("minecraft:written_book"),
3913 1,
3914 )
3915 };
3916 }
3917 if ok {
3918 yog_logging::info!("startup grant {} given to {}", sg.id, p);
3919 granted.insert(key, true);
3920 any_new = true;
3921 } else {
3922 all_ok = false;
3923 }
3924 }
3925 if !all_ok && ticks < MAX_TICKS {
3926 still_pending.push((p, u, ticks + 1));
3927 }
3928 }
3929 *pending = still_pending;
3930 if any_new {
3931 save_startup_granted(&granted);
3932 }
3933}
3934
3935#[no_mangle]
3936pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerLeave<'l>(
3937 mut env: JNIEnv<'l>,
3938 _class: JClass<'l>,
3939 player: JString<'l>,
3940 uuid: JString<'l>,
3941 dimension: JString<'l>,
3942) {
3943 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
3944 let ev = yog_abi::YogPlayerEvent {
3945 player: YogStr::from_str(&p),
3946 uuid: YogStr::from_str(&u),
3947 dimension: YogStr::from_str(&d),
3948 };
3949 let srv = srv_ptr();
3950 guard("on_player_leave", || {
3951 for (module, ud, f) in &handlers().player_leave {
3952 if module.is_retiring() { continue; }
3953 let Some(_call) = module.enter() else { continue };
3954 unsafe { f(*ud, srv, &ev, 1) };
3955 }
3956 });
3957}
3958
3959#[no_mangle]
3960pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItem<'l>(
3961 mut env: JNIEnv<'l>,
3962 _class: JClass<'l>,
3963 player: JString<'l>,
3964 item: JString<'l>,
3965 sneaking: jni::sys::jboolean,
3966 dimension: JString<'l>,
3967) {
3968 let (p, i, d) = (jstr!(env, player), jstr!(env, item), jstr!(env, dimension));
3969 let ev = yog_abi::YogUseItemEvent {
3970 player: YogStr::from_str(&p),
3971 item: YogStr::from_str(&i),
3972 sneaking: sneaking != 0,
3973 dimension: YogStr::from_str(&d),
3974 };
3975 let srv = srv_ptr();
3976 guard("on_use_item", || {
3977 for (module, ud, f) in &handlers().use_item {
3978 if module.is_retiring() { continue; }
3979 let Some(_call) = module.enter() else { continue };
3980 unsafe { f(*ud, srv, &ev, 1) };
3981 }
3982 });
3983}
3984
3985#[no_mangle]
3986pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItemPre<'l>(
3987 mut env: JNIEnv<'l>,
3988 _class: JClass<'l>,
3989 player: JString<'l>,
3990 item: JString<'l>,
3991 sneaking: jni::sys::jboolean,
3992 dimension: JString<'l>,
3993) -> jni::sys::jboolean {
3994 let h = handlers();
3995 if h.use_item.is_empty() {
3996 return 1;
3997 }
3998 let p = match env.get_string(&player) {
3999 Ok(s) => String::from(s),
4000 Err(_) => return 1,
4001 };
4002 let i = match env.get_string(&item) {
4003 Ok(s) => String::from(s),
4004 Err(_) => return 1,
4005 };
4006 let d = match env.get_string(&dimension) {
4007 Ok(s) => String::from(s),
4008 Err(_) => return 1,
4009 };
4010 let ev = yog_abi::YogUseItemEvent {
4011 player: YogStr::from_str(&p),
4012 item: YogStr::from_str(&i),
4013 sneaking: sneaking != 0,
4014 dimension: YogStr::from_str(&d),
4015 };
4016 let srv = srv_ptr();
4017 let mut allow = true;
4018 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4019 for (module, ud, f) in &h.use_item {
4020 if module.is_retiring() { continue; }
4021 let Some(_call) = module.enter() else { continue };
4022 if !unsafe { f(*ud, srv, &ev, 0) } {
4023 allow = false;
4024 break;
4025 }
4026 }
4027 }))
4028 .ok();
4029 allow as jni::sys::jboolean
4030}
4031
4032#[no_mangle]
4033pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlock<'l>(
4034 mut env: JNIEnv<'l>,
4035 _class: JClass<'l>,
4036 player: JString<'l>,
4037 block: JString<'l>,
4038 x: jint,
4039 y: jint,
4040 z: jint,
4041 dimension: JString<'l>,
4042) {
4043 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4044 let ev = yog_abi::YogUseBlockEvent {
4045 player: YogStr::from_str(&p),
4046 block: YogStr::from_str(&b),
4047 pos: YogBlockPos { x, y, z },
4048 dimension: YogStr::from_str(&d),
4049 };
4050 let srv = srv_ptr();
4051 guard("on_use_block", || {
4052 for (module, ud, f) in &handlers().use_block {
4053 if module.is_retiring() { continue; }
4054 let Some(_call) = module.enter() else { continue };
4055 unsafe { f(*ud, srv, &ev, 1) };
4056 }
4057 });
4058}
4059
4060#[no_mangle]
4061pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlockPre<'l>(
4062 mut env: JNIEnv<'l>,
4063 _class: JClass<'l>,
4064 player: JString<'l>,
4065 block: JString<'l>,
4066 x: jint,
4067 y: jint,
4068 z: jint,
4069 dimension: JString<'l>,
4070) -> jni::sys::jboolean {
4071 let h = handlers();
4072 if h.use_block.is_empty() {
4073 return 1;
4074 }
4075 let p = match env.get_string(&player) {
4076 Ok(s) => String::from(s),
4077 Err(_) => return 1,
4078 };
4079 let b = match env.get_string(&block) {
4080 Ok(s) => String::from(s),
4081 Err(_) => return 1,
4082 };
4083 let d = match env.get_string(&dimension) {
4084 Ok(s) => String::from(s),
4085 Err(_) => return 1,
4086 };
4087 let ev = yog_abi::YogUseBlockEvent {
4088 player: YogStr::from_str(&p),
4089 block: YogStr::from_str(&b),
4090 pos: YogBlockPos { x, y, z },
4091 dimension: YogStr::from_str(&d),
4092 };
4093 let srv = srv_ptr();
4094 let mut allow = true;
4095 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4096 for (module, ud, f) in &h.use_block {
4097 if module.is_retiring() { continue; }
4098 let Some(_call) = module.enter() else { continue };
4099 if !unsafe { f(*ud, srv, &ev, 0) } {
4100 allow = false;
4101 break;
4102 }
4103 }
4104 }))
4105 .ok();
4106 allow as jni::sys::jboolean
4107}
4108
4109#[no_mangle]
4110pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAttackEntity<'l>(
4111 mut env: JNIEnv<'l>,
4112 _class: JClass<'l>,
4113 player: JString<'l>,
4114 target_type: JString<'l>,
4115 target_uuid: JString<'l>,
4116 dimension: JString<'l>,
4117) {
4118 let (p, tt, tu, d) = (
4119 jstr!(env, player),
4120 jstr!(env, target_type),
4121 jstr!(env, target_uuid),
4122 jstr!(env, dimension),
4123 );
4124 let ev = yog_abi::YogAttackEntityEvent {
4125 player: YogStr::from_str(&p),
4126 target_type: YogStr::from_str(&tt),
4127 target_uuid: YogStr::from_str(&tu),
4128 dimension: YogStr::from_str(&d),
4129 };
4130 let srv = srv_ptr();
4131 guard("on_attack_entity", || {
4132 for (module, ud, f) in &handlers().attack_entity {
4133 if module.is_retiring() { continue; }
4134 let Some(_call) = module.enter() else { continue };
4135 unsafe { f(*ud, srv, &ev, 1) };
4136 }
4137 });
4138}
4139
4140#[no_mangle]
4141pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamage<'l>(
4142 mut env: JNIEnv<'l>,
4143 _class: JClass<'l>,
4144 entity_type: JString<'l>,
4145 uuid: JString<'l>,
4146 amount: jfloat,
4147 source: JString<'l>,
4148 dimension: JString<'l>,
4149) {
4150 let (et, u, s, d) = (
4151 jstr!(env, entity_type),
4152 jstr!(env, uuid),
4153 jstr!(env, source),
4154 jstr!(env, dimension),
4155 );
4156 let ev = yog_abi::YogEntityDamageEvent {
4157 entity_type: YogStr::from_str(&et),
4158 uuid: YogStr::from_str(&u),
4159 amount,
4160 source: YogStr::from_str(&s),
4161 dimension: YogStr::from_str(&d),
4162 };
4163 let srv = srv_ptr();
4164 guard("on_entity_damage", || {
4165 for (module, ud, f) in &handlers().entity_damage {
4166 if module.is_retiring() { continue; }
4167 let Some(_call) = module.enter() else { continue };
4168 unsafe { f(*ud, srv, &ev, 1) };
4169 }
4170 });
4171}
4172
4173#[no_mangle]
4174pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDeath<'l>(
4175 mut env: JNIEnv<'l>,
4176 _class: JClass<'l>,
4177 entity_type: JString<'l>,
4178 uuid: JString<'l>,
4179 source: JString<'l>,
4180 dimension: JString<'l>,
4181) {
4182 let (et, u, s, d) = (
4183 jstr!(env, entity_type),
4184 jstr!(env, uuid),
4185 jstr!(env, source),
4186 jstr!(env, dimension),
4187 );
4188 let ev = yog_abi::YogEntityDeathEvent {
4189 entity_type: YogStr::from_str(&et),
4190 uuid: YogStr::from_str(&u),
4191 source: YogStr::from_str(&s),
4192 dimension: YogStr::from_str(&d),
4193 };
4194 let srv = srv_ptr();
4195 guard("on_entity_death", || {
4196 for (module, ud, f) in &handlers().entity_death {
4197 if module.is_retiring() { continue; }
4198 let Some(_call) = module.enter() else { continue };
4199 unsafe { f(*ud, srv, &ev, 1) };
4200 }
4201 });
4202}
4203
4204#[no_mangle]
4205pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnTick<'l>(
4206 _env: JNIEnv<'l>,
4207 _class: JClass<'l>,
4208) {
4209 let h = handlers();
4210 let srv = srv_ptr();
4211 guard("on_tick", || {
4212 for (module, ud, f) in &h.server_tick {
4213 if module.is_retiring() { continue; }
4214 let Some(_call) = module.enter() else { continue };
4215 unsafe { f(*ud, srv) };
4216 }
4217 });
4218
4219 guard("startup_grants", process_pending_grants);
4220
4221 {
4223 let mut sched = h.scheduler.lock().expect("scheduler poisoned");
4224 let mut to_fire: Vec<(LoadedModule, *mut c_void, YogScheduledFn)> = Vec::new();
4225 let mut remaining = Vec::new();
4226 for task in sched.once_tasks.drain(..) {
4227 if task.delay_remaining == 0 {
4228 to_fire.push((task.module.clone(), task.ud, task.f));
4229 } else {
4230 remaining.push(OnceTask {
4231 delay_remaining: task.delay_remaining - 1,
4232 ..task
4233 });
4234 }
4235 }
4236 sched.once_tasks = remaining;
4237 drop(sched);
4238 for (module, ud, f) in to_fire {
4239 if module.is_retiring() {
4240 continue;
4241 }
4242 let Some(_call) = module.enter() else { continue };
4243 guard("schedule_once", || unsafe { f(ud, srv) });
4244 }
4245 }
4246
4247 {
4249 let mut sched = h.scheduler.lock().expect("scheduler poisoned");
4250 let mut to_fire: Vec<(LoadedModule, *mut c_void, YogScheduledFn)> = Vec::new();
4251 for task in &mut sched.repeating_tasks {
4252 if task.ticks_left == 0 {
4253 to_fire.push((task.module.clone(), task.ud, task.f));
4254 task.ticks_left = task.period;
4255 } else {
4256 task.ticks_left -= 1;
4257 }
4258 }
4259 drop(sched);
4260 for (module, ud, f) in to_fire {
4261 if module.is_retiring() {
4262 continue;
4263 }
4264 let Some(_call) = module.enter() else { continue };
4265 guard("schedule_repeating", || unsafe { f(ud, srv) });
4266 }
4267 }
4268}
4269
4270static WORLD_DIR: Mutex<Option<std::path::PathBuf>> = Mutex::new(None);
4272
4273fn startup_grants_path() -> std::path::PathBuf {
4276 let base = WORLD_DIR
4277 .lock()
4278 .ok()
4279 .and_then(|d| d.clone())
4280 .unwrap_or_else(|| std::path::PathBuf::from("."));
4281 base.join("yog-data").join("startup_grants.json")
4282}
4283
4284fn load_startup_granted() {
4285 let h = handlers();
4286 let mut granted = h.startup_granted.lock().expect("startup_granted poisoned");
4287 granted.clear();
4290 h.pending_grants
4291 .lock()
4292 .expect("pending_grants poisoned")
4293 .clear();
4294 let path = startup_grants_path();
4295 let Ok(data) = std::fs::read_to_string(&path) else {
4296 return;
4297 };
4298 let Ok(keys) = serde_json::from_str::<Vec<String>>(&data) else {
4299 yog_logging::warn!("startup_grants.json: invalid JSON, ignoring");
4300 return;
4301 };
4302 for k in keys {
4303 granted.insert(k, true);
4304 }
4305 yog_logging::info!("loaded {} startup grants from disk", granted.len());
4306}
4307
4308fn save_startup_granted(granted: &HashMap<String, bool>) {
4309 let path = startup_grants_path();
4310 if let Some(parent) = path.parent() {
4311 let _ = std::fs::create_dir_all(parent);
4312 }
4313 let keys: Vec<&String> = granted.keys().collect();
4314 match serde_json::to_string(&keys) {
4315 Ok(json) => {
4316 let _ = std::fs::write(&path, json);
4317 }
4318 Err(e) => yog_logging::error!("failed to save startup_grants.json: {}", e),
4319 }
4320}
4321
4322#[no_mangle]
4323pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStarted<'l>(
4324 mut env: JNIEnv<'l>,
4325 _class: JClass<'l>,
4326 world_dir: JString<'l>,
4327) {
4328 let dir = jstr!(env, world_dir);
4329 *WORLD_DIR.lock().expect("WORLD_DIR poisoned") = if dir.is_empty() {
4330 None
4331 } else {
4332 Some(std::path::PathBuf::from(dir))
4333 };
4334 load_startup_granted();
4335 let srv = srv_ptr();
4336 guard("on_server_started", || {
4337 for (module, ud, f) in &handlers().server_started {
4338 if module.is_retiring() { continue; }
4339 let Some(_call) = module.enter() else { continue };
4340 unsafe { f(*ud, srv) };
4341 }
4342 });
4343}
4344
4345#[no_mangle]
4346pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStopping<'l>(
4347 _env: JNIEnv<'l>,
4348 _class: JClass<'l>,
4349) {
4350 let srv = srv_ptr();
4351 guard("on_server_stopping", || {
4352 for (module, ud, f) in &handlers().server_stopping {
4353 if module.is_retiring() { continue; }
4354 let Some(_call) = module.enter() else { continue };
4355 unsafe { f(*ud, srv) };
4356 }
4357 });
4358}
4359
4360#[no_mangle]
4361pub extern "system" fn Java_dev_yog_NativeBridge_nativeCommandNames<'l>(
4362 env: JNIEnv<'l>,
4363 _class: JClass<'l>,
4364) -> jstring {
4365 let names = handlers()
4366 .commands
4367 .keys()
4368 .cloned()
4369 .collect::<Vec<_>>()
4370 .join("\n");
4371 env.new_string(names)
4372 .map(|s| s.into_raw())
4373 .unwrap_or(std::ptr::null_mut())
4374}
4375
4376#[no_mangle]
4377pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreakPre<'l>(
4378 mut env: JNIEnv<'l>,
4379 _class: JClass<'l>,
4380 player: JString<'l>,
4381 block: JString<'l>,
4382 x: jint,
4383 y: jint,
4384 z: jint,
4385 dimension: JString<'l>,
4386) -> jni::sys::jboolean {
4387 let h = handlers();
4388 if h.block_break.is_empty() {
4389 return 1;
4390 }
4391 let p = match env.get_string(&player) {
4392 Ok(s) => String::from(s),
4393 Err(_) => return 1,
4394 };
4395 let b = match env.get_string(&block) {
4396 Ok(s) => String::from(s),
4397 Err(_) => return 1,
4398 };
4399 let d = match env.get_string(&dimension) {
4400 Ok(s) => String::from(s),
4401 Err(_) => return 1,
4402 };
4403 let ev = yog_abi::YogBlockBreakEvent {
4404 player: YogStr::from_str(&p),
4405 block: YogStr::from_str(&b),
4406 pos: YogBlockPos { x, y, z },
4407 dimension: YogStr::from_str(&d),
4408 };
4409 let srv = srv_ptr();
4410 let mut allow = true;
4411 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4412 for (module, ud, f) in &h.block_break {
4413 if module.is_retiring() { continue; }
4414 let Some(_call) = module.enter() else { continue };
4415 if !unsafe { f(*ud, srv, &ev, 0) } {
4416 allow = false;
4417 break;
4418 }
4419 }
4420 }))
4421 .ok();
4422 allow as jni::sys::jboolean
4423}
4424
4425#[no_mangle]
4426pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChatPre<'l>(
4427 mut env: JNIEnv<'l>,
4428 _class: JClass<'l>,
4429 player: JString<'l>,
4430 message: JString<'l>,
4431 dimension: JString<'l>,
4432) -> jni::sys::jboolean {
4433 let h = handlers();
4434 if h.chat.is_empty() {
4435 return 1;
4436 }
4437 let p = match env.get_string(&player) {
4438 Ok(s) => String::from(s),
4439 Err(_) => return 1,
4440 };
4441 let m = match env.get_string(&message) {
4442 Ok(s) => String::from(s),
4443 Err(_) => return 1,
4444 };
4445 let d = match env.get_string(&dimension) {
4446 Ok(s) => String::from(s),
4447 Err(_) => return 1,
4448 };
4449 let ev = yog_abi::YogChatEvent {
4450 player: YogStr::from_str(&p),
4451 message: YogStr::from_str(&m),
4452 dimension: YogStr::from_str(&d),
4453 };
4454 let srv = srv_ptr();
4455 let mut allow = true;
4456 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4457 for (module, ud, f) in &h.chat {
4458 if module.is_retiring() { continue; }
4459 let Some(_call) = module.enter() else { continue };
4460 if !unsafe { f(*ud, srv, &ev, 0) } {
4461 allow = false;
4462 break;
4463 }
4464 }
4465 }))
4466 .ok();
4467 allow as jni::sys::jboolean
4468}
4469
4470#[no_mangle]
4471pub extern "system" fn Java_dev_yog_NativeBridge_nativeRecipeJsons<'l>(
4472 env: JNIEnv<'l>,
4473 _class: JClass<'l>,
4474) -> jstring {
4475 let s = handlers()
4476 .recipes
4477 .iter()
4478 .map(|(ns, name, json)| format!("{}\t{}\t{}", ns, name, json))
4479 .collect::<Vec<_>>()
4480 .join("\n");
4481 env.new_string(s)
4482 .map(|s| s.into_raw())
4483 .unwrap_or(std::ptr::null_mut())
4484}
4485
4486#[no_mangle]
4487pub extern "system" fn Java_dev_yog_NativeBridge_nativeDimensionJsons<'l>(
4488 env: JNIEnv<'l>,
4489 _class: JClass<'l>,
4490) -> jstring {
4491 let s = handlers()
4495 .dimensions
4496 .iter()
4497 .map(|(id, json)| format!("{}\t{}", id, json))
4498 .collect::<Vec<_>>()
4499 .join("\n");
4500 env.new_string(s)
4501 .map(|s| s.into_raw())
4502 .unwrap_or(std::ptr::null_mut())
4503}
4504
4505unsafe extern "C" fn chunk_writer_set_block(
4510 _ctx: *mut c_void,
4511 x: i32,
4512 y: i32,
4513 z: i32,
4514 block_id: YogStr,
4515) -> bool {
4516 let Some(mut env) = get_env() else {
4517 return false;
4518 };
4519 let Some(jb) = ys_to_java(&mut env, block_id) else {
4520 return false;
4521 };
4522 env.call_static_method(
4523 "dev/yog/NativeBridge",
4524 "setBlockInGeneratingChunk",
4525 "(IIILjava/lang/String;)Z",
4526 &[
4527 JValue::Int(x),
4528 JValue::Int(y),
4529 JValue::Int(z),
4530 JValue::Object(&jb),
4531 ],
4532 )
4533 .and_then(|v| v.z())
4534 .unwrap_or(false)
4535}
4536
4537#[no_mangle]
4538pub extern "system" fn Java_dev_yog_NativeBridge_nativeGenerateChunk<'l>(
4539 mut env: JNIEnv<'l>,
4540 _class: JClass<'l>,
4541 generator_type_id: JString<'l>,
4542 chunk_x: jint,
4543 chunk_z: jint,
4544 min_y: jint,
4545 height: jint,
4546) {
4547 let id: String = match env.get_string(&generator_type_id) {
4548 Ok(s) => String::from(s),
4549 Err(_) => return,
4550 };
4551 let Some((module, ud, cb)) = handlers().chunk_generators.get(&id).cloned() else {
4552 return;
4553 };
4554 if module.is_retiring() {
4555 return;
4556 }
4557 let Some(_call) = module.enter() else { return };
4558 let writer = yog_abi::YogChunkWriterApi {
4559 chunk_x,
4560 chunk_z,
4561 min_y,
4562 height,
4563 ctx: std::ptr::null_mut(),
4564 set_block: chunk_writer_set_block,
4565 };
4566 guard("chunk_generator", || unsafe {
4567 cb(ud, &writer as *const _);
4568 });
4569}
4570
4571#[no_mangle]
4572pub extern "system" fn Java_dev_yog_NativeBridge_nativeTypedCommandSchemas<'l>(
4573 env: JNIEnv<'l>,
4574 _class: JClass<'l>,
4575) -> jstring {
4576 let mut lines = Vec::new();
4581 for (name, regs) in handlers().commands.iter() {
4582 let mut seen = std::collections::HashSet::new();
4583 for (_module, schema, _, _) in regs {
4584 if let Some(schema) = schema {
4585 if seen.insert(schema.as_str()) {
4586 lines.push(format!("{}\t{}", name, schema));
4587 }
4588 }
4589 }
4590 }
4591 env.new_string(lines.join("\n"))
4592 .map(|s| s.into_raw())
4593 .unwrap_or(std::ptr::null_mut())
4594}
4595
4596#[no_mangle]
4597pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnCommand<'l>(
4598 mut env: JNIEnv<'l>,
4599 _class: JClass<'l>,
4600 name: JString<'l>,
4601 args: JString<'l>,
4602 source: JString<'l>,
4603 uuid: JString<'l>,
4604 dimension: JString<'l>,
4605) -> jstring {
4606 let (n, a, s, u, d) = (
4607 env.get_string(&name).map(String::from).unwrap_or_default(),
4608 env.get_string(&args).map(String::from).unwrap_or_default(),
4609 env.get_string(&source)
4610 .map(String::from)
4611 .unwrap_or_default(),
4612 env.get_string(&uuid).map(String::from).unwrap_or_default(),
4613 env.get_string(&dimension)
4614 .map(String::from)
4615 .unwrap_or_default(),
4616 );
4617 let ev = yog_abi::YogCommandEvent {
4618 name: YogStr::from_str(&n),
4619 args: YogStr::from_str(&a),
4620 source: YogStr::from_str(&s),
4621 uuid: YogStr::from_str(&u),
4622 dimension: YogStr::from_str(&d),
4623 };
4624 let h = handlers();
4625 let srv = srv_ptr();
4626 let actual_argc = if a.is_empty() {
4628 0
4629 } else {
4630 a.split('\t').count()
4631 };
4632 let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4633 let Some(regs) = h.commands.get(&n) else {
4634 return String::new();
4635 };
4636 for (module, schema, ud, f) in regs {
4637 let expected_argc = schema.as_ref().map_or(0, |s| s.split_whitespace().count());
4638 if expected_argc != actual_argc {
4639 continue;
4640 }
4641 if module.is_retiring() {
4642 continue;
4643 }
4644 let Some(_call) = module.enter() else { continue };
4645 let mut buf = [0u8; 4096];
4646 let mut reply_len: u32 = 0;
4647 unsafe {
4648 f(
4649 *ud,
4650 srv,
4651 &ev,
4652 buf.as_mut_ptr(),
4653 buf.len() as u32,
4654 &mut reply_len,
4655 )
4656 };
4657 if reply_len > 0 {
4658 return String::from_utf8_lossy(&buf[..reply_len as usize]).into_owned();
4659 }
4660 }
4661 String::new()
4662 }))
4663 .unwrap_or_else(|_| {
4664 yog_logging::error!("a mod panicked handling command `{}`", n);
4665 String::new()
4666 });
4667
4668 env.new_string(reply)
4669 .map(|s| s.into_raw())
4670 .unwrap_or(std::ptr::null_mut())
4671}
4672
4673#[no_mangle]
4674pub extern "system" fn Java_dev_yog_NativeBridge_nativeItemDefs<'l>(
4675 env: JNIEnv<'l>,
4676 _class: JClass<'l>,
4677) -> jstring {
4678 let s = handlers()
4679 .items
4680 .iter()
4681 .map(|d| {
4682 let mut parts = vec![d.id.clone()];
4683 parts.push(format!("max_stack={}", d.max_stack));
4684 if let Some(n) = &d.name {
4685 parts.push(format!("name={n}"));
4686 }
4687 if let Some(t) = &d.tooltip {
4688 parts.push(format!("tooltip={t}"));
4689 }
4690 if d.max_damage > 0 {
4691 parts.push(format!("max_damage={}", d.max_damage));
4692 }
4693 if d.fire_resistant {
4694 parts.push("fire_resistant=1".into());
4695 }
4696 if d.fuel_ticks > 0 {
4697 parts.push(format!("fuel_ticks={}", d.fuel_ticks));
4698 }
4699 if let Some(f) = &d.food {
4700 parts.push(format!(
4701 "food={}:{}:{}",
4702 f.nutrition,
4703 f.saturation,
4704 if f.can_always_eat { 1 } else { 0 }
4705 ));
4706 }
4707 parts.join("\t")
4708 })
4709 .collect::<Vec<_>>()
4710 .join("\n");
4711 env.new_string(s)
4712 .map(|s| s.into_raw())
4713 .unwrap_or(std::ptr::null_mut())
4714}
4715
4716#[no_mangle]
4717pub extern "system" fn Java_dev_yog_NativeBridge_nativeBlockDefs<'l>(
4718 env: JNIEnv<'l>,
4719 _class: JClass<'l>,
4720) -> jstring {
4721 let s = handlers()
4722 .blocks
4723 .iter()
4724 .map(|d| {
4725 let mut parts = vec![d.id.clone()];
4726 parts.push(format!("hardness={}", d.hardness));
4727 parts.push(format!("resistance={}", d.resistance));
4728 if let Some(n) = &d.name {
4729 parts.push(format!("name={n}"));
4730 }
4731 if let Some(sh) = d.shape {
4732 parts.push(format!(
4733 "shape={}:{}:{}:{}:{}:{}",
4734 sh[0], sh[1], sh[2], sh[3], sh[4], sh[5]
4735 ));
4736 }
4737 if d.light_level > 0 {
4738 parts.push(format!("light={}", d.light_level));
4739 }
4740 if let Some(snd) = &d.sound {
4741 parts.push(format!("sound={snd}"));
4742 }
4743 if d.requires_tool {
4744 parts.push("requires_tool=1".into());
4745 }
4746 if d.no_collision {
4747 parts.push("no_collision=1".into());
4748 }
4749 if d.slipperiness > 0.0 {
4750 parts.push(format!("slipperiness={}", d.slipperiness));
4751 }
4752 if d.connects {
4753 parts.push("connects=1".into());
4754 }
4755 if !d.connect_groups.is_empty() {
4756 parts.push(format!("connect_groups={}", d.connect_groups.join(",")));
4757 }
4758 if let Some(inv) = &d.inventory_id {
4759 parts.push(format!("inventory_id={inv}"));
4760 }
4761 parts.join("\t")
4762 })
4763 .collect::<Vec<_>>()
4764 .join("\n");
4765 env.new_string(s)
4766 .map(|s| s.into_raw())
4767 .unwrap_or(std::ptr::null_mut())
4768}
4769
4770#[no_mangle]
4774pub extern "system" fn Java_dev_yog_NativeBridge_nativeInventoryDefs<'l>(
4775 env: JNIEnv<'l>,
4776 _class: JClass<'l>,
4777) -> jstring {
4778 let s = handlers()
4779 .inventories
4780 .iter()
4781 .map(|d| {
4782 let mut parts = vec![d.id.clone()];
4783 parts.push(format!("slot_count={}", d.slot_count));
4784 parts.push(format!(
4785 "layout={}",
4786 yog_inventory::encode_layout(&d.resolved_layout())
4787 ));
4788 parts.push(format!(
4789 "include_player_inventory={}",
4790 d.include_player_inventory as u8
4791 ));
4792 parts.push(format!(
4793 "player_inv={}:{}",
4794 d.player_inv_offset.0, d.player_inv_offset.1
4795 ));
4796 if let Some(tex) = &d.background_texture {
4797 parts.push(format!("background_texture={tex}"));
4798 }
4799 if !d.title.is_empty() {
4800 parts.push(format!("title={}", d.title));
4801 }
4802 if let Some((w, h)) = d.background_size {
4803 parts.push(format!("background_size={w}:{h}"));
4804 }
4805 parts.join("\t")
4806 })
4807 .collect::<Vec<_>>()
4808 .join("\n");
4809 env.new_string(s)
4810 .map(|s| s.into_raw())
4811 .unwrap_or(std::ptr::null_mut())
4812}
4813
4814#[no_mangle]
4815pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPacket<'l>(
4816 mut env: JNIEnv<'l>,
4817 _class: JClass<'l>,
4818 channel: JString<'l>,
4819 player: JString<'l>,
4820 payload: JByteArray<'l>,
4821) {
4822 let ch = env
4823 .get_string(&channel)
4824 .map(String::from)
4825 .unwrap_or_default();
4826 let pl = env
4827 .get_string(&player)
4828 .map(String::from)
4829 .unwrap_or_default();
4830 let data = env.convert_byte_array(&payload).unwrap_or_default();
4831 let ev = yog_abi::YogPacketEvent {
4832 channel: YogStr::from_str(&ch),
4833 player: YogStr::from_str(&pl),
4834 payload: data.as_ptr(),
4835 payload_len: data.len() as u32,
4836 };
4837 let h = handlers();
4838 let srv = srv_ptr();
4839 guard("on_packet", || {
4840 if let Some((module, ud, f)) = h.packets.get(&ch) {
4841 if module.is_retiring() {
4842 return;
4843 }
4844 let Some(_call) = module.enter() else { return };
4845 unsafe { f(*ud, srv, &ev) };
4846 }
4847 });
4848}
4849
4850#[no_mangle]
4851pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientPacket<'l>(
4852 mut env: JNIEnv<'l>,
4853 _class: JClass<'l>,
4854 channel: JString<'l>,
4855 payload: JByteArray<'l>,
4856) {
4857 let ch = env
4858 .get_string(&channel)
4859 .map(String::from)
4860 .unwrap_or_default();
4861 let data = env.convert_byte_array(&payload).unwrap_or_default();
4862 let ev = yog_abi::YogPacketEvent {
4863 channel: YogStr::from_str(&ch),
4864 player: YogStr::EMPTY,
4865 payload: data.as_ptr(),
4866 payload_len: data.len() as u32,
4867 };
4868 let h = handlers();
4869 let srv = srv_ptr();
4870 guard("on_client_packet", || {
4871 if let Some((module, ud, f)) = h.client_packets.get(&ch) {
4872 if module.is_retiring() {
4873 return;
4874 }
4875 let Some(_call) = module.enter() else { return };
4876 unsafe { f(*ud, srv, &ev) };
4877 }
4878 });
4879}
4880
4881#[no_mangle]
4882pub extern "system" fn Java_dev_yog_NativeBridge_nativePacketChannels<'l>(
4883 env: JNIEnv<'l>,
4884 _class: JClass<'l>,
4885) -> jstring {
4886 let s = handlers()
4887 .packets
4888 .keys()
4889 .cloned()
4890 .collect::<Vec<_>>()
4891 .join("\n");
4892 env.new_string(s)
4893 .map(|s| s.into_raw())
4894 .unwrap_or(std::ptr::null_mut())
4895}
4896
4897#[no_mangle]
4898pub extern "system" fn Java_dev_yog_NativeBridge_nativeClientPacketChannels<'l>(
4899 env: JNIEnv<'l>,
4900 _class: JClass<'l>,
4901) -> jstring {
4902 let s = handlers()
4903 .client_packets
4904 .keys()
4905 .cloned()
4906 .collect::<Vec<_>>()
4907 .join("\n");
4908 env.new_string(s)
4909 .map(|s| s.into_raw())
4910 .unwrap_or(std::ptr::null_mut())
4911}
4912
4913#[no_mangle]
4914pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawn<'l>(
4915 mut env: JNIEnv<'l>,
4916 _class: JClass<'l>,
4917 entity_type: JString<'l>,
4918 uuid: JString<'l>,
4919 dimension: JString<'l>,
4920) {
4921 let h = handlers();
4922 if h.entity_spawn.is_empty() {
4923 return;
4924 }
4925 let (et, u, d) = (
4926 jstr!(env, entity_type),
4927 jstr!(env, uuid),
4928 jstr!(env, dimension),
4929 );
4930 let ev = yog_abi::YogEntitySpawnEvent {
4931 entity_type: YogStr::from_str(&et),
4932 uuid: YogStr::from_str(&u),
4933 dimension: YogStr::from_str(&d),
4934 };
4935 let srv = srv_ptr();
4936 guard("on_entity_spawn", || {
4937 for (module, ud, f) in &h.entity_spawn {
4938 if module.is_retiring() { continue; }
4939 let Some(_call) = module.enter() else { continue };
4940 unsafe { f(*ud, srv, &ev, 1) };
4941 }
4942 });
4943}
4944
4945#[no_mangle]
4946pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawnPre<'l>(
4947 mut env: JNIEnv<'l>,
4948 _class: JClass<'l>,
4949 entity_type: JString<'l>,
4950 uuid: JString<'l>,
4951 dimension: JString<'l>,
4952) -> jni::sys::jboolean {
4953 let h = handlers();
4954 if h.entity_spawn.is_empty() {
4955 return 1;
4956 }
4957 let et = match env.get_string(&entity_type) {
4958 Ok(s) => String::from(s),
4959 Err(_) => return 1,
4960 };
4961 let u = match env.get_string(&uuid) {
4962 Ok(s) => String::from(s),
4963 Err(_) => return 1,
4964 };
4965 let d = match env.get_string(&dimension) {
4966 Ok(s) => String::from(s),
4967 Err(_) => return 1,
4968 };
4969 let ev = yog_abi::YogEntitySpawnEvent {
4970 entity_type: YogStr::from_str(&et),
4971 uuid: YogStr::from_str(&u),
4972 dimension: YogStr::from_str(&d),
4973 };
4974 let srv = srv_ptr();
4975 let mut allow = true;
4976 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4977 for (module, ud, f) in &h.entity_spawn {
4978 if module.is_retiring() { continue; }
4979 let Some(_call) = module.enter() else { continue };
4980 if !unsafe { f(*ud, srv, &ev, 0) } {
4981 allow = false;
4982 break;
4983 }
4984 }
4985 }))
4986 .ok();
4987 allow as jni::sys::jboolean
4988}
4989
4990#[no_mangle]
4991pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamagePre<'l>(
4992 mut env: JNIEnv<'l>,
4993 _class: JClass<'l>,
4994 entity_type: JString<'l>,
4995 uuid: JString<'l>,
4996 amount: jfloat,
4997 source: JString<'l>,
4998 dimension: JString<'l>,
4999) -> jni::sys::jboolean {
5000 let h = handlers();
5001 if h.entity_damage.is_empty() {
5002 return 1;
5003 }
5004 let et = match env.get_string(&entity_type) {
5005 Ok(s) => String::from(s),
5006 Err(_) => return 1,
5007 };
5008 let u = match env.get_string(&uuid) {
5009 Ok(s) => String::from(s),
5010 Err(_) => return 1,
5011 };
5012 let s = match env.get_string(&source) {
5013 Ok(s) => String::from(s),
5014 Err(_) => return 1,
5015 };
5016 let d = match env.get_string(&dimension) {
5017 Ok(s) => String::from(s),
5018 Err(_) => return 1,
5019 };
5020 let ev = yog_abi::YogEntityDamageEvent {
5021 entity_type: YogStr::from_str(&et),
5022 uuid: YogStr::from_str(&u),
5023 amount,
5024 source: YogStr::from_str(&s),
5025 dimension: YogStr::from_str(&d),
5026 };
5027 let srv = srv_ptr();
5028 let mut allow = true;
5029 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5030 for (module, ud, f) in &h.entity_damage {
5031 if module.is_retiring() { continue; }
5032 let Some(_call) = module.enter() else { continue };
5033 if !unsafe { f(*ud, srv, &ev, 0) } {
5034 allow = false;
5035 break;
5036 }
5037 }
5038 }))
5039 .ok();
5040 allow as jni::sys::jboolean
5041}
5042
5043#[no_mangle]
5044pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlockPre<'l>(
5045 mut env: JNIEnv<'l>,
5046 _class: JClass<'l>,
5047 player: JString<'l>,
5048 block: JString<'l>,
5049 x: jint,
5050 y: jint,
5051 z: jint,
5052 dimension: JString<'l>,
5053) -> jni::sys::jboolean {
5054 let h = handlers();
5055 if h.player_place_block.is_empty() {
5056 return 1;
5057 }
5058 let p = match env.get_string(&player) {
5059 Ok(s) => String::from(s),
5060 Err(_) => return 1,
5061 };
5062 let b = match env.get_string(&block) {
5063 Ok(s) => String::from(s),
5064 Err(_) => return 1,
5065 };
5066 let d = match env.get_string(&dimension) {
5067 Ok(s) => String::from(s),
5068 Err(_) => return 1,
5069 };
5070 let ev = YogPlaceBlockEvent {
5071 player: YogStr::from_str(&p),
5072 block: YogStr::from_str(&b),
5073 pos: YogBlockPos { x, y, z },
5074 dimension: YogStr::from_str(&d),
5075 };
5076 let srv = srv_ptr();
5077 let mut allow = true;
5078 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5079 for (module, ud, f) in &h.player_place_block {
5080 if module.is_retiring() { continue; }
5081 let Some(_call) = module.enter() else { continue };
5082 if !unsafe { f(*ud, srv, &ev, 0) } {
5083 allow = false;
5084 break;
5085 }
5086 }
5087 }))
5088 .ok();
5089 allow as jni::sys::jboolean
5090}
5091
5092#[no_mangle]
5093pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlock<'l>(
5094 mut env: JNIEnv<'l>,
5095 _class: JClass<'l>,
5096 player: JString<'l>,
5097 block: JString<'l>,
5098 x: jint,
5099 y: jint,
5100 z: jint,
5101 dimension: JString<'l>,
5102) {
5103 let h = handlers();
5104 if h.player_place_block.is_empty() {
5105 return;
5106 }
5107 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
5108 let ev = YogPlaceBlockEvent {
5109 player: YogStr::from_str(&p),
5110 block: YogStr::from_str(&b),
5111 pos: YogBlockPos { x, y, z },
5112 dimension: YogStr::from_str(&d),
5113 };
5114 let srv = srv_ptr();
5115 guard("on_player_place_block", || {
5116 for (module, ud, f) in &h.player_place_block {
5117 if module.is_retiring() { continue; }
5118 let Some(_call) = module.enter() else { continue };
5119 unsafe { f(*ud, srv, &ev, 1) };
5120 }
5121 });
5122}
5123
5124#[no_mangle]
5125pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeathPre<'l>(
5126 mut env: JNIEnv<'l>,
5127 _class: JClass<'l>,
5128 player: JString<'l>,
5129 uuid: JString<'l>,
5130 source: JString<'l>,
5131 dimension: JString<'l>,
5132) -> jni::sys::jboolean {
5133 let h = handlers();
5134 if h.player_death.is_empty() {
5135 return 1;
5136 }
5137 let p = match env.get_string(&player) {
5138 Ok(s) => String::from(s),
5139 Err(_) => return 1,
5140 };
5141 let u = match env.get_string(&uuid) {
5142 Ok(s) => String::from(s),
5143 Err(_) => return 1,
5144 };
5145 let s = match env.get_string(&source) {
5146 Ok(s) => String::from(s),
5147 Err(_) => return 1,
5148 };
5149 let d = match env.get_string(&dimension) {
5150 Ok(s) => String::from(s),
5151 Err(_) => return 1,
5152 };
5153 let ev = YogPlayerDeathEvent {
5154 player: YogStr::from_str(&p),
5155 uuid: YogStr::from_str(&u),
5156 source: YogStr::from_str(&s),
5157 dimension: YogStr::from_str(&d),
5158 };
5159 let srv = srv_ptr();
5160 let mut allow = true;
5161 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5162 for (module, ud, f) in &h.player_death {
5163 if module.is_retiring() { continue; }
5164 let Some(_call) = module.enter() else { continue };
5165 if !unsafe { f(*ud, srv, &ev, 0) } {
5166 allow = false;
5167 break;
5168 }
5169 }
5170 }))
5171 .ok();
5172 allow as jni::sys::jboolean
5173}
5174
5175#[no_mangle]
5176pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeath<'l>(
5177 mut env: JNIEnv<'l>,
5178 _class: JClass<'l>,
5179 player: JString<'l>,
5180 uuid: JString<'l>,
5181 source: JString<'l>,
5182 dimension: JString<'l>,
5183) {
5184 let h = handlers();
5185 if h.player_death.is_empty() {
5186 return;
5187 }
5188 let (p, u, s, d) = (
5189 jstr!(env, player),
5190 jstr!(env, uuid),
5191 jstr!(env, source),
5192 jstr!(env, dimension),
5193 );
5194 let ev = YogPlayerDeathEvent {
5195 player: YogStr::from_str(&p),
5196 uuid: YogStr::from_str(&u),
5197 source: YogStr::from_str(&s),
5198 dimension: YogStr::from_str(&d),
5199 };
5200 let srv = srv_ptr();
5201 guard("on_player_death", || {
5202 for (module, ud, f) in &h.player_death {
5203 if module.is_retiring() { continue; }
5204 let Some(_call) = module.enter() else { continue };
5205 unsafe { f(*ud, srv, &ev, 1) };
5206 }
5207 });
5208}
5209
5210#[no_mangle]
5211pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerRespawn<'l>(
5212 mut env: JNIEnv<'l>,
5213 _class: JClass<'l>,
5214 player: JString<'l>,
5215 uuid: JString<'l>,
5216 at_anchor: jni::sys::jboolean,
5217 dimension: JString<'l>,
5218) {
5219 let h = handlers();
5220 if h.player_respawn.is_empty() {
5221 return;
5222 }
5223 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
5224 let ev = YogPlayerRespawnEvent {
5225 player: YogStr::from_str(&p),
5226 uuid: YogStr::from_str(&u),
5227 at_anchor: at_anchor != 0,
5228 dimension: YogStr::from_str(&d),
5229 };
5230 let srv = srv_ptr();
5231 guard("on_player_respawn", || {
5232 for (module, ud, f) in &h.player_respawn {
5233 if module.is_retiring() { continue; }
5234 let Some(_call) = module.enter() else { continue };
5235 unsafe { f(*ud, srv, &ev, 1) };
5236 }
5237 });
5238}
5239
5240#[no_mangle]
5241pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAdvancement<'l>(
5242 mut env: JNIEnv<'l>,
5243 _class: JClass<'l>,
5244 player: JString<'l>,
5245 uuid: JString<'l>,
5246 advancement: JString<'l>,
5247 dimension: JString<'l>,
5248) {
5249 let h = handlers();
5250 if h.advancement.is_empty() {
5251 return;
5252 }
5253 let (p, u, a, d) = (
5254 jstr!(env, player),
5255 jstr!(env, uuid),
5256 jstr!(env, advancement),
5257 jstr!(env, dimension),
5258 );
5259 let ev = YogAdvancementEvent {
5260 player: YogStr::from_str(&p),
5261 uuid: YogStr::from_str(&u),
5262 advancement: YogStr::from_str(&a),
5263 dimension: YogStr::from_str(&d),
5264 };
5265 let srv = srv_ptr();
5266 guard("on_advancement", || {
5267 for (module, ud, f) in &h.advancement {
5268 if module.is_retiring() { continue; }
5269 let Some(_call) = module.enter() else { continue };
5270 unsafe { f(*ud, srv, &ev, 1) };
5271 }
5272 });
5273}
5274
5275#[no_mangle]
5276pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteractPre<'l>(
5277 mut env: JNIEnv<'l>,
5278 _class: JClass<'l>,
5279 player: JString<'l>,
5280 player_uuid: JString<'l>,
5281 entity_type: JString<'l>,
5282 entity_uuid: JString<'l>,
5283 hand: JString<'l>,
5284 dimension: JString<'l>,
5285) -> jni::sys::jboolean {
5286 let h = handlers();
5287 if h.entity_interact.is_empty() {
5288 return 1;
5289 }
5290 let p = match env.get_string(&player) {
5291 Ok(s) => String::from(s),
5292 Err(_) => return 1,
5293 };
5294 let pu = match env.get_string(&player_uuid) {
5295 Ok(s) => String::from(s),
5296 Err(_) => return 1,
5297 };
5298 let et = match env.get_string(&entity_type) {
5299 Ok(s) => String::from(s),
5300 Err(_) => return 1,
5301 };
5302 let eu = match env.get_string(&entity_uuid) {
5303 Ok(s) => String::from(s),
5304 Err(_) => return 1,
5305 };
5306 let ha = match env.get_string(&hand) {
5307 Ok(s) => String::from(s),
5308 Err(_) => return 1,
5309 };
5310 let d = match env.get_string(&dimension) {
5311 Ok(s) => String::from(s),
5312 Err(_) => return 1,
5313 };
5314 let ev = YogEntityInteractEvent {
5315 player: YogStr::from_str(&p),
5316 player_uuid: YogStr::from_str(&pu),
5317 entity_type: YogStr::from_str(&et),
5318 entity_uuid: YogStr::from_str(&eu),
5319 hand: YogStr::from_str(&ha),
5320 dimension: YogStr::from_str(&d),
5321 };
5322 let srv = srv_ptr();
5323 let mut allow = true;
5324 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5325 for (module, ud, f) in &h.entity_interact {
5326 if module.is_retiring() { continue; }
5327 let Some(_call) = module.enter() else { continue };
5328 if !unsafe { f(*ud, srv, &ev, 0) } {
5329 allow = false;
5330 break;
5331 }
5332 }
5333 }))
5334 .ok();
5335 allow as jni::sys::jboolean
5336}
5337
5338#[no_mangle]
5339pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteract<'l>(
5340 mut env: JNIEnv<'l>,
5341 _class: JClass<'l>,
5342 player: JString<'l>,
5343 player_uuid: JString<'l>,
5344 entity_type: JString<'l>,
5345 entity_uuid: JString<'l>,
5346 hand: JString<'l>,
5347 dimension: JString<'l>,
5348) {
5349 let h = handlers();
5350 if h.entity_interact.is_empty() {
5351 return;
5352 }
5353 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5354 let (et, eu, ha, d) = (
5355 jstr!(env, entity_type),
5356 jstr!(env, entity_uuid),
5357 jstr!(env, hand),
5358 jstr!(env, dimension),
5359 );
5360 let ev = YogEntityInteractEvent {
5361 player: YogStr::from_str(&p),
5362 player_uuid: YogStr::from_str(&pu),
5363 entity_type: YogStr::from_str(&et),
5364 entity_uuid: YogStr::from_str(&eu),
5365 hand: YogStr::from_str(&ha),
5366 dimension: YogStr::from_str(&d),
5367 };
5368 let srv = srv_ptr();
5369 guard("on_entity_interact", || {
5370 for (module, ud, f) in &h.entity_interact {
5371 if module.is_retiring() { continue; }
5372 let Some(_call) = module.enter() else { continue };
5373 unsafe { f(*ud, srv, &ev, 1) };
5374 }
5375 });
5376}
5377
5378#[no_mangle]
5379pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemCraft<'l>(
5380 mut env: JNIEnv<'l>,
5381 _class: JClass<'l>,
5382 player: JString<'l>,
5383 player_uuid: JString<'l>,
5384 result_item: JString<'l>,
5385 result_count: jint,
5386 dimension: JString<'l>,
5387) {
5388 let h = handlers();
5389 if h.item_craft.is_empty() {
5390 return;
5391 }
5392 let (p, pu, ri, d) = (
5393 jstr!(env, player),
5394 jstr!(env, player_uuid),
5395 jstr!(env, result_item),
5396 jstr!(env, dimension),
5397 );
5398 let ev = YogCraftEvent {
5399 player: YogStr::from_str(&p),
5400 player_uuid: YogStr::from_str(&pu),
5401 result_item: YogStr::from_str(&ri),
5402 result_count: result_count as u32,
5403 dimension: YogStr::from_str(&d),
5404 };
5405 let srv = srv_ptr();
5406 guard("on_item_craft", || {
5407 for (module, ud, f) in &h.item_craft {
5408 if module.is_retiring() { continue; }
5409 let Some(_call) = module.enter() else { continue };
5410 unsafe { f(*ud, srv, &ev, 1) };
5411 }
5412 });
5413}
5414
5415#[no_mangle]
5416pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosionPre<'l>(
5417 mut env: JNIEnv<'l>,
5418 _class: JClass<'l>,
5419 dimension: JString<'l>,
5420 x: jdouble,
5421 y: jdouble,
5422 z: jdouble,
5423 power: jfloat,
5424 cause_uuid: JString<'l>,
5425) -> jni::sys::jboolean {
5426 let h = handlers();
5427 if h.explosion.is_empty() {
5428 return 1;
5429 }
5430 let d = match env.get_string(&dimension) {
5431 Ok(s) => String::from(s),
5432 Err(_) => return 1,
5433 };
5434 let cu = match env.get_string(&cause_uuid) {
5435 Ok(s) => String::from(s),
5436 Err(_) => return 1,
5437 };
5438 let ev = YogExplosionEvent {
5439 dimension: YogStr::from_str(&d),
5440 x,
5441 y,
5442 z,
5443 power,
5444 cause_uuid: YogStr::from_str(&cu),
5445 };
5446 let srv = srv_ptr();
5447 let mut allow = true;
5448 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5449 for (module, ud, f) in &h.explosion {
5450 if module.is_retiring() { continue; }
5451 let Some(_call) = module.enter() else { continue };
5452 if !unsafe { f(*ud, srv, &ev, 0) } {
5453 allow = false;
5454 break;
5455 }
5456 }
5457 }))
5458 .ok();
5459 allow as jni::sys::jboolean
5460}
5461
5462#[no_mangle]
5463pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosion<'l>(
5464 mut env: JNIEnv<'l>,
5465 _class: JClass<'l>,
5466 dimension: JString<'l>,
5467 x: jdouble,
5468 y: jdouble,
5469 z: jdouble,
5470 power: jfloat,
5471 cause_uuid: JString<'l>,
5472) {
5473 let h = handlers();
5474 if h.explosion.is_empty() {
5475 return;
5476 }
5477 let (d, cu) = (jstr!(env, dimension), jstr!(env, cause_uuid));
5478 let ev = YogExplosionEvent {
5479 dimension: YogStr::from_str(&d),
5480 x,
5481 y,
5482 z,
5483 power,
5484 cause_uuid: YogStr::from_str(&cu),
5485 };
5486 let srv = srv_ptr();
5487 guard("on_explosion", || {
5488 for (module, ud, f) in &h.explosion {
5489 if module.is_retiring() { continue; }
5490 let Some(_call) = module.enter() else { continue };
5491 unsafe { f(*ud, srv, &ev, 1) };
5492 }
5493 });
5494}
5495
5496#[no_mangle]
5499pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickupPre<'l>(
5500 mut env: JNIEnv<'l>,
5501 _class: JClass<'l>,
5502 player: JString<'l>,
5503 player_uuid: JString<'l>,
5504 item_id: JString<'l>,
5505 item_count: jint,
5506 entity_uuid: JString<'l>,
5507 dimension: JString<'l>,
5508) -> jni::sys::jboolean {
5509 let h = handlers();
5510 if h.item_pickup.is_empty() {
5511 return 1;
5512 }
5513 let p = match env.get_string(&player) {
5514 Ok(s) => String::from(s),
5515 Err(_) => return 1,
5516 };
5517 let pu = match env.get_string(&player_uuid) {
5518 Ok(s) => String::from(s),
5519 Err(_) => return 1,
5520 };
5521 let ii = match env.get_string(&item_id) {
5522 Ok(s) => String::from(s),
5523 Err(_) => return 1,
5524 };
5525 let eu = match env.get_string(&entity_uuid) {
5526 Ok(s) => String::from(s),
5527 Err(_) => return 1,
5528 };
5529 let d = match env.get_string(&dimension) {
5530 Ok(s) => String::from(s),
5531 Err(_) => return 1,
5532 };
5533 let ev = YogItemPickupEvent {
5534 player: YogStr::from_str(&p),
5535 player_uuid: YogStr::from_str(&pu),
5536 item_id: YogStr::from_str(&ii),
5537 item_count: item_count as u32,
5538 entity_uuid: YogStr::from_str(&eu),
5539 dimension: YogStr::from_str(&d),
5540 };
5541 let srv = srv_ptr();
5542 let mut allow = true;
5543 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5544 for (module, ud, f) in &h.item_pickup {
5545 if module.is_retiring() { continue; }
5546 let Some(_call) = module.enter() else { continue };
5547 if !unsafe { f(*ud, srv, &ev, 0) } {
5548 allow = false;
5549 break;
5550 }
5551 }
5552 }))
5553 .ok();
5554 allow as jni::sys::jboolean
5555}
5556
5557#[no_mangle]
5558pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickup<'l>(
5559 mut env: JNIEnv<'l>,
5560 _class: JClass<'l>,
5561 player: JString<'l>,
5562 player_uuid: JString<'l>,
5563 item_id: JString<'l>,
5564 item_count: jint,
5565 entity_uuid: JString<'l>,
5566 dimension: JString<'l>,
5567) {
5568 let h = handlers();
5569 if h.item_pickup.is_empty() {
5570 return;
5571 }
5572 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5573 let (ii, eu, d) = (
5574 jstr!(env, item_id),
5575 jstr!(env, entity_uuid),
5576 jstr!(env, dimension),
5577 );
5578 let ev = YogItemPickupEvent {
5579 player: YogStr::from_str(&p),
5580 player_uuid: YogStr::from_str(&pu),
5581 item_id: YogStr::from_str(&ii),
5582 item_count: item_count as u32,
5583 entity_uuid: YogStr::from_str(&eu),
5584 dimension: YogStr::from_str(&d),
5585 };
5586 let srv = srv_ptr();
5587 guard("on_item_pickup", || {
5588 for (module, ud, f) in &h.item_pickup {
5589 if module.is_retiring() { continue; }
5590 let Some(_call) = module.enter() else { continue };
5591 unsafe { f(*ud, srv, &ev, 1) };
5592 }
5593 });
5594}
5595
5596#[no_mangle]
5597pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerMove<'l>(
5598 mut env: JNIEnv<'l>,
5599 _class: JClass<'l>,
5600 player: JString<'l>,
5601 player_uuid: JString<'l>,
5602 x: jdouble,
5603 y: jdouble,
5604 z: jdouble,
5605 yaw: jfloat,
5606 pitch: jfloat,
5607 dimension: JString<'l>,
5608) {
5609 let h = handlers();
5610 if h.player_move.is_empty() {
5611 return;
5612 }
5613 let (p, pu, d) = (
5614 jstr!(env, player),
5615 jstr!(env, player_uuid),
5616 jstr!(env, dimension),
5617 );
5618 let ev = YogPlayerMoveEvent {
5619 player: YogStr::from_str(&p),
5620 player_uuid: YogStr::from_str(&pu),
5621 x,
5622 y,
5623 z,
5624 yaw,
5625 pitch,
5626 dimension: YogStr::from_str(&d),
5627 };
5628 let srv = srv_ptr();
5629 guard("on_player_move", || {
5630 for (module, ud, f) in &h.player_move {
5631 if module.is_retiring() { continue; }
5632 let Some(_call) = module.enter() else { continue };
5633 unsafe { f(*ud, srv, &ev, 1) };
5634 }
5635 });
5636}
5637
5638#[no_mangle]
5639pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpenPre<'l>(
5640 mut env: JNIEnv<'l>,
5641 _class: JClass<'l>,
5642 player: JString<'l>,
5643 player_uuid: JString<'l>,
5644 dimension: JString<'l>,
5645) -> jni::sys::jboolean {
5646 let h = handlers();
5647 if h.container_open.is_empty() {
5648 return 1;
5649 }
5650 let p = match env.get_string(&player) {
5651 Ok(s) => String::from(s),
5652 Err(_) => return 1,
5653 };
5654 let pu = match env.get_string(&player_uuid) {
5655 Ok(s) => String::from(s),
5656 Err(_) => return 1,
5657 };
5658 let d = match env.get_string(&dimension) {
5659 Ok(s) => String::from(s),
5660 Err(_) => return 1,
5661 };
5662 let ev = YogContainerOpenEvent {
5663 player: YogStr::from_str(&p),
5664 player_uuid: YogStr::from_str(&pu),
5665 container_type: YogStr::EMPTY,
5666 dimension: YogStr::from_str(&d),
5667 };
5668 let srv = srv_ptr();
5669 let mut allow = true;
5670 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5671 for (module, ud, f) in &h.container_open {
5672 if module.is_retiring() { continue; }
5673 let Some(_call) = module.enter() else { continue };
5674 if !unsafe { f(*ud, srv, &ev, 0) } {
5675 allow = false;
5676 break;
5677 }
5678 }
5679 }))
5680 .ok();
5681 allow as jni::sys::jboolean
5682}
5683
5684#[no_mangle]
5685pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpen<'l>(
5686 mut env: JNIEnv<'l>,
5687 _class: JClass<'l>,
5688 player: JString<'l>,
5689 player_uuid: JString<'l>,
5690 container_type: JString<'l>,
5691 dimension: JString<'l>,
5692) {
5693 let h = handlers();
5694 if h.container_open.is_empty() {
5695 return;
5696 }
5697 let (p, pu, ct, d) = (
5698 jstr!(env, player),
5699 jstr!(env, player_uuid),
5700 jstr!(env, container_type),
5701 jstr!(env, dimension),
5702 );
5703 let ev = YogContainerOpenEvent {
5704 player: YogStr::from_str(&p),
5705 player_uuid: YogStr::from_str(&pu),
5706 container_type: YogStr::from_str(&ct),
5707 dimension: YogStr::from_str(&d),
5708 };
5709 let srv = srv_ptr();
5710 guard("on_container_open", || {
5711 for (module, ud, f) in &h.container_open {
5712 if module.is_retiring() { continue; }
5713 let Some(_call) = module.enter() else { continue };
5714 unsafe { f(*ud, srv, &ev, 1) };
5715 }
5716 });
5717}
5718
5719#[no_mangle]
5720pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerClose<'l>(
5721 mut env: JNIEnv<'l>,
5722 _class: JClass<'l>,
5723 player: JString<'l>,
5724 player_uuid: JString<'l>,
5725 dimension: JString<'l>,
5726) {
5727 let h = handlers();
5728 if h.container_close.is_empty() {
5729 return;
5730 }
5731 let (p, pu, d) = (
5732 jstr!(env, player),
5733 jstr!(env, player_uuid),
5734 jstr!(env, dimension),
5735 );
5736 let ev = YogContainerCloseEvent {
5737 player: YogStr::from_str(&p),
5738 player_uuid: YogStr::from_str(&pu),
5739 dimension: YogStr::from_str(&d),
5740 };
5741 let srv = srv_ptr();
5742 guard("on_container_close", || {
5743 for (module, ud, f) in &h.container_close {
5744 if module.is_retiring() { continue; }
5745 let Some(_call) = module.enter() else { continue };
5746 unsafe { f(*ud, srv, &ev, 1) };
5747 }
5748 });
5749}
5750
5751#[no_mangle]
5752pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHitPre<'l>(
5753 mut env: JNIEnv<'l>,
5754 _class: JClass<'l>,
5755 projectile_type: JString<'l>,
5756 projectile_uuid: JString<'l>,
5757 shooter_uuid: JString<'l>,
5758 hit_type: JString<'l>,
5759 hit_entity_uuid: JString<'l>,
5760 x: jdouble,
5761 y: jdouble,
5762 z: jdouble,
5763 dimension: JString<'l>,
5764) -> jni::sys::jboolean {
5765 let h = handlers();
5766 if h.projectile_hit.is_empty() {
5767 return 1;
5768 }
5769 let pt = match env.get_string(&projectile_type) {
5770 Ok(s) => String::from(s),
5771 Err(_) => return 1,
5772 };
5773 let pu = match env.get_string(&projectile_uuid) {
5774 Ok(s) => String::from(s),
5775 Err(_) => return 1,
5776 };
5777 let su = match env.get_string(&shooter_uuid) {
5778 Ok(s) => String::from(s),
5779 Err(_) => return 1,
5780 };
5781 let ht = match env.get_string(&hit_type) {
5782 Ok(s) => String::from(s),
5783 Err(_) => return 1,
5784 };
5785 let heu = match env.get_string(&hit_entity_uuid) {
5786 Ok(s) => String::from(s),
5787 Err(_) => return 1,
5788 };
5789 let dim = match env.get_string(&dimension) {
5790 Ok(s) => String::from(s),
5791 Err(_) => return 1,
5792 };
5793 let ev = YogProjectileHitEvent {
5794 projectile_type: YogStr::from_str(&pt),
5795 projectile_uuid: YogStr::from_str(&pu),
5796 shooter_uuid: YogStr::from_str(&su),
5797 hit_type: YogStr::from_str(&ht),
5798 hit_entity_uuid: YogStr::from_str(&heu),
5799 x,
5800 y,
5801 z,
5802 dimension: YogStr::from_str(&dim),
5803 };
5804 let srv = srv_ptr();
5805 let mut allow = true;
5806 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
5807 for (module, ud, f) in &h.projectile_hit {
5808 if module.is_retiring() { continue; }
5809 let Some(_call) = module.enter() else { continue };
5810 if !unsafe { f(*ud, srv, &ev, 0) } {
5811 allow = false;
5812 break;
5813 }
5814 }
5815 }))
5816 .ok();
5817 allow as jni::sys::jboolean
5818}
5819
5820#[no_mangle]
5821pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHit<'l>(
5822 mut env: JNIEnv<'l>,
5823 _class: JClass<'l>,
5824 projectile_type: JString<'l>,
5825 projectile_uuid: JString<'l>,
5826 shooter_uuid: JString<'l>,
5827 hit_type: JString<'l>,
5828 hit_entity_uuid: JString<'l>,
5829 x: jdouble,
5830 y: jdouble,
5831 z: jdouble,
5832 dimension: JString<'l>,
5833) {
5834 let h = handlers();
5835 if h.projectile_hit.is_empty() {
5836 return;
5837 }
5838 let (pt, pu) = (jstr!(env, projectile_type), jstr!(env, projectile_uuid));
5839 let (su, ht) = (jstr!(env, shooter_uuid), jstr!(env, hit_type));
5840 let (heu, dim) = (jstr!(env, hit_entity_uuid), jstr!(env, dimension));
5841 let ev = YogProjectileHitEvent {
5842 projectile_type: YogStr::from_str(&pt),
5843 projectile_uuid: YogStr::from_str(&pu),
5844 shooter_uuid: YogStr::from_str(&su),
5845 hit_type: YogStr::from_str(&ht),
5846 hit_entity_uuid: YogStr::from_str(&heu),
5847 x,
5848 y,
5849 z,
5850 dimension: YogStr::from_str(&dim),
5851 };
5852 let srv = srv_ptr();
5853 guard("on_projectile_hit", || {
5854 for (module, ud, f) in &h.projectile_hit {
5855 if module.is_retiring() { continue; }
5856 let Some(_call) = module.enter() else { continue };
5857 unsafe { f(*ud, srv, &ev, 1) };
5858 }
5859 });
5860}
5861
5862#[no_mangle]
5865pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientTick<'l>(
5866 _env: JNIEnv<'l>,
5867 _class: JClass<'l>,
5868) {
5869 let h = handlers();
5870 if h.client_tick.is_empty() {
5871 return;
5872 }
5873 guard("on_client_tick", || {
5874 for (module, ud, f) in &h.client_tick {
5875 if module.is_retiring() { continue; }
5876 let Some(_call) = module.enter() else { continue };
5877 unsafe { f(*ud) };
5878 }
5879 });
5880}
5881
5882#[no_mangle]
5883pub extern "system" fn Java_dev_yog_NativeBridge_nativeGlInit<'l>(
5884 _env: JNIEnv<'l>,
5885 _class: JClass<'l>,
5886) {
5887 if GL.get().is_some() {
5888 return;
5889 }
5890 let mut raw_get_binary: usize = 0;
5891 let mut raw_prog_binary: usize = 0;
5892 let gl = unsafe {
5893 glow::Context::from_loader_function(|sym| {
5894 let Some(mut env) = get_env() else {
5895 return std::ptr::null();
5896 };
5897 let jsym = match env.new_string(sym) {
5898 Ok(s) => s,
5899 Err(_) => return std::ptr::null(),
5900 };
5901 let jsym_obj: JObject = jsym.into();
5902 let val = env.call_static_method(
5903 "dev/yog/NativeBridge",
5904 "glProcAddress",
5905 "(Ljava/lang/String;)J",
5906 &[JValue::Object(&jsym_obj)],
5907 );
5908 let ptr = match val.and_then(|v| v.j()) {
5909 Ok(p) if p != 0 => p as usize as *const _,
5910 _ => std::ptr::null(),
5911 };
5912 match sym {
5914 "glGetProgramBinary" => raw_get_binary = ptr as usize,
5915 "glProgramBinary" => raw_prog_binary = ptr as usize,
5916 _ => {}
5917 }
5918 ptr
5919 })
5920 };
5921 let _ = GL.set(GlCtx(gl));
5922 let _ = GL_GET_PROGRAM_BINARY.set(if raw_get_binary != 0 {
5923 Some(raw_get_binary)
5924 } else {
5925 None
5926 });
5927 let _ = GL_PROGRAM_BINARY.set(if raw_prog_binary != 0 {
5928 Some(raw_prog_binary)
5929 } else {
5930 None
5931 });
5932 if let Some(mut env) = get_env() {
5935 if let Ok(jsym) = env.new_string("glGetProgramiv") {
5936 let jsym_obj: JObject = jsym.into();
5937 if let Ok(jv) = env.call_static_method(
5938 "dev/yog/NativeBridge",
5939 "glProcAddress",
5940 "(Ljava/lang/String;)J",
5941 &[JValue::Object(&jsym_obj)],
5942 ) {
5943 if let Ok(ptr) = jv.j() {
5944 let _ = GL_GET_PROGRAM_IV.set(if ptr != 0 { Some(ptr as usize) } else { None });
5945 }
5946 }
5947 }
5948 }
5949}
5950
5951#[no_mangle]
5952pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnHudRender<'l>(
5953 _env: JNIEnv<'l>,
5954 _class: JClass<'l>,
5955 delta_tick: jfloat,
5956 screen_w: jint,
5957 screen_h: jint,
5958 scale_factor: jfloat,
5959 player_x: jfloat,
5960 player_y: jfloat,
5961 player_z: jfloat,
5962) {
5963 let h = handlers();
5964 let mut gfx = GFX_FN_TABLE;
5965 gfx.screen_w = screen_w;
5966 gfx.screen_h = screen_h;
5967 gfx.delta_tick = delta_tick;
5968 gfx.scale_factor = scale_factor;
5969 gfx.player_pos = [player_x, player_y, player_z];
5970
5971 if !h.hud_render.is_empty() {
5973 guard("on_hud_render", || {
5974 for (module, ud, f) in &h.hud_render {
5975 if module.is_retiring() { continue; }
5976 let Some(_call) = module.enter() else { continue };
5977 unsafe { f(*ud, &gfx) };
5978 }
5979 });
5980 }
5981
5982 let active_layers: Vec<UiLayer> = h.active_uis.lock().unwrap().clone();
5984 let sw = screen_w as f32;
5985 let sh = screen_h as f32;
5986 let ctx = unsafe { yog_gfx::GfxContext::from_raw(&gfx as *const _) };
5987 let mut renderers = h.book_renderers.lock().unwrap();
5988 for layer in &active_layers {
5989 if !layer.visible {
5990 continue;
5991 }
5992 if let Some(renderer) = renderers.get_mut(&layer.id) {
5993 renderer.render(&ctx, sw, sh);
5994 }
5995 }
5996}
5997
5998#[no_mangle]
5999pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnWorldRender<'l>(
6000 env: JNIEnv<'l>,
6001 _class: JClass<'l>,
6002 delta_tick: jfloat,
6003 screen_w: jint,
6004 screen_h: jint,
6005 scale_factor: jfloat,
6006 view_proj_arr: JFloatArray<'l>,
6007 cam_x: jfloat,
6008 cam_y: jfloat,
6009 cam_z: jfloat,
6010 player_x: jfloat,
6011 player_y: jfloat,
6012 player_z: jfloat,
6013) {
6014 let h = handlers();
6015 if h.world_render.is_empty() {
6016 return;
6017 }
6018 let mut view_proj = [0f32; 16];
6019 if env
6020 .get_float_array_region(&view_proj_arr, 0, &mut view_proj)
6021 .is_err()
6022 {
6023 return;
6024 }
6025 let mut gfx = GFX_FN_TABLE;
6026 gfx.screen_w = screen_w;
6027 gfx.screen_h = screen_h;
6028 gfx.delta_tick = delta_tick;
6029 gfx.scale_factor = scale_factor;
6030 gfx.view_proj = view_proj;
6031 gfx.camera_pos = [cam_x, cam_y, cam_z];
6032 gfx.player_pos = [player_x, player_y, player_z];
6033 guard("on_world_render", || {
6034 for (module, ud, f) in &h.world_render {
6035 if module.is_retiring() { continue; }
6036 let Some(_call) = module.enter() else { continue };
6037 unsafe { f(*ud, &gfx) };
6038 }
6039 });
6040}
6041
6042#[no_mangle]
6043pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnKeyPress<'l>(
6044 _env: JNIEnv<'l>,
6045 _class: JClass<'l>,
6046 key_code: jint,
6047 scan_code: jint,
6048 action: jint,
6049 modifiers: jint,
6050) -> jni::sys::jboolean {
6051 let h = handlers();
6052 if h.key_press.is_empty() {
6053 return 1;
6054 }
6055 let ev = YogKeyPressEvent {
6056 key_code,
6057 scan_code,
6058 action,
6059 modifiers,
6060 };
6061 let mut allow = true;
6062 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
6063 for (module, ud, f) in &h.key_press {
6064 if module.is_retiring() { continue; }
6065 let Some(_call) = module.enter() else { continue };
6066 if !unsafe { f(*ud, &ev) } {
6067 allow = false;
6068 break;
6069 }
6070 }
6071 }))
6072 .ok();
6073 allow as jni::sys::jboolean
6074}
6075
6076#[no_mangle]
6077pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenOpen<'l>(
6078 mut env: JNIEnv<'l>,
6079 _class: JClass<'l>,
6080 screen_class: JString<'l>,
6081) {
6082 let h = handlers();
6083 if h.screen_open.is_empty() {
6084 return;
6085 }
6086 let sc = match env.get_string(&screen_class) {
6087 Ok(s) => String::from(s),
6088 Err(_) => return,
6089 };
6090 guard("on_screen_open", || {
6091 for (module, ud, f) in &h.screen_open {
6092 if module.is_retiring() { continue; }
6093 let Some(_call) = module.enter() else { continue };
6094 unsafe { f(*ud, YogStr::from_str(&sc)) };
6095 }
6096 });
6097}
6098
6099#[no_mangle]
6100pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenClose<'l>(
6101 mut env: JNIEnv<'l>,
6102 _class: JClass<'l>,
6103 screen_class: JString<'l>,
6104) {
6105 let h = handlers();
6106 if h.screen_close.is_empty() {
6107 return;
6108 }
6109 let sc = match env.get_string(&screen_class) {
6110 Ok(s) => String::from(s),
6111 Err(_) => return,
6112 };
6113 guard("on_screen_close", || {
6114 for (module, ud, f) in &h.screen_close {
6115 if module.is_retiring() { continue; }
6116 let Some(_call) = module.enter() else { continue };
6117 unsafe { f(*ud, YogStr::from_str(&sc)) };
6118 }
6119 });
6120}
6121
6122mod control_socket;
6123mod ui_jni;