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