Skip to main content

yog_api/
lib.rs

1//! Yog API — the single crate mod authors depend on.
2//!
3//! A facade that re-exports every Yog domain plus the central [`Registry`] hub.
4//! Add a new domain crate, re-export it here, and mods pick it up via
5//! `yog_api::*`. Items are available both flat (`yog_api::Registry`) and
6//! namespaced by domain (`yog_api::world::World`).
7
8mod interop;
9mod lang;
10mod registry;
11
12pub use interop::Interop;
13pub use lang::{format_translation, tr, tr_args, translation};
14pub use registry::{
15    close_ui, installed_mods, open_ui, server, CServer, Mod, ModInfo, Registry, UiHandled,
16};
17pub use yog_gfx::{core as gfx_core, draw2d as gfx_draw2d, gl as gfx_gl, image as gfx_image, GfxContext};
18
19/// Stable C ABI — re-exported so mods don't need a direct `yog-abi` dependency.
20pub use yog_abi::{YogApi, ABI_VERSION};
21
22/// Inter-mod communication proc-macros.
23pub use yog_interop::{import, yog_export};
24
25/// rkyv — zero-copy serialization for interop. Re-exported so generated
26/// code and mods can use `rkyv::Archive`, `rkyv::Serialize`, `rkyv::Deserialize`
27/// without a direct dependency.
28pub use rkyv;
29
30/// Re-exported so `#[yog_export]`-generated code can emit `#[::yog_api::ctor::ctor(unsafe)]`
31/// without mods needing a direct `ctor` dependency. Constructors run when the
32/// mod's `cdylib` is loaded — guaranteed to complete before the runtime calls
33/// `yog_mod_register`, so the export registry is fully populated by then.
34pub use ctor;
35
36#[doc(hidden)]
37pub use std::os::raw::c_void as __c_void;
38
39/// Export a [`Mod`] as a dynamically loadable Yog mod.
40///
41/// Generates the two C-ABI entry points the runtime looks up:
42/// - `yog_abi_version() -> u32`  — version check before loading
43/// - `yog_mod_register(*const YogApi, *const c_char)` — registration entry point,
44///   receives the mod's `id` from its manifest
45///
46/// Put this once at the crate root of a `cdylib` mod:
47///
48/// ```ignore
49/// yog_api::export_mod!(MyMod);
50/// ```
51#[macro_export]
52macro_rules! export_mod {
53    ($mod_ty:ty) => {
54        #[no_mangle]
55        pub extern "C" fn yog_abi_version() -> u32 {
56            $crate::ABI_VERSION
57        }
58
59        #[no_mangle]
60        pub unsafe extern "C" fn yog_mod_register(
61            api: *const $crate::YogApi,
62            mod_id_ptr: *const ::std::os::raw::c_char,
63        ) {
64            // Parse mod_id from the C string passed by the runtime.
65            let mod_id: &str = if mod_id_ptr.is_null() {
66                "unknown"
67            } else {
68                match ::std::ffi::CStr::from_ptr(mod_id_ptr).to_str() {
69                    Ok(s) => s,
70                    Err(_) => "unknown",
71                }
72            };
73            // Store for interop use (yog_api::interop::current_mod_id()).
74            $crate::__set_current_mod_id(mod_id);
75            // Every mod is handed the same global `YogApi` table, so any
76            // mod's own pointer works for reconstructing a `Registry` inside
77            // an exported function later — even when called from a thread
78            // that never ran `yog_mod_register` itself.
79            $crate::__set_current_api_ptr(api as usize);
80
81            // Catch panics so they never unwind across this `extern "C"` boundary
82            // back into the runtime (which would be undefined behaviour).
83            let outcome = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
84                // SAFETY: the runtime passes a valid YogApi pointer, verified via
85                // yog_abi_version() and abi_version/size checks before this call.
86                let mut registry = unsafe { $crate::Registry::from_raw(api) };
87                <$mod_ty as $crate::Mod>::register(&mut registry);
88
89                // Auto-register all #[yog_export] functions (populated by
90                // per-function `ctor` constructors at cdylib load time).
91                for entry in $crate::__yog_export_registry().lock().unwrap().iter() {
92                    registry
93                        .interop()
94                        .export(entry.name, entry.ptr as *const ::std::os::raw::c_void);
95                }
96
97                // Resolve pending imports (may partially succeed — runtime
98                // does a final pass after all mods loaded).
99                $crate::__yog_resolve_pending_imports(&registry);
100            }));
101            if outcome.is_err() {
102                $crate::error!(
103                    "mod {} panicked during register",
104                    ::core::stringify!($mod_ty)
105                );
106            }
107        }
108
109        /// Called by the runtime after ALL mods are loaded — resolves any
110        /// imports that weren't satisfied during initial registration.
111        #[no_mangle]
112        pub unsafe extern "C" fn __yog_resolve_imports_final(api: *const $crate::YogApi) {
113            let registry = unsafe { $crate::Registry::from_raw(api) };
114            $crate::__yog_resolve_pending_imports(&registry);
115        }
116    };
117}
118
119/// Internal: set by `export_mod!` before calling `Mod::register`.
120/// Used by `yog_api::interop::current_mod_id()` so `Interop::export` knows
121/// which mod is calling.
122#[doc(hidden)]
123pub fn __set_current_mod_id(id: &str) {
124    CURRENT_MOD_ID.with(|cell| cell.replace(Some(id.to_string())));
125}
126
127/// Internal: the current mod's id, set during `yog_mod_register`.
128#[doc(hidden)]
129pub fn __current_mod_id() -> Option<String> {
130    CURRENT_MOD_ID.with(|cell| cell.borrow().clone())
131}
132
133/// Internal: the global `YogApi` pointer, set by every mod's `yog_mod_register`
134/// (it's the same singleton table for the whole process). Process-wide (not
135/// thread-local) because `#[yog_export]`-generated wrappers read it whenever
136/// an exported function is *called*, which may happen on any thread, long
137/// after the loading thread's `yog_mod_register` returned.
138#[doc(hidden)]
139pub fn __set_current_api_ptr(ptr: usize) {
140    API_PTR.store(ptr, std::sync::atomic::Ordering::Release);
141}
142
143#[doc(hidden)]
144pub fn __current_api_ptr() -> usize {
145    API_PTR.load(std::sync::atomic::Ordering::Acquire)
146}
147
148static API_PTR: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
149
150std::thread_local! {
151    static CURRENT_MOD_ID: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
152}
153
154// ── Auto-export registry ─────────────────────────────────────────────────
155
156#[doc(hidden)]
157pub struct YogExportEntry {
158    pub name: &'static str,
159    pub ptr: usize,
160}
161
162#[doc(hidden)]
163pub struct YogImportEntry {
164    pub mod_id: &'static str,
165    pub symbol: &'static str,
166    pub bind_fn: usize,
167}
168
169#[doc(hidden)]
170pub fn __yog_export_registry() -> &'static std::sync::Mutex<Vec<YogExportEntry>> {
171    static REG: std::sync::LazyLock<std::sync::Mutex<Vec<YogExportEntry>>> =
172        std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
173    &REG
174}
175
176#[doc(hidden)]
177pub fn __yog_import_registry() -> &'static std::sync::Mutex<Vec<YogImportEntry>> {
178    static REG: std::sync::LazyLock<std::sync::Mutex<Vec<YogImportEntry>>> =
179        std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
180    &REG
181}
182
183/// Called by `export_mod!` after registration. Iterates pending imports and
184/// resolves them via the interop table. Also exported as `#[no_mangle]` so the
185/// runtime can call it after all mods are loaded (final resolution pass).
186#[doc(hidden)]
187pub unsafe fn __yog_resolve_pending_imports(registry: &crate::Registry) {
188    let interop = registry.interop();
189    for entry in __yog_import_registry().lock().unwrap().iter() {
190        if let Some(ptr) = interop.import_raw(entry.mod_id, entry.symbol) {
191            let bind: unsafe extern "C" fn(*const std::ffi::c_void) =
192                std::mem::transmute(entry.bind_fn);
193            bind(ptr);
194        }
195    }
196}
197
198pub use yog_book::{
199    crafting_page, entity_page, image_page, pattern_page, relations_page, smelting_page,
200    spotlight_page, text_page, text_page_titled,
201};
202pub use yog_book::{Book, BookCategory, BookEntry, BookMacro, BookPage, BookRegistry};
203pub use yog_book::{BookFontRegistry, BookRenderer};
204pub use yog_command::CommandContext;
205pub use yog_config::Config;
206pub use yog_core::{BlockPos, Server};
207pub use yog_dimensions::{ChunkWriter, YogDimensionDef, YogDimensionTypeDef};
208pub use yog_entity::Entity;
209pub use yog_event::{
210    AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
211    ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
212    EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent, ItemPickupEvent,
213    KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent, PlayerLeaveEvent,
214    PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent, UseBlockEvent,
215    UseItemEvent,
216};
217pub use yog_inventory::{InventoryDef, SlotLayout};
218#[doc(inline)]
219pub use yog_network::packet;
220pub use yog_network::{Packet, PacketEvent, PacketField};
221pub use yog_player::Player;
222pub use yog_registry::{
223    AdvancementReward, BlockDef, BookRecipe, FoodDef, FurnaceRecipe, ItemDef, ItemModifier,
224    ShapedRecipe, ShapelessRecipe, StartupGrant,
225};
226pub use yog_storage::{Storage, StorageScope, Value};
227pub use yog_ui::{widget, Align, Dock, FlexDir, FocusStyle, LayoutNode, Rect, Stretch, UiRoot, Units};
228pub use yog_world::World;
229
230/// Logging macros (`yog_api::info!`, `warn!`, `error!`).
231pub use yog_logging::{error, info, warn};
232
233/// Core types and handles.
234pub mod core {
235    pub use yog_core::*;
236}
237
238/// Events and the subscription registry.
239pub mod event {
240    pub use yog_event::*;
241}
242
243/// World access (block get/set, dimensions).
244pub mod world {
245    pub use yog_world::*;
246}
247
248/// Entity access (teleport, position, health, ... by UUID).
249pub mod entity {
250    pub use yog_entity::*;
251}
252
253/// Player access (give item, teleport).
254pub mod player {
255    pub use yog_player::*;
256}
257
258/// Content registration (custom items / blocks / food).
259pub mod content {
260    pub use yog_registry::*;
261}
262
263/// Networking (raw-byte packets over channels).
264pub mod network {
265    pub use yog_network::*;
266}
267
268/// Commands.
269pub mod command {
270    pub use yog_command::*;
271}
272
273/// Custom dimensions — dimension-type definitions and runtime chunk generation.
274pub mod dimension {
275    pub use yog_dimensions::*;
276}
277
278/// Persistent key-value storage for mod data.
279pub mod storage {
280    pub use yog_storage::*;
281}
282
283/// Mod configuration (typed key/value files).
284pub mod config {
285    pub use yog_config::*;
286}
287
288/// In-game book/documentation system (Patchouli-like).
289pub mod book {
290    pub use yog_book::*;
291}
292
293/// UI framework — flexbox layout + widgets on top of yog-gfx.
294pub mod ui {
295    pub use yog_ui::*;
296}
297
298/// Pulls a `.yui` screen's generated code into the current module — the
299/// code-behind's half of `src/<name>.yui`:
300///
301/// ```ignore
302/// // src/mod_list.rs, next to src/mod_list.yui
303/// yog_api::ui_include!("mod_list");
304/// ```
305///
306/// `<name>` is the `.yui` file's path under `src/` without the extension
307/// (`"screens/options"` for `src/screens/options.yui`). `yog build`,
308/// `yog check` and `yog ui gen` write the code to
309/// `.yog-build/generated/<name>.g.rs` (the build crate's own directory,
310/// `CARGO_MANIFEST_DIR`), so it's never committed and never stale.
311#[macro_export]
312macro_rules! ui_include {
313    ($name:literal) => {
314        include!(concat!(env!("CARGO_MANIFEST_DIR"), "/generated/", $name, ".g.rs"));
315    };
316}
317
318/// Inventory framework — real Container/Menu screens (BlockEntity-backed),
319/// as opposed to `ui`'s HUD-drawn overlays. See `yog-inventory`'s DESIGN.md.
320pub mod inventory {
321    pub use yog_inventory::*;
322}