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