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
8pub mod advancements;
9pub mod body;
10mod bus;
11pub mod client;
12mod draw;
13pub mod entities;
14pub mod java;
15pub mod loot;
16pub mod vanilla;
17pub use bus::{EventCx, Flow, IntoFlow, emit};
18pub use service::call;
19mod interop;
20mod lang;
21mod registry;
22mod scheduler;
23mod service;
24
25#[doc(hidden)]
26pub use interop::{__yog_export_registry, __yog_serve, YogExportEntry};
27pub use interop::{call_export, export_address};
28pub use lang::{format_translation, tag_name, tr, tr_args, translation};
29pub use registry::{Mod, ModInfo, Registry, UiHandled, close_ui, installed_mods, open_ui};
30pub use yog_gfx::{
31 GfxContext, core as gfx_core, draw2d as gfx_draw2d, gl as gfx_gl, image as gfx_image,
32};
33
34/// Stable C ABI — re-exported so mods don't need a direct `yog-abi` dependency.
35pub use yog_abi::{ABI_VERSION, YogApi};
36#[doc(hidden)]
37pub use yog_abi::{YogBytes, YogReply, YogStatus};
38
39/// Yog Data — what content definitions and book pages' `data`
40/// (`Registry::on_book_page`) are; re-exported for mods.
41pub use yog_data;
42/// The names Yog shares — kinds, namespaces, lang keys — for a mod to use as the loader does.
43pub use yog_ids;
44/// The sync primitives Yog code uses (locks that outlive a panic) — a mod's too.
45pub use yog_sync as sync;
46
47pub use java::mod_loaded;
48
49/// Inter-mod communication proc-macros.
50pub use yog_interop::{bench, import, yog_export};
51
52/// Re-exported so `#[yog_export]`-generated code can emit `#[::yog_api::ctor::ctor(unsafe)]`
53/// without mods needing a direct `ctor` dependency. Constructors run when the
54/// mod's `cdylib` is loaded — guaranteed to complete before the runtime calls
55/// `yog_mod_register`, so the export registry is fully populated by then.
56pub use ctor;
57
58#[doc(hidden)]
59pub use std::os::raw::c_void as __c_void;
60#[doc(hidden)]
61pub use yog_abi::{YogGfxApi as __YogGfxApi, YogStr as __YogStr};
62
63/// Export a [`Mod`] as a dynamically loadable Yog mod.
64///
65/// Generates the two C-ABI entry points the runtime looks up:
66/// - `yog_abi_version() -> u32` — version check before loading
67/// - `yog_mod_register(*const YogApi, *const c_char)` — registration entry point,
68/// receives the mod's `id` from its manifest
69///
70/// Put this once at the crate root of a `cdylib` mod:
71///
72/// ```ignore
73/// yog_api::export_mod!(MyMod);
74/// ```
75#[macro_export]
76macro_rules! export_mod {
77 ($mod_ty:ty) => {
78 #[unsafe(no_mangle)]
79 pub extern "C" fn yog_abi_version() -> u32 {
80 $crate::ABI_VERSION
81 }
82
83 #[unsafe(no_mangle)]
84 pub unsafe extern "C" fn yog_mod_register(
85 api: *const $crate::YogApi,
86 mod_id_ptr: *const ::std::os::raw::c_char,
87 ) {
88 // Parse mod_id from the C string passed by the runtime.
89 let mod_id: &str = if mod_id_ptr.is_null() {
90 "unknown"
91 } else {
92 // SAFETY: a non-null id is a NUL-terminated string from the runtime.
93 match unsafe { ::std::ffi::CStr::from_ptr(mod_id_ptr) }.to_str() {
94 Ok(s) => s,
95 Err(_) => "unknown",
96 }
97 };
98 // Store for interop use (yog_api::interop::current_mod_id()).
99 $crate::__set_current_mod_id(mod_id);
100 // Every mod is handed the same global `YogApi` table, so any
101 // mod's own pointer works for reconstructing a `Registry` inside
102 // an exported function later — even when called from a thread
103 // that never ran `yog_mod_register` itself.
104 $crate::__set_current_api_ptr(api as usize);
105
106 // Catch panics so they never unwind across this `extern "C"` boundary
107 // back into the runtime (which would be undefined behaviour).
108 let outcome = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
109 // SAFETY: the runtime passes a valid YogApi pointer, verified via
110 // yog_abi_version() and abi_version/size checks before this call.
111 let mut registry = unsafe { $crate::Registry::from_raw(api) };
112 <$mod_ty as $crate::Mod>::register(&mut registry);
113
114 // Blocks and items of `content/**/*.yblock|*.yitem`
115 // (`yog_api::content!()`, self-registered by `ctor`).
116 let content: ::std::vec::Vec<fn(&mut $crate::Registry)> =
117 $crate::__yog_content_registrars().lock().clone();
118 for register in content {
119 register(&mut registry);
120 }
121
122 // Every #[yog_export] function (registered by its `ctor`
123 // when the library loaded) provides `mod_id:name`.
124 for entry in $crate::__yog_export_registry().lock().iter() {
125 registry.provide_export(mod_id, entry);
126 }
127 }));
128 if outcome.is_err() {
129 $crate::error!(
130 "mod {} panicked during register",
131 ::core::stringify!($mod_ty)
132 );
133 }
134 }
135
136 /// Yog-IDLE's screen designer: the version of the protocol below.
137 #[unsafe(no_mangle)]
138 pub extern "C" fn yog_ui_designer_abi() -> u32 {
139 $crate::ui::component::designer::ABI
140 }
141
142 /// Yog-IDLE's screen designer: lists this mod's custom components
143 /// and draws them (see `yog_ui::component::designer`). Never called
144 /// in game — only by the designer host, which loads the mod's
145 /// library built for the developer's machine.
146 #[unsafe(no_mangle)]
147 pub unsafe extern "C" fn yog_ui_designer(
148 request: $crate::YogBytes,
149 gfx: *const $crate::__YogGfxApi,
150 user_data: *mut $crate::__c_void,
151 write: $crate::ui::component::designer::WriteFn,
152 ) -> bool {
153 // SAFETY: forwarded under the same contract.
154 unsafe { $crate::ui::component::designer::call(request, gfx, user_data, write) }
155 }
156 };
157}
158
159/// Registers `.yui` components (types implementing
160/// [`ui::component::Component`]) when the mod's library loads, so screens
161/// can use `<Name …/>` and Yog-IDLE's designer host can draw them. The code
162/// `yog build` generates from a `<Component>` `.yui` calls this — mods
163/// don't write it themselves.
164#[macro_export]
165macro_rules! ui_component {
166 ($($ty:ty),+ $(,)?) => {
167 const _: () = {
168 #[$crate::ctor::ctor(unsafe, crate_path = $crate::ctor)]
169 fn __yog_register_ui_components() {
170 $( $crate::ui::component::register::<$ty>(); )+
171 }
172 };
173 };
174}
175
176/// Compiles in the mod's component `.yui` files that have no code-behind
177/// `.rs` (those with one are pulled in by its `ui_include!`) — call it once,
178/// in `src/lib.rs`:
179///
180/// ```ignore
181/// yog_api::ui_components!();
182/// ```
183///
184/// `yog build`, `yog check` and `yog ui gen` write the list to
185/// `.yog-build/generated/components.g.rs` (a module per component).
186#[macro_export]
187macro_rules! ui_components {
188 () => {
189 include!(concat!(
190 env!("CARGO_MANIFEST_DIR"),
191 "/generated/components.g.rs"
192 ));
193 };
194}
195
196/// Internal: set by `export_mod!` before calling `Mod::register`.
197/// Used by `yog_api::interop::current_mod_id()` so `Interop::export` knows
198/// which mod is calling.
199#[doc(hidden)]
200pub fn __set_current_mod_id(id: &str) {
201 CURRENT_MOD_ID.with(|cell| cell.replace(Some(id.to_string())));
202}
203
204/// Internal: the current mod's id, set during `yog_mod_register`.
205#[doc(hidden)]
206pub fn __current_mod_id() -> Option<String> {
207 CURRENT_MOD_ID.with(|cell| cell.borrow().clone())
208}
209
210/// Internal: the global `YogApi` pointer, set by every mod's `yog_mod_register`
211/// (it's the same singleton table for the whole process). Process-wide (not
212/// thread-local) because `#[yog_export]`-generated wrappers read it whenever
213/// an exported function is *called*, which may happen on any thread, long
214/// after the loading thread's `yog_mod_register` returned.
215#[doc(hidden)]
216pub fn __set_current_api_ptr(ptr: usize) {
217 API_PTR.store(ptr, yog_sync::atomic::Ordering::Release);
218}
219
220#[doc(hidden)]
221pub fn __current_api_ptr() -> usize {
222 API_PTR.load(yog_sync::atomic::Ordering::Acquire)
223}
224
225static API_PTR: yog_sync::atomic::AtomicUsize = yog_sync::atomic::AtomicUsize::new(0);
226
227std::thread_local! {
228 static CURRENT_MOD_ID: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
229}
230
231pub use yog_book::BookRenderer;
232pub use yog_command::CommandContext;
233pub use yog_config::Config;
234pub use yog_core::{
235 BlockHit, BlockInfo, BlockPos, EntityInfo, Face, PlacedBlock, PlayerInfo, Point, Rotation,
236 ScreenSlot, SlotStack, Stack, entity_flags, split_state,
237};
238pub use yog_dimensions::{GeneratedChunk, YogDimensionDef, YogDimensionTypeDef};
239pub use yog_event::{Entity, Event, EventPhase, Message, Player, Service, World};
240/// A packet the server or the client received (`on_packet` / `on_client_packet`).
241pub use yog_event::{client::net::Packet as ClientPacketEvent, server::net::Packet as PacketEvent};
242pub use yog_formats::book::{Book, BookCategory, BookEntry, BookPage};
243pub use yog_inventory::{InventoryDef, SlotLayout};
244pub use yog_network::Packet;
245pub use yog_registry::{
246 ArmorDefense, ArmorMaterial, ArmorPiece, ArmorSlot, BlockDef, BlockProperty, Cooking,
247 EntityDef, EntityKind, Facing, FoodDef, ItemDef, Placement, Recipe, RecipeKind, SoundGroup,
248 StartupGrant, ToolItem, ToolMaterial,
249};
250pub use yog_storage::{Storage, StorageScope, Value};
251pub use yog_ui::{
252 Align, Dock, FlexDir, FocusStyle, LayoutNode, Rect, Stretch, UiRoot, Units, widget,
253};
254
255/// Logging macros (`yog_api::info!`, `warn!`, `error!`).
256pub use yog_logging::{error, info, warn};
257
258/// Core types and handles.
259pub mod core {
260 pub use yog_core::*;
261}
262
263/// Events by plane — `event::player::session::Join`, `event::block::Break`, … —
264/// subscribed with `Registry::on` / `before` / `after`.
265pub mod event {
266 pub use yog_event::*;
267}
268
269/// The bus's domains: each one's actions as functions
270/// (`world::get_block(dim, pos)`, `entity::position(uuid)`,
271/// `player::give_item(name, item, count)`) beside its events.
272pub use yog_event::{
273 block, bossbar, chat, entity, game, item, net, particle, player, scoreboard, sound, tag, world,
274};
275
276/// Content registration (custom items / blocks / food).
277pub mod content {
278 pub use yog_registry::*;
279}
280
281/// Networking (raw-byte packets over channels).
282pub mod network {
283 pub use yog_network::*;
284}
285
286/// Commands.
287pub mod command {
288 pub use yog_command::*;
289}
290
291/// Custom dimensions — dimension-type definitions and runtime chunk generation.
292pub mod dimension {
293 pub use yog_dimensions::*;
294}
295
296/// Persistent key-value storage for mod data.
297pub mod storage {
298 pub use yog_storage::*;
299}
300
301/// Mod configuration (typed key/value files).
302pub mod config {
303 pub use yog_config::*;
304}
305
306/// Guide books: the `.ybook` format and its renderer.
307pub mod book {
308 pub use yog_book::*;
309 pub use yog_formats::book::*;
310}
311
312/// UI framework — flexbox layout + widgets on top of yog-gfx.
313pub mod ui {
314 pub use yog_ui::*;
315}
316
317/// Pulls a `.yui` screen's generated code into the current module — the
318/// code-behind's half of `src/<name>.yui`:
319///
320/// ```ignore
321/// // src/mod_list.rs, next to src/mod_list.yui
322/// yog_api::ui_include!("mod_list");
323/// ```
324///
325/// `<name>` is the `.yui` file's path under `src/` without the extension
326/// (`"screens/options"` for `src/screens/options.yui`). `yog build`,
327/// `yog check` and `yog ui gen` write the code to
328/// `.yog-build/generated/<name>.g.rs` (the build crate's own directory,
329/// `CARGO_MANIFEST_DIR`), so it's never committed and never stale.
330#[macro_export]
331macro_rules! ui_include {
332 ($name:literal) => {
333 include!(concat!(
334 env!("CARGO_MANIFEST_DIR"),
335 "/generated/",
336 $name,
337 ".g.rs"
338 ));
339 };
340}
341
342/// Pulls the mod's `.yres` resources in as module `res` — one type per
343/// resource set, with an accessor per entry:
344///
345/// ```ignore
346/// // src/lib.rs
347/// yog_api::resources!();
348///
349/// use crate::res::{Icons, Strings};
350/// let title = Strings::screen_title(); // the text in the player's language
351/// let count = Strings::mods_count(58); // "{count} mods" → "58 mods"
352/// let logo = Icons::logo(); // an image source, "mymod:images/logo"
353/// ```
354///
355/// `yog build`, `yog check` and `yog res check` generate one
356/// `.yog-build/generated/resources/<Set>.g.rs` per `resources/**/<Set>.yres`
357/// plus `.yog-build/generated/resources.g.rs` including them all (see
358/// Yog-IDLE's `docs/yres.md`); never committed, never stale.
359#[macro_export]
360macro_rules! resources {
361 () => {
362 pub mod res {
363 include!(concat!(
364 env!("CARGO_MANIFEST_DIR"),
365 "/generated/resources.g.rs"
366 ));
367 }
368 };
369}
370
371/// Pulls the mod's blocks and items — `content/**/*.yblock` and `*.yitem`,
372/// made in Yog-IDLE's model editor — in as module `content`, and registers
373/// them (every file without `"register": "manual"`) right after
374/// `Mod::register`:
375///
376/// ```ignore
377/// // src/lib.rs — that's all it takes
378/// yog_api::content!();
379///
380/// // Ids and definitions, for code that needs them:
381/// use crate::content::{Blocks, Items};
382/// let id: &str = Blocks::RUBY_ORE; // "mymod:ruby_ore"
383/// registry.register_item(Items::ruby().max_stack(16)); // a "manual" one, tweaked
384/// ```
385///
386/// `yog build`, `yog check` and `yog content check` generate
387/// `.yog-build/generated/yog-content.g.rs` — never committed, never stale.
388/// The models, blockstates, loot tables, tags and lang entries go into the
389/// `.yog` as assets. See Yog-IDLE's `docs/blocks-items.md`.
390#[macro_export]
391macro_rules! content {
392 () => {
393 pub mod content {
394 include!(concat!(
395 env!("CARGO_MANIFEST_DIR"),
396 "/generated/yog-content.g.rs"
397 ));
398 }
399 };
400}
401
402/// Inventory framework — real Container/Menu screens (BlockEntity-backed),
403/// as opposed to `ui`'s HUD-drawn overlays. See `yog-inventory`'s DESIGN.md.
404pub mod inventory {
405 pub use yog_inventory::*;
406}