yog-api 0.92.10

Yog — ergonomic Rust API for writing Minecraft mods (facade).
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
//! Yog API — the single crate mod authors depend on.
//!
//! A facade that re-exports every Yog domain plus the central [`Registry`] hub.
//! Add a new domain crate, re-export it here, and mods pick it up via
//! `yog_api::*`. Items are available both flat (`yog_api::Registry`) and
//! namespaced by domain (`yog_api::world::World`).

pub mod client;
mod interop;
mod lang;
mod registry;

pub use interop::Interop;
pub use lang::{format_translation, tr, tr_args, translation};
pub use registry::{
    CServer, Mod, ModInfo, Registry, UiHandled, close_ui, installed_mods, open_ui, server,
};
pub use yog_gfx::{
    GfxContext, core as gfx_core, draw2d as gfx_draw2d, gl as gfx_gl, image as gfx_image,
};

/// Stable C ABI — re-exported so mods don't need a direct `yog-abi` dependency.
pub use yog_abi::{ABI_VERSION, YogApi};

/// Inter-mod communication proc-macros.
pub use yog_interop::{bench, import, yog_export};

/// rkyv — zero-copy serialization for interop. Re-exported so generated
/// code and mods can use `rkyv::Archive`, `rkyv::Serialize`, `rkyv::Deserialize`
/// without a direct dependency.
pub use rkyv;

/// Re-exported so `#[yog_export]`-generated code can emit `#[::yog_api::ctor::ctor(unsafe)]`
/// without mods needing a direct `ctor` dependency. Constructors run when the
/// mod's `cdylib` is loaded — guaranteed to complete before the runtime calls
/// `yog_mod_register`, so the export registry is fully populated by then.
pub use ctor;

#[doc(hidden)]
pub use std::os::raw::c_void as __c_void;
#[doc(hidden)]
pub use yog_abi::{YogGfxApi as __YogGfxApi, YogStr as __YogStr};

/// Export a [`Mod`] as a dynamically loadable Yog mod.
///
/// Generates the two C-ABI entry points the runtime looks up:
/// - `yog_abi_version() -> u32`  — version check before loading
/// - `yog_mod_register(*const YogApi, *const c_char)` — registration entry point,
///   receives the mod's `id` from its manifest
///
/// Put this once at the crate root of a `cdylib` mod:
///
/// ```ignore
/// yog_api::export_mod!(MyMod);
/// ```
#[macro_export]
macro_rules! export_mod {
    ($mod_ty:ty) => {
        #[unsafe(no_mangle)]
        pub extern "C" fn yog_abi_version() -> u32 {
            $crate::ABI_VERSION
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn yog_mod_register(
            api: *const $crate::YogApi,
            mod_id_ptr: *const ::std::os::raw::c_char,
        ) {
            // Parse mod_id from the C string passed by the runtime.
            let mod_id: &str = if mod_id_ptr.is_null() {
                "unknown"
            } else {
                // SAFETY: a non-null id is a NUL-terminated string from the runtime.
                match unsafe { ::std::ffi::CStr::from_ptr(mod_id_ptr) }.to_str() {
                    Ok(s) => s,
                    Err(_) => "unknown",
                }
            };
            // Store for interop use (yog_api::interop::current_mod_id()).
            $crate::__set_current_mod_id(mod_id);
            // Every mod is handed the same global `YogApi` table, so any
            // mod's own pointer works for reconstructing a `Registry` inside
            // an exported function later — even when called from a thread
            // that never ran `yog_mod_register` itself.
            $crate::__set_current_api_ptr(api as usize);

            // Catch panics so they never unwind across this `extern "C"` boundary
            // back into the runtime (which would be undefined behaviour).
            let outcome = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
                // SAFETY: the runtime passes a valid YogApi pointer, verified via
                // yog_abi_version() and abi_version/size checks before this call.
                let mut registry = unsafe { $crate::Registry::from_raw(api) };
                <$mod_ty as $crate::Mod>::register(&mut registry);

                // Blocks and items of `content/**/*.yblock|*.yitem`
                // (`yog_api::content!()`, self-registered by `ctor`).
                let content: ::std::vec::Vec<fn(&mut $crate::Registry)> =
                    $crate::__yog_content_registrars()
                        .lock()
                        .unwrap_or_else(|e| e.into_inner())
                        .clone();
                for register in content {
                    register(&mut registry);
                }

                // Auto-register all #[yog_export] functions (populated by
                // per-function `ctor` constructors at cdylib load time).
                for entry in $crate::__yog_export_registry().lock().unwrap().iter() {
                    registry
                        .interop()
                        .export(entry.name, entry.ptr as *const ::std::os::raw::c_void);
                }

                // Resolve pending imports (may partially succeed — runtime
                // does a final pass after all mods loaded).
                $crate::__yog_resolve_pending_imports(&registry);
            }));
            if outcome.is_err() {
                $crate::error!(
                    "mod {} panicked during register",
                    ::core::stringify!($mod_ty)
                );
            }
        }

        /// Called by the runtime after ALL mods are loaded — resolves any
        /// imports that weren't satisfied during initial registration.
        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn __yog_resolve_imports_final(api: *const $crate::YogApi) {
            let registry = unsafe { $crate::Registry::from_raw(api) };
            $crate::__yog_resolve_pending_imports(&registry);
        }

        /// Yog-IDLE's screen designer: the version of the protocol below.
        #[unsafe(no_mangle)]
        pub extern "C" fn yog_ui_designer_abi() -> u32 {
            $crate::ui::component::designer::ABI
        }

        /// Yog-IDLE's screen designer: lists this mod's custom components
        /// and draws them (see `yog_ui::component::designer`). Never called
        /// in game — only by the designer host, which loads the mod's
        /// library built for the developer's machine.
        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn yog_ui_designer(
            request: $crate::__YogStr,
            gfx: *const $crate::__YogGfxApi,
            user_data: *mut $crate::__c_void,
            write: $crate::ui::component::designer::WriteFn,
        ) -> bool {
            // SAFETY: forwarded under the same contract.
            unsafe { $crate::ui::component::designer::call(request, gfx, user_data, write) }
        }
    };
}

