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(yog_ids::sep::FIELD)
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_tag_items(id: &str) -> Vec<String> {
1253 let items: Option<String> = unsafe { host!("dev/yog/YogClientApi", "tagItems", [JArg::S(YogStr::from_str(id))]) };
1254 items.map(|s| s.split(yog_ids::sep::FIELD).filter(|i| !i.is_empty()).map(str::to_string).collect()).unwrap_or_default()
1255}
1256
1257fn client_advancement_done(id: &str) -> bool {
1258 unsafe { host!("dev/yog/YogClientApi", "advancementDone", [JArg::S(YogStr::from_str(id))]).unwrap_or(false) }
1259}
1260
1261thread_local! {
1264 static LOGGING: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
1267}
1268
1269fn host_log(level: yog_logging::Level, message: &str) {
1272 let sent = !LOGGING.with(|busy| busy.replace(true)) && {
1273 let r: Option<()> = unsafe { host!("dev/yog/YogLog", "log", [JArg::I(level as i32), JArg::S(YogStr::from_str(message))]) };
1274 LOGGING.with(|busy| busy.set(false));
1275 r.is_some()
1276 };
1277 if !sent {
1278 eprintln!("[yog] [{level:?}] {message}");
1279 }
1280}
1281
1282unsafe extern "C" fn api_log(level: u32, message: YogStr) {
1284 host_log(yog_logging::Level::from_u32(level), unsafe { message.as_str() });
1285}
1286
1287unsafe extern "C" fn api_client_advancement_done(_ctx: *mut c_void, id: YogStr) -> bool {
1288 client_advancement_done(unsafe { id.as_str() })
1289}
1290
1291unsafe extern "C" fn srv_entity_attribute_get(
1292 _ctx: *mut c_void,
1293 uuid: YogStr,
1294 attr: YogStr,
1295) -> f64 {
1296 unsafe { bridge!("entityAttributeGet", [S(uuid), S(attr)]).unwrap_or(f64::NAN) }
1297}
1298
1299unsafe extern "C" fn srv_entity_attribute_set(
1300 _ctx: *mut c_void,
1301 uuid: YogStr,
1302 attr: YogStr,
1303 value: f64,
1304) -> bool {
1305 unsafe { bridge!("entityAttributeSet", [S(uuid), S(attr), D(value)]).unwrap_or(false) }
1306}
1307
1308unsafe extern "C" fn srv_get_held_item_nbt(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
1309 unsafe { owned(bridge!("getHeldItemNbt", [S(player)])) }
1310}
1311
1312unsafe extern "C" fn srv_set_held_item_nbt(
1313 _ctx: *mut c_void,
1314 player: YogStr,
1315 snbt: YogStr,
1316) -> bool {
1317 unsafe { bridge!("setHeldItemNbt", [S(player), S(snbt)]).unwrap_or(false) }
1318}
1319
1320unsafe extern "C" fn srv_get_offhand_item_nbt(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
1321 unsafe { owned(bridge!("getOffhandItemNbt", [S(player)])) }
1322}
1323
1324unsafe extern "C" fn srv_set_offhand_item_nbt(
1325 _ctx: *mut c_void,
1326 player: YogStr,
1327 snbt: YogStr,
1328) -> bool {
1329 unsafe { bridge!("setOffhandItemNbt", [S(player), S(snbt)]).unwrap_or(false) }
1330}
1331
1332unsafe extern "C" fn srv_get_slot_item(
1333 _ctx: *mut c_void,
1334 player: YogStr,
1335 slot: u32,
1336) -> YogOwnedStr {
1337 unsafe { owned(bridge!("getSlotItem", [S(player), I(slot as i32)])) }
1338}
1339
1340unsafe extern "C" fn srv_set_slot_item(
1341 _ctx: *mut c_void,
1342 player: YogStr,
1343 slot: u32,
1344 item_id: YogStr,
1345 count: u32,
1346 snbt: YogStr,
1347) -> bool {
1348 unsafe {
1349 bridge!(
1350 "setSlotItem",
1351 [
1352 S(player),
1353 I(slot as i32),
1354 S(item_id),
1355 I(count as i32),
1356 S(snbt)
1357 ]
1358 )
1359 .unwrap_or(false)
1360 }
1361}
1362
1363fn gl_draw_mode(mode: u8) -> u32 {
1372 match mode {
1373 1 => glow::LINES,
1374 2 => glow::LINE_STRIP,
1375 3 => glow::TRIANGLE_STRIP,
1376 4 => glow::TRIANGLE_FAN,
1377 _ => glow::TRIANGLES,
1378 }
1379}
1380
1381fn gl_attr_type(dtype: u8) -> u32 {
1382 match dtype {
1383 1 => glow::UNSIGNED_BYTE,
1384 2 => glow::INT,
1385 3 => glow::UNSIGNED_INT,
1386 _ => glow::FLOAT,
1387 }
1388}
1389
1390unsafe fn compile_shader(gl: &glow::Context, stage: u32, src: &str) -> Option<glow::NativeShader> {
1391 unsafe {
1392 let sh = gl.create_shader(stage).ok()?;
1393 gl.shader_source(sh, src);
1394 gl.compile_shader(sh);
1395 if !gl.get_shader_compile_status(sh) {
1396 yog_logging::error!("yog-gfx shader compile: {}", gl.get_shader_info_log(sh));
1397 gl.delete_shader(sh);
1398 return None;
1399 }
1400 Some(sh)
1401 }
1402}
1403
1404unsafe extern "C" fn gfx_buf_create() -> u32 {
1407 unsafe {
1408 let Some(g) = GL.get() else { return 0 };
1409 g.0.create_buffer().map(|b| b.0.get()).unwrap_or(0)
1410 }
1411}
1412
1413unsafe extern "C" fn gfx_buf_delete(handle: u32) {
1414 unsafe {
1415 let Some(g) = GL.get() else { return };
1416 let Some(n) = NonZeroU32::new(handle) else {
1417 return;
1418 };
1419 g.0.delete_buffer(glow::NativeBuffer(n));
1420 }
1421}
1422
1423unsafe extern "C" fn gfx_buf_data(handle: u32, bytes: *const u8, len: u32, dynamic: bool) {
1424 unsafe {
1425 let Some(g) = GL.get() else { return };
1426 let Some(n) = NonZeroU32::new(handle) else {
1427 return;
1428 };
1429 let gl = &g.0;
1430 let data = std::slice::from_raw_parts(bytes, len as usize);
1431 let usage = if dynamic {
1432 glow::DYNAMIC_DRAW
1433 } else {
1434 glow::STATIC_DRAW
1435 };
1436 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(n)));
1437 gl.buffer_data_u8_slice(glow::ARRAY_BUFFER, data, usage);
1438 gl.bind_buffer(glow::ARRAY_BUFFER, None);
1439 }
1440}
1441
1442unsafe extern "C" fn gfx_buf_subdata(handle: u32, offset: u32, bytes: *const u8, len: u32) {
1443 unsafe {
1444 let Some(g) = GL.get() else { return };
1445 let Some(n) = NonZeroU32::new(handle) else {
1446 return;
1447 };
1448 let gl = &g.0;
1449 let data = std::slice::from_raw_parts(bytes, len as usize);
1450 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(n)));
1451 gl.buffer_sub_data_u8_slice(glow::ARRAY_BUFFER, offset as i32, data);
1452 gl.bind_buffer(glow::ARRAY_BUFFER, None);
1453 }
1454}
1455
1456unsafe extern "C" fn gfx_vao_create() -> u32 {
1459 unsafe {
1460 let Some(g) = GL.get() else { return 0 };
1461 g.0.create_vertex_array().map(|v| v.0.get()).unwrap_or(0)
1462 }
1463}
1464
1465unsafe extern "C" fn gfx_vao_delete(handle: u32) {
1466 unsafe {
1467 let Some(g) = GL.get() else { return };
1468 let Some(n) = NonZeroU32::new(handle) else {
1469 return;
1470 };
1471 g.0.delete_vertex_array(glow::NativeVertexArray(n));
1472 }
1473}
1474
1475unsafe extern "C" fn gfx_vao_attrib(
1476 vao: u32,
1477 vbo: u32,
1478 index: u32,
1479 components: u8,
1480 dtype: u8,
1481 normalized: bool,
1482 stride: u32,
1483 offset: u32,
1484) {
1485 unsafe {
1486 let Some(g) = GL.get() else { return };
1487 let (Some(vn), Some(bn)) = (NonZeroU32::new(vao), NonZeroU32::new(vbo)) else {
1488 return;
1489 };
1490 let gl = &g.0;
1491 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1492 gl.bind_buffer(glow::ARRAY_BUFFER, Some(glow::NativeBuffer(bn)));
1493 let gl_type = gl_attr_type(dtype);
1494 if dtype == 2 || dtype == 3 {
1495 gl.vertex_attrib_pointer_i32(
1496 index,
1497 components as i32,
1498 gl_type,
1499 stride as i32,
1500 offset as i32,
1501 );
1502 } else {
1503 gl.vertex_attrib_pointer_f32(
1504 index,
1505 components as i32,
1506 gl_type,
1507 normalized,
1508 stride as i32,
1509 offset as i32,
1510 );
1511 }
1512 gl.enable_vertex_attrib_array(index);
1513 gl.bind_vertex_array(None);
1514 }
1515}
1516
1517unsafe extern "C" fn gfx_vao_set_ebo(vao: u32, ebo: u32) {
1518 unsafe {
1519 let Some(g) = GL.get() else { return };
1520 let (Some(vn), Some(en)) = (NonZeroU32::new(vao), NonZeroU32::new(ebo)) else {
1521 return;
1522 };
1523 let gl = &g.0;
1524 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1525 gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(glow::NativeBuffer(en)));
1526 gl.bind_vertex_array(None);
1527 }
1528}
1529
1530fn shader_cache_path(vert: &str, frag: &str) -> Option<PathBuf> {
1535 use std::hash::{Hash, Hasher};
1536 let mut h = std::collections::hash_map::DefaultHasher::new();
1537 vert.hash(&mut h);
1538 frag.hash(&mut h);
1539 let hash = h.finish();
1540 let dir = std::env::var("HOME")
1541 .map(|home| {
1542 PathBuf::from(home)
1543 .join(".cache")
1544 .join("yog")
1545 .join("shaders")
1546 })
1547 .ok()?;
1548 std::fs::create_dir_all(&dir).ok()?;
1549 Some(dir.join(format!("{hash:016x}.ysc")))
1550}
1551
1552unsafe fn load_shader_binary(gl: &glow::Context, data: &[u8]) -> Option<glow::NativeProgram> {
1558 unsafe {
1559 if data.len() < 4 {
1560 return None;
1561 }
1562 type ProgramBinaryFn = unsafe extern "system" fn(u32, u32, *const c_void, i32);
1563 let fn_ptr = (*GL_PROGRAM_BINARY.get()?)?;
1564 let program_binary: ProgramBinaryFn = std::mem::transmute(fn_ptr);
1565
1566 let fmt = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
1567 let prog = gl.create_program().ok()?;
1568 program_binary(
1569 prog.0.get(),
1570 fmt,
1571 data[4..].as_ptr() as *const c_void,
1572 (data.len() - 4) as i32,
1573 );
1574 if gl.get_program_link_status(prog) {
1575 Some(prog)
1576 } else {
1577 gl.delete_program(prog);
1578 None
1579 }
1580 }
1581}
1582
1583unsafe fn get_shader_binary(prog: glow::NativeProgram) -> (Vec<u8>, u32) {
1585 unsafe {
1586 type GetProgramivFn = unsafe extern "system" fn(u32, u32, *mut i32);
1587 type GetProgramBinaryFn =
1588 unsafe extern "system" fn(u32, i32, *mut i32, *mut u32, *mut c_void);
1589
1590 let Some(get_iv_raw) = GL_GET_PROGRAM_IV.get().and_then(|v| *v) else {
1591 return (vec![], 0);
1592 };
1593 let Some(get_bin_raw) = GL_GET_PROGRAM_BINARY.get().and_then(|v| *v) else {
1594 return (vec![], 0);
1595 };
1596 let get_program_iv: GetProgramivFn = std::mem::transmute(get_iv_raw);
1597 let get_program_binary: GetProgramBinaryFn = std::mem::transmute(get_bin_raw);
1598
1599 const PROGRAM_BINARY_LENGTH: u32 = 0x8741;
1601 let mut size: i32 = 0;
1602 get_program_iv(prog.0.get(), PROGRAM_BINARY_LENGTH, &mut size);
1603 if size <= 0 {
1604 return (vec![], 0);
1605 }
1606
1607 let mut buf = vec![0u8; size as usize];
1608 let mut actual_len: i32 = 0;
1609 let mut fmt: u32 = 0;
1610 get_program_binary(
1611 prog.0.get(),
1612 size,
1613 &mut actual_len,
1614 &mut fmt,
1615 buf.as_mut_ptr() as *mut c_void,
1616 );
1617 buf.truncate(actual_len.max(0) as usize);
1618 (buf, fmt)
1619 }
1620}
1621
1622unsafe extern "C" fn gfx_prog_create(vert: YogStr, frag: YogStr, out: *mut u32) -> bool {
1623 unsafe {
1624 let Some(g) = GL.get() else { return false };
1625 let gl = &g.0;
1626 let vert_s = vert.as_str();
1627 let frag_s = frag.as_str();
1628
1629 let cache_path = shader_cache_path(vert_s, frag_s);
1631 if let Some(ref path) = cache_path
1632 && let Ok(data) = std::fs::read(path)
1633 {
1634 if let Some(prog) = load_shader_binary(gl, &data) {
1635 *out = prog.0.get();
1636 return true;
1637 }
1638 let _ = std::fs::remove_file(path);
1640 }
1641
1642 let vs = match compile_shader(gl, glow::VERTEX_SHADER, vert_s) {
1644 Some(s) => s,
1645 None => return false,
1646 };
1647 let fs = match compile_shader(gl, glow::FRAGMENT_SHADER, frag_s) {
1648 Some(s) => s,
1649 None => {
1650 gl.delete_shader(vs);
1651 return false;
1652 }
1653 };
1654 let prog = match gl.create_program() {
1655 Ok(p) => p,
1656 Err(e) => {
1657 yog_logging::error!("yog-gfx: create_program: {}", e);
1658 gl.delete_shader(vs);
1659 gl.delete_shader(fs);
1660 return false;
1661 }
1662 };
1663 gl.attach_shader(prog, vs);
1664 gl.attach_shader(prog, fs);
1665 gl.link_program(prog);
1666 gl.detach_shader(prog, vs);
1667 gl.detach_shader(prog, fs);
1668 gl.delete_shader(vs);
1669 gl.delete_shader(fs);
1670 if !gl.get_program_link_status(prog) {
1671 yog_logging::error!("yog-gfx: shader link: {}", gl.get_program_info_log(prog));
1672 gl.delete_program(prog);
1673 return false;
1674 }
1675
1676 if let Some(ref path) = cache_path {
1678 let (binary, fmt) = get_shader_binary(prog);
1679 if !binary.is_empty() {
1680 let mut blob = fmt.to_le_bytes().to_vec();
1681 blob.extend_from_slice(&binary);
1682 let _ = std::fs::write(path, &blob);
1683 }
1684 }
1685
1686 *out = prog.0.get();
1687 true
1688 }
1689}
1690
1691unsafe extern "C" fn gfx_prog_delete(handle: u32) {
1692 unsafe {
1693 let Some(g) = GL.get() else { return };
1694 let Some(n) = NonZeroU32::new(handle) else {
1695 return;
1696 };
1697 g.0.delete_program(glow::NativeProgram(n));
1698 }
1699}
1700
1701macro_rules! with_prog {
1702 ($handle:expr_2021, |$gl:ident, $prog:ident, $loc:ident ($name:expr_2021)| $body:expr_2021) => {{
1703 let Some(g) = GL.get() else { return };
1704 let Some(n) = NonZeroU32::new($handle) else {
1705 return;
1706 };
1707 let $gl = &g.0;
1708 let $prog = glow::NativeProgram(n);
1709 $gl.use_program(Some($prog));
1710 let $loc = $gl.get_uniform_location($prog, $name.as_str());
1711 $body
1712 }};
1713}
1714
1715unsafe extern "C" fn gfx_prog_uniform_1i(prog: u32, name: YogStr, v: i32) {
1716 unsafe {
1717 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_1_i32(loc.as_ref(), v));
1718 }
1719}
1720unsafe extern "C" fn gfx_prog_uniform_1f(prog: u32, name: YogStr, v: f32) {
1721 unsafe {
1722 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_1_f32(loc.as_ref(), v));
1723 }
1724}
1725unsafe extern "C" fn gfx_prog_uniform_2f(prog: u32, name: YogStr, x: f32, y: f32) {
1726 unsafe {
1727 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_2_f32(
1728 loc.as_ref(),
1729 x,
1730 y
1731 ));
1732 }
1733}
1734unsafe extern "C" fn gfx_prog_uniform_3f(prog: u32, name: YogStr, x: f32, y: f32, z: f32) {
1735 unsafe {
1736 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_3_f32(
1737 loc.as_ref(),
1738 x,
1739 y,
1740 z
1741 ));
1742 }
1743}
1744unsafe extern "C" fn gfx_prog_uniform_4f(prog: u32, name: YogStr, x: f32, y: f32, z: f32, w: f32) {
1745 unsafe {
1746 with_prog!(prog, |gl, _p, loc(name)| gl.uniform_4_f32(
1747 loc.as_ref(),
1748 x,
1749 y,
1750 z,
1751 w
1752 ));
1753 }
1754}
1755unsafe extern "C" fn gfx_prog_uniform_mat4(prog: u32, name: YogStr, col_major: *const f32) {
1756 unsafe {
1757 with_prog!(prog, |gl, _p, loc(name)| {
1758 let data = std::slice::from_raw_parts(col_major, 16);
1759 gl.uniform_matrix_4_f32_slice(loc.as_ref(), false, data);
1760 });
1761 }
1762}
1763
1764unsafe extern "C" fn gfx_tex_create(w: u32, h: u32, rgba: *const u8, linear: bool) -> u32 {
1767 unsafe {
1768 let Some(g) = GL.get() else { return 0 };
1769 let gl = &g.0;
1770 let tex = match gl.create_texture() {
1771 Ok(t) => t,
1772 Err(_) => return 0,
1773 };
1774 gl.bind_texture(glow::TEXTURE_2D, Some(tex));
1775 let filter = if linear { glow::LINEAR } else { glow::NEAREST };
1776 gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, filter as i32);
1777 gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, filter as i32);
1778 gl.tex_parameter_i32(
1779 glow::TEXTURE_2D,
1780 glow::TEXTURE_WRAP_S,
1781 glow::CLAMP_TO_EDGE as i32,
1782 );
1783 gl.tex_parameter_i32(
1784 glow::TEXTURE_2D,
1785 glow::TEXTURE_WRAP_T,
1786 glow::CLAMP_TO_EDGE as i32,
1787 );
1788 let pixels = std::slice::from_raw_parts(rgba, (w * h * 4) as usize);
1789 unpack_tightly(gl);
1792 gl.tex_image_2d(
1793 glow::TEXTURE_2D,
1794 0,
1795 glow::RGBA8 as i32,
1796 w as i32,
1797 h as i32,
1798 0,
1799 glow::RGBA,
1800 glow::UNSIGNED_BYTE,
1801 glow::PixelUnpackData::Slice(Some(pixels)),
1802 );
1803 gl.bind_texture(glow::TEXTURE_2D, None);
1804 tex.0.get()
1805 }
1806}
1807
1808unsafe fn unpack_tightly(gl: &glow::Context) {
1810 unsafe {
1811 gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
1812 gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
1813 gl.pixel_store_i32(glow::UNPACK_SKIP_ROWS, 0);
1814 gl.pixel_store_i32(glow::UNPACK_SKIP_PIXELS, 0);
1815 gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
1816 }
1817}
1818
1819unsafe extern "C" fn gfx_tex_delete(handle: u32) {
1820 unsafe {
1821 let Some(g) = GL.get() else { return };
1822 let Some(n) = NonZeroU32::new(handle) else {
1823 return;
1824 };
1825 g.0.delete_texture(glow::NativeTexture(n));
1826 }
1827}
1828
1829unsafe extern "C" fn gfx_tex_bind(unit: u32, handle: u32) {
1830 unsafe {
1831 let Some(g) = GL.get() else { return };
1832 let gl = &g.0;
1833 gl.active_texture(glow::TEXTURE0 + unit);
1834 match NonZeroU32::new(handle) {
1835 Some(n) => gl.bind_texture(glow::TEXTURE_2D, Some(glow::NativeTexture(n))),
1836 None => gl.bind_texture(glow::TEXTURE_2D, None),
1837 }
1838 }
1839}
1840
1841unsafe extern "C" fn gfx_tex_from_mc(id: YogStr) -> u32 {
1842 unsafe { host!("dev/yog/NativeDraw", "getMcTextureId", [S(id)]).map_or(0, |id: i32| id as u32) }
1843}
1844
1845unsafe extern "C" fn gfx_draw_arrays(vao: u32, prog: u32, mode: u8, first: u32, count: u32) {
1848 unsafe {
1849 let Some(g) = GL.get() else { return };
1850 let (Some(vn), Some(pn)) = (NonZeroU32::new(vao), NonZeroU32::new(prog)) else {
1851 return;
1852 };
1853 let gl = &g.0;
1854 gl.use_program(Some(glow::NativeProgram(pn)));
1855 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1856 gl.draw_arrays(gl_draw_mode(mode), first as i32, count as i32);
1857 gl.bind_vertex_array(None);
1858 }
1859}
1860
1861unsafe extern "C" fn gfx_draw_elements(
1862 vao: u32,
1863 ebo: u32,
1864 prog: u32,
1865 mode: u8,
1866 count: u32,
1867 u32_idx: bool,
1868) {
1869 unsafe {
1870 let Some(g) = GL.get() else { return };
1871 let (Some(vn), Some(pn)) = (NonZeroU32::new(vao), NonZeroU32::new(prog)) else {
1872 return;
1873 };
1874 let gl = &g.0;
1875 gl.use_program(Some(glow::NativeProgram(pn)));
1876 gl.bind_vertex_array(Some(glow::NativeVertexArray(vn)));
1877 let _ = ebo;
1879 let idx_type = if u32_idx {
1880 glow::UNSIGNED_INT
1881 } else {
1882 glow::UNSIGNED_SHORT
1883 };
1884 gl.draw_elements(gl_draw_mode(mode), count as i32, idx_type, 0);
1885 gl.bind_vertex_array(None);
1886 }
1887}
1888
1889unsafe extern "C" fn gfx_set_blend(enabled: bool, src: u32, dst: u32) {
1892 unsafe {
1893 let Some(g) = GL.get() else { return };
1894 let gl = &g.0;
1895 if enabled {
1896 gl.enable(glow::BLEND);
1897 gl.blend_func(src, dst);
1898 } else {
1899 gl.disable(glow::BLEND);
1900 }
1901 }
1902}
1903
1904unsafe extern "C" fn gfx_set_depth(test: bool, write: bool) {
1905 unsafe {
1906 let Some(g) = GL.get() else { return };
1907 let gl = &g.0;
1908 if test {
1909 gl.enable(glow::DEPTH_TEST);
1910 } else {
1911 gl.disable(glow::DEPTH_TEST);
1912 }
1913 gl.depth_mask(write);
1914 if !test {
1916 gl.disable(glow::CULL_FACE);
1917 }
1918 }
1919}
1920
1921unsafe extern "C" fn gfx_set_scissor(x: i32, y: i32, w: i32, h: i32) {
1922 unsafe {
1923 let Some(g) = GL.get() else { return };
1924 let gl = &g.0;
1925 gl.enable(glow::SCISSOR_TEST);
1926 gl.scissor(x, y, w, h);
1927 }
1928}
1929
1930unsafe extern "C" fn gfx_clear_scissor() {
1931 unsafe {
1932 let Some(g) = GL.get() else { return };
1933 g.0.disable(glow::SCISSOR_TEST);
1934 }
1935}
1936
1937unsafe extern "C" fn gfx_set_viewport(x: i32, y: i32, w: i32, h: i32) {
1938 unsafe {
1939 let Some(g) = GL.get() else { return };
1940 g.0.viewport(x, y, w, h);
1941 }
1942}
1943
1944unsafe extern "C" fn gfx_draw2d_rect(x1: f32, y1: f32, x2: f32, y2: f32, color: u32) {
1947 unsafe {
1948 let _: Option<()> = host!(
1949 "dev/yog/NativeDraw",
1950 "drawRect",
1951 [F(x1), F(y1), F(x2), F(y2), I(color as i32)]
1952 );
1953 }
1954}
1955
1956unsafe extern "C" fn gfx_draw2d_gradient(
1957 x1: f32,
1958 y1: f32,
1959 x2: f32,
1960 y2: f32,
1961 top: u32,
1962 bottom: u32,
1963) {
1964 unsafe {
1965 let _: Option<()> = host!(
1966 "dev/yog/NativeDraw",
1967 "drawGradientRect",
1968 [F(x1), F(y1), F(x2), F(y2), I(top as i32), I(bottom as i32)]
1969 );
1970 }
1971}
1972
1973unsafe extern "C" fn gfx_draw2d_text(text: YogStr, x: f32, y: f32, color: u32, shadow: bool) {
1974 unsafe {
1975 let _: Option<()> = host!(
1976 "dev/yog/NativeDraw",
1977 "drawText",
1978 [S(text), F(x), F(y), I(color as i32), Z(shadow)]
1979 );
1980 }
1981}
1982
1983unsafe extern "C" fn gfx_draw2d_text_font(
1984 text: YogStr,
1985 font: YogStr,
1986 x: f32,
1987 y: f32,
1988 color: u32,
1989 shadow: bool,
1990) {
1991 unsafe {
1992 let _: Option<()> = host!(
1993 "dev/yog/NativeDraw",
1994 "drawTextFont",
1995 [S(text), S(font), F(x), F(y), I(color as i32), Z(shadow)]
1996 );
1997 }
1998}
1999
2000unsafe extern "C" fn gfx_draw2d_text_scaled(
2001 text: YogStr,
2002 font: YogStr,
2003 x: f32,
2004 y: f32,
2005 scale: f32,
2006 color: u32,
2007 shadow: bool,
2008) {
2009 unsafe {
2010 let _: Option<()> = host!(
2011 "dev/yog/NativeDraw",
2012 "drawTextScaled",
2013 [S(text), S(font), F(x), F(y), F(scale), I(color as i32), Z(shadow)]
2014 );
2015 }
2016}
2017
2018unsafe extern "C" fn gfx_draw2d_blocks(blocks: YogStr, x: f32, y: f32, w: f32, h: f32, yaw: f32, pitch: f32) {
2019 if w <= 0.0 || h <= 0.0 {
2020 return;
2021 }
2022 unsafe {
2023 let _: Option<()> = host!(
2024 "dev/yog/NativeDraw",
2025 "drawBlocks",
2026 [S(blocks), F(x), F(y), F(w), F(h), F(yaw), F(pitch)]
2027 );
2028 }
2029}
2030
2031unsafe extern "C" fn gfx_text_width(text: YogStr, font: YogStr) -> f32 {
2032 unsafe { host!("dev/yog/NativeDraw", "textWidth", [S(text), S(font)]).unwrap_or(0.0) }
2033}
2034
2035unsafe extern "C" fn gfx_draw2d_item(id: YogStr, x: f32, y: f32, size: f32) {
2036 unsafe {
2037 let _: Option<()> = host!(
2038 "dev/yog/NativeDraw",
2039 "drawItem",
2040 [S(id), F(x), F(y), F(size)]
2041 );
2042 }
2043}
2044
2045static GFX_FN_TABLE: YogGfxApi = YogGfxApi {
2048 screen_w: 0,
2050 screen_h: 0,
2051 delta_tick: 0.0,
2052 scale_factor: 1.0,
2053 view_proj: [0.0; 16],
2054 camera_pos: [0.0; 3],
2055 player_pos: [0.0; 3],
2056 _pad1: 0.0,
2057 buf_create: gfx_buf_create,
2058 buf_delete: gfx_buf_delete,
2059 buf_data: gfx_buf_data,
2060 buf_subdata: gfx_buf_subdata,
2061 vao_create: gfx_vao_create,
2062 vao_delete: gfx_vao_delete,
2063 vao_attrib: gfx_vao_attrib,
2064 vao_set_ebo: gfx_vao_set_ebo,
2065 prog_create: gfx_prog_create,
2066 prog_delete: gfx_prog_delete,
2067 prog_uniform_1i: gfx_prog_uniform_1i,
2068 prog_uniform_1f: gfx_prog_uniform_1f,
2069 prog_uniform_2f: gfx_prog_uniform_2f,
2070 prog_uniform_3f: gfx_prog_uniform_3f,
2071 prog_uniform_4f: gfx_prog_uniform_4f,
2072 prog_uniform_mat4: gfx_prog_uniform_mat4,
2073 tex_create: gfx_tex_create,
2074 tex_delete: gfx_tex_delete,
2075 tex_bind: gfx_tex_bind,
2076 tex_from_mc: gfx_tex_from_mc,
2077 draw_arrays: gfx_draw_arrays,
2078 draw_elements: gfx_draw_elements,
2079 set_blend: gfx_set_blend,
2080 set_depth: gfx_set_depth,
2081 set_scissor: gfx_set_scissor,
2082 clear_scissor: gfx_clear_scissor,
2083 set_viewport: gfx_set_viewport,
2084 draw2d_rect: gfx_draw2d_rect,
2085 draw2d_gradient: gfx_draw2d_gradient,
2086 draw2d_text: gfx_draw2d_text,
2087 draw2d_item: gfx_draw2d_item,
2088 draw2d_text_font: gfx_draw2d_text_font,
2089 text_width: gfx_text_width,
2090 draw2d_image: images::gfx_draw2d_image,
2091 image_size: images::gfx_image_size,
2092 draw2d_svg: svg::gfx_draw2d_svg,
2093 draw2d_entity: svg::gfx_draw2d_entity,
2094 draw2d_text_scaled: gfx_draw2d_text_scaled,
2095 draw2d_blocks: gfx_draw2d_blocks,
2096};
2097
2098macro_rules! api_event {
2105 ($name:ident, $field:ident, $fn_ty:ty) => {
2106 unsafe extern "C" fn $name(ctx: *mut c_void, ud: *mut c_void, h: $fn_ty) {
2107 unsafe {
2108 let mut handlers = handlers_mut(ctx);
2109 handlers.$field.push((current_registration(), ud, h));
2110 }
2111 }
2112 };
2113}
2114
2115api_event!(api_on_block_break, block_break, YogBlockBreakFn);
2116api_event!(api_on_chat, chat, YogChatFn);
2117api_event!(api_on_player_join, player_join, YogPlayerFn);
2118api_event!(api_on_player_leave, player_leave, YogPlayerFn);
2119api_event!(api_on_use_item, use_item, YogUseItemFn);
2120api_event!(api_on_use_block, use_block, YogUseBlockFn);
2121api_event!(api_on_attack_entity, attack_entity, YogAttackEntityFn);
2122api_event!(api_on_entity_damage, entity_damage, YogEntityDamageFn);
2123api_event!(api_on_entity_death, entity_death, YogEntityDeathFn);
2124api_event!(api_on_entity_spawn, entity_spawn, YogEntitySpawnFn);
2125api_event!(
2126 api_on_player_place_block,
2127 player_place_block,
2128 YogPlaceBlockFn
2129);
2130api_event!(api_on_player_death, player_death, YogPlayerDeathFn);
2131api_event!(api_on_player_respawn, player_respawn, YogPlayerRespawnFn);
2132api_event!(api_on_advancement, advancement, YogAdvancementFn);
2133api_event!(api_on_entity_interact, entity_interact, YogEntityInteractFn);
2134api_event!(api_on_item_craft, item_craft, YogCraftFn);
2135api_event!(api_on_machine_craft, machine_craft, YogMachineCraftFn);
2136api_event!(api_on_explosion, explosion, YogExplosionFn);
2137api_event!(api_on_item_pickup, item_pickup, YogItemPickupFn);
2138api_event!(api_on_item_insert, item_insert, YogItemInsertFn);
2139api_event!(api_on_player_move, player_move, YogPlayerMoveFn);
2140api_event!(api_on_container_open, container_open, YogContainerOpenFn);
2141api_event!(api_on_container_close, container_close, YogContainerCloseFn);
2142api_event!(api_on_projectile_hit, projectile_hit, YogProjectileHitFn);
2143api_event!(api_on_client_tick, client_tick, YogClientFn);
2144api_event!(api_on_hud_render, hud_render, YogHudRenderFn);
2145api_event!(api_on_world_render, world_render, YogWorldRenderFn);
2146api_event!(api_on_key_press, key_press, YogKeyPressFn);
2147api_event!(api_on_screen_open, screen_open, YogScreenFn);
2148api_event!(api_on_screen_close, screen_close, YogScreenFn);
2149api_event!(api_on_server_tick, server_tick, YogServerFn);
2150api_event!(api_on_server_started, server_started, YogServerFn);
2151api_event!(api_on_server_stopping, server_stopping, YogServerFn);
2152
2153unsafe extern "C" fn api_on_packet(
2154 ctx: *mut c_void,
2155 channel: YogStr,
2156 ud: *mut c_void,
2157 h: YogPacketFn,
2158) {
2159 unsafe {
2160 let mut handlers = handlers_mut(ctx);
2161 handlers
2162 .packets
2163 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2164 }
2165}
2166
2167unsafe extern "C" fn api_on_client_packet(
2168 ctx: *mut c_void,
2169 channel: YogStr,
2170 ud: *mut c_void,
2171 h: YogPacketFn,
2172) {
2173 unsafe {
2174 let mut handlers = handlers_mut(ctx);
2175 handlers
2176 .client_packets
2177 .insert(channel.as_str().to_owned(), (current_registration(), ud, h));
2178 }
2179}
2180
2181unsafe extern "C" fn api_register_command(
2182 ctx: *mut c_void,
2183 name: YogStr,
2184 ud: *mut c_void,
2185 h: YogCommandFn,
2186) {
2187 unsafe {
2188 let mut handlers = handlers_mut(ctx);
2189 handlers
2190 .commands
2191 .entry(name.as_str().to_owned())
2192 .or_default()
2193 .push((current_registration(), None, ud, h));
2194 }
2195}
2196
2197unsafe extern "C" fn api_register_typed_command(
2198 ctx: *mut c_void,
2199 name: YogStr,
2200 schema: YogStr,
2201 ud: *mut c_void,
2202 h: YogCommandFn,
2203) {
2204 unsafe {
2205 let mut handlers = handlers_mut(ctx);
2206 let n = name.as_str().to_owned();
2207 handlers.commands.entry(n).or_default().push((
2208 current_registration(),
2209 Some(schema.as_str().to_owned()),
2210 ud,
2211 h,
2212 ));
2213 }
2214}
2215
2216unsafe fn handlers_mut<'a>(ctx: *mut c_void) -> std::sync::RwLockWriteGuard<'a, RuntimeHandlers> {
2224 unsafe { (*(ctx as *const RwLock<RuntimeHandlers>)).write().unwrap_or_else(|e| e.into_inner()) }
2225}
2226
2227unsafe extern "C" fn api_register_content(ctx: *mut c_void, kind: YogStr, id: YogStr, data: *const u8, len: usize) {
2230 unsafe {
2231 let kind = kind.as_str();
2232 let id = id.as_str().to_owned();
2233 let bytes = if data.is_null() || len == 0 { &[][..] } else { std::slice::from_raw_parts(data, len) };
2234 let arena = yog_data::Bump::new();
2236 let value = match yog_data::binary::decode_ref(bytes, &arena) {
2237 Ok(v) => v,
2238 Err(e) => {
2239 yog_logging::warn!("{kind} {id}: not Yog Data: {e}");
2240 return;
2241 }
2242 };
2243 register_content(&mut handlers_mut(ctx), kind, id, &value);
2244 }
2245}
2246
2247fn register_content(h: &mut RuntimeHandlers, kind: &str, id: String, value: &yog_data::ValueRef) {
2249 *h.content_blob.get_mut().unwrap_or_else(|e| e.into_inner()) = None;
2250 fn read<T: yog_data::Schema>(kind: &str, id: &str, v: &yog_data::ValueRef) -> Option<T> {
2251 match yog_data::read_ref_value::<T>(v) {
2252 Ok(value) => Some(value),
2253 Err(problems) => {
2254 let list: Vec<String> = problems.iter().map(|p| format!("{}: {}", p.path, p.message)).collect();
2255 yog_logging::warn!("{kind} {id}: {}", list.join("; "));
2256 None
2257 }
2258 }
2259 }
2260 let none_if_empty = |s: &mut Option<String>| {
2261 if s.as_deref() == Some("") {
2262 *s = None;
2263 }
2264 };
2265 match kind {
2266 yog_ids::kind::ITEM => {
2267 if let Some(mut d) = read::<ItemDef>(kind, &id, value) {
2268 none_if_empty(&mut d.name);
2269 none_if_empty(&mut d.tooltip);
2270 h.items.push(d);
2271 }
2272 }
2273 yog_ids::kind::BLOCK => {
2274 if let Some(mut d) = read::<BlockDef>(kind, &id, value) {
2275 none_if_empty(&mut d.name);
2276 none_if_empty(&mut d.sound);
2277 none_if_empty(&mut d.inventory_id);
2278 none_if_empty(&mut d.base);
2279 none_if_empty(&mut d.set);
2280 h.blocks.push(d);
2281 }
2282 }
2283 yog_ids::kind::ENTITY => {
2284 if read::<yog_registry::EntityDef>(kind, &id, value).is_some() {
2285 h.entities.push(value.to_value());
2286 }
2287 }
2288 yog_ids::kind::ATTRIBUTE => {
2289 if read::<yog_registry::AttributeDef>(kind, &id, value).is_some() {
2290 h.attributes.push(value.to_value());
2291 }
2292 }
2293 yog_ids::kind::EFFECT => {
2294 if read::<yog_registry::EffectDef>(kind, &id, value).is_some() {
2295 h.effects.push(value.to_value());
2296 }
2297 }
2298 yog_ids::kind::POTION => {
2299 if read::<yog_registry::PotionDef>(kind, &id, value).is_some() {
2300 h.potions.push(value.to_value());
2301 }
2302 }
2303 yog_ids::kind::BIOME_FEATURE => {
2304 if let Some(f) = read::<yog_registry::BiomeFeature>(kind, &id, value) {
2305 h.biome_features.push(f);
2306 }
2307 }
2308 yog_ids::kind::TOOL_MATERIAL => {
2309 if let Some(m) = read::<yog_registry::ToolMaterial>(kind, &id, value) {
2310 h.tool_materials.insert(m.id.clone(), m);
2311 }
2312 }
2313 yog_ids::kind::ARMOR_MATERIAL => {
2314 if let Some(m) = read::<yog_registry::ArmorMaterial>(kind, &id, value) {
2315 h.armor_materials.insert(m.id.clone(), m);
2316 }
2317 }
2318 yog_ids::kind::SOUND_GROUP => {
2319 if let Some(g) = read::<yog_registry::SoundGroup>(kind, &id, value) {
2320 h.sound_groups.insert(g.id.clone(), g);
2321 }
2322 }
2323 yog_ids::kind::INVENTORY => {
2324 if let Some(d) = read::<yog_inventory::InventoryDef>(kind, &id, value) {
2325 h.inventories.push(d);
2326 }
2327 }
2328 yog_ids::kind::STARTUP_GRANT => {
2329 if let Some(mut g) = read::<yog_registry::StartupGrant>(kind, &id, value) {
2330 none_if_empty(&mut g.book);
2331 none_if_empty(&mut g.command);
2332 h.startup_grants.push(g);
2333 }
2334 }
2335 yog_ids::kind::RECIPE => {
2336 let (ns, name) = yog_ids::split(&id);
2337 let recipe = value.to_value();
2338 for r in h.book_renderers.lock().unwrap().values_mut() {
2340 r.add_recipe(format!("{ns}:{name}"), &recipe);
2341 }
2342 h.recipes.push((ns.to_string(), name.to_string(), recipe));
2343 }
2344 yog_ids::kind::BOOK => {
2345 if let Some(mut book) = read::<yog_formats::book::Book>(kind, &id, value) {
2347 book.entries.retain(|e| e.requires.iter().all(|m| mod_loaded(m)));
2349 h.books.insert(id.clone());
2350 let mut renderer = yog_book::BookRenderer::new(book);
2351 renderer.set_translator(translate);
2353 for (ns, name, recipe) in &h.recipes {
2355 renderer.add_recipe(format!("{ns}:{name}"), recipe);
2356 }
2357 h.book_renderers.get_mut().unwrap_or_else(|e| e.into_inner()).insert(id, renderer);
2358 }
2359 }
2360 yog_ids::kind::DIMENSION => {
2361 yog_logging::info!("registered dimension type: {}", id);
2362 let mut def = value.to_value();
2363 if let Some(t) = value.get("terrain") {
2365 let Some(terrain) = read::<yog_worldgen::Terrain>(kind, &id, t) else { return };
2366 if let yog_data::Value::Map(m) = &mut def {
2367 m.remove("terrain");
2368 m.insert("generator_type", yog_data::Value::from(id.as_str()));
2369 }
2370 h.terrains.insert(id.clone(), Arc::new(yog_worldgen::Generator::new(terrain)));
2371 }
2372 h.dimensions.insert(id, def);
2373 }
2374 other => yog_logging::warn!("unknown kind of content `{other}` ({id}) — this runtime doesn't know it"),
2375 }
2376}
2377
2378
2379
2380
2381unsafe extern "C" fn api_schedule_once(
2382 ctx: *mut c_void,
2383 delay_ticks: u64,
2384 ud: *mut c_void,
2385 h: YogScheduledFn,
2386) {
2387 unsafe {
2388 let handlers = handlers_mut(ctx);
2389 handlers
2390 .scheduler
2391 .lock()
2392 .expect("scheduler poisoned")
2393 .once_tasks
2394 .push(OnceTask {
2395 module: current_registration(),
2396 delay_remaining: delay_ticks,
2397 ud,
2398 f: h,
2399 });
2400 }
2401}
2402
2403unsafe extern "C" fn api_schedule_repeating(
2404 ctx: *mut c_void,
2405 period_ticks: u64,
2406 ud: *mut c_void,
2407 h: YogScheduledFn,
2408) {
2409 unsafe {
2410 let handlers = handlers_mut(ctx);
2411 handlers
2412 .scheduler
2413 .lock()
2414 .expect("scheduler poisoned")
2415 .repeating_tasks
2416 .push(RepeatingTask {
2417 module: current_registration(),
2418 period: period_ticks,
2419 ticks_left: period_ticks.max(1),
2420 ud,
2421 f: h,
2422 });
2423 }
2424}
2425
2426
2427
2428
2429unsafe extern "C" fn api_register_chunk_generator(
2430 ctx: *mut c_void,
2431 id: YogStr,
2432 ud: *mut c_void,
2433 h: yog_abi::YogChunkGeneratorFn,
2434) {
2435 unsafe {
2436 let mut handlers = handlers_mut(ctx);
2437 let id = id.as_str().to_owned();
2438 yog_logging::info!("registered chunk generator for dimension: {}", id);
2439 handlers
2440 .chunk_generators
2441 .insert(id, (current_registration(), ud, h));
2442 }
2443}
2444
2445unsafe extern "C" fn api_register_ui(
2446 ctx: *mut c_void,
2447 ui_id: YogStr,
2448 _layout_json: YogStr,
2449 ud: *mut c_void,
2450 h: yog_abi::YogUIEventFn,
2451) {
2452 unsafe {
2453 let mut handlers = handlers_mut(ctx);
2454 let id = ui_id.as_str().to_owned();
2455 handlers
2456 .ui_handlers
2457 .insert(id.clone(), (current_registration(), ud, h));
2458 yog_logging::info!("registered UI handler: {}", id);
2459 }
2460}
2461
2462unsafe extern "C" fn api_on_ui_render(
2463 ctx: *mut c_void,
2464 ui_id: YogStr,
2465 ud: *mut c_void,
2466 h: YogHudRenderFn,
2467) {
2468 unsafe {
2469 let mut handlers = handlers_mut(ctx);
2470 let id = ui_id.as_str().to_owned();
2471 handlers
2472 .ui_render_handlers
2473 .entry(id)
2474 .or_default()
2475 .push((current_registration(), ud, h));
2476 }
2477}
2478
2479unsafe extern "C" fn api_on_book_page(
2480 ctx: *mut c_void,
2481 drawer: YogStr,
2482 ud: *mut c_void,
2483 f: yog_abi::YogPageDrawFn,
2484) {
2485 unsafe {
2486 let mut handlers = handlers_mut(ctx);
2487 handlers
2488 .page_drawers
2489 .insert(drawer.as_str().to_owned(), (current_registration(), ud, f));
2490 }
2491}
2492
2493unsafe extern "C" fn api_on_item_tooltip(
2494 ctx: *mut c_void,
2495 item: YogStr,
2496 ud: *mut c_void,
2497 size: yog_abi::YogTooltipSizeFn,
2498 draw: yog_abi::YogTooltipDrawFn,
2499) {
2500 unsafe {
2501 let mut handlers = handlers_mut(ctx);
2502 handlers
2503 .item_tooltips
2504 .entry(item.as_str().to_owned())
2505 .or_default()
2506 .push((current_registration(), ud, size, draw));
2507 }
2508}
2509
2510unsafe extern "C" fn api_on_entity_tick(ctx: *mut c_void, entity_type: YogStr, ud: *mut c_void, f: yog_abi::YogEntityTickFn) {
2511 unsafe {
2512 handlers_mut(ctx).entity_ticks.entry(entity_type.as_str().to_owned()).or_default().push((current_registration(), ud, f));
2513 }
2514}
2515
2516#[unsafe(no_mangle)]
2518pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityTick<'l>(
2519 mut env: JNIEnv<'l>,
2520 _class: JClass<'l>,
2521 entity_type: JString<'l>,
2522 uuid: JString<'l>,
2523) {
2524 let (t, u) = (jstr!(env, entity_type), jstr!(env, uuid));
2525 let entries = handlers().entity_ticks.get(&t).cloned().unwrap_or_default();
2526 let srv = srv_ptr();
2527 guard("on_entity_tick", || each(&entries, |ud, f| unsafe { f(ud, srv, YogStr::from_str(&u)) }));
2528}
2529
2530unsafe extern "C" fn api_on_item_text(ctx: *mut c_void, item: YogStr, ud: *mut c_void, f: yog_abi::YogItemTextFn) {
2531 unsafe {
2532 handlers_mut(ctx).item_texts.entry(item.as_str().to_owned()).or_default().push((current_registration(), ud, f));
2533 }
2534}
2535
2536unsafe extern "C" fn copy_text(sink: *mut c_void, text: YogStr) {
2537 unsafe { (*(sink as *mut String)).push_str(text.as_str()) }
2539}
2540
2541fn item_text(item: &str, snbt: &str) -> Option<String> {
2544 let entries = handlers().item_texts.get(item).cloned().unwrap_or_default();
2545 for (module, ud, f) in entries {
2546 let Some(_call) = module.enter() else { continue };
2547 let mut out = String::new();
2548 let mut given = false;
2549 guard("item_text", || unsafe {
2550 given = f(ud, YogStr::from_str(item), YogStr::from_str(snbt), &mut out as *mut String as *mut c_void, copy_text);
2551 });
2552 if given {
2553 return Some(out);
2554 }
2555 }
2556 None
2557}
2558
2559#[unsafe(no_mangle)]
2560pub extern "system" fn Java_dev_yog_NativeBridge_nativeItemText<'l>(
2561 mut env: JNIEnv<'l>,
2562 _class: JClass<'l>,
2563 item: JString<'l>,
2564 snbt: JString<'l>,
2565) -> jstring {
2566 let item: String = env.get_string(&item).map(String::from).unwrap_or_default();
2567 let snbt: String = env.get_string(&snbt).map(String::from).unwrap_or_default();
2568 match item_text(&item, &snbt) {
2569 Some(t) => env.new_string(t).map(JString::into_raw).unwrap_or(std::ptr::null_mut()),
2570 None => std::ptr::null_mut(),
2571 }
2572}
2573
2574fn machine_event<'a>(
2578 m: &'a yog_inventory::Machine,
2579 def: &'a str,
2580 found: &'a machine::Found,
2581 dim: &'a str,
2582 pos: YogBlockPos,
2583 player: &'a str,
2584) -> YogMachineCraftEvent {
2585 YogMachineCraftEvent {
2586 machine: YogStr::from_str(def),
2587 kind: YogStr::from_str(&m.kind),
2588 recipe: YogStr::from_str(&found.recipe),
2589 result_item: YogStr::from_str(&found.result),
2590 result_count: found.count,
2591 values: YogStr::from_str(&found.values),
2592 pos,
2593 dimension: YogStr::from_str(dim),
2594 player: YogStr::from_str(player),
2595 }
2596}
2597
2598fn machine_recipe(def: &str, dim: &str, pos: YogBlockPos, items: &[String], recipe: Option<&str>) -> Option<machine::Found> {
2601 let h = handlers();
2602 let m = h.inventories.iter().find(|d| d.id == def)?.machine.as_ref()?;
2603 let has_tag = |item: &str, tag: &str| unsafe { srv_has_item_tag(std::ptr::null_mut(), YogStr::from_str(item), YogStr::from_str(tag)) };
2604 let recipes = h.recipes.iter().map(|(ns, name, r)| (format!("{ns}:{name}"), r)).filter(|(id, _)| recipe.is_none_or(|r| r == id));
2605 let srv = srv_ptr();
2606 machine::find(m, recipes, items, &has_tag).find(|found| {
2607 let ev = machine_event(m, def, found, dim, pos, "");
2608 allowed(&h.machine_craft, |ud, f| unsafe { f(ud, srv, &ev, 0) })
2609 })
2610}
2611
2612#[unsafe(no_mangle)]
2615pub extern "system" fn Java_dev_yog_NativeBridge_nativeMachineResult<'l>(
2616 mut env: JNIEnv<'l>,
2617 _class: JClass<'l>,
2618 def: JString<'l>,
2619 dimension: JString<'l>,
2620 x: jint,
2621 y: jint,
2622 z: jint,
2623 items: JString<'l>,
2624) -> jstring {
2625 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()));
2626 let items: Vec<String> = items.split('\n').map(str::to_string).collect();
2627 let mut found = None;
2628 guard("machine_result", || found = machine_recipe(&def, &d, YogBlockPos { x, y, z }, &items, None));
2629 match found {
2630 Some(f) => env.new_string(format!("{}\n{}\n{}", f.recipe, f.result, f.count)).map(JString::into_raw).unwrap_or(std::ptr::null_mut()),
2631 None => std::ptr::null_mut(),
2632 }
2633}
2634
2635#[unsafe(no_mangle)]
2638#[allow(clippy::too_many_arguments)]
2639pub extern "system" fn Java_dev_yog_NativeBridge_nativeMachineCraft<'l>(
2640 mut env: JNIEnv<'l>,
2641 _class: JClass<'l>,
2642 def: JString<'l>,
2643 recipe: JString<'l>,
2644 dimension: JString<'l>,
2645 x: jint,
2646 y: jint,
2647 z: jint,
2648 items: JString<'l>,
2649 player: JString<'l>,
2650) -> jni::sys::jboolean {
2651 let (def, recipe, d, items, player) =
2652 (jstr!(env, def, 0), jstr!(env, recipe, 0), jstr!(env, dimension, 0), jstr!(env, items, 0), jstr!(env, player, 0));
2653 let items: Vec<String> = items.split('\n').map(str::to_string).collect();
2654 let pos = YogBlockPos { x, y, z };
2655 let mut made = false;
2656 guard("machine_craft", || {
2657 let Some(found) = machine_recipe(&def, &d, pos, &items, Some(&recipe)) else { return };
2658 let h = handlers();
2659 let Some(m) = h.inventories.iter().find(|i| i.id == def).and_then(|i| i.machine.as_ref()) else { return };
2660 let ev = machine_event(m, &def, &found, &d, pos, &player);
2661 let srv = srv_ptr();
2662 each(&h.machine_craft, |ud, f| unsafe { f(ud, srv, &ev, 1) });
2663 made = true;
2664 });
2665 made as jni::sys::jboolean
2666}
2667
2668static YOG_MOD_IDS: RwLock<std::collections::BTreeSet<String>> = RwLock::new(std::collections::BTreeSet::new());
2670
2671fn mod_loaded(id: &str) -> bool {
2674 YOG_MOD_IDS.read().unwrap_or_else(|e| e.into_inner()).contains(id)
2675 || HOT_RELOADER.get().is_some_and(|r| r.all_active().iter().any(|(m, _)| m == id))
2676 || unsafe { bridge!("isModLoaded", [JArg::S(YogStr::from_str(id))]) }.unwrap_or(false)
2678}
2679
2680unsafe extern "C" fn api_is_mod_loaded(_ctx: *mut c_void, id: YogStr) -> bool {
2681 unsafe { mod_loaded(id.as_str()) }
2682}
2683
2684unsafe extern "C" fn api_java_call(_ctx: *mut c_void, expr: YogStr, args: YogStr) -> YogOwnedStr {
2685 unsafe {
2686 let args = match yog_data::text::parse_value(args.as_str()) {
2687 Ok(v) => v,
2688 Err(e) => {
2689 yog_logging::warn!("java_call {}: its arguments aren't Yog Data: {}", expr.as_str(), e.message);
2690 return YogOwnedStr::NONE;
2691 }
2692 };
2693 let bytes = yog_data::binary::encode(&args);
2694 let out: Option<Vec<u8>> = host!("dev/yog/data/YogJava", "call", [JArg::S(expr), JArg::Bytes(&bytes)]);
2695 match out.map(|b| yog_data::binary::decode(&b)) {
2696 Some(Ok(v)) => YogOwnedStr::from_string(yog_data::text::to_string(&v)),
2697 _ => YogOwnedStr::NONE,
2698 }
2699 }
2700}
2701
2702fn tsv_clean(v: &str) -> String {
2704 v.replace(['\t', '\n', '\r'], " ")
2705}
2706
2707unsafe extern "C" fn api_ui_open(_ctx: *mut c_void, ui_id: YogStr, modal: bool, pause: bool) {
2708 unsafe {
2709 if ui_id.as_str().is_empty() {
2710 return;
2711 }
2712 let _: Option<()> = bridge!("openUI", [S(ui_id), Z(modal), Z(pause)]);
2714 }
2715}
2716
2717unsafe extern "C" fn api_ui_close(_ctx: *mut c_void, ui_id: YogStr) {
2718 unsafe {
2719 let _: Option<()> = bridge!("closeUI", [S(ui_id)]);
2722 }
2723}
2724
2725unsafe extern "C" fn api_ui_consume_event(_ctx: *mut c_void) {
2726 ui_jni::consume_event();
2727}
2728
2729unsafe extern "C" fn api_translate(_ctx: *mut c_void, key: YogStr) -> YogOwnedStr {
2730 unsafe { translate(key.as_str()).map_or(YogOwnedStr::NONE, YogOwnedStr::from_string) }
2731}
2732
2733fn translate(key: &str) -> Option<String> {
2736 if key.is_empty() {
2737 return None;
2738 }
2739 unsafe { bridge!("translate", [S(YogStr::from_str(key))]) }
2740}
2741
2742unsafe extern "C" fn api_mods_list(_ctx: *mut c_void) -> YogOwnedStr {
2743 unsafe {
2744 let mut lines: Vec<String> = Vec::new();
2745 let infos = MOD_INFOS.lock().expect("mod infos lock poisoned").clone();
2746 for m in &infos {
2747 let localized = |text: &str, key: &str| {
2749 translate(key).map_or_else(|| text.to_string(), |t| tsv_clean(&t))
2750 };
2751 lines.push(format!(
2752 "yog\t{}\t{}\t{}\t{}\t{}",
2753 m[0],
2754 localized(&m[1], &m[5]),
2755 m[2],
2756 m[3],
2757 localized(&m[4], &m[6])
2758 ));
2759 }
2760 let platform: Option<String> = bridge!("listPlatformMods", []);
2762 for line in platform.iter().flat_map(|s| s.lines()) {
2763 if !line.trim().is_empty() {
2764 lines.push(format!("platform\t{line}"));
2765 }
2766 }
2767 YogOwnedStr::from_string(lines.join("\n"))
2768 }
2769}
2770
2771unsafe extern "C" fn api_register_menu_entry(ctx: *mut c_void, label: YogStr, ui_id: YogStr) {
2772 unsafe {
2773 let mut handlers = handlers_mut(ctx);
2774 let l = label.as_str().to_owned();
2775 let u = ui_id.as_str().to_owned();
2776 handlers.menu_entries.push((l, u));
2777 }
2778}
2779
2780fn vec_args(v: YogVec3) -> [JArg<'static>; 3] {
2786 [JArg::D(v.x), JArg::D(v.y), JArg::D(v.z)]
2787}
2788
2789fn pos_args(p: YogBlockPos) -> [JArg<'static>; 3] {
2790 [JArg::I(p.x), JArg::I(p.y), JArg::I(p.z)]
2791}
2792
2793unsafe extern "C" fn srv_entity_info(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
2794 unsafe { owned(bridge!("entityInfo", [JArg::S(uuid)])) }
2795}
2796
2797unsafe extern "C" fn srv_entities_in_box(
2798 _ctx: *mut c_void,
2799 dim: YogStr,
2800 min: YogVec3,
2801 max: YogVec3,
2802) -> YogOwnedStr {
2803 unsafe {
2804 let [a, b, c] = vec_args(min);
2805 let [d, e, f] = vec_args(max);
2806 owned(bridge!("entitiesInBox", [JArg::S(dim), a, b, c, d, e, f]))
2807 }
2808}
2809
2810unsafe extern "C" fn srv_raycast_block(
2811 _ctx: *mut c_void,
2812 dim: YogStr,
2813 from: YogVec3,
2814 to: YogVec3,
2815) -> YogOwnedStr {
2816 unsafe {
2817 let [a, b, c] = vec_args(from);
2818 let [d, e, f] = vec_args(to);
2819 owned(bridge!("raycastBlock", [JArg::S(dim), a, b, c, d, e, f]))
2820 }
2821}
2822
2823unsafe extern "C" fn srv_raycast_entity(
2824 _ctx: *mut c_void,
2825 dim: YogStr,
2826 from: YogVec3,
2827 to: YogVec3,
2828 exclude: YogStr,
2829) -> YogOwnedStr {
2830 unsafe {
2831 let [a, b, c] = vec_args(from);
2832 let [d, e, f] = vec_args(to);
2833 owned(bridge!(
2834 "raycastEntity",
2835 [JArg::S(dim), a, b, c, d, e, f, JArg::S(exclude)]
2836 ))
2837 }
2838}
2839
2840unsafe extern "C" fn srv_block_info(
2841 _ctx: *mut c_void,
2842 dim: YogStr,
2843 pos: YogBlockPos,
2844) -> YogOwnedStr {
2845 unsafe {
2846 let [x, y, z] = pos_args(pos);
2847 owned(bridge!("blockInfo", [JArg::S(dim), x, y, z]))
2848 }
2849}
2850
2851unsafe extern "C" fn srv_break_block(
2852 _ctx: *mut c_void,
2853 dim: YogStr,
2854 pos: YogBlockPos,
2855 drop: bool,
2856 breaker: YogStr,
2857) -> bool {
2858 unsafe {
2859 let [x, y, z] = pos_args(pos);
2860 bridge!(
2861 "breakBlock",
2862 [JArg::S(dim), x, y, z, JArg::Z(drop), JArg::S(breaker)]
2863 )
2864 .unwrap_or(false)
2865 }
2866}
2867
2868unsafe extern "C" fn srv_place_block_from_slot(
2869 _ctx: *mut c_void,
2870 player: YogStr,
2871 slot: u32,
2872 dim: YogStr,
2873 pos: YogBlockPos,
2874) -> bool {
2875 unsafe {
2876 let [x, y, z] = pos_args(pos);
2877 bridge!(
2878 "placeBlockFromSlot",
2879 [JArg::S(player), JArg::I(slot as i32), JArg::S(dim), x, y, z]
2880 )
2881 .unwrap_or(false)
2882 }
2883}
2884
2885unsafe extern "C" fn srv_bonemeal(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> bool {
2886 unsafe {
2887 let [x, y, z] = pos_args(pos);
2888 bridge!("bonemeal", [JArg::S(dim), x, y, z]).unwrap_or(false)
2889 }
2890}
2891
2892unsafe extern "C" fn srv_explode(
2893 _ctx: *mut c_void,
2894 dim: YogStr,
2895 pos: YogVec3,
2896 power: f32,
2897 fire: bool,
2898 source: YogStr,
2899) -> bool {
2900 unsafe {
2901 let [x, y, z] = vec_args(pos);
2902 bridge!(
2903 "explode",
2904 [
2905 JArg::S(dim),
2906 x,
2907 y,
2908 z,
2909 JArg::F(power),
2910 JArg::Z(fire),
2911 JArg::S(source),
2912 ]
2913 )
2914 .unwrap_or(false)
2915 }
2916}
2917
2918unsafe extern "C" fn srv_tellraw(_ctx: *mut c_void, player: YogStr, json: YogStr) -> bool {
2919 unsafe { bridge!("tellraw", [JArg::S(player), JArg::S(json)]).unwrap_or(false) }
2920}
2921
2922unsafe extern "C" fn srv_entity_damage(
2923 _ctx: *mut c_void,
2924 uuid: YogStr,
2925 amount: f32,
2926 damage_type: YogStr,
2927 attacker: YogStr,
2928) -> bool {
2929 unsafe {
2930 bridge!(
2931 "entityDamage",
2932 [
2933 JArg::S(uuid),
2934 JArg::F(amount),
2935 JArg::S(damage_type),
2936 JArg::S(attacker),
2937 ]
2938 )
2939 .unwrap_or(false)
2940 }
2941}
2942
2943unsafe extern "C" fn srv_entity_set_fire_ticks(
2944 _ctx: *mut c_void,
2945 uuid: YogStr,
2946 ticks: i32,
2947) -> bool {
2948 unsafe { bridge!("entitySetFireTicks", [JArg::S(uuid), JArg::I(ticks)]).unwrap_or(false) }
2949}
2950
2951unsafe extern "C" fn srv_player_add_xp(_ctx: *mut c_void, player: YogStr, amount: i32) -> bool {
2952 unsafe { bridge!("playerAddXp", [JArg::S(player), JArg::I(amount)]).unwrap_or(false) }
2953}
2954
2955unsafe extern "C" fn srv_player_set_may_fly(
2956 _ctx: *mut c_void,
2957 player: YogStr,
2958 may_fly: bool,
2959) -> bool {
2960 unsafe { bridge!("playerSetMayFly", [JArg::S(player), JArg::Z(may_fly)]).unwrap_or(false) }
2961}
2962
2963unsafe extern "C" fn srv_player_info(_ctx: *mut c_void, player: YogStr) -> YogOwnedStr {
2964 unsafe { owned(bridge!("playerInfo", [JArg::S(player)])) }
2965}
2966
2967unsafe extern "C" fn srv_spawn_item(
2968 _ctx: *mut c_void,
2969 dim: YogStr,
2970 pos: YogVec3,
2971 vel: YogVec3,
2972 item_id: YogStr,
2973 count: u32,
2974 snbt: YogStr,
2975) -> YogOwnedStr {
2976 unsafe {
2977 let [a, b, c] = vec_args(pos);
2978 let [d, e, f] = vec_args(vel);
2979 owned(bridge!(
2980 "spawnItem",
2981 [
2982 JArg::S(dim),
2983 a,
2984 b,
2985 c,
2986 d,
2987 e,
2988 f,
2989 JArg::S(item_id),
2990 JArg::I(count as i32),
2991 JArg::S(snbt),
2992 ]
2993 ))
2994 }
2995}
2996
2997unsafe extern "C" fn srv_item_entity_stack(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
2998 unsafe { owned(bridge!("itemEntityStack", [JArg::S(uuid)])) }
2999}
3000
3001unsafe extern "C" fn srv_actionbar_json(_ctx: *mut c_void, player: YogStr, json: YogStr) -> bool {
3002 unsafe { bridge!("actionbarJson", [JArg::S(player), JArg::S(json)]).unwrap_or(false) }
3003}
3004
3005unsafe extern "C" fn srv_use_item_on_block(
3006 _ctx: *mut c_void,
3007 dim: YogStr,
3008 pos: YogBlockPos,
3009 face: u8,
3010 item_id: YogStr,
3011 player: YogStr,
3012) -> bool {
3013 unsafe {
3014 let [x, y, z] = pos_args(pos);
3015 bridge!(
3016 "useItemOnBlock",
3017 [
3018 JArg::S(dim),
3019 x,
3020 y,
3021 z,
3022 JArg::I(face as i32),
3023 JArg::S(item_id),
3024 JArg::S(player),
3025 ]
3026 )
3027 .unwrap_or(false)
3028 }
3029}
3030
3031unsafe extern "C" fn srv_pour_fluid(
3032 _ctx: *mut c_void,
3033 dim: YogStr,
3034 pos: YogBlockPos,
3035 bucket_item_id: YogStr,
3036) -> bool {
3037 unsafe {
3038 let [x, y, z] = pos_args(pos);
3039 bridge!(
3040 "pourFluid",
3041 [JArg::S(dim), x, y, z, JArg::S(bucket_item_id)]
3042 )
3043 .unwrap_or(false)
3044 }
3045}
3046
3047unsafe extern "C" fn srv_has_entity_tag(
3048 _ctx: *mut c_void,
3049 entity_type: YogStr,
3050 tag: YogStr,
3051) -> bool {
3052 unsafe { bridge!("hasEntityTag", [JArg::S(entity_type), JArg::S(tag)]).unwrap_or(false) }
3053}
3054
3055unsafe extern "C" fn srv_item_block(_ctx: *mut c_void, item_id: YogStr) -> YogOwnedStr {
3056 unsafe { owned(bridge!("itemBlock", [JArg::S(item_id)])) }
3057}
3058
3059unsafe extern "C" fn srv_place_feature(
3060 _ctx: *mut c_void,
3061 dim: YogStr,
3062 pos: YogBlockPos,
3063 feature_id: YogStr,
3064) -> bool {
3065 unsafe {
3066 let [x, y, z] = pos_args(pos);
3067 bridge!("placeFeature", [JArg::S(dim), x, y, z, JArg::S(feature_id)]).unwrap_or(false)
3068 }
3069}
3070
3071unsafe extern "C" fn srv_item_entity_set_stack(
3072 _ctx: *mut c_void,
3073 uuid: YogStr,
3074 item_id: YogStr,
3075 count: u32,
3076 snbt: YogStr,
3077) -> bool {
3078 unsafe {
3079 bridge!(
3080 "itemEntitySetStack",
3081 [
3082 JArg::S(uuid),
3083 JArg::S(item_id),
3084 JArg::I(count as i32),
3085 JArg::S(snbt),
3086 ]
3087 )
3088 .unwrap_or(false)
3089 }
3090}
3091
3092fn build_server_table() -> YogServer {
3095 YogServer {
3096 ctx: std::ptr::null_mut(),
3097 abi_version: ABI_VERSION,
3098 size: std::mem::size_of::<YogServer>() as u32,
3099 free_str: yog_free_str,
3100 broadcast: srv_broadcast,
3101 get_block: srv_get_block,
3102 set_block: srv_set_block,
3103 world_time: srv_world_time,
3104 set_time: srv_set_time,
3105 is_raining: srv_is_raining,
3106 set_weather: srv_set_weather,
3107 give_item: srv_give_item,
3108 player_teleport: srv_player_teleport,
3109 send_to_player: srv_send_to_player,
3110 send_to_server: srv_send_to_server,
3111 kick_player: srv_kick_player,
3112 set_gamemode: srv_set_gamemode,
3113 send_title: srv_send_title,
3114 send_actionbar: srv_send_actionbar,
3115 play_sound: srv_play_sound,
3116 play_sound_player: srv_play_sound_player,
3117 entity_teleport: srv_entity_teleport,
3118 entity_position: srv_entity_position,
3119 entity_rotation: srv_entity_rotation,
3120 entity_health: srv_entity_health,
3121 entity_set_health: srv_entity_set_health,
3122 entity_kill: srv_entity_kill,
3123 spawn_entity: srv_spawn_entity,
3124 entity_add_effect: srv_entity_add_effect,
3125 entity_remove_effect: srv_entity_remove_effect,
3126 entity_clear_effects: srv_entity_clear_effects,
3127 entity_velocity: srv_entity_velocity,
3128 entity_set_velocity: srv_entity_set_velocity,
3129 entity_add_velocity: srv_entity_add_velocity,
3130 has_item_tag: srv_has_item_tag,
3131 has_block_tag: srv_has_block_tag,
3132 drop_loot: srv_drop_loot,
3133 scoreboard_get: srv_scoreboard_get,
3134 scoreboard_set: srv_scoreboard_set,
3135 scoreboard_add: srv_scoreboard_add,
3136 bossbar_create: srv_bossbar_create,
3137 bossbar_remove: srv_bossbar_remove,
3138 bossbar_set_title: srv_bossbar_set_title,
3139 bossbar_set_progress: srv_bossbar_set_progress,
3140 bossbar_set_color: srv_bossbar_set_color,
3141 bossbar_add_player: srv_bossbar_add_player,
3142 bossbar_remove_player: srv_bossbar_remove_player,
3143 bossbar_set_visible: srv_bossbar_set_visible,
3144 game_dir: srv_game_dir,
3145 get_block_nbt: srv_get_block_nbt,
3146 set_block_nbt: srv_set_block_nbt,
3147 get_inventory_slot: srv_get_inventory_slot,
3148 set_inventory_slot: srv_set_inventory_slot,
3149 player_inventory: srv_player_inventory,
3150 player_set_slot: srv_player_set_slot,
3151 player_teleport_dim: srv_player_teleport_dim,
3152 entity_teleport_dim: srv_entity_teleport_dim,
3153 online_players: srv_online_players,
3154 world_entity_count: srv_world_entity_count,
3155 entity_get_nbt: srv_entity_get_nbt,
3156 entity_set_nbt: srv_entity_set_nbt,
3157 spawn_particles: srv_spawn_particles,
3158 entity_attribute_get: srv_entity_attribute_get,
3159 entity_attribute_set: srv_entity_attribute_set,
3160 get_held_item_nbt: srv_get_held_item_nbt,
3161 set_held_item_nbt: srv_set_held_item_nbt,
3162 get_offhand_item_nbt: srv_get_offhand_item_nbt,
3163 set_offhand_item_nbt: srv_set_offhand_item_nbt,
3164 get_slot_item: srv_get_slot_item,
3165 set_slot_item: srv_set_slot_item,
3166 entity_info: srv_entity_info,
3167 entities_in_box: srv_entities_in_box,
3168 raycast_block: srv_raycast_block,
3169 raycast_entity: srv_raycast_entity,
3170 block_info: srv_block_info,
3171 break_block: srv_break_block,
3172 place_block_from_slot: srv_place_block_from_slot,
3173 bonemeal: srv_bonemeal,
3174 explode: srv_explode,
3175 tellraw: srv_tellraw,
3176 entity_damage: srv_entity_damage,
3177 entity_set_fire_ticks: srv_entity_set_fire_ticks,
3178 player_add_xp: srv_player_add_xp,
3179 player_set_may_fly: srv_player_set_may_fly,
3180 player_info: srv_player_info,
3181 spawn_item: srv_spawn_item,
3182 item_entity_stack: srv_item_entity_stack,
3183 item_entity_set_stack: srv_item_entity_set_stack,
3184 actionbar_json: srv_actionbar_json,
3185 use_item_on_block: srv_use_item_on_block,
3186 pour_fluid: srv_pour_fluid,
3187 has_entity_tag: srv_has_entity_tag,
3188 item_block: srv_item_block,
3189 place_feature: srv_place_feature,
3190 set_item_cooldown: srv_set_item_cooldown,
3191 entity_vehicle: srv_entity_vehicle,
3192 entity_passengers: srv_entity_passengers,
3193 entity_mount: srv_entity_mount,
3194 entity_dismount: srv_entity_dismount,
3195 redstone_power: srv_redstone_power,
3196 entity_set_dimensions: srv_entity_set_dimensions,
3197 entity_data_set: entity_data::srv_entity_data_set,
3198 entity_data_get: entity_data::srv_entity_data_get,
3199 world_dir: srv_world_dir,
3200 entity_attribute_value: srv_entity_attribute_value,
3201 advancement_grant: srv_advancement_grant,
3202 advancement_done: srv_advancement_done,
3203 entity_set_fall_distance: srv_entity_set_fall_distance,
3204 redstone_signal: srv_redstone_signal,
3205 set_redstone_output: srv_set_redstone_output,
3206 get_block_state: srv_get_block_state,
3207 set_block_state: srv_set_block_state,
3208 world_is_thundering: srv_world_is_thundering,
3209 world_set_thunder: srv_world_set_thunder,
3210 fill_region: srv_fill_region,
3211 scan_region: srv_scan_region,
3212 }
3213}
3214
3215unsafe extern "C" fn srv_set_item_cooldown(_ctx: *mut c_void, player: YogStr, item_id: YogStr, ticks: i32) -> bool {
3218 unsafe { bridge!("setItemCooldown", [S(player), S(item_id), I(ticks)]).unwrap_or(false) }
3219}
3220
3221unsafe extern "C" fn srv_entity_vehicle(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
3222 unsafe { owned(bridge!("entityVehicle", [S(uuid)])) }
3223}
3224
3225unsafe extern "C" fn srv_entity_passengers(_ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr {
3226 unsafe { owned(bridge!("entityPassengers", [S(uuid)])) }
3227}
3228
3229unsafe extern "C" fn srv_entity_mount(_ctx: *mut c_void, rider: YogStr, vehicle: YogStr, force: bool) -> bool {
3230 unsafe { bridge!("entityMount", [S(rider), S(vehicle), Z(force)]).unwrap_or(false) }
3231}
3232
3233unsafe extern "C" fn srv_entity_dismount(_ctx: *mut c_void, uuid: YogStr) -> bool {
3234 unsafe { bridge!("entityDismount", [S(uuid)]).unwrap_or(false) }
3235}
3236
3237unsafe extern "C" fn srv_entity_set_dimensions(_ctx: *mut c_void, uuid: YogStr, w: f32, h: f32, eye: f32) -> bool {
3239 let found: bool = unsafe { bridge!("entitySetDimensions", [S(uuid), F(w), F(h), F(eye)]).unwrap_or(false) };
3240 if found {
3241 let value = (w >= 0.0).then(|| entity_data::encode_dims(w, h, eye));
3242 entity_data::set(unsafe { uuid.as_str() }, entity_data::DIMENSIONS, value.as_deref(), false);
3243 }
3244 found
3245}
3246
3247unsafe extern "C" fn srv_entity_set_fall_distance(_ctx: *mut c_void, uuid: YogStr, distance: f32) -> bool {
3248 unsafe { bridge!("entitySetFallDistance", [S(uuid), F(distance)]).unwrap_or(false) }
3249}
3250
3251unsafe extern "C" fn srv_redstone_signal(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32 {
3252 unsafe { bridge!("redstoneSignal", [S(dim), I(pos.x), I(pos.y), I(pos.z)]).unwrap_or(-1) }
3253}
3254
3255unsafe extern "C" fn srv_redstone_power(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32 {
3256 unsafe { bridge!("redstonePower", [S(dim), I(pos.x), I(pos.y), I(pos.z)]).unwrap_or(-1) }
3257}
3258
3259unsafe extern "C" fn srv_set_redstone_output(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, level: i32) -> bool {
3260 unsafe { bridge!("setRedstoneOutput", [S(dim), I(pos.x), I(pos.y), I(pos.z), I(level)]).unwrap_or(false) }
3261}
3262
3263unsafe extern "C" fn srv_get_block_state(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr {
3264 unsafe { owned(bridge!("getBlockState", [S(dim), I(pos.x), I(pos.y), I(pos.z)])) }
3265}
3266
3267unsafe extern "C" fn srv_set_block_state(_ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, state: YogStr) -> bool {
3268 unsafe { bridge!("setBlockState", [S(dim), I(pos.x), I(pos.y), I(pos.z), S(state)]).unwrap_or(false) }
3269}
3270
3271unsafe extern "C" fn srv_world_is_thundering(_ctx: *mut c_void, dim: YogStr) -> bool {
3272 unsafe { bridge!("worldIsThundering", [S(dim)]).unwrap_or(false) }
3273}
3274
3275unsafe extern "C" fn srv_world_set_thunder(_ctx: *mut c_void, dim: YogStr, thundering: bool, duration_ticks: i32) -> bool {
3276 unsafe { bridge!("worldSetThunder", [S(dim), Z(thundering), I(duration_ticks)]).unwrap_or(false) }
3277}
3278
3279unsafe extern "C" fn srv_fill_region(
3280 _ctx: *mut c_void,
3281 dim: YogStr,
3282 from: YogBlockPos,
3283 to: YogBlockPos,
3284 state: YogStr,
3285) -> i32 {
3286 unsafe {
3287 bridge!(
3288 "fillRegion",
3289 [S(dim), I(from.x), I(from.y), I(from.z), I(to.x), I(to.y), I(to.z), S(state)]
3290 )
3291 .unwrap_or(-1)
3292 }
3293}
3294
3295unsafe extern "C" fn srv_scan_region(_ctx: *mut c_void, dim: YogStr, from: YogBlockPos, to: YogBlockPos) -> YogOwnedStr {
3296 unsafe { owned(bridge!("scanRegion", [S(dim), I(from.x), I(from.y), I(from.z), I(to.x), I(to.y), I(to.z)])) }
3297}
3298
3299unsafe extern "C" fn interop_export_impl(
3302 _ctx: *mut c_void,
3303 mod_id: YogStr,
3304 symbol: YogStr,
3305 ptr: *const c_void,
3306) {
3307 unsafe {
3308 let mod_id = mod_id.as_str();
3309 let sym = symbol.as_str();
3310 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3311 registry
3312 .lock()
3313 .expect("interop symbols lock poisoned")
3314 .entry(mod_id.to_string())
3315 .or_default()
3316 .insert(sym.to_string(), ptr as usize);
3317 }
3318}
3319
3320unsafe extern "C" fn interop_import_impl(
3321 _ctx: *mut c_void,
3322 mod_id: YogStr,
3323 symbol: YogStr,
3324) -> *const c_void {
3325 unsafe {
3326 let mod_id = mod_id.as_str();
3327 let sym = symbol.as_str();
3328 let registry = INTEROP_SYMBOLS.get_or_init(|| Mutex::new(HashMap::new()));
3329 registry
3330 .lock()
3331 .expect("interop symbols lock poisoned")
3332 .get(mod_id)
3333 .and_then(|m: &HashMap<String, usize>| m.get(sym))
3334 .map(|&ptr| ptr as *const c_void)
3335 .unwrap_or(std::ptr::null())
3336 }
3337}
3338
3339fn build_api_table(ctx: *const RwLock<RuntimeHandlers>, server: *const YogServer) -> YogApi {
3340 YogApi {
3341 abi_version: ABI_VERSION,
3342 size: std::mem::size_of::<YogApi>() as u32,
3343 ctx: ctx as *mut c_void,
3344 server,
3345 on_block_break: api_on_block_break,
3346 on_chat: api_on_chat,
3347 on_player_join: api_on_player_join,
3348 on_player_leave: api_on_player_leave,
3349 on_use_item: api_on_use_item,
3350 on_use_block: api_on_use_block,
3351 on_attack_entity: api_on_attack_entity,
3352 on_entity_damage: api_on_entity_damage,
3353 on_entity_death: api_on_entity_death,
3354 on_entity_spawn: api_on_entity_spawn,
3355 on_player_place_block: api_on_player_place_block,
3356 on_player_death: api_on_player_death,
3357 on_player_respawn: api_on_player_respawn,
3358 on_advancement: api_on_advancement,
3359 on_entity_interact: api_on_entity_interact,
3360 on_item_craft: api_on_item_craft,
3361 on_explosion: api_on_explosion,
3362 on_item_pickup: api_on_item_pickup,
3363 on_item_insert: api_on_item_insert,
3364 on_player_move: api_on_player_move,
3365 on_container_open: api_on_container_open,
3366 on_container_close: api_on_container_close,
3367 on_projectile_hit: api_on_projectile_hit,
3368 on_server_tick: api_on_server_tick,
3369 on_server_started: api_on_server_started,
3370 on_server_stopping: api_on_server_stopping,
3371 on_packet: api_on_packet,
3372 on_client_packet: api_on_client_packet,
3373 register_command: api_register_command,
3374 register_typed_command: api_register_typed_command,
3375 register_content: api_register_content,
3376 schedule_once: api_schedule_once,
3377 schedule_repeating: api_schedule_repeating,
3378 on_client_tick: api_on_client_tick,
3379 on_hud_render: api_on_hud_render,
3380 on_key_press: api_on_key_press,
3381 on_screen_open: api_on_screen_open,
3382 on_screen_close: api_on_screen_close,
3383 on_world_render: api_on_world_render,
3384 register_ui: api_register_ui,
3385 on_ui_render: api_on_ui_render,
3386 register_menu_entry: api_register_menu_entry,
3387 mods_list: api_mods_list,
3388 free_str: yog_free_str,
3389 ui_open: api_ui_open,
3390 interop_export: interop_export_impl,
3391 interop_import: interop_import_impl,
3392 register_chunk_generator: api_register_chunk_generator,
3393 ui_close: api_ui_close,
3394 ui_consume_event: api_ui_consume_event,
3395 translate: api_translate,
3396 ui_open_flags: client_api::api_ui_open_flags,
3397 client_play_sound: client_api::api_client_play_sound,
3398 client_stop_sound: client_api::api_client_stop_sound,
3399 client_held_item: client_api::api_client_held_item,
3400 client_set_block_tint: client_api::api_client_set_block_tint,
3401 client_key_down: client_api::api_client_key_down,
3402 on_book_page: api_on_book_page,
3403 on_item_tooltip: api_on_item_tooltip,
3404 is_mod_loaded: api_is_mod_loaded,
3405 java_call: api_java_call,
3406 on_item_text: api_on_item_text,
3407 client_body_track: body::api_client_body_track,
3408 client_body_track_type: body::api_client_body_track_type,
3409 client_set_dimensions: body::api_client_set_dimensions,
3410 client_entity_data: entity_data::api_client_entity_data,
3411 on_body: body::api_on_body,
3412 on_camera: body::api_on_camera,
3413 on_travel: body::api_on_travel,
3414 on_machine_craft: api_on_machine_craft,
3415 on_entity_tick: api_on_entity_tick,
3416 on_loot: loot::api_on_loot,
3417 client_attribute_value: api_client_attribute_value,
3418 client_advancement_done: api_client_advancement_done,
3419 client_op: client_api::api_client_op,
3420 log: api_log,
3421 }
3422}
3423
3424fn platform_tag() -> String {
3427 format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH)
3428}
3429
3430type AbiVersionFn = unsafe extern "C" fn() -> u32;
3431type RegisterFn = unsafe extern "C" fn(*const YogApi, *const std::os::raw::c_char);
3433
3434fn load_mods(dir: &Path, api: &YogApi) {
3435 let entries = match std::fs::read_dir(dir) {
3436 Ok(e) => e,
3437 Err(_) => {
3438 yog_logging::info!("no mods directory at {} — none loaded", dir.display());
3439 return;
3440 }
3441 };
3442
3443 struct ModMeta {
3445 id: String,
3446 deps: Vec<String>,
3447 yog_path: PathBuf,
3448 }
3449 let mut metas: Vec<ModMeta> = Vec::new();
3450 for entry in entries.flatten() {
3451 let path = entry.path();
3452 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3453 continue;
3454 }
3455 let info = read_mod_info(&path);
3456 let deps = read_mod_deps(&path);
3457 metas.push(ModMeta {
3458 id: info[0].clone(),
3459 deps,
3460 yog_path: path,
3461 });
3462 }
3463
3464 let ids: Vec<String> = metas.iter().map(|m| m.id.clone()).collect();
3466 YOG_MOD_IDS.write().unwrap_or_else(|e| e.into_inner()).extend(ids.iter().cloned());
3469 let deps_list: Vec<Vec<String>> = metas.iter().map(|m| m.deps.clone()).collect();
3470 let sorted = match topo_sort_mods(&ids, &deps_list) {
3471 Some(v) => v,
3472 None => {
3473 yog_logging::error!("circular dependency detected — loading in file order");
3474 (0..metas.len()).collect()
3475 }
3476 };
3477
3478 let mut loaded: Vec<LoadedModule> = Vec::new();
3479 for idx in sorted {
3480 let meta = &metas[idx];
3481 let lib_path = match extract_yog(&meta.yog_path) {
3482 Some(p) => p,
3483 None => {
3484 yog_logging::error!(
3485 "no native for {} in {}",
3486 platform_tag(),
3487 meta.yog_path.display()
3488 );
3489 continue;
3490 }
3491 };
3492 if let Some(module) = load_mod_lib(&lib_path, api, &meta.id) {
3493 loaded.push(module);
3494 #[cfg(feature = "dev")]
3495 registrations::record(&meta.id, &meta.yog_path);
3496 MOD_INFOS
3497 .lock()
3498 .expect("mod infos lock poisoned")
3499 .push(read_mod_info(&meta.yog_path));
3500 }
3501 }
3502 yog_logging::info!("loaded {} mod(s) from {}", loaded.len(), dir.display());
3503
3504 resolve_all_imports(api, &loaded);
3507}
3508
3509fn resolve_all_imports(api: &YogApi, loaded: &[LoadedModule]) {
3512 for module in loaded {
3513 module.with_library(|lib| unsafe {
3514 type ResolveFn = unsafe extern "C" fn(*const YogApi);
3515 if let Ok(resolve) = lib.get::<ResolveFn>(b"__yog_resolve_imports_final") {
3516 resolve(api as *const YogApi);
3517 }
3518 });
3519 }
3520}
3521
3522fn topo_sort_mods(ids: &[String], deps_list: &[Vec<String>]) -> Option<Vec<usize>> {
3524 let n = ids.len();
3525 let name_to_idx: HashMap<&str, usize> = ids
3526 .iter()
3527 .enumerate()
3528 .map(|(i, id)| (id.as_str(), i))
3529 .collect();
3530 let mut in_degree = vec![0u32; n];
3531 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
3532 for (i, deps) in deps_list.iter().enumerate() {
3533 for dep in deps {
3534 if let Some(&j) = name_to_idx.get(dep.as_str()) {
3535 adj[j].push(i);
3536 in_degree[i] += 1;
3537 }
3538 }
3539 }
3540 let mut queue: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
3541 let mut order = Vec::with_capacity(n);
3542 while let Some(u) = queue.pop() {
3543 order.push(u);
3544 for &v in &adj[u] {
3545 in_degree[v] -= 1;
3546 if in_degree[v] == 0 {
3547 queue.push(v);
3548 }
3549 }
3550 }
3551 if order.len() == n { Some(order) } else { None }
3552}
3553
3554fn load_mod_lib(path: &Path, api: &YogApi, mod_id: &str) -> Option<LoadedModule> {
3562 let module = match unsafe { hot_reloader().load_initial(mod_id, path) } {
3563 Ok(m) => m,
3564 Err(e) => {
3565 yog_logging::error!("failed to load {}: {}", path.display(), e);
3566 return None;
3567 }
3568 };
3569
3570 let abi = module.with_library(|lib| unsafe {
3571 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3572 Some(abi())
3573 });
3574 match abi.flatten() {
3575 Some(v) if yog_abi::compatible(v) => {}
3576 Some(v) => {
3577 yog_logging::error!(
3578 "{}: ABI {} incompatible with runtime ABI {}",
3579 path.display(),
3580 v,
3581 ABI_VERSION
3582 );
3583 module.retire(|| {});
3584 return None;
3585 }
3586 None => {
3587 yog_logging::error!("{} is not a Yog mod (no yog_abi_version)", path.display());
3588 module.retire(|| {});
3589 return None;
3590 }
3591 }
3592
3593 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3594 yog_logging::error!("{}: {}", path.display(), e);
3595 module.retire(|| {});
3596 return None;
3597 }
3598
3599 Some(module)
3600}
3601
3602fn invoke_mod_register(module: &LoadedModule, api: &YogApi, mod_id: &str) -> Result<(), String> {
3606 module
3607 .with_library(|lib| unsafe {
3608 let register: Symbol<RegisterFn> = lib
3609 .get(b"yog_mod_register")
3610 .map_err(|_| "missing yog_mod_register".to_string())?;
3611 let mod_id_c = std::ffi::CString::new(mod_id).unwrap();
3612 *CURRENT_REGISTRATION.lock().unwrap() = Some(module.clone());
3613 register(api as *const YogApi, mod_id_c.as_ptr());
3614 *CURRENT_REGISTRATION.lock().unwrap() = None;
3615 Ok(())
3616 })
3617 .unwrap_or_else(|| Err("module was unloaded before registration could run".to_string()))
3618}
3619
3620fn read_mod_info(path: &Path) -> [String; 7] {
3625 let stem = path
3626 .file_stem()
3627 .map(|s| s.to_string_lossy().into_owned())
3628 .unwrap_or_default();
3629 let mut info = [
3630 stem.clone(),
3631 stem,
3632 String::new(),
3633 String::new(),
3634 String::new(),
3635 String::new(),
3636 String::new(),
3637 ];
3638 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3639 return info;
3640 }
3641 let Some(text) = (|| {
3642 let file = std::fs::File::open(path).ok()?;
3643 let mut archive = zip::ZipArchive::new(file).ok()?;
3644 let mut entry = archive.by_name("yog.toml").ok()?;
3645 let mut text = String::new();
3646 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3647 Some(text)
3648 })() else {
3649 return info;
3650 };
3651 let mut in_mod = true;
3656 for line in text.lines() {
3657 let line = line.trim();
3658 if line.starts_with('[') {
3659 in_mod = line == "[mod]";
3660 continue;
3661 }
3662 if !in_mod {
3663 continue;
3664 }
3665 let Some((key, value)) = line.split_once('=') else {
3666 continue;
3667 };
3668 let key = key.trim();
3669 let value = value.trim();
3670 let unquote = |v: &str| v.trim().trim_matches('"').to_string();
3671 match key {
3672 "id" => info[0] = tsv_clean(&unquote(value)),
3673 "name" => info[1] = tsv_clean(&unquote(value)),
3674 "version" => info[2] = tsv_clean(&unquote(value)),
3675 "description" => info[4] = tsv_clean(&unquote(value)),
3676 "name_key" => info[5] = unquote(value),
3677 "description_key" => info[6] = unquote(value),
3678 "authors" => {
3679 let list = value.trim_start_matches('[').trim_end_matches(']');
3680 let authors: Vec<String> = list
3681 .split(',')
3682 .map(unquote)
3683 .filter(|a| !a.is_empty())
3684 .collect();
3685 info[3] = tsv_clean(&authors.join(", "));
3686 }
3687 _ => {}
3688 }
3689 }
3690 info
3691}
3692
3693fn read_mod_deps(path: &Path) -> Vec<String> {
3695 if path.extension().and_then(|e| e.to_str()) != Some("yog") {
3696 return Vec::new();
3697 }
3698 let text = (|| {
3699 let file = std::fs::File::open(path).ok()?;
3700 let mut archive = zip::ZipArchive::new(file).ok()?;
3701 let mut entry = archive.by_name("yog.toml").ok()?;
3702 let mut text = String::new();
3703 std::io::Read::read_to_string(&mut entry, &mut text).ok()?;
3704 Some(text)
3705 })();
3706 let Some(text) = text else { return Vec::new() };
3707 let mut deps = Vec::new();
3708 let mut in_deps = false;
3709 for line in text.lines() {
3710 let line = line.trim();
3711 if line.starts_with('[') {
3712 in_deps = line == "[dependencies]";
3713 continue;
3714 }
3715 if !in_deps {
3716 continue;
3717 }
3718 if let Some((key, _)) = line.split_once('=') {
3720 deps.push(key.trim().to_string());
3721 }
3722 }
3723 deps
3724}
3725
3726fn extract_yog(path: &Path) -> Option<PathBuf> {
3727 let file = std::fs::File::open(path).ok()?;
3728 let mut archive = zip::ZipArchive::new(file).ok()?;
3729 let prefix = format!("natives/{}/", platform_tag());
3730 let mut entry_name = None;
3731 for i in 0..archive.len() {
3732 let f = archive.by_index(i).ok()?;
3733 let name = f.name().ok()?;
3734 if name.starts_with(&prefix) && !name.ends_with('/') {
3735 entry_name = Some(name.to_string());
3736 break;
3737 }
3738 }
3739 let entry_name = entry_name?;
3740 let ext = Path::new(&entry_name)
3741 .extension()
3742 .and_then(|e| e.to_str())
3743 .unwrap_or("bin");
3744 let stem = path.file_stem()?.to_string_lossy().into_owned();
3745 static EXTRACTIONS: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
3749 let n = EXTRACTIONS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3750 let out =
3751 std::env::temp_dir().join(format!("yog-{}-{}-{}.{}", stem, std::process::id(), n, ext));
3752 let mut entry = archive.by_name(&entry_name).ok()?;
3753 let mut out_file = std::fs::File::create(&out).ok()?;
3754 std::io::copy(&mut entry, &mut out_file).ok()?;
3755 Some(out)
3756}
3757
3758fn srv_ptr() -> *const YogServer {
3761 SERVER.get().expect("yog: SERVER not initialised") as *const YogServer
3762}
3763
3764#[unsafe(no_mangle)]
3767pub extern "system" fn Java_dev_yog_NativeBridge_nativeInit<'l>(
3768 mut env: JNIEnv<'l>,
3769 _class: JClass<'l>,
3770 mods_dir: JString<'l>,
3771) {
3772 if let Ok(vm) = env.get_java_vm() {
3773 let _ = JAVA_VM.set(vm);
3774 }
3775 yog_logging::set_sink(host_log);
3777
3778 let dir = env
3779 .get_string(&mods_dir)
3780 .map(String::from)
3781 .unwrap_or_default();
3782
3783 let _ = SERVER.set(build_server_table());
3785 let server_ptr = SERVER.get().unwrap() as *const YogServer;
3786
3787 let handlers_lock = HANDLERS.get_or_init(|| RwLock::new(RuntimeHandlers::default()));
3793 let handlers_ptr = handlers_lock as *const RwLock<RuntimeHandlers>;
3794
3795 let api: &'static YogApi = API_TABLE.get_or_init(|| build_api_table(handlers_ptr, server_ptr));
3796
3797 let _ = HOT_RELOADER.get_or_init(|| HotReloader::new(RuntimeModuleRegistry));
3798 yog_book::set_unlocked(client_advancement_done);
3800 yog_book::set_tag_items(client_tag_items);
3802
3803 guard("mod loading", || {
3804 load_mods(Path::new(&dir), api);
3805 });
3806
3807 #[cfg(feature = "dev")]
3808 control_socket::start_if_requested();
3809
3810 yog_logging::info!("runtime initialised — the gate is open.");
3811}
3812
3813#[cfg(feature = "dev")]
3823pub(crate) fn trigger_hot_reload(mod_id: &str, new_yog_path: &Path) -> bool {
3824 let Some(native_path) = extract_yog(new_yog_path) else {
3825 yog_logging::error!(
3826 "hot-reload {}: no native for {} in {}",
3827 mod_id,
3828 platform_tag(),
3829 new_yog_path.display()
3830 );
3831 return false;
3832 };
3833
3834 let api = API_TABLE.get().expect("yog: nativeInit not called yet");
3835
3836 let module = match unsafe { hot_reloader().reload(mod_id, &native_path) } {
3837 Ok(m) => m,
3838 Err(e) => {
3839 yog_logging::error!(
3840 "hot-reload {}: failed to load {}: {}",
3841 mod_id,
3842 native_path.display(),
3843 e
3844 );
3845 return false;
3846 }
3847 };
3848
3849 let abi = module.with_library(|lib| unsafe {
3850 let abi: Symbol<AbiVersionFn> = lib.get(b"yog_abi_version").ok()?;
3851 Some(abi())
3852 });
3853 match abi.flatten() {
3854 Some(v) if yog_abi::compatible(v) => {}
3855 other => {
3856 yog_logging::error!(
3857 "hot-reload {}: ABI {:?} incompatible with runtime ABI {} — mod has no active handlers until a compatible reload succeeds",
3858 mod_id,
3859 other,
3860 ABI_VERSION
3861 );
3862 return false;
3863 }
3864 }
3865
3866 if let Err(e) = invoke_mod_register(&module, api, mod_id) {
3867 yog_logging::error!("hot-reload {}: {}", mod_id, e);
3868 return false;
3869 }
3870
3871 yog_logging::info!("hot-reloaded mod {}", mod_id);
3872 true
3873}
3874
3875#[unsafe(no_mangle)]
3876pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreak<'l>(
3877 mut env: JNIEnv<'l>,
3878 _class: JClass<'l>,
3879 player: JString<'l>,
3880 block: JString<'l>,
3881 x: jint,
3882 y: jint,
3883 z: jint,
3884 dimension: JString<'l>,
3885) {
3886 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
3887 let ev = yog_abi::YogBlockBreakEvent {
3888 player: YogStr::from_str(&p),
3889 block: YogStr::from_str(&b),
3890 pos: YogBlockPos { x, y, z },
3891 dimension: YogStr::from_str(&d),
3892 };
3893 let srv = srv_ptr();
3894 guard("on_block_break", || {
3895 each(&handlers().block_break, |ud, f| unsafe {
3896 f(ud, srv, &ev, 1)
3897 });
3898 });
3899}
3900
3901#[unsafe(no_mangle)]
3902pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChat<'l>(
3903 mut env: JNIEnv<'l>,
3904 _class: JClass<'l>,
3905 player: JString<'l>,
3906 message: JString<'l>,
3907 dimension: JString<'l>,
3908) {
3909 let (p, m, d) = (
3910 jstr!(env, player),
3911 jstr!(env, message),
3912 jstr!(env, dimension),
3913 );
3914 let ev = yog_abi::YogChatEvent {
3915 player: YogStr::from_str(&p),
3916 message: YogStr::from_str(&m),
3917 dimension: YogStr::from_str(&d),
3918 };
3919 let srv = srv_ptr();
3920 guard("on_chat", || {
3921 each(&handlers().chat, |ud, f| unsafe { f(ud, srv, &ev, 1) });
3922 });
3923}
3924
3925#[unsafe(no_mangle)]
3926pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerJoin<'l>(
3927 mut env: JNIEnv<'l>,
3928 _class: JClass<'l>,
3929 player: JString<'l>,
3930 uuid: JString<'l>,
3931 dimension: JString<'l>,
3932) {
3933 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
3934 let ev = yog_abi::YogPlayerEvent {
3935 player: YogStr::from_str(&p),
3936 uuid: YogStr::from_str(&u),
3937 dimension: YogStr::from_str(&d),
3938 };
3939 let srv = srv_ptr();
3940 guard("on_player_join", || {
3941 each(&handlers().player_join, |ud, f| unsafe {
3942 f(ud, srv, &ev, 1)
3943 });
3944 });
3945 let h = handlers();
3949 if !h.startup_grants.is_empty() {
3950 h.pending_grants
3951 .lock()
3952 .expect("pending_grants poisoned")
3953 .push((p, u, 0));
3954 }
3955}
3956
3957fn grant_tag(id: &str) -> String {
3962 format!("yog.grant.{id}")
3963}
3964
3965fn process_pending_grants() {
3968 let h = handlers();
3969 let mut pending = match h.pending_grants.try_lock() {
3970 Ok(g) => g,
3971 Err(_) => return,
3972 };
3973 if pending.is_empty() {
3974 return;
3975 }
3976 const FIRST_TRY_TICK: u32 = 2;
3978 const MAX_TICKS: u32 = 600;
3980
3981 let mut still_pending = Vec::new();
3982 for (p, u, ticks) in pending.drain(..) {
3983 if ticks < FIRST_TRY_TICK {
3984 still_pending.push((p, u, ticks + 1));
3985 continue;
3986 }
3987 let player = YogStr::from_str(&p);
3988 let mut all_ok = true;
3989 for sg in &h.startup_grants {
3990 let tag = grant_tag(&sg.id);
3991 let tag = YogStr::from_str(&tag);
3992 if unsafe { bridge!("playerHasTag", [S(player), S(tag)]) }.unwrap_or(false) {
3993 continue;
3994 }
3995 let book = sg.book.as_ref().map(|_| yog_ids::game::WRITTEN_BOOK);
3996 let mut ok = true;
3997 for item_id in sg.items.iter().map(String::as_str).chain(book) {
3998 ok &= unsafe { srv_give_item(std::ptr::null_mut(), player, YogStr::from_str(item_id), 1) };
3999 }
4000 if ok && unsafe { bridge!("addPlayerTag", [S(player), S(tag)]) }.unwrap_or(false) {
4001 yog_logging::info!("startup grant {} given to {}", sg.id, p);
4002 } else {
4003 all_ok = false;
4004 }
4005 }
4006 if !all_ok && ticks < MAX_TICKS {
4007 still_pending.push((p, u, ticks + 1));
4008 }
4009 }
4010 *pending = still_pending;
4011}
4012
4013#[unsafe(no_mangle)]
4014pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerLeave<'l>(
4015 mut env: JNIEnv<'l>,
4016 _class: JClass<'l>,
4017 player: JString<'l>,
4018 uuid: JString<'l>,
4019 dimension: JString<'l>,
4020) {
4021 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4022 let ev = yog_abi::YogPlayerEvent {
4023 player: YogStr::from_str(&p),
4024 uuid: YogStr::from_str(&u),
4025 dimension: YogStr::from_str(&d),
4026 };
4027 let srv = srv_ptr();
4028 guard("on_player_leave", || {
4029 each(&handlers().player_leave, |ud, f| unsafe {
4030 f(ud, srv, &ev, 1)
4031 });
4032 });
4033}
4034
4035#[unsafe(no_mangle)]
4036pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItem<'l>(
4037 mut env: JNIEnv<'l>,
4038 _class: JClass<'l>,
4039 player: JString<'l>,
4040 item: JString<'l>,
4041 sneaking: jni::sys::jboolean,
4042 dimension: JString<'l>,
4043) {
4044 let (p, i, d) = (jstr!(env, player), jstr!(env, item), jstr!(env, dimension));
4045 let ev = yog_abi::YogUseItemEvent {
4046 player: YogStr::from_str(&p),
4047 item: YogStr::from_str(&i),
4048 sneaking: sneaking != 0,
4049 dimension: YogStr::from_str(&d),
4050 };
4051 let srv = srv_ptr();
4052 guard("on_use_item", || {
4053 each(&handlers().use_item, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4054 });
4055}
4056
4057#[unsafe(no_mangle)]
4058pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseItemPre<'l>(
4059 mut env: JNIEnv<'l>,
4060 _class: JClass<'l>,
4061 player: JString<'l>,
4062 item: JString<'l>,
4063 sneaking: jni::sys::jboolean,
4064 dimension: JString<'l>,
4065) -> jni::sys::jboolean {
4066 let h = handlers();
4067 if h.use_item.is_empty() {
4068 return 1;
4069 }
4070 let p = jstr!(env, player, 1);
4071 let i = jstr!(env, item, 1);
4072 let d = jstr!(env, dimension, 1);
4073 let ev = yog_abi::YogUseItemEvent {
4074 player: YogStr::from_str(&p),
4075 item: YogStr::from_str(&i),
4076 sneaking: sneaking != 0,
4077 dimension: YogStr::from_str(&d),
4078 };
4079 let srv = srv_ptr();
4080 allowed(&h.use_item, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4081}
4082
4083#[unsafe(no_mangle)]
4084pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlock<'l>(
4085 mut env: JNIEnv<'l>,
4086 _class: JClass<'l>,
4087 player: JString<'l>,
4088 block: JString<'l>,
4089 x: jint,
4090 y: jint,
4091 z: jint,
4092 dimension: JString<'l>,
4093 sneaking: jni::sys::jboolean,
4094) {
4095 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4096 let ev = yog_abi::YogUseBlockEvent {
4097 player: YogStr::from_str(&p),
4098 block: YogStr::from_str(&b),
4099 pos: YogBlockPos { x, y, z },
4100 dimension: YogStr::from_str(&d),
4101 sneaking: sneaking != 0,
4102 };
4103 let srv = srv_ptr();
4104 guard("on_use_block", || {
4105 each(&handlers().use_block, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4106 });
4107}
4108
4109#[unsafe(no_mangle)]
4110pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnUseBlockPre<'l>(
4111 mut env: JNIEnv<'l>,
4112 _class: JClass<'l>,
4113 player: JString<'l>,
4114 block: JString<'l>,
4115 x: jint,
4116 y: jint,
4117 z: jint,
4118 dimension: JString<'l>,
4119 sneaking: jni::sys::jboolean,
4120) -> jni::sys::jboolean {
4121 let h = handlers();
4122 if h.use_block.is_empty() {
4123 return 1;
4124 }
4125 let p = jstr!(env, player, 1);
4126 let b = jstr!(env, block, 1);
4127 let d = jstr!(env, dimension, 1);
4128 let ev = yog_abi::YogUseBlockEvent {
4129 player: YogStr::from_str(&p),
4130 block: YogStr::from_str(&b),
4131 pos: YogBlockPos { x, y, z },
4132 dimension: YogStr::from_str(&d),
4133 sneaking: sneaking != 0,
4134 };
4135 let srv = srv_ptr();
4136 allowed(&h.use_block, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4137}
4138
4139#[unsafe(no_mangle)]
4140pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAttackEntity<'l>(
4141 mut env: JNIEnv<'l>,
4142 _class: JClass<'l>,
4143 player: JString<'l>,
4144 target_type: JString<'l>,
4145 target_uuid: JString<'l>,
4146 dimension: JString<'l>,
4147) {
4148 let (p, tt, tu, d) = (
4149 jstr!(env, player),
4150 jstr!(env, target_type),
4151 jstr!(env, target_uuid),
4152 jstr!(env, dimension),
4153 );
4154 let ev = yog_abi::YogAttackEntityEvent {
4155 player: YogStr::from_str(&p),
4156 target_type: YogStr::from_str(&tt),
4157 target_uuid: YogStr::from_str(&tu),
4158 dimension: YogStr::from_str(&d),
4159 };
4160 let srv = srv_ptr();
4161 guard("on_attack_entity", || {
4162 each(&handlers().attack_entity, |ud, f| unsafe {
4163 f(ud, srv, &ev, 1)
4164 });
4165 });
4166}
4167
4168#[unsafe(no_mangle)]
4169pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamage<'l>(
4170 mut env: JNIEnv<'l>,
4171 _class: JClass<'l>,
4172 entity_type: JString<'l>,
4173 uuid: JString<'l>,
4174 amount: jfloat,
4175 source: JString<'l>,
4176 dimension: JString<'l>,
4177) {
4178 let (et, u, s, d) = (
4179 jstr!(env, entity_type),
4180 jstr!(env, uuid),
4181 jstr!(env, source),
4182 jstr!(env, dimension),
4183 );
4184 let ev = yog_abi::YogEntityDamageEvent {
4185 entity_type: YogStr::from_str(&et),
4186 uuid: YogStr::from_str(&u),
4187 amount,
4188 source: YogStr::from_str(&s),
4189 dimension: YogStr::from_str(&d),
4190 };
4191 let srv = srv_ptr();
4192 guard("on_entity_damage", || {
4193 each(&handlers().entity_damage, |ud, f| unsafe {
4194 f(ud, srv, &ev, 1)
4195 });
4196 });
4197}
4198
4199#[unsafe(no_mangle)]
4200pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDeath<'l>(
4201 mut env: JNIEnv<'l>,
4202 _class: JClass<'l>,
4203 entity_type: JString<'l>,
4204 uuid: JString<'l>,
4205 source: JString<'l>,
4206 dimension: JString<'l>,
4207) {
4208 let (et, u, s, d) = (
4209 jstr!(env, entity_type),
4210 jstr!(env, uuid),
4211 jstr!(env, source),
4212 jstr!(env, dimension),
4213 );
4214 let ev = yog_abi::YogEntityDeathEvent {
4215 entity_type: YogStr::from_str(&et),
4216 uuid: YogStr::from_str(&u),
4217 source: YogStr::from_str(&s),
4218 dimension: YogStr::from_str(&d),
4219 };
4220 let srv = srv_ptr();
4221 guard("on_entity_death", || {
4222 each(&handlers().entity_death, |ud, f| unsafe {
4223 f(ud, srv, &ev, 1)
4224 });
4225 });
4226}
4227
4228#[unsafe(no_mangle)]
4229pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnTick<'l>(
4230 _env: JNIEnv<'l>,
4231 _class: JClass<'l>,
4232) {
4233 let h = handlers();
4234 let srv = srv_ptr();
4235 guard("on_tick", || {
4236 each(&h.server_tick, |ud, f| unsafe { f(ud, srv) });
4237 });
4238
4239 guard("startup_grants", process_pending_grants);
4240
4241 guard("entity data", entity_data::flush);
4243
4244 let due = h.scheduler.lock().expect("scheduler poisoned").due();
4246 for (module, ud, f) in due {
4247 if module.is_retiring() {
4248 continue;
4249 }
4250 let Some(_call) = module.enter() else {
4251 continue;
4252 };
4253 guard("scheduled task", || unsafe { f(ud, srv) });
4254 }
4255}
4256
4257#[unsafe(no_mangle)]
4258pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStarted<'l>(
4259 _env: JNIEnv<'l>,
4260 _class: JClass<'l>,
4261 _world_dir: JString<'l>,
4262) {
4263 handlers().pending_grants.lock().expect("pending_grants poisoned").clear();
4265 let srv = srv_ptr();
4266 guard("on_server_started", || {
4267 each(&handlers().server_started, |ud, f| unsafe { f(ud, srv) });
4268 });
4269}
4270
4271#[unsafe(no_mangle)]
4272pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnServerStopping<'l>(
4273 _env: JNIEnv<'l>,
4274 _class: JClass<'l>,
4275) {
4276 let srv = srv_ptr();
4277 guard("on_server_stopping", || {
4278 each(&handlers().server_stopping, |ud, f| unsafe { f(ud, srv) });
4279 });
4280 entity_data::server_stopped();
4281}
4282
4283#[unsafe(no_mangle)]
4284pub extern "system" fn Java_dev_yog_NativeBridge_nativeCommandNames<'l>(
4285 env: JNIEnv<'l>,
4286 _class: JClass<'l>,
4287) -> jstring {
4288 let names = handlers()
4289 .commands
4290 .keys()
4291 .cloned()
4292 .collect::<Vec<_>>()
4293 .join("\n");
4294 env.new_string(names)
4295 .map(jni::objects::JString::into_raw)
4296 .unwrap_or(std::ptr::null_mut())
4297}
4298
4299#[unsafe(no_mangle)]
4300pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnBlockBreakPre<'l>(
4301 mut env: JNIEnv<'l>,
4302 _class: JClass<'l>,
4303 player: JString<'l>,
4304 block: JString<'l>,
4305 x: jint,
4306 y: jint,
4307 z: jint,
4308 dimension: JString<'l>,
4309) -> jni::sys::jboolean {
4310 let h = handlers();
4311 if h.block_break.is_empty() {
4312 return 1;
4313 }
4314 let p = jstr!(env, player, 1);
4315 let b = jstr!(env, block, 1);
4316 let d = jstr!(env, dimension, 1);
4317 let ev = yog_abi::YogBlockBreakEvent {
4318 player: YogStr::from_str(&p),
4319 block: YogStr::from_str(&b),
4320 pos: YogBlockPos { x, y, z },
4321 dimension: YogStr::from_str(&d),
4322 };
4323 let srv = srv_ptr();
4324 allowed(&h.block_break, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4325}
4326
4327#[unsafe(no_mangle)]
4328pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnChatPre<'l>(
4329 mut env: JNIEnv<'l>,
4330 _class: JClass<'l>,
4331 player: JString<'l>,
4332 message: JString<'l>,
4333 dimension: JString<'l>,
4334) -> jni::sys::jboolean {
4335 let h = handlers();
4336 if h.chat.is_empty() {
4337 return 1;
4338 }
4339 let p = jstr!(env, player, 1);
4340 let m = jstr!(env, message, 1);
4341 let d = jstr!(env, dimension, 1);
4342 let ev = yog_abi::YogChatEvent {
4343 player: YogStr::from_str(&p),
4344 message: YogStr::from_str(&m),
4345 dimension: YogStr::from_str(&d),
4346 };
4347 let srv = srv_ptr();
4348 allowed(&h.chat, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4349}
4350
4351unsafe extern "C" fn chunk_writer_set_block(
4356 _ctx: *mut c_void,
4357 x: i32,
4358 y: i32,
4359 z: i32,
4360 block_id: YogStr,
4361) -> bool {
4362 unsafe {
4363 host!(
4364 "dev/yog/YogCallbackChunkGenerator",
4365 "setBlockInGeneratingChunk",
4366 [I(x), I(y), I(z), S(block_id)]
4367 )
4368 .unwrap_or(false)
4369 }
4370}
4371
4372#[unsafe(no_mangle)]
4373pub extern "system" fn Java_dev_yog_NativeBridge_nativeGenerateChunk<'l>(
4374 mut env: JNIEnv<'l>,
4375 _class: JClass<'l>,
4376 generator_type_id: JString<'l>,
4377 chunk_x: jint,
4378 chunk_z: jint,
4379 min_y: jint,
4380 height: jint,
4381) {
4382 let id: String = match env.get_string(&generator_type_id) {
4383 Ok(s) => String::from(s),
4384 Err(_) => return,
4385 };
4386 let terrain = handlers().terrains.get(&id).cloned();
4387 if let Some(g) = terrain {
4388 guard("chunk_generator", || {
4389 g.generate(chunk_x, chunk_z, min_y, height, |x, y, z, block| unsafe {
4390 chunk_writer_set_block(std::ptr::null_mut(), x, y, z, YogStr::from_str(block));
4391 });
4392 });
4393 return;
4394 }
4395 let Some((module, ud, cb)) = handlers().chunk_generators.get(&id).cloned() else {
4396 return;
4397 };
4398 if module.is_retiring() {
4399 return;
4400 }
4401 let Some(_call) = module.enter() else { return };
4402 let writer = yog_abi::YogChunkWriterApi {
4403 chunk_x,
4404 chunk_z,
4405 min_y,
4406 height,
4407 ctx: std::ptr::null_mut(),
4408 set_block: chunk_writer_set_block,
4409 };
4410 guard("chunk_generator", || unsafe {
4411 cb(ud, &writer as *const _);
4412 });
4413}
4414
4415#[unsafe(no_mangle)]
4416pub extern "system" fn Java_dev_yog_NativeBridge_nativeTypedCommandSchemas<'l>(
4417 env: JNIEnv<'l>,
4418 _class: JClass<'l>,
4419) -> jstring {
4420 let mut lines = Vec::new();
4425 for (name, regs) in handlers().commands.iter() {
4426 let mut seen = std::collections::HashSet::new();
4427 for (_module, schema, _, _) in regs {
4428 if let Some(schema) = schema
4429 && seen.insert(schema.as_str())
4430 {
4431 lines.push(format!("{name}\t{schema}"));
4432 }
4433 }
4434 }
4435 env.new_string(lines.join("\n"))
4436 .map(jni::objects::JString::into_raw)
4437 .unwrap_or(std::ptr::null_mut())
4438}
4439
4440#[unsafe(no_mangle)]
4441pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnCommand<'l>(
4442 mut env: JNIEnv<'l>,
4443 _class: JClass<'l>,
4444 name: JString<'l>,
4445 args: JString<'l>,
4446 source: JString<'l>,
4447 uuid: JString<'l>,
4448 dimension: JString<'l>,
4449) -> jstring {
4450 let (n, a, s, u, d) = (
4451 env.get_string(&name).map(String::from).unwrap_or_default(),
4452 env.get_string(&args).map(String::from).unwrap_or_default(),
4453 env.get_string(&source)
4454 .map(String::from)
4455 .unwrap_or_default(),
4456 env.get_string(&uuid).map(String::from).unwrap_or_default(),
4457 env.get_string(&dimension)
4458 .map(String::from)
4459 .unwrap_or_default(),
4460 );
4461 let h = handlers();
4462 let srv = srv_ptr();
4463 let args: Vec<&str> = if a.is_empty() { Vec::new() } else { a.split('\t').collect() };
4464 let call = |ud: &*mut c_void, f: &YogCommandFn, args: &str| -> Option<String> {
4466 let ev = yog_abi::YogCommandEvent {
4467 name: YogStr::from_str(&n),
4468 args: YogStr::from_str(args),
4469 source: YogStr::from_str(&s),
4470 uuid: YogStr::from_str(&u),
4471 dimension: YogStr::from_str(&d),
4472 };
4473 let mut buf = [0u8; 4096];
4474 let mut reply_len: u32 = 0;
4475 unsafe { f(*ud, srv, &ev, buf.as_mut_ptr(), buf.len() as u32, &mut reply_len) };
4476 (reply_len > 0).then(|| String::from_utf8_lossy(&buf[..reply_len as usize]).into_owned())
4477 };
4478 let reply = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
4479 let Some(regs) = h.commands.get(&n) else {
4480 return String::new();
4481 };
4482 let live = regs.iter().filter(|(module, ..)| !module.is_retiring());
4483 for pass in 0..3 {
4488 for (module, schema, ud, f) in live.clone() {
4489 let joined;
4490 let these = match (pass, schema) {
4491 (0, Some(schema)) if schema.split_whitespace().count() == args.len() => a.as_str(),
4492 (1, Some(schema)) => {
4493 let types: Vec<&str> = schema.split_whitespace().collect();
4494 let k = types.len();
4495 if types.last() != Some(&"string") || k == 0 || args.len() <= k {
4496 continue;
4497 }
4498 let mut parts: Vec<String> = args[..k - 1].iter().map(|x| x.to_string()).collect();
4499 parts.push(args[k - 1..].join(" "));
4500 joined = parts.join("\t");
4501 joined.as_str()
4502 }
4503 (2, None) => a.as_str(),
4504 _ => continue,
4505 };
4506 let Some(_call) = module.enter() else {
4507 continue;
4508 };
4509 if let Some(r) = call(ud, f, these) {
4510 return r;
4511 }
4512 }
4513 }
4514 String::new()
4515 }))
4516 .unwrap_or_else(|_| {
4517 yog_logging::error!("a mod panicked handling command `{}`", n);
4518 String::new()
4519 });
4520
4521 env.new_string(reply)
4522 .map(jni::objects::JString::into_raw)
4523 .unwrap_or(std::ptr::null_mut())
4524}
4525
4526#[unsafe(no_mangle)]
4529pub extern "system" fn Java_dev_yog_NativeBridge_nativeContent<'l>(
4530 env: JNIEnv<'l>,
4531 _class: JClass<'l>,
4532) -> jni::sys::jbyteArray {
4533 let h = handlers();
4534 let bytes = {
4535 let mut cached = h.content_blob.lock().unwrap_or_else(|e| e.into_inner());
4536 cached.get_or_insert_with(|| std::sync::Arc::new(content_blob::blob(&h))).clone()
4537 };
4538 env.byte_array_from_slice(&bytes)
4539 .map(|a| a.into_raw())
4540 .unwrap_or(std::ptr::null_mut())
4541}
4542
4543#[unsafe(no_mangle)]
4544pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPacket<'l>(
4545 mut env: JNIEnv<'l>,
4546 _class: JClass<'l>,
4547 channel: JString<'l>,
4548 player: JString<'l>,
4549 payload: JByteArray<'l>,
4550) {
4551 let ch = env
4552 .get_string(&channel)
4553 .map(String::from)
4554 .unwrap_or_default();
4555 let pl = env
4556 .get_string(&player)
4557 .map(String::from)
4558 .unwrap_or_default();
4559 let data = env.convert_byte_array(&payload).unwrap_or_default();
4560 let ev = yog_abi::YogPacketEvent {
4561 channel: YogStr::from_str(&ch),
4562 player: YogStr::from_str(&pl),
4563 payload: data.as_ptr(),
4564 payload_len: data.len() as u32,
4565 };
4566 let h = handlers();
4567 let srv = srv_ptr();
4568 guard("on_packet", || {
4569 if let Some((module, ud, f)) = h.packets.get(&ch) {
4570 if module.is_retiring() {
4571 return;
4572 }
4573 let Some(_call) = module.enter() else { return };
4574 unsafe { f(*ud, srv, &ev) };
4575 }
4576 });
4577}
4578
4579#[unsafe(no_mangle)]
4580pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientPacket<'l>(
4581 mut env: JNIEnv<'l>,
4582 _class: JClass<'l>,
4583 channel: JString<'l>,
4584 payload: JByteArray<'l>,
4585) {
4586 let ch = env
4587 .get_string(&channel)
4588 .map(String::from)
4589 .unwrap_or_default();
4590 let data = env.convert_byte_array(&payload).unwrap_or_default();
4591 if ch == entity_data::ENTITY_DATA {
4592 guard("entity data", || entity_data::receive(&data));
4593 return;
4594 }
4595 let ev = yog_abi::YogPacketEvent {
4596 channel: YogStr::from_str(&ch),
4597 player: YogStr::EMPTY,
4598 payload: data.as_ptr(),
4599 payload_len: data.len() as u32,
4600 };
4601 let h = handlers();
4602 let srv = srv_ptr();
4603 guard("on_client_packet", || {
4604 if let Some((module, ud, f)) = h.client_packets.get(&ch) {
4605 if module.is_retiring() {
4606 return;
4607 }
4608 let Some(_call) = module.enter() else { return };
4609 unsafe { f(*ud, srv, &ev) };
4610 }
4611 });
4612}
4613
4614#[unsafe(no_mangle)]
4615pub extern "system" fn Java_dev_yog_NativeBridge_nativePacketChannels<'l>(
4616 env: JNIEnv<'l>,
4617 _class: JClass<'l>,
4618) -> jstring {
4619 let s = handlers()
4620 .packets
4621 .keys()
4622 .cloned()
4623 .collect::<Vec<_>>()
4624 .join("\n");
4625 env.new_string(s)
4626 .map(jni::objects::JString::into_raw)
4627 .unwrap_or(std::ptr::null_mut())
4628}
4629
4630#[unsafe(no_mangle)]
4631pub extern "system" fn Java_dev_yog_NativeBridge_nativeClientPacketChannels<'l>(
4632 env: JNIEnv<'l>,
4633 _class: JClass<'l>,
4634) -> jstring {
4635 let s = handlers()
4636 .client_packets
4637 .keys()
4638 .cloned()
4639 .chain([entity_data::ENTITY_DATA.to_owned()])
4640 .collect::<Vec<_>>()
4641 .join("\n");
4642 env.new_string(s)
4643 .map(jni::objects::JString::into_raw)
4644 .unwrap_or(std::ptr::null_mut())
4645}
4646
4647#[unsafe(no_mangle)]
4648pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawn<'l>(
4649 mut env: JNIEnv<'l>,
4650 _class: JClass<'l>,
4651 entity_type: JString<'l>,
4652 uuid: JString<'l>,
4653 dimension: JString<'l>,
4654) {
4655 let h = handlers();
4656 if h.entity_spawn.is_empty() {
4657 return;
4658 }
4659 let (et, u, d) = (
4660 jstr!(env, entity_type),
4661 jstr!(env, uuid),
4662 jstr!(env, dimension),
4663 );
4664 let ev = yog_abi::YogEntitySpawnEvent {
4665 entity_type: YogStr::from_str(&et),
4666 uuid: YogStr::from_str(&u),
4667 dimension: YogStr::from_str(&d),
4668 };
4669 let srv = srv_ptr();
4670 guard("on_entity_spawn", || {
4671 each(&h.entity_spawn, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4672 });
4673}
4674
4675#[unsafe(no_mangle)]
4676pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntitySpawnPre<'l>(
4677 mut env: JNIEnv<'l>,
4678 _class: JClass<'l>,
4679 entity_type: JString<'l>,
4680 uuid: JString<'l>,
4681 dimension: JString<'l>,
4682) -> jni::sys::jboolean {
4683 let h = handlers();
4684 if h.entity_spawn.is_empty() {
4685 return 1;
4686 }
4687 let et = jstr!(env, entity_type, 1);
4688 let u = jstr!(env, uuid, 1);
4689 let d = jstr!(env, dimension, 1);
4690 let ev = yog_abi::YogEntitySpawnEvent {
4691 entity_type: YogStr::from_str(&et),
4692 uuid: YogStr::from_str(&u),
4693 dimension: YogStr::from_str(&d),
4694 };
4695 let srv = srv_ptr();
4696 allowed(&h.entity_spawn, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4697}
4698
4699#[unsafe(no_mangle)]
4700pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityDamagePre<'l>(
4701 mut env: JNIEnv<'l>,
4702 _class: JClass<'l>,
4703 entity_type: JString<'l>,
4704 uuid: JString<'l>,
4705 amount: jfloat,
4706 source: JString<'l>,
4707 dimension: JString<'l>,
4708) -> jni::sys::jboolean {
4709 let h = handlers();
4710 if h.entity_damage.is_empty() {
4711 return 1;
4712 }
4713 let et = jstr!(env, entity_type, 1);
4714 let u = jstr!(env, uuid, 1);
4715 let s = jstr!(env, source, 1);
4716 let d = jstr!(env, dimension, 1);
4717 let ev = yog_abi::YogEntityDamageEvent {
4718 entity_type: YogStr::from_str(&et),
4719 uuid: YogStr::from_str(&u),
4720 amount,
4721 source: YogStr::from_str(&s),
4722 dimension: YogStr::from_str(&d),
4723 };
4724 let srv = srv_ptr();
4725 allowed(&h.entity_damage, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4726}
4727
4728#[unsafe(no_mangle)]
4729pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlockPre<'l>(
4730 mut env: JNIEnv<'l>,
4731 _class: JClass<'l>,
4732 player: JString<'l>,
4733 block: JString<'l>,
4734 x: jint,
4735 y: jint,
4736 z: jint,
4737 dimension: JString<'l>,
4738 sneaking: jni::sys::jboolean,
4739) -> jni::sys::jboolean {
4740 let h = handlers();
4741 if h.player_place_block.is_empty() {
4742 return 1;
4743 }
4744 let p = jstr!(env, player, 1);
4745 let b = jstr!(env, block, 1);
4746 let d = jstr!(env, dimension, 1);
4747 let ev = YogPlaceBlockEvent {
4748 player: YogStr::from_str(&p),
4749 block: YogStr::from_str(&b),
4750 pos: YogBlockPos { x, y, z },
4751 dimension: YogStr::from_str(&d),
4752 sneaking: sneaking != 0,
4753 };
4754 let srv = srv_ptr();
4755 allowed(&h.player_place_block, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4756}
4757
4758#[unsafe(no_mangle)]
4759pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlaceBlock<'l>(
4760 mut env: JNIEnv<'l>,
4761 _class: JClass<'l>,
4762 player: JString<'l>,
4763 block: JString<'l>,
4764 x: jint,
4765 y: jint,
4766 z: jint,
4767 dimension: JString<'l>,
4768 sneaking: jni::sys::jboolean,
4769) {
4770 let h = handlers();
4771 if h.player_place_block.is_empty() {
4772 return;
4773 }
4774 let (p, b, d) = (jstr!(env, player), jstr!(env, block), jstr!(env, dimension));
4775 let ev = YogPlaceBlockEvent {
4776 player: YogStr::from_str(&p),
4777 block: YogStr::from_str(&b),
4778 pos: YogBlockPos { x, y, z },
4779 dimension: YogStr::from_str(&d),
4780 sneaking: sneaking != 0,
4781 };
4782 let srv = srv_ptr();
4783 guard("on_player_place_block", || {
4784 each(&h.player_place_block, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4785 });
4786}
4787
4788#[unsafe(no_mangle)]
4789pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeathPre<'l>(
4790 mut env: JNIEnv<'l>,
4791 _class: JClass<'l>,
4792 player: JString<'l>,
4793 uuid: JString<'l>,
4794 source: JString<'l>,
4795 dimension: JString<'l>,
4796) -> jni::sys::jboolean {
4797 let h = handlers();
4798 if h.player_death.is_empty() {
4799 return 1;
4800 }
4801 let p = jstr!(env, player, 1);
4802 let u = jstr!(env, uuid, 1);
4803 let s = jstr!(env, source, 1);
4804 let d = jstr!(env, dimension, 1);
4805 let ev = YogPlayerDeathEvent {
4806 player: YogStr::from_str(&p),
4807 uuid: YogStr::from_str(&u),
4808 source: YogStr::from_str(&s),
4809 dimension: YogStr::from_str(&d),
4810 };
4811 let srv = srv_ptr();
4812 allowed(&h.player_death, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4813}
4814
4815#[unsafe(no_mangle)]
4816pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerDeath<'l>(
4817 mut env: JNIEnv<'l>,
4818 _class: JClass<'l>,
4819 player: JString<'l>,
4820 uuid: JString<'l>,
4821 source: JString<'l>,
4822 dimension: JString<'l>,
4823) {
4824 let h = handlers();
4825 if h.player_death.is_empty() {
4826 return;
4827 }
4828 let (p, u, s, d) = (
4829 jstr!(env, player),
4830 jstr!(env, uuid),
4831 jstr!(env, source),
4832 jstr!(env, dimension),
4833 );
4834 let ev = YogPlayerDeathEvent {
4835 player: YogStr::from_str(&p),
4836 uuid: YogStr::from_str(&u),
4837 source: YogStr::from_str(&s),
4838 dimension: YogStr::from_str(&d),
4839 };
4840 let srv = srv_ptr();
4841 guard("on_player_death", || {
4842 each(&h.player_death, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4843 });
4844}
4845
4846#[unsafe(no_mangle)]
4847pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerRespawn<'l>(
4848 mut env: JNIEnv<'l>,
4849 _class: JClass<'l>,
4850 player: JString<'l>,
4851 uuid: JString<'l>,
4852 at_anchor: jni::sys::jboolean,
4853 dimension: JString<'l>,
4854) {
4855 let h = handlers();
4856 if h.player_respawn.is_empty() {
4857 return;
4858 }
4859 let (p, u, d) = (jstr!(env, player), jstr!(env, uuid), jstr!(env, dimension));
4860 let ev = YogPlayerRespawnEvent {
4861 player: YogStr::from_str(&p),
4862 uuid: YogStr::from_str(&u),
4863 at_anchor: at_anchor != 0,
4864 dimension: YogStr::from_str(&d),
4865 };
4866 let srv = srv_ptr();
4867 guard("on_player_respawn", || {
4868 each(&h.player_respawn, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4869 });
4870}
4871
4872#[unsafe(no_mangle)]
4873pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnAdvancement<'l>(
4874 mut env: JNIEnv<'l>,
4875 _class: JClass<'l>,
4876 player: JString<'l>,
4877 uuid: JString<'l>,
4878 advancement: JString<'l>,
4879 dimension: JString<'l>,
4880) {
4881 let h = handlers();
4882 if h.advancement.is_empty() {
4883 return;
4884 }
4885 let (p, u, a, d) = (
4886 jstr!(env, player),
4887 jstr!(env, uuid),
4888 jstr!(env, advancement),
4889 jstr!(env, dimension),
4890 );
4891 let ev = YogAdvancementEvent {
4892 player: YogStr::from_str(&p),
4893 uuid: YogStr::from_str(&u),
4894 advancement: YogStr::from_str(&a),
4895 dimension: YogStr::from_str(&d),
4896 };
4897 let srv = srv_ptr();
4898 guard("on_advancement", || {
4899 each(&h.advancement, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4900 });
4901}
4902
4903#[unsafe(no_mangle)]
4904pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteractPre<'l>(
4905 mut env: JNIEnv<'l>,
4906 _class: JClass<'l>,
4907 player: JString<'l>,
4908 player_uuid: JString<'l>,
4909 entity_type: JString<'l>,
4910 entity_uuid: JString<'l>,
4911 hand: JString<'l>,
4912 dimension: JString<'l>,
4913) -> jni::sys::jboolean {
4914 let h = handlers();
4915 if h.entity_interact.is_empty() {
4916 return 1;
4917 }
4918 let p = jstr!(env, player, 1);
4919 let pu = jstr!(env, player_uuid, 1);
4920 let et = jstr!(env, entity_type, 1);
4921 let eu = jstr!(env, entity_uuid, 1);
4922 let ha = jstr!(env, hand, 1);
4923 let d = jstr!(env, dimension, 1);
4924 let ev = YogEntityInteractEvent {
4925 player: YogStr::from_str(&p),
4926 player_uuid: YogStr::from_str(&pu),
4927 entity_type: YogStr::from_str(&et),
4928 entity_uuid: YogStr::from_str(&eu),
4929 hand: YogStr::from_str(&ha),
4930 dimension: YogStr::from_str(&d),
4931 };
4932 let srv = srv_ptr();
4933 allowed(&h.entity_interact, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
4934}
4935
4936#[unsafe(no_mangle)]
4937pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnEntityInteract<'l>(
4938 mut env: JNIEnv<'l>,
4939 _class: JClass<'l>,
4940 player: JString<'l>,
4941 player_uuid: JString<'l>,
4942 entity_type: JString<'l>,
4943 entity_uuid: JString<'l>,
4944 hand: JString<'l>,
4945 dimension: JString<'l>,
4946) {
4947 let h = handlers();
4948 if h.entity_interact.is_empty() {
4949 return;
4950 }
4951 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
4952 let (et, eu, ha, d) = (
4953 jstr!(env, entity_type),
4954 jstr!(env, entity_uuid),
4955 jstr!(env, hand),
4956 jstr!(env, dimension),
4957 );
4958 let ev = YogEntityInteractEvent {
4959 player: YogStr::from_str(&p),
4960 player_uuid: YogStr::from_str(&pu),
4961 entity_type: YogStr::from_str(&et),
4962 entity_uuid: YogStr::from_str(&eu),
4963 hand: YogStr::from_str(&ha),
4964 dimension: YogStr::from_str(&d),
4965 };
4966 let srv = srv_ptr();
4967 guard("on_entity_interact", || {
4968 each(&h.entity_interact, |ud, f| unsafe { f(ud, srv, &ev, 1) });
4969 });
4970}
4971
4972#[unsafe(no_mangle)]
4973pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemCraft<'l>(
4974 mut env: JNIEnv<'l>,
4975 _class: JClass<'l>,
4976 player: JString<'l>,
4977 player_uuid: JString<'l>,
4978 result_item: JString<'l>,
4979 result_count: jint,
4980 dimension: JString<'l>,
4981) {
4982 let h = handlers();
4983 if h.item_craft.is_empty() {
4984 return;
4985 }
4986 let (p, pu, ri, d) = (
4987 jstr!(env, player),
4988 jstr!(env, player_uuid),
4989 jstr!(env, result_item),
4990 jstr!(env, dimension),
4991 );
4992 let ev = YogCraftEvent {
4993 player: YogStr::from_str(&p),
4994 player_uuid: YogStr::from_str(&pu),
4995 result_item: YogStr::from_str(&ri),
4996 result_count: result_count as u32,
4997 dimension: YogStr::from_str(&d),
4998 };
4999 let srv = srv_ptr();
5000 guard("on_item_craft", || {
5001 each(&h.item_craft, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5002 });
5003}
5004
5005#[unsafe(no_mangle)]
5006pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosionPre<'l>(
5007 mut env: JNIEnv<'l>,
5008 _class: JClass<'l>,
5009 dimension: JString<'l>,
5010 x: jdouble,
5011 y: jdouble,
5012 z: jdouble,
5013 power: jfloat,
5014 cause_uuid: JString<'l>,
5015) -> jni::sys::jboolean {
5016 let h = handlers();
5017 if h.explosion.is_empty() {
5018 return 1;
5019 }
5020 let d = jstr!(env, dimension, 1);
5021 let cu = jstr!(env, cause_uuid, 1);
5022 let ev = YogExplosionEvent {
5023 dimension: YogStr::from_str(&d),
5024 x,
5025 y,
5026 z,
5027 power,
5028 cause_uuid: YogStr::from_str(&cu),
5029 };
5030 let srv = srv_ptr();
5031 allowed(&h.explosion, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5032}
5033
5034#[unsafe(no_mangle)]
5035pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnExplosion<'l>(
5036 mut env: JNIEnv<'l>,
5037 _class: JClass<'l>,
5038 dimension: JString<'l>,
5039 x: jdouble,
5040 y: jdouble,
5041 z: jdouble,
5042 power: jfloat,
5043 cause_uuid: JString<'l>,
5044) {
5045 let h = handlers();
5046 if h.explosion.is_empty() {
5047 return;
5048 }
5049 let (d, cu) = (jstr!(env, dimension), jstr!(env, cause_uuid));
5050 let ev = YogExplosionEvent {
5051 dimension: YogStr::from_str(&d),
5052 x,
5053 y,
5054 z,
5055 power,
5056 cause_uuid: YogStr::from_str(&cu),
5057 };
5058 let srv = srv_ptr();
5059 guard("on_explosion", || {
5060 each(&h.explosion, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5061 });
5062}
5063
5064#[unsafe(no_mangle)]
5069pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemInsert<'l>(
5070 mut env: JNIEnv<'l>,
5071 _class: JClass<'l>,
5072 block: JString<'l>,
5073 x: jint,
5074 y: jint,
5075 z: jint,
5076 dimension: JString<'l>,
5077 item_id: JString<'l>,
5078 count: jint,
5079 nbt: JString<'l>,
5080 simulate: jni::sys::jboolean,
5081) -> jni::sys::jboolean {
5082 let h = handlers();
5083 if h.item_insert.is_empty() {
5084 return 0;
5085 }
5086 let (b, d, i, n) = (jstr!(env, block, 0), jstr!(env, dimension, 0), jstr!(env, item_id, 0), jstr!(env, nbt, 0));
5087 let ev = YogItemInsertEvent {
5088 block: YogStr::from_str(&b),
5089 pos: YogBlockPos { x, y, z },
5090 dimension: YogStr::from_str(&d),
5091 item_id: YogStr::from_str(&i),
5092 count: count.max(0) as u32,
5093 nbt: YogStr::from_str(&n),
5094 };
5095 let srv = srv_ptr();
5096 if simulate != 0 {
5097 !allowed(&h.item_insert, |ud, f| unsafe { !f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5099 } else {
5100 let mut taken = false;
5101 guard("on_item_insert", || {
5102 each(&h.item_insert, |ud, f| taken |= unsafe { f(ud, srv, &ev, 1) });
5103 });
5104 taken as jni::sys::jboolean
5105 }
5106}
5107
5108#[unsafe(no_mangle)]
5111pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickupPre<'l>(
5112 mut env: JNIEnv<'l>,
5113 _class: JClass<'l>,
5114 player: JString<'l>,
5115 player_uuid: JString<'l>,
5116 item_id: JString<'l>,
5117 item_count: jint,
5118 entity_uuid: JString<'l>,
5119 dimension: JString<'l>,
5120) -> jni::sys::jboolean {
5121 let h = handlers();
5122 if h.item_pickup.is_empty() {
5123 return 1;
5124 }
5125 let p = jstr!(env, player, 1);
5126 let pu = jstr!(env, player_uuid, 1);
5127 let ii = jstr!(env, item_id, 1);
5128 let eu = jstr!(env, entity_uuid, 1);
5129 let d = jstr!(env, dimension, 1);
5130 let ev = YogItemPickupEvent {
5131 player: YogStr::from_str(&p),
5132 player_uuid: YogStr::from_str(&pu),
5133 item_id: YogStr::from_str(&ii),
5134 item_count: item_count as u32,
5135 entity_uuid: YogStr::from_str(&eu),
5136 dimension: YogStr::from_str(&d),
5137 };
5138 let srv = srv_ptr();
5139 allowed(&h.item_pickup, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5140}
5141
5142#[unsafe(no_mangle)]
5143pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnItemPickup<'l>(
5144 mut env: JNIEnv<'l>,
5145 _class: JClass<'l>,
5146 player: JString<'l>,
5147 player_uuid: JString<'l>,
5148 item_id: JString<'l>,
5149 item_count: jint,
5150 entity_uuid: JString<'l>,
5151 dimension: JString<'l>,
5152) {
5153 let h = handlers();
5154 if h.item_pickup.is_empty() {
5155 return;
5156 }
5157 let (p, pu) = (jstr!(env, player), jstr!(env, player_uuid));
5158 let (ii, eu, d) = (
5159 jstr!(env, item_id),
5160 jstr!(env, entity_uuid),
5161 jstr!(env, dimension),
5162 );
5163 let ev = YogItemPickupEvent {
5164 player: YogStr::from_str(&p),
5165 player_uuid: YogStr::from_str(&pu),
5166 item_id: YogStr::from_str(&ii),
5167 item_count: item_count as u32,
5168 entity_uuid: YogStr::from_str(&eu),
5169 dimension: YogStr::from_str(&d),
5170 };
5171 let srv = srv_ptr();
5172 guard("on_item_pickup", || {
5173 each(&h.item_pickup, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5174 });
5175}
5176
5177#[unsafe(no_mangle)]
5178pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnPlayerMove<'l>(
5179 mut env: JNIEnv<'l>,
5180 _class: JClass<'l>,
5181 player: JString<'l>,
5182 player_uuid: JString<'l>,
5183 x: jdouble,
5184 y: jdouble,
5185 z: jdouble,
5186 yaw: jfloat,
5187 pitch: jfloat,
5188 dimension: JString<'l>,
5189) {
5190 let h = handlers();
5191 if h.player_move.is_empty() {
5192 return;
5193 }
5194 let (p, pu, d) = (
5195 jstr!(env, player),
5196 jstr!(env, player_uuid),
5197 jstr!(env, dimension),
5198 );
5199 let ev = YogPlayerMoveEvent {
5200 player: YogStr::from_str(&p),
5201 player_uuid: YogStr::from_str(&pu),
5202 x,
5203 y,
5204 z,
5205 yaw,
5206 pitch,
5207 dimension: YogStr::from_str(&d),
5208 };
5209 let srv = srv_ptr();
5210 guard("on_player_move", || {
5211 each(&h.player_move, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5212 });
5213}
5214
5215#[unsafe(no_mangle)]
5216pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpenPre<'l>(
5217 mut env: JNIEnv<'l>,
5218 _class: JClass<'l>,
5219 player: JString<'l>,
5220 player_uuid: JString<'l>,
5221 dimension: JString<'l>,
5222) -> jni::sys::jboolean {
5223 let h = handlers();
5224 if h.container_open.is_empty() {
5225 return 1;
5226 }
5227 let p = jstr!(env, player, 1);
5228 let pu = jstr!(env, player_uuid, 1);
5229 let d = jstr!(env, dimension, 1);
5230 let ev = YogContainerOpenEvent {
5231 player: YogStr::from_str(&p),
5232 player_uuid: YogStr::from_str(&pu),
5233 container_type: YogStr::EMPTY,
5234 dimension: YogStr::from_str(&d),
5235 };
5236 let srv = srv_ptr();
5237 allowed(&h.container_open, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5238}
5239
5240#[unsafe(no_mangle)]
5241pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerOpen<'l>(
5242 mut env: JNIEnv<'l>,
5243 _class: JClass<'l>,
5244 player: JString<'l>,
5245 player_uuid: JString<'l>,
5246 container_type: JString<'l>,
5247 dimension: JString<'l>,
5248) {
5249 let h = handlers();
5250 if h.container_open.is_empty() {
5251 return;
5252 }
5253 let (p, pu, ct, d) = (
5254 jstr!(env, player),
5255 jstr!(env, player_uuid),
5256 jstr!(env, container_type),
5257 jstr!(env, dimension),
5258 );
5259 let ev = YogContainerOpenEvent {
5260 player: YogStr::from_str(&p),
5261 player_uuid: YogStr::from_str(&pu),
5262 container_type: YogStr::from_str(&ct),
5263 dimension: YogStr::from_str(&d),
5264 };
5265 let srv = srv_ptr();
5266 guard("on_container_open", || {
5267 each(&h.container_open, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5268 });
5269}
5270
5271#[unsafe(no_mangle)]
5272pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnContainerClose<'l>(
5273 mut env: JNIEnv<'l>,
5274 _class: JClass<'l>,
5275 player: JString<'l>,
5276 player_uuid: JString<'l>,
5277 dimension: JString<'l>,
5278) {
5279 let h = handlers();
5280 if h.container_close.is_empty() {
5281 return;
5282 }
5283 let (p, pu, d) = (
5284 jstr!(env, player),
5285 jstr!(env, player_uuid),
5286 jstr!(env, dimension),
5287 );
5288 let ev = YogContainerCloseEvent {
5289 player: YogStr::from_str(&p),
5290 player_uuid: YogStr::from_str(&pu),
5291 dimension: YogStr::from_str(&d),
5292 };
5293 let srv = srv_ptr();
5294 guard("on_container_close", || {
5295 each(&h.container_close, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5296 });
5297}
5298
5299#[unsafe(no_mangle)]
5300pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHitPre<'l>(
5301 mut env: JNIEnv<'l>,
5302 _class: JClass<'l>,
5303 projectile_type: JString<'l>,
5304 projectile_uuid: JString<'l>,
5305 shooter_uuid: JString<'l>,
5306 hit_type: JString<'l>,
5307 hit_entity_uuid: JString<'l>,
5308 x: jdouble,
5309 y: jdouble,
5310 z: jdouble,
5311 dimension: JString<'l>,
5312) -> jni::sys::jboolean {
5313 let h = handlers();
5314 if h.projectile_hit.is_empty() {
5315 return 1;
5316 }
5317 let pt = jstr!(env, projectile_type, 1);
5318 let pu = jstr!(env, projectile_uuid, 1);
5319 let su = jstr!(env, shooter_uuid, 1);
5320 let ht = jstr!(env, hit_type, 1);
5321 let heu = jstr!(env, hit_entity_uuid, 1);
5322 let dim = jstr!(env, dimension, 1);
5323 let ev = YogProjectileHitEvent {
5324 projectile_type: YogStr::from_str(&pt),
5325 projectile_uuid: YogStr::from_str(&pu),
5326 shooter_uuid: YogStr::from_str(&su),
5327 hit_type: YogStr::from_str(&ht),
5328 hit_entity_uuid: YogStr::from_str(&heu),
5329 x,
5330 y,
5331 z,
5332 dimension: YogStr::from_str(&dim),
5333 };
5334 let srv = srv_ptr();
5335 allowed(&h.projectile_hit, |ud, f| unsafe { f(ud, srv, &ev, 0) }) as jni::sys::jboolean
5336}
5337
5338#[unsafe(no_mangle)]
5339pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnProjectileHit<'l>(
5340 mut env: JNIEnv<'l>,
5341 _class: JClass<'l>,
5342 projectile_type: JString<'l>,
5343 projectile_uuid: JString<'l>,
5344 shooter_uuid: JString<'l>,
5345 hit_type: JString<'l>,
5346 hit_entity_uuid: JString<'l>,
5347 x: jdouble,
5348 y: jdouble,
5349 z: jdouble,
5350 dimension: JString<'l>,
5351) {
5352 let h = handlers();
5353 if h.projectile_hit.is_empty() {
5354 return;
5355 }
5356 let (pt, pu) = (jstr!(env, projectile_type), jstr!(env, projectile_uuid));
5357 let (su, ht) = (jstr!(env, shooter_uuid), jstr!(env, hit_type));
5358 let (heu, dim) = (jstr!(env, hit_entity_uuid), jstr!(env, dimension));
5359 let ev = YogProjectileHitEvent {
5360 projectile_type: YogStr::from_str(&pt),
5361 projectile_uuid: YogStr::from_str(&pu),
5362 shooter_uuid: YogStr::from_str(&su),
5363 hit_type: YogStr::from_str(&ht),
5364 hit_entity_uuid: YogStr::from_str(&heu),
5365 x,
5366 y,
5367 z,
5368 dimension: YogStr::from_str(&dim),
5369 };
5370 let srv = srv_ptr();
5371 guard("on_projectile_hit", || {
5372 each(&h.projectile_hit, |ud, f| unsafe { f(ud, srv, &ev, 1) });
5373 });
5374}
5375
5376#[unsafe(no_mangle)]
5379pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnClientTick<'l>(
5380 _env: JNIEnv<'l>,
5381 _class: JClass<'l>,
5382) {
5383 let h = handlers();
5384 if h.client_tick.is_empty() {
5385 return;
5386 }
5387 guard("on_client_tick", || {
5388 each(&h.client_tick, |ud, f| unsafe { f(ud) });
5389 });
5390}
5391
5392fn gl_proc_address(sym: &str) -> usize {
5394 let ptr: Option<i64> = unsafe { bridge!("glProcAddress", [S(YogStr::from_str(sym))]) };
5395 ptr.unwrap_or(0) as usize
5396}
5397
5398#[unsafe(no_mangle)]
5399pub extern "system" fn Java_dev_yog_NativeBridge_nativeGlInit<'l>(
5400 _env: JNIEnv<'l>,
5401 _class: JClass<'l>,
5402) {
5403 if GL.get().is_some() {
5404 return;
5405 }
5406 let mut raw_get_binary: usize = 0;
5407 let mut raw_prog_binary: usize = 0;
5408 let gl = unsafe {
5409 glow::Context::from_loader_function(|sym| {
5410 let ptr = gl_proc_address(sym);
5411 match sym {
5413 "glGetProgramBinary" => raw_get_binary = ptr,
5414 "glProgramBinary" => raw_prog_binary = ptr,
5415 _ => {}
5416 }
5417 ptr as *const _
5418 })
5419 };
5420 let _ = GL.set(GlCtx(gl));
5421 let _ = GL_GET_PROGRAM_BINARY.set((raw_get_binary != 0).then_some(raw_get_binary));
5422 let _ = GL_PROGRAM_BINARY.set((raw_prog_binary != 0).then_some(raw_prog_binary));
5423 let get_program_iv = gl_proc_address("glGetProgramiv");
5426 let _ = GL_GET_PROGRAM_IV.set((get_program_iv != 0).then_some(get_program_iv));
5427}
5428
5429#[unsafe(no_mangle)]
5430pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnHudRender<'l>(
5431 _env: JNIEnv<'l>,
5432 _class: JClass<'l>,
5433 delta_tick: jfloat,
5434 screen_w: jint,
5435 screen_h: jint,
5436 scale_factor: jfloat,
5437 player_x: jfloat,
5438 player_y: jfloat,
5439 player_z: jfloat,
5440) {
5441 let h = handlers();
5442 let mut gfx = GFX_FN_TABLE;
5443 gfx.screen_w = screen_w;
5444 gfx.screen_h = screen_h;
5445 gfx.delta_tick = delta_tick;
5446 gfx.scale_factor = scale_factor;
5447 gfx.player_pos = [player_x, player_y, player_z];
5448
5449 if !h.hud_render.is_empty() {
5451 guard("on_hud_render", || {
5452 each(&h.hud_render, |ud, f| unsafe { f(ud, &gfx) });
5453 });
5454 }
5455
5456 let active_layers: Vec<UiLayer> = h.active_uis.lock().unwrap().clone();
5458 let sw = screen_w as f32;
5459 let sh = screen_h as f32;
5460 let ctx = unsafe { yog_gfx::GfxContext::from_raw(&gfx as *const _) };
5461 let mut renderers = h.book_renderers.lock().unwrap();
5462 for layer in &active_layers {
5463 if !layer.visible {
5464 continue;
5465 }
5466 if let Some(renderer) = renderers.get_mut(&layer.id) {
5467 renderer.render(&ctx, sw, sh);
5468 }
5469 }
5470}
5471
5472#[unsafe(no_mangle)]
5473pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnWorldRender<'l>(
5474 env: JNIEnv<'l>,
5475 _class: JClass<'l>,
5476 delta_tick: jfloat,
5477 screen_w: jint,
5478 screen_h: jint,
5479 scale_factor: jfloat,
5480 view_proj_arr: JFloatArray<'l>,
5481 cam_x: jfloat,
5482 cam_y: jfloat,
5483 cam_z: jfloat,
5484 player_x: jfloat,
5485 player_y: jfloat,
5486 player_z: jfloat,
5487) {
5488 let h = handlers();
5489 if h.world_render.is_empty() {
5490 return;
5491 }
5492 let mut view_proj = [0f32; 16];
5493 if env
5494 .get_float_array_region(&view_proj_arr, 0, &mut view_proj)
5495 .is_err()
5496 {
5497 return;
5498 }
5499 let mut gfx = GFX_FN_TABLE;
5500 gfx.screen_w = screen_w;
5501 gfx.screen_h = screen_h;
5502 gfx.delta_tick = delta_tick;
5503 gfx.scale_factor = scale_factor;
5504 gfx.view_proj = view_proj;
5505 gfx.camera_pos = [cam_x, cam_y, cam_z];
5506 gfx.player_pos = [player_x, player_y, player_z];
5507 guard("on_world_render", || {
5508 each(&h.world_render, |ud, f| unsafe { f(ud, &gfx) });
5509 });
5510}
5511
5512#[unsafe(no_mangle)]
5513pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnKeyPress<'l>(
5514 _env: JNIEnv<'l>,
5515 _class: JClass<'l>,
5516 key_code: jint,
5517 scan_code: jint,
5518 action: jint,
5519 modifiers: jint,
5520) -> jni::sys::jboolean {
5521 let h = handlers();
5522 if h.key_press.is_empty() {
5523 return 1;
5524 }
5525 let ev = YogKeyPressEvent {
5526 key_code,
5527 scan_code,
5528 action,
5529 modifiers,
5530 };
5531 allowed(&h.key_press, |ud, f| unsafe { f(ud, &ev) }) as jni::sys::jboolean
5532}
5533
5534#[unsafe(no_mangle)]
5535pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenOpen<'l>(
5536 mut env: JNIEnv<'l>,
5537 _class: JClass<'l>,
5538 screen_class: JString<'l>,
5539) {
5540 let h = handlers();
5541 if h.screen_open.is_empty() {
5542 return;
5543 }
5544 let sc = match env.get_string(&screen_class) {
5545 Ok(s) => String::from(s),
5546 Err(_) => return,
5547 };
5548 guard("on_screen_open", || {
5549 each(&h.screen_open, |ud, f| unsafe {
5550 f(ud, YogStr::from_str(&sc))
5551 });
5552 });
5553}
5554
5555#[unsafe(no_mangle)]
5556pub extern "system" fn Java_dev_yog_NativeBridge_nativeOnScreenClose<'l>(
5557 mut env: JNIEnv<'l>,
5558 _class: JClass<'l>,
5559 screen_class: JString<'l>,
5560) {
5561 let h = handlers();
5562 if h.screen_close.is_empty() {
5563 return;
5564 }
5565 let sc = match env.get_string(&screen_class) {
5566 Ok(s) => String::from(s),
5567 Err(_) => return,
5568 };
5569 guard("on_screen_close", || {
5570 each(&h.screen_close, |ud, f| unsafe {
5571 f(ud, YogStr::from_str(&sc))
5572 });
5573 });
5574}
5575
5576mod body;
5577mod loot;
5578#[cfg(feature = "dev")]
5580mod registrations;
5581mod entity_data;
5582mod client_api;
5583mod content_blob;
5584mod machine;
5585#[cfg(feature = "dev")]
5586mod control_socket;
5587mod images;
5588mod svg;
5589mod jcall;
5590mod ui_jni;
5591#[cfg(feature = "bench")]
5593#[doc(hidden)]
5594pub mod bench_support;
5595#[cfg(test)]
5596mod tests;