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