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