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