/// Registers `.yui` components (types implementing
/// [`ui::component::Component`]) when the mod's library loads, so screens
/// can use `<Name …/>` and Yog-IDLE's designer host can draw them. The code
/// `yog build` generates from a `<Component>` `.yui` calls this — mods
/// don't write it themselves.
#[macro_export]
macro_rules! ui_component {
    ($($ty:ty),+ $(,)?) => {
        const _: () = {
            #[$crate::ctor::ctor(unsafe, crate_path = $crate::ctor)]
            fn __yog_register_ui_components() {
                $( $crate::ui::component::register::<$ty>(); )+
            }
        };
    };
}

/// Compiles in the mod's component `.yui` files that have no code-behind
/// `.rs` (those with one are pulled in by its `ui_include!`) — call it once,
/// in `src/lib.rs`:
///
/// ```ignore
/// yog_api::ui_components!();
/// ```
///
/// `yog build`, `yog check` and `yog ui gen` write the list to
/// `.yog-build/generated/components.g.rs` (a module per component).
#[macro_export]
macro_rules! ui_components {
    () => {
        include!(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/generated/components.g.rs"
        ));
    };
}

/// Internal: set by `export_mod!` before calling `Mod::register`.
/// Used by `yog_api::interop::current_mod_id()` so `Interop::export` knows
/// which mod is calling.
#[doc(hidden)]
pub fn __set_current_mod_id(id: &str) {
    CURRENT_MOD_ID.with(|cell| cell.replace(Some(id.to_string())));
}

/// Internal: the current mod's id, set during `yog_mod_register`.
#[doc(hidden)]
pub fn __current_mod_id() -> Option<String> {
    CURRENT_MOD_ID.with(|cell| cell.borrow().clone())
}

/// Internal: the global `YogApi` pointer, set by every mod's `yog_mod_register`
/// (it's the same singleton table for the whole process). Process-wide (not
/// thread-local) because `#[yog_export]`-generated wrappers read it whenever
/// an exported function is *called*, which may happen on any thread, long
/// after the loading thread's `yog_mod_register` returned.
#[doc(hidden)]
pub fn __set_current_api_ptr(ptr: usize) {
    API_PTR.store(ptr, std::sync::atomic::Ordering::Release);
}

#[doc(hidden)]
pub fn __current_api_ptr() -> usize {
    API_PTR.load(std::sync::atomic::Ordering::Acquire)
}

static API_PTR: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);

std::thread_local! {
    static CURRENT_MOD_ID: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
}

// ── Auto-export registry ─────────────────────────────────────────────────

#[doc(hidden)]
pub struct YogExportEntry {
    pub name: &'static str,
    pub ptr: usize,
}

#[doc(hidden)]
pub struct YogImportEntry {
    pub mod_id: &'static str,
    pub symbol: &'static str,
    pub bind_fn: usize,
}

#[doc(hidden)]
pub fn __yog_export_registry() -> &'static std::sync::Mutex<Vec<YogExportEntry>> {
    static REG: std::sync::LazyLock<std::sync::Mutex<Vec<YogExportEntry>>> =
        std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
    &REG
}

/// The `register` functions of `yog_api::content!()` — run by
/// `export_mod!` after `Mod::register`.
#[doc(hidden)]
pub fn __yog_content_registrars() -> &'static std::sync::Mutex<Vec<fn(&mut Registry)>> {
    static REG: std::sync::Mutex<Vec<fn(&mut Registry)>> = std::sync::Mutex::new(Vec::new());
    &REG
}

#[doc(hidden)]
pub fn __yog_import_registry() -> &'static std::sync::Mutex<Vec<YogImportEntry>> {
    static REG: std::sync::LazyLock<std::sync::Mutex<Vec<YogImportEntry>>> =
        std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
    &REG
}

/// Called by `export_mod!` after registration. Iterates pending imports and
/// resolves them via the interop table. Also exported as `#[unsafe(no_mangle)]` so the
/// runtime can call it after all mods are loaded (final resolution pass).
#[doc(hidden)]
pub unsafe fn __yog_resolve_pending_imports(registry: &crate::Registry) {
    unsafe {
        let interop = registry.interop();
        for entry in __yog_import_registry().lock().unwrap().iter() {
            if let Some(ptr) = interop.import_raw(entry.mod_id, entry.symbol) {
                let bind: unsafe extern "C" fn(*const std::ffi::c_void) =
                    std::mem::transmute(entry.bind_fn);
                bind(ptr);
            }
        }
    }
}

pub use yog_book::{Book, BookCategory, BookEntry, BookMacro, BookPage, BookRegistry};
pub use yog_book::{BookFontRegistry, BookRenderer};
pub use yog_book::{
    crafting_page, entity_page, image_page, pattern_page, relations_page, smelting_page,
    spotlight_page, text_page, text_page_titled,
};
pub use yog_command::CommandContext;
pub use yog_config::Config;
pub use yog_core::{BlockPos, Server};
pub use yog_dimensions::{ChunkWriter, YogDimensionDef, YogDimensionTypeDef};
pub use yog_entity::Entity;
pub use yog_event::{
    AdvancementEvent, AttackEntityEvent, BlockBreakEvent, ChatEvent, ClientTickEvent,
    ContainerCloseEvent, ContainerOpenEvent, CraftEvent, EntityDamageEvent, EntityDeathEvent,
    EntityInteractEvent, EntitySpawnEvent, EventPhase, ExplosionEvent, ItemPickupEvent,
    KeyPressEvent, PlaceBlockEvent, PlayerDeathEvent, PlayerJoinEvent, PlayerLeaveEvent,
    PlayerMoveEvent, PlayerRespawnEvent, ProjectileHitEvent, ScreenEvent, UseBlockEvent,
    UseItemEvent,
};
pub use yog_inventory::{InventoryDef, SlotLayout};
#[doc(inline)]
pub use yog_network::packet;
pub use yog_network::{Packet, PacketEvent, PacketField};
pub use yog_player::Player;
pub use yog_registry::{
    AdvancementReward, BlockDef, BookRecipe, FoodDef, FurnaceRecipe, ItemDef, ItemModifier,
    ShapedRecipe, ShapelessRecipe, StartupGrant,
};
pub use yog_storage::{Storage, StorageScope, Value};
pub use yog_ui::{
    Align, Dock, FlexDir, FocusStyle, LayoutNode, Rect, Stretch, UiRoot, Units, widget,
};
pub use yog_world::World;

/// Logging macros (`yog_api::info!`, `warn!`, `error!`).
pub use yog_logging::{error, info, warn};

/// Core types and handles.
pub mod core {
    pub use yog_core::*;
}

/// Events and the subscription registry.
pub mod event {
    pub use yog_event::*;
}

/// World access (block get/set, dimensions).
pub mod world {
    pub use yog_world::*;
}

/// Entity access (teleport, position, health, ... by UUID).
pub mod entity {
    pub use yog_entity::*;
}

/// Player access (give item, teleport).
pub mod player {
    pub use yog_player::*;
}

/// Content registration (custom items / blocks / food).
pub mod content {
    pub use yog_registry::*;
}

/// Networking (raw-byte packets over channels).
pub mod network {
    pub use yog_network::*;
}

/// Commands.
pub mod command {
    pub use yog_command::*;
}

/// Custom dimensions — dimension-type definitions and runtime chunk generation.
pub mod dimension {
    pub use yog_dimensions::*;
}

/// Persistent key-value storage for mod data.
pub mod storage {
    pub use yog_storage::*;
}

/// Mod configuration (typed key/value files).
pub mod config {
    pub use yog_config::*;
}

/// In-game book/documentation system (Patchouli-like).
pub mod book {
    pub use yog_book::*;
}

/// UI framework — flexbox layout + widgets on top of yog-gfx.
pub mod ui {
    pub use yog_ui::*;
}

/// Pulls a `.yui` screen's generated code into the current module — the
/// code-behind's half of `src/<name>.yui`:
///
/// ```ignore
/// // src/mod_list.rs, next to src/mod_list.yui
/// yog_api::ui_include!("mod_list");
/// ```
///
/// `<name>` is the `.yui` file's path under `src/` without the extension
/// (`"screens/options"` for `src/screens/options.yui`). `yog build`,
/// `yog check` and `yog ui gen` write the code to
/// `.yog-build/generated/<name>.g.rs` (the build crate's own directory,
/// `CARGO_MANIFEST_DIR`), so it's never committed and never stale.
#[macro_export]
macro_rules! ui_include {
    ($name:literal) => {
        include!(concat!(
            env!("CARGO_MANIFEST_DIR"),
            "/generated/",
            $name,
            ".g.rs"
        ));
    };
}

/// Pulls the mod's `.yres` resources in as module `res` — one type per
/// resource set, with an accessor per entry:
///
/// ```ignore
/// // src/lib.rs
/// yog_api::resources!();
///
/// use crate::res::{Icons, Strings};
/// let title = Strings::screen_title();        // the text in the player's language
/// let count = Strings::mods_count(58);        // "{count} mods" → "58 mods"
/// let logo = Icons::logo();                   // an image source, "mymod:images/logo"
/// ```
///
/// `yog build`, `yog check` and `yog res check` generate one
/// `.yog-build/generated/resources/<Set>.g.rs` per `resources/**/<Set>.yres`
/// plus `.yog-build/generated/resources.g.rs` including them all (see
/// Yog-IDLE's `docs/yres.md`); never committed, never stale.
#[macro_export]
macro_rules! resources {
    () => {
        pub mod res {
            include!(concat!(
                env!("CARGO_MANIFEST_DIR"),
                "/generated/resources.g.rs"
            ));
        }
    };
}

/// Pulls the mod's blocks and items — `content/**/*.yblock` and `*.yitem`,
/// made in Yog-IDLE's model editor — in as module `content`, and registers
/// them (every file without `"register": "manual"`) right after
/// `Mod::register`:
///
/// ```ignore
/// // src/lib.rs — that's all it takes
/// yog_api::content!();
///
/// // Ids and definitions, for code that needs them:
/// use crate::content::{Blocks, Items};
/// let id: &str = Blocks::RUBY_ORE;                 // "mymod:ruby_ore"
/// registry.register_item(Items::ruby().max_stack(16)); // a "manual" one, tweaked
/// ```
///
/// `yog build`, `yog check` and `yog content check` generate
/// `.yog-build/generated/yog-content.g.rs` — never committed, never stale.
/// The models, blockstates, loot tables, tags and lang entries go into the
/// `.yog` as assets. See Yog-IDLE's `docs/blocks-items.md`.
#[macro_export]
macro_rules! content {
    () => {
        pub mod content {
            include!(concat!(
                env!("CARGO_MANIFEST_DIR"),
                "/generated/yog-content.g.rs"
            ));
        }
    };
}

/// Inventory framework — real Container/Menu screens (BlockEntity-backed),
/// as opposed to `ui`'s HUD-drawn overlays. See `yog-inventory`'s DESIGN.md.
pub mod inventory {
    pub use yog_inventory::*;
}