Skip to main content

yog_abi/
lib.rs

1//! Yog stable C ABI — the ONLY types that cross the mod/runtime boundary.
2//!
3//! Rules this file must never break:
4//!  - Every type is `#[repr(C)]`.
5//!  - No Rust trait objects, no generics, no std types in public structs.
6//!  - New fields are appended only at the END of structs; increment `ABI_MINOR`.
7//!  - ABI_MAJOR bumps only when an existing field is removed or reordered.
8//!
9//! Mods and the runtime are compiled independently. They are compatible when
10//! `ABI_MAJOR` matches and `mod_ABI_MINOR <= runtime_ABI_MINOR`.
11
12use std::os::raw::c_void;
13
14// ── Version ──────────────────────────────────────────────────────────────────
15
16pub const ABI_MAJOR: u32 = 0;
17pub const ABI_MINOR: u32 = 30;
18/// `ABI_MAJOR * 10_000 + ABI_MINOR`.  Checked at mod load time.
19pub const ABI_VERSION: u32 = ABI_MAJOR * 10_000 + ABI_MINOR;
20
21// ── Primitive types ───────────────────────────────────────────────────────────
22
23/// Borrowed UTF-8 byte slice passed to a function. NOT null-terminated.
24/// Valid only for the duration of the call that provides it — never store.
25#[repr(C)]
26#[derive(Copy, Clone)]
27pub struct YogStr {
28    pub ptr: *const u8,
29    pub len: u32,
30}
31
32impl YogStr {
33    pub const EMPTY: Self = Self {
34        ptr: std::ptr::null(),
35        len: 0,
36    };
37
38    #[inline]
39    pub fn from_str(s: &str) -> Self {
40        Self {
41            ptr: s.as_ptr(),
42            len: s.len() as u32,
43        }
44    }
45
46    #[inline]
47    pub fn is_empty(self) -> bool {
48        self.len == 0 || self.ptr.is_null()
49    }
50
51    /// SAFETY: `ptr` must be valid UTF-8 of `len` bytes for at least the
52    /// duration of the call that provided this `YogStr`.
53    #[inline]
54    pub unsafe fn as_str<'a>(self) -> &'a str {
55        if self.ptr.is_null() || self.len == 0 {
56            return "";
57        }
58        std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.ptr, self.len as usize))
59    }
60}
61
62/// Heap-allocated UTF-8 string owned by the RUNTIME.
63/// `ptr == null` encodes `None` / "not found".
64/// When `ptr` is non-null the caller MUST free it via `YogServer::free_str`.
65#[repr(C)]
66#[derive(Copy, Clone)]
67pub struct YogOwnedStr {
68    pub ptr: *mut u8,
69    pub len: u32,
70}
71
72impl YogOwnedStr {
73    pub const NONE: Self = Self {
74        ptr: std::ptr::null_mut(),
75        len: 0,
76    };
77
78    /// Allocate a new `YogOwnedStr` from a Rust `String`.
79    pub fn from_string(s: String) -> Self {
80        let len = s.len() as u32;
81        let ptr = Box::into_raw(s.into_bytes().into_boxed_slice()) as *mut u8;
82        Self { ptr, len }
83    }
84
85    #[inline]
86    pub fn is_none(self) -> bool {
87        self.ptr.is_null()
88    }
89}
90
91/// Integer 3-D block position.
92#[repr(C)]
93#[derive(Copy, Clone)]
94pub struct YogBlockPos {
95    pub x: i32,
96    pub y: i32,
97    pub z: i32,
98}
99
100/// Float 3-D vector (position, velocity, …).
101#[repr(C)]
102#[derive(Copy, Clone)]
103pub struct YogVec3 {
104    pub x: f64,
105    pub y: f64,
106    pub z: f64,
107}
108
109// ── Event structs (Java → Rust) ───────────────────────────────────────────────
110
111#[repr(C)]
112pub struct YogBlockBreakEvent {
113    pub player: YogStr,
114    pub block: YogStr,
115    pub pos: YogBlockPos,
116    /// Dimension the block was broken in, e.g. `minecraft:overworld`.
117    pub dimension: YogStr,
118}
119
120#[repr(C)]
121pub struct YogChatEvent {
122    pub player: YogStr,
123    pub message: YogStr,
124    pub dimension: YogStr,
125}
126
127/// Shared by player_join and player_leave.
128#[repr(C)]
129pub struct YogPlayerEvent {
130    pub player: YogStr,
131    pub uuid: YogStr,
132    pub dimension: YogStr,
133}
134
135#[repr(C)]
136pub struct YogUseItemEvent {
137    pub player: YogStr,
138    pub item: YogStr,
139    /// ABI minor 25: whether the player was sneaking (shift) during use.
140    pub sneaking: bool,
141    pub dimension: YogStr,
142}
143
144#[repr(C)]
145pub struct YogUseBlockEvent {
146    pub player: YogStr,
147    pub block: YogStr,
148    pub pos: YogBlockPos,
149    pub dimension: YogStr,
150}
151
152#[repr(C)]
153pub struct YogAttackEntityEvent {
154    pub player: YogStr,
155    pub target_type: YogStr,
156    pub target_uuid: YogStr,
157    pub dimension: YogStr,
158}
159
160#[repr(C)]
161pub struct YogEntityDamageEvent {
162    pub entity_type: YogStr,
163    pub uuid: YogStr,
164    pub amount: f32,
165    pub source: YogStr,
166    pub dimension: YogStr,
167}
168
169#[repr(C)]
170pub struct YogEntityDeathEvent {
171    pub entity_type: YogStr,
172    pub uuid: YogStr,
173    pub source: YogStr,
174    pub dimension: YogStr,
175}
176
177#[repr(C)]
178pub struct YogEntitySpawnEvent {
179    pub entity_type: YogStr,
180    pub uuid: YogStr,
181    pub dimension: YogStr,
182}
183
184/// Fired when a player dies (Pre: before death is processed; Post: after death).
185/// Pre phase — return false to prevent death (keep entity alive at 0.5 HP).
186#[repr(C)]
187pub struct YogPlayerDeathEvent {
188    pub player: YogStr,
189    pub uuid: YogStr,
190    /// Damage source identifier, e.g. `"player"`, `"fall"`.
191    pub source: YogStr,
192    pub dimension: YogStr,
193}
194
195/// Fired when a player respawns after death.
196#[repr(C)]
197pub struct YogPlayerRespawnEvent {
198    pub player: YogStr,
199    pub uuid: YogStr,
200    /// True if respawning at a bed or anchor, false at world spawn.
201    pub at_anchor: bool,
202    /// Dimension respawned into (may differ from where the player died).
203    pub dimension: YogStr,
204}
205
206/// Fired when a player earns an advancement (Post only).
207#[repr(C)]
208pub struct YogAdvancementEvent {
209    pub player: YogStr,
210    pub uuid: YogStr,
211    /// Namespaced id, e.g. `"minecraft:story/mine_stone"`.
212    pub advancement: YogStr,
213    pub dimension: YogStr,
214}
215
216/// Fired when a player right-clicks (interacts with) an entity (Pre: before; Post: after).
217/// Pre phase — return false to cancel the interaction.
218#[repr(C)]
219pub struct YogEntityInteractEvent {
220    pub player: YogStr,
221    pub player_uuid: YogStr,
222    pub entity_type: YogStr,
223    pub entity_uuid: YogStr,
224    /// `"main_hand"` or `"off_hand"`.
225    pub hand: YogStr,
226    pub dimension: YogStr,
227}
228
229/// Fired when a player takes a crafted item from a crafting output slot (Post only).
230#[repr(C)]
231pub struct YogCraftEvent {
232    pub player: YogStr,
233    pub player_uuid: YogStr,
234    pub result_item: YogStr,
235    pub result_count: u32,
236    pub dimension: YogStr,
237}
238
239/// Fired when an explosion occurs in a world (Pre: before block destruction; Post: after).
240/// Pre phase — return false to cancel the explosion (block and entity damage suppressed).
241#[repr(C)]
242pub struct YogExplosionEvent {
243    pub dimension: YogStr,
244    pub x: f64,
245    pub y: f64,
246    pub z: f64,
247    pub power: f32,
248    /// UUID of the entity that caused the explosion, or empty if world/tnt.
249    pub cause_uuid: YogStr,
250}
251
252// ── ABI minor 9–10 event structs ──────────────────────────────────────────────
253
254/// Fired when a player picks up an item entity (Pre: cancellable; Post: informational).
255#[repr(C)]
256pub struct YogItemPickupEvent {
257    pub player: YogStr,
258    pub player_uuid: YogStr,
259    /// Registry id of the item, e.g. `"minecraft:diamond"`.
260    pub item_id: YogStr,
261    pub item_count: u32,
262    /// UUID of the item entity that was picked up.
263    pub entity_uuid: YogStr,
264    pub dimension: YogStr,
265}
266
267/// Fired when a player sends a movement packet (Post only; very high frequency).
268#[repr(C)]
269pub struct YogPlayerMoveEvent {
270    pub player: YogStr,
271    pub player_uuid: YogStr,
272    pub x: f64,
273    pub y: f64,
274    pub z: f64,
275    pub yaw: f32,
276    pub pitch: f32,
277    pub dimension: YogStr,
278}
279
280/// Fired when a player opens a container screen (Pre: cancellable; Post: informational).
281/// `container_type` is the screen handler registry id, e.g. `"minecraft:chest"`.
282/// Empty string if the type is not in the registry (e.g. the player inventory).
283#[repr(C)]
284pub struct YogContainerOpenEvent {
285    pub player: YogStr,
286    pub player_uuid: YogStr,
287    pub container_type: YogStr,
288    pub dimension: YogStr,
289}
290
291/// Fired when a player closes a container screen (Post only).
292#[repr(C)]
293pub struct YogContainerCloseEvent {
294    pub player: YogStr,
295    pub player_uuid: YogStr,
296    pub dimension: YogStr,
297}
298
299/// Fired when a persistent projectile (arrow, trident) hits a target (Pre: cancellable).
300/// Pre phase — return false to cancel the hit (projectile passes through).
301#[repr(C)]
302pub struct YogProjectileHitEvent {
303    /// Registry id of the projectile, e.g. `"minecraft:arrow"`.
304    pub projectile_type: YogStr,
305    pub projectile_uuid: YogStr,
306    /// UUID of the entity that shot/threw this projectile, or empty.
307    pub shooter_uuid: YogStr,
308    /// `"block"` or `"entity"`.
309    pub hit_type: YogStr,
310    /// UUID of the entity that was hit (empty for block hits).
311    pub hit_entity_uuid: YogStr,
312    pub x: f64,
313    pub y: f64,
314    pub z: f64,
315    pub dimension: YogStr,
316}
317
318/// Fired when a player places a block (Pre: before placement; Post: after).
319#[repr(C)]
320pub struct YogPlaceBlockEvent {
321    pub player: YogStr,
322    pub block: YogStr,
323    pub pos: YogBlockPos,
324    pub dimension: YogStr,
325}
326
327#[repr(C)]
328pub struct YogPacketEvent {
329    pub channel: YogStr,
330    pub player: YogStr, // empty string on client-received packets
331    pub payload: *const u8,
332    pub payload_len: u32,
333}
334
335#[repr(C)]
336pub struct YogCommandEvent {
337    pub name: YogStr,
338    pub args: YogStr,
339    pub source: YogStr,
340    pub uuid: YogStr,
341    /// Empty for console-run commands (no world).
342    pub dimension: YogStr,
343}
344
345/// A startup grant definition — items/books to give on first join.
346#[repr(C)]
347pub struct YogStartupGrantDef {
348    pub id: YogStr,
349    pub items: YogStr,   // '|'-separated item ids
350    pub book: YogStr,    // empty = none
351    pub command: YogStr, // empty = none
352}
353
354// ── Content definition structs (mod → runtime) ────────────────────────────────
355
356#[repr(C)]
357pub struct YogItemDef {
358    pub id: YogStr,
359    pub max_stack: u32,
360    pub name: YogStr,    // empty = no override
361    pub tooltip: YogStr, // empty = none
362    pub max_damage: u32,
363    pub fire_resistant: bool,
364    pub fuel_ticks: u32,
365    pub food_nutrition: u32, // 0 = not a food item
366    pub food_saturation: f32,
367    pub food_always_eat: bool,
368}
369
370#[repr(C)]
371pub struct YogBlockDef {
372    pub id: YogStr,
373    pub hardness: f32,
374    pub resistance: f32,
375    pub name: YogStr,
376    pub light_level: u8,
377    pub sound: YogStr, // empty = default stone sound
378    pub requires_tool: bool,
379    pub no_collision: bool,
380    pub slipperiness: f32,
381    /// Bounding box in pixels: `[x1, y1, z1, x2, y2, z2]`. All zeros = full cube.
382    pub shape: [f32; 6],
383    /// Fence/pipe-style dynamic arm growth toward compatible neighbors — see
384    /// `yog_registry::BlockDef::connects_to_neighbors`.
385    pub connects: bool,
386    /// Comma-joined connection compatibility tags — see
387    /// `yog_registry::BlockDef::connect_groups`. Empty = no tags.
388    pub connect_groups: YogStr,
389    /// Id of a `YogInventoryDef` (registered via `register_inventory`) this
390    /// block is backed by — empty = no inventory (plain block, as before).
391    /// See `yog_inventory`'s DESIGN.md.
392    pub inventory_id: YogStr,
393}
394
395/// An inventory-backed screen definition — see `yog_inventory::InventoryDef`.
396#[repr(C)]
397pub struct YogInventoryDef {
398    pub id: YogStr,
399    pub slot_count: u32,
400    /// `"x:y,x:y,..."` — one pair per slot, in slot-index order. Empty =
401    /// use the default vanilla-style grid (see `yog_inventory::InventoryDef::default_grid`).
402    pub layout: YogStr,
403    pub include_player_inventory: bool,
404    pub player_inv_x: f32,
405    pub player_inv_y: f32,
406    /// Empty = default vanilla-style panel texture.
407    pub background_texture: YogStr,
408    pub title: YogStr,
409}
410
411// ── ABI minor 10 client event structs ─────────────────────────────────────────
412
413/// Key press / release / repeat from the keyboard (client-side only).
414#[repr(C)]
415#[derive(Copy, Clone)]
416pub struct YogKeyPressEvent {
417    /// GLFW key code (e.g. `GLFW_KEY_E = 69`).
418    pub key_code: i32,
419    pub scan_code: i32,
420    /// 0 = release, 1 = press, 2 = repeat.
421    pub action: i32,
422    /// Modifier bitmask: 1=shift, 2=ctrl, 4=alt, 8=super.
423    pub modifiers: i32,
424}
425
426// ── Handler function-pointer types ────────────────────────────────────────────
427//
428// All event handlers receive a `phase: u8` argument:
429//   0 = Pre  — fires before the action; return value matters (false = cancel).
430//   1 = Post — fires after the action; return value is ignored.
431//
432// This unified signature lets one registered closure handle both phases.
433//
434// Client-side handlers (minor 10) do NOT receive a `YogServer*` — they run on
435// the render thread and have no server context.
436
437pub type YogBlockBreakFn =
438    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogBlockBreakEvent, u8) -> bool;
439pub type YogChatFn =
440    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogChatEvent, u8) -> bool;
441pub type YogPlayerFn =
442    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerEvent, u8) -> bool;
443pub type YogUseItemFn =
444    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogUseItemEvent, u8) -> bool;
445pub type YogUseBlockFn =
446    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogUseBlockEvent, u8) -> bool;
447pub type YogAttackEntityFn =
448    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogAttackEntityEvent, u8) -> bool;
449pub type YogEntityDamageFn =
450    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityDamageEvent, u8) -> bool;
451pub type YogEntityDeathFn =
452    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityDeathEvent, u8) -> bool;
453pub type YogEntitySpawnFn =
454    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntitySpawnEvent, u8) -> bool;
455pub type YogPlaceBlockFn =
456    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlaceBlockEvent, u8) -> bool;
457pub type YogPlayerDeathFn =
458    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerDeathEvent, u8) -> bool;
459pub type YogPlayerRespawnFn =
460    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerRespawnEvent, u8) -> bool;
461pub type YogAdvancementFn =
462    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogAdvancementEvent, u8) -> bool;
463pub type YogEntityInteractFn =
464    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityInteractEvent, u8) -> bool;
465pub type YogCraftFn =
466    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogCraftEvent, u8) -> bool;
467pub type YogExplosionFn =
468    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogExplosionEvent, u8) -> bool;
469pub type YogItemPickupFn =
470    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogItemPickupEvent, u8) -> bool;
471pub type YogPlayerMoveFn =
472    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerMoveEvent, u8) -> bool;
473pub type YogContainerOpenFn =
474    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogContainerOpenEvent, u8) -> bool;
475pub type YogContainerCloseFn =
476    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogContainerCloseEvent, u8) -> bool;
477pub type YogProjectileHitFn =
478    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogProjectileHitEvent, u8) -> bool;
479
480/// Packet events — always Post, no phase.
481pub type YogPacketFn = unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPacketEvent);
482/// Server lifecycle / tick — no event struct, always fires.
483pub type YogServerFn = unsafe extern "C" fn(*mut c_void, *const YogServer);
484/// Command handler.
485pub type YogCommandFn = unsafe extern "C" fn(
486    ud: *mut c_void,
487    srv: *const YogServer,
488    ev: *const YogCommandEvent,
489    reply_buf: *mut u8,
490    reply_cap: u32,
491    reply_len: *mut u32,
492);
493/// Scheduler handler (once or repeating).
494pub type YogScheduledFn = unsafe extern "C" fn(*mut c_void, *const YogServer);
495
496// ── ABI minor 10 — client-side function pointer types ────────────────────────
497
498/// Client tick — no event, no server context.
499pub type YogClientFn = unsafe extern "C" fn(ud: *mut c_void);
500/// HUD render — `gfx` is the graphics context for this frame; only valid for
501/// the call duration.  `draw2d_*` functions in `gfx` work here.
502pub type YogUIEventFn = unsafe extern "C" fn(ud: *mut c_void, ui_id: YogStr, event_id: YogStr);
503pub type YogHudRenderFn = unsafe extern "C" fn(ud: *mut c_void, gfx: *const YogGfxApi);
504/// World render — `gfx` contains `view_proj` and `camera_pos` for 3D rendering.
505/// Valid only for the call duration.
506pub type YogWorldRenderFn = unsafe extern "C" fn(ud: *mut c_void, gfx: *const YogGfxApi);
507/// Key press — return `false` to cancel (prevent Minecraft from processing the key).
508pub type YogKeyPressFn = unsafe extern "C" fn(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool;
509/// Screen event — `screen_class` is the simple class name (e.g. `"InventoryScreen"`).
510/// For `on_screen_open` return `false` to prevent the screen from opening.
511pub type YogScreenFn = unsafe extern "C" fn(ud: *mut c_void, screen_class: YogStr) -> bool;
512
513// ── ABI minor 14 — low-level GPU pipeline ────────────────────────────────────
514
515/// Low-level GPU context passed to render handlers.
516///
517/// Provides direct access to the OpenGL pipeline: buffer objects, vertex arrays,
518/// shader programs, textures, draw calls, and render state.
519///
520/// The per-frame fields (`screen_w/h`, `delta_tick`, `view_proj`, `camera_pos`)
521/// are filled by the runtime before calling the handler; the function pointers
522/// point to statically-allocated implementations.
523///
524/// Valid only for the duration of the render callback — never store the pointer.
525/// GPU resource **handles** (`u32`) may be stored between frames.
526///
527/// Colors are `0xAARRGGBB` (Minecraft convention).
528#[repr(C)]
529#[derive(Copy, Clone)]
530pub struct YogGfxApi {
531    // ── Per-frame context ─────────────────────────────────────────────────────
532    /// GUI-pixel screen width.
533    pub screen_w: i32,
534    /// GUI-pixel screen height.
535    pub screen_h: i32,
536    /// Partial-tick interpolation factor (0.0–1.0).
537    pub delta_tick: f32,
538    /// GUI scale factor: physical pixels per GUI pixel (e.g. 2.0 for 2× GUI scale).
539    /// Useful for converting GUI-pixel coordinates to physical pixels for OpenGL calls.
540    pub scale_factor: f32,
541    /// View-projection matrix in camera-relative space (column-major, 16 × f32).
542    /// All zeros during `on_hud_render`; filled during `on_world_render`.
543    pub view_proj: [f32; 16],
544    /// Camera world-space position.  All zeros during `on_hud_render`.
545    pub camera_pos: [f32; 3],
546    /// Local player world-space position (eye height).  All zeros during `on_hud_render`.
547    /// Differs from `camera_pos` in third-person view.
548    pub player_pos: [f32; 3],
549    pub _pad1: f32,
550
551    // ── GPU buffers ───────────────────────────────────────────────────────────
552    /// Allocate a new GPU buffer (VBO / EBO). Returns 0 on failure.
553    pub buf_create: unsafe extern "C" fn() -> u32,
554    /// Delete a buffer created by `buf_create`.
555    pub buf_delete: unsafe extern "C" fn(handle: u32),
556    /// Upload `len` bytes from `bytes` into `handle`.
557    /// `dynamic`: hints frequent updates (`GL_DYNAMIC_DRAW` vs `GL_STATIC_DRAW`).
558    pub buf_data: unsafe extern "C" fn(handle: u32, bytes: *const u8, len: u32, dynamic: bool),
559    /// Overwrite `len` bytes at `offset` in `handle`.
560    pub buf_subdata: unsafe extern "C" fn(handle: u32, offset: u32, bytes: *const u8, len: u32),
561
562    // ── Vertex arrays ─────────────────────────────────────────────────────────
563    /// Create a vertex array object. Returns 0 on failure.
564    pub vao_create: unsafe extern "C" fn() -> u32,
565    /// Delete a VAO created by `vao_create`.
566    pub vao_delete: unsafe extern "C" fn(handle: u32),
567    /// Declare one vertex attribute in `vao`, sourced from `vbo`.
568    /// `dtype`: 0=f32, 1=u8, 2=i32, 3=u32.
569    pub vao_attrib: unsafe extern "C" fn(
570        vao: u32,
571        vbo: u32,
572        index: u32,
573        components: u8,
574        dtype: u8,
575        normalized: bool,
576        stride: u32,
577        offset: u32,
578    ),
579    /// Bind an index buffer (EBO) to `vao`.
580    pub vao_set_ebo: unsafe extern "C" fn(vao: u32, ebo: u32),
581
582    // ── Shader programs ───────────────────────────────────────────────────────
583    /// Compile + link `vert_src` / `frag_src` (GLSL 150 core).
584    /// Writes the program handle to `*out`. Returns false and logs on error.
585    pub prog_create: unsafe extern "C" fn(vert: YogStr, frag: YogStr, out: *mut u32) -> bool,
586    /// Delete a shader program.
587    pub prog_delete: unsafe extern "C" fn(handle: u32),
588    pub prog_uniform_1i: unsafe extern "C" fn(prog: u32, name: YogStr, v: i32),
589    pub prog_uniform_1f: unsafe extern "C" fn(prog: u32, name: YogStr, v: f32),
590    pub prog_uniform_2f: unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32),
591    pub prog_uniform_3f: unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32, z: f32),
592    pub prog_uniform_4f:
593        unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32, z: f32, w: f32),
594    /// Set a mat4 uniform from 16 column-major floats.
595    pub prog_uniform_mat4: unsafe extern "C" fn(prog: u32, name: YogStr, col_major: *const f32),
596
597    // ── Textures ──────────────────────────────────────────────────────────────
598    /// Upload RGBA8 pixel data as a new texture.
599    /// `linear`: `GL_LINEAR` if true, `GL_NEAREST` if false.
600    pub tex_create: unsafe extern "C" fn(w: u32, h: u32, rgba: *const u8, linear: bool) -> u32,
601    /// Delete a texture created by `tex_create`.
602    pub tex_delete: unsafe extern "C" fn(handle: u32),
603    /// Bind `handle` to texture unit `unit` (0–7).
604    pub tex_bind: unsafe extern "C" fn(unit: u32, handle: u32),
605    /// Return the GL texture handle Minecraft uses for a namespaced resource
606    /// (e.g. `"minecraft:textures/gui/icons.png"`). Returns 0 if not found.
607    /// Do **not** delete handles obtained this way — Minecraft owns them.
608    pub tex_from_mc: unsafe extern "C" fn(id: YogStr) -> u32,
609
610    // ── Draw calls ────────────────────────────────────────────────────────────
611    /// Draw `count` primitives from `vao`, using shader `prog`.
612    /// `mode`: 0=Triangles, 1=Lines, 2=LineStrip, 3=TriangleStrip, 4=TriangleFan.
613    pub draw_arrays: unsafe extern "C" fn(vao: u32, prog: u32, mode: u8, first: u32, count: u32),
614    /// Draw indexed primitives.  `ebo` must be bound to `vao` via `vao_set_ebo`.
615    /// `u32_idx`: `true` for `u32` indices, `false` for `u16` indices.
616    pub draw_elements:
617        unsafe extern "C" fn(vao: u32, ebo: u32, prog: u32, mode: u8, count: u32, u32_idx: bool),
618
619    // ── Render state ──────────────────────────────────────────────────────────
620    /// Enable/disable blending. `src`/`dst` are raw GL blend factor enum values.
621    pub set_blend: unsafe extern "C" fn(enabled: bool, src: u32, dst: u32),
622    /// Enable/disable depth testing and depth writes.
623    pub set_depth: unsafe extern "C" fn(test: bool, write: bool),
624    /// Enable scissor clipping (GUI-pixel rectangle).
625    pub set_scissor: unsafe extern "C" fn(x: i32, y: i32, w: i32, h: i32),
626    /// Disable scissor clipping.
627    pub clear_scissor: unsafe extern "C" fn(),
628    /// Set the GL viewport (physical pixel coordinates).
629    pub set_viewport: unsafe extern "C" fn(x: i32, y: i32, w: i32, h: i32),
630
631    // ── 2D convenience (HUD-render only — uses MC's DrawContext) ─────────────
632    /// Filled rectangle. Only valid during `on_hud_render`.
633    pub draw2d_rect: unsafe extern "C" fn(x1: f32, y1: f32, x2: f32, y2: f32, color: u32),
634    /// Vertical-gradient rectangle. Only valid during `on_hud_render`.
635    pub draw2d_gradient:
636        unsafe extern "C" fn(x1: f32, y1: f32, x2: f32, y2: f32, top: u32, bottom: u32),
637    /// MC text renderer string. Only valid during `on_hud_render`.
638    pub draw2d_text: unsafe extern "C" fn(text: YogStr, x: f32, y: f32, color: u32, shadow: bool),
639    /// Blit from a Minecraft-managed texture. Only valid during `on_hud_render`.
640    /// `(u0, v0)` in texels; `(w, h)` in pixels; `(tw, th)` full texture size.
641    pub draw2d_mc_tex: unsafe extern "C" fn(
642        id: YogStr,
643        x: f32,
644        y: f32,
645        u0: f32,
646        v0: f32,
647        w: f32,
648        h: f32,
649        tw: f32,
650        th: f32,
651    ),
652
653    // ── appended in ABI minor 21 ──────────────────────────────────────────────
654    /// Render an item stack (3D block models included) via MC's item renderer,
655    /// like Patchouli's `renderItemStack`. `id` is a registry item id
656    /// ("minecraft:crafting_table"); `size` is the on-screen size in GUI px
657    /// (16 = inventory size). Only valid during `on_hud_render`.
658    pub draw2d_item: unsafe extern "C" fn(id: YogStr, x: f32, y: f32, size: f32),
659
660    // ── appended in ABI minor 30 ──────────────────────────────────────────────
661    /// Snapshot of the currently-open inventory screen's slots (custom slots +
662    /// player inventory), refreshed fresh into this per-call struct before
663    /// `on_ui_render` handlers run — the first `inv_slot_count` entries of
664    /// `inv_slots` are valid, the rest are unspecified. Backing `item_id`
665    /// strings are valid only for the duration of the render call, same as
666    /// every other per-frame field on this struct (`player_pos`, `screen_w`, ...).
667    ///
668    /// This rides in the per-call struct, not a module-level cache, because a
669    /// plain Rust `static` does not cross the cdylib boundary between the
670    /// runtime and a mod's own dynamic library — each gets its own copy.
671    pub inv_slot_count: u32,
672    pub inv_slots: [YogInvSlotRaw; MAX_INV_SLOTS],
673}
674
675/// Capacity of [`YogGfxApi::inv_slots`] — covers the largest realistic
676/// inventory screen (a mod's own slots + player main inventory + hotbar).
677pub const MAX_INV_SLOTS: usize = 64;
678
679/// One inventory slot's render data for [`YogGfxApi::inv_slots`].
680#[repr(C)]
681#[derive(Copy, Clone)]
682pub struct YogInvSlotRaw {
683    /// Empty (`len == 0`) if the slot itself is empty.
684    pub item_id: YogStr,
685    pub count: u32,
686    /// Screen-space pixel position.
687    pub x: i32,
688    pub y: i32,
689}
690
691impl YogInvSlotRaw {
692    pub const EMPTY: Self = Self {
693        item_id: YogStr::EMPTY,
694        count: 0,
695        x: 0,
696        y: 0,
697    };
698}
699
700unsafe impl Send for YogGfxApi {}
701unsafe impl Sync for YogGfxApi {}
702
703// ── Server action table (runtime → mod direction is wrong; it's mod → runtime) ─
704
705/// All Minecraft-mutating calls available inside a handler.
706///
707/// `ctx` is an opaque pointer to the runtime's JNI state.  Every function takes
708/// it as its first argument.  The pointer is valid for the lifetime of the process.
709///
710/// Strings **returned** by functions in this table are heap-allocated by the
711/// runtime and must be freed with `free_str` after the caller has read them.
712#[repr(C)]
713pub struct YogServer {
714    pub ctx: *mut c_void,
715    pub abi_version: u32,
716    /// `sizeof(YogServer)` at build time — allows mods compiled against an older
717    /// table to detect and skip fields they don't know about.
718    pub size: u32,
719
720    /// Free a string returned by any function in this table.
721    pub free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
722
723    // ── chat ─────────────────────────────────────────────────────────────────
724    pub broadcast: unsafe extern "C" fn(ctx: *mut c_void, msg: YogStr),
725
726    // ── world ────────────────────────────────────────────────────────────────
727    pub get_block:
728        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
729    pub set_block: unsafe extern "C" fn(
730        ctx: *mut c_void,
731        dim: YogStr,
732        pos: YogBlockPos,
733        block: YogStr,
734    ) -> bool,
735    pub world_time: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, out: *mut i64) -> bool,
736    pub set_time: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, time: i64) -> bool,
737    pub is_raining: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr) -> bool,
738    pub set_weather:
739        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, raining: bool, dur: i32) -> bool,
740
741    // ── player ───────────────────────────────────────────────────────────────
742    pub give_item:
743        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, item: YogStr, count: u32) -> bool,
744    pub player_teleport:
745        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, pos: YogVec3) -> bool,
746    pub send_to_player: unsafe extern "C" fn(
747        ctx: *mut c_void,
748        player: YogStr,
749        channel: YogStr,
750        data: *const u8,
751        len: u32,
752    ) -> bool,
753    pub send_to_server:
754        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, data: *const u8, len: u32) -> bool,
755    pub kick_player: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, reason: YogStr) -> bool,
756    pub set_gamemode: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, mode: YogStr) -> bool,
757    pub send_title: unsafe extern "C" fn(
758        ctx: *mut c_void,
759        player: YogStr,
760        title: YogStr,
761        sub: YogStr,
762        fi: i32,
763        stay: i32,
764        fo: i32,
765    ) -> bool,
766    pub send_actionbar: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, msg: YogStr) -> bool,
767    pub play_sound: unsafe extern "C" fn(
768        ctx: *mut c_void,
769        dim: YogStr,
770        pos: YogVec3,
771        sound: YogStr,
772        vol: f32,
773        pitch: f32,
774    ) -> bool,
775    pub play_sound_player: unsafe extern "C" fn(
776        ctx: *mut c_void,
777        player: YogStr,
778        sound: YogStr,
779        vol: f32,
780        pitch: f32,
781    ) -> bool,
782
783    // ── entity ───────────────────────────────────────────────────────────────
784    pub entity_teleport: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, pos: YogVec3) -> bool,
785    pub entity_position:
786        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
787    pub entity_health: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut f32) -> bool,
788    pub entity_set_health: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, hp: f32) -> bool,
789    pub entity_kill: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> bool,
790    pub spawn_entity: unsafe extern "C" fn(
791        ctx: *mut c_void,
792        type_id: YogStr,
793        dim: YogStr,
794        pos: YogVec3,
795    ) -> YogOwnedStr,
796    pub entity_add_effect: unsafe extern "C" fn(
797        ctx: *mut c_void,
798        uuid: YogStr,
799        fx: YogStr,
800        dur: i32,
801        amp: u8,
802        particles: bool,
803    ) -> bool,
804    pub entity_remove_effect:
805        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, fx: YogStr) -> bool,
806    pub entity_clear_effects: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> bool,
807    pub entity_velocity:
808        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
809    pub entity_set_velocity:
810        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, vel: YogVec3) -> bool,
811    pub entity_add_velocity:
812        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, vel: YogVec3) -> bool,
813
814    // ── tags & loot ──────────────────────────────────────────────────────────
815    pub has_item_tag: unsafe extern "C" fn(ctx: *mut c_void, item: YogStr, tag: YogStr) -> bool,
816    pub has_block_tag: unsafe extern "C" fn(ctx: *mut c_void, block: YogStr, tag: YogStr) -> bool,
817    pub drop_loot:
818        unsafe extern "C" fn(ctx: *mut c_void, table: YogStr, dim: YogStr, pos: YogVec3) -> bool,
819
820    // ── scoreboard ───────────────────────────────────────────────────────────
821    pub scoreboard_get:
822        unsafe extern "C" fn(ctx: *mut c_void, obj: YogStr, player: YogStr, out: *mut i32) -> bool,
823    pub scoreboard_set:
824        unsafe extern "C" fn(ctx: *mut c_void, obj: YogStr, player: YogStr, score: i32) -> bool,
825    pub scoreboard_add: unsafe extern "C" fn(
826        ctx: *mut c_void,
827        obj: YogStr,
828        player: YogStr,
829        delta: i32,
830        out: *mut i32,
831    ) -> bool,
832
833    // ── boss bar ─────────────────────────────────────────────────────────────
834    pub bossbar_create: unsafe extern "C" fn(
835        ctx: *mut c_void,
836        id: YogStr,
837        title: YogStr,
838        color: YogStr,
839        style: YogStr,
840    ) -> bool,
841    pub bossbar_remove: unsafe extern "C" fn(ctx: *mut c_void, id: YogStr) -> bool,
842    pub bossbar_set_title:
843        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, title: YogStr) -> bool,
844    pub bossbar_set_progress:
845        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, progress: f32) -> bool,
846    pub bossbar_set_color:
847        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, color: YogStr) -> bool,
848    pub bossbar_add_player:
849        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, player: YogStr) -> bool,
850    pub bossbar_remove_player:
851        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, player: YogStr) -> bool,
852    pub bossbar_set_visible:
853        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, visible: bool) -> bool,
854
855    // ── misc ─────────────────────────────────────────────────────────────────
856    pub game_dir: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
857
858    // ── player query (ABI minor 4) ────────────────────────────────────────────
859    /// Newline-separated list of online player names, or NONE if server not up.
860    pub online_players: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
861
862    // ── block entity (NBT, ABI minor 3) ──────────────────────────────────────
863    /// SNBT string of the block entity at `pos`, or NONE if there is none.
864    pub get_block_nbt:
865        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
866    /// Write SNBT into the block entity at `pos`. Returns false if no block entity exists.
867    pub set_block_nbt:
868        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, snbt: YogStr) -> bool,
869
870    // ── inventory (ABI minor 3) ───────────────────────────────────────────────
871    /// Tab/newline-encoded inventory: one line per occupied slot, `slot\titem_id\tcount`.
872    pub player_inventory: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
873    /// Set (or clear when count==0) a specific inventory slot.
874    pub player_set_slot: unsafe extern "C" fn(
875        ctx: *mut c_void,
876        player: YogStr,
877        slot: u32,
878        item_id: YogStr,
879        count: u32,
880    ) -> bool,
881
882    // ── inventory-backed block slots (ABI minor 25, yog-inventory) ──────────
883    /// `"item_id\tcount"` for the given slot of the inventory-backed block at
884    /// `pos`, or NONE if the slot is empty / there is no such inventory there.
885    pub get_inventory_slot: unsafe extern "C" fn(
886        ctx: *mut c_void,
887        dim: YogStr,
888        pos: YogBlockPos,
889        slot: u32,
890    ) -> YogOwnedStr,
891    /// Set (or clear when count==0) a specific slot of the inventory-backed
892    /// block at `pos`. Returns false if there is no such inventory there.
893    pub set_inventory_slot: unsafe extern "C" fn(
894        ctx: *mut c_void,
895        dim: YogStr,
896        pos: YogBlockPos,
897        slot: u32,
898        item_id: YogStr,
899        count: u32,
900    ) -> bool,
901
902    // ── cross-dimension teleport (ABI minor 3) ────────────────────────────────
903    pub player_teleport_dim:
904        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, dim: YogStr, pos: YogVec3) -> bool,
905    pub entity_teleport_dim:
906        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, dim: YogStr, pos: YogVec3) -> bool,
907
908    // ── entity counting ───────────────────────────────────────────────────────
909    /// Count loaded instances of `entity_type` in `dimension`. Returns -1 on error.
910    pub world_entity_count:
911        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, entity_type: YogStr) -> i32,
912
913    // ── entity NBT (ABI minor 6) ──────────────────────────────────────────────
914    /// SNBT of the entity's persistent data, or NONE if entity not found.
915    pub entity_get_nbt: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
916    /// Merge SNBT data into the entity. Returns false if entity not found.
917    pub entity_set_nbt: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, snbt: YogStr) -> bool,
918
919    // ── particles (ABI minor 6) ───────────────────────────────────────────────
920    /// Spawn `count` particles at `pos` in `dim`.
921    /// `dx/dy/dz` control spread, `speed` controls particle speed.
922    /// Returns false if the dimension or particle type is unknown.
923    pub spawn_particles: unsafe extern "C" fn(
924        ctx: *mut c_void,
925        dim: YogStr,
926        pos: YogVec3,
927        particle_type: YogStr,
928        count: i32,
929        dx: f64,
930        dy: f64,
931        dz: f64,
932        speed: f64,
933    ) -> bool,
934
935    // ── attributes (ABI minor 7) ──────────────────────────────────────────────
936    /// Get the base value of an attribute on a living entity.
937    /// `attribute_id` is a registry id, e.g. `"minecraft:generic.max_health"`.
938    /// Returns `f64::NAN` if entity or attribute is not found.
939    pub entity_attribute_get:
940        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, attribute_id: YogStr) -> f64,
941    /// Set the base value of an attribute. Returns false if entity or attribute is not found.
942    pub entity_attribute_set: unsafe extern "C" fn(
943        ctx: *mut c_void,
944        uuid: YogStr,
945        attribute_id: YogStr,
946        value: f64,
947    ) -> bool,
948
949    // ── held item NBT (ABI minor 11) ─────────────────────────────────────────
950    /// SNBT of the item currently held in the player's main hand.
951    /// Returns NONE if the player is offline or holding air.
952    pub get_held_item_nbt: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
953    /// Merge `snbt` data into the NBT of the player's held main-hand item in-place.
954    /// Returns false if the player is offline or holding air.
955    pub set_held_item_nbt:
956        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, snbt: YogStr) -> bool,
957
958    // ── item stack query (ABI minor 12) ──────────────────────────────────────
959    /// SNBT of the item in the player's off hand, or NONE if offline / holding air.
960    pub get_offhand_item_nbt: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
961    /// Merge `snbt` into the NBT of the player's off-hand item.
962    /// Returns false if offline or holding air.
963    pub set_offhand_item_nbt:
964        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, snbt: YogStr) -> bool,
965    /// Full item stack at inventory `slot`: tab-separated `item_id\tcount\tsnbt`.
966    /// `snbt` is `{}` when the item has no NBT. Returns NONE if offline or slot empty.
967    pub get_slot_item:
968        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, slot: u32) -> YogOwnedStr,
969    /// Replace inventory `slot` with an item stack. `snbt` may be empty to clear NBT.
970    /// Pass `count == 0` to clear the slot (ignores `item_id` and `snbt`).
971    pub set_slot_item: unsafe extern "C" fn(
972        ctx: *mut c_void,
973        player: YogStr,
974        slot: u32,
975        item_id: YogStr,
976        count: u32,
977        snbt: YogStr,
978    ) -> bool,
979
980    // ── entity rotation (ABI minor 25) ───────────────────────────────────────
981    /// Yaw and pitch (degrees) of an entity by UUID. Returns false if the
982    /// entity does not exist. `out` receives x=yaw, y=pitch, z=0.
983    pub entity_rotation:
984        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
985}
986
987// ctx = *mut JavaVM which is global/stable. All fn ptrs are pure C-ABI.
988unsafe impl Send for YogServer {}
989unsafe impl Sync for YogServer {}
990
991// ── Chunk generator callback (ABI minor 29) ───────────────────────────────────
992
993/// Per-callback context handed to a registered chunk generator (see
994/// `YogApi::register_chunk_generator`) — mirrors `YogGfxApi`'s pattern of
995/// bundling per-call context with the function pointer(s) needed to act on
996/// it. Valid only for the duration of the generator callback.
997#[repr(C)]
998#[derive(Copy, Clone)]
999pub struct YogChunkWriterApi {
1000    /// Chunk X coordinate (in chunks, not blocks).
1001    pub chunk_x: i32,
1002    /// Chunk Z coordinate (in chunks, not blocks).
1003    pub chunk_z: i32,
1004    /// Minimum build height (world Y) for this dimension.
1005    pub min_y: i32,
1006    /// Total world height for this dimension.
1007    pub height: i32,
1008    /// Opaque host-side handle for the chunk currently being generated.
1009    pub ctx: *mut c_void,
1010    /// Set a block within this chunk column. `x`/`z` are local (0..16)
1011    /// chunk-relative coordinates; `y` is world-absolute.
1012    pub set_block:
1013        unsafe extern "C" fn(ctx: *mut c_void, x: i32, y: i32, z: i32, block_id: YogStr) -> bool,
1014}
1015
1016unsafe impl Send for YogChunkWriterApi {}
1017unsafe impl Sync for YogChunkWriterApi {}
1018
1019/// Called by the host once per chunk column that needs generating in the
1020/// registered dimension. `writer` is valid only for the duration of the call.
1021pub type YogChunkGeneratorFn =
1022    unsafe extern "C" fn(ud: *mut c_void, writer: *const YogChunkWriterApi);
1023
1024// ── Registration table (passed to yog_mod_register) ──────────────────────────
1025
1026/// Passed to `yog_mod_register`. Call the function pointers here to register
1027/// handlers, commands, content, and schedulers.
1028///
1029/// When mods compiled against ABI `N` load on a runtime with ABI `M > N`:
1030/// fields beyond `size` are not present in the mod's view — check `size` before
1031/// accessing fields added in later minor versions.
1032#[repr(C)]
1033pub struct YogApi {
1034    pub abi_version: u32,
1035    /// `sizeof(YogApi)` at the runtime's build time.
1036    pub size: u32,
1037    /// Opaque pointer to runtime handler storage.
1038    pub ctx: *mut c_void,
1039    /// Stable server action table — pass to handlers.
1040    pub server: *const YogServer,
1041
1042    // ── events — all handlers receive (ud, srv, event, phase: u8) → bool ────────
1043    // phase 0 = Pre (return false to cancel), phase 1 = Post (return ignored).
1044    pub on_block_break: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogBlockBreakFn),
1045    pub on_chat: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogChatFn),
1046    pub on_player_join: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerFn),
1047    pub on_player_leave: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerFn),
1048    pub on_use_item: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogUseItemFn),
1049    pub on_use_block: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogUseBlockFn),
1050    pub on_attack_entity:
1051        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogAttackEntityFn),
1052    pub on_entity_damage:
1053        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityDamageFn),
1054    pub on_entity_death:
1055        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityDeathFn),
1056    pub on_entity_spawn:
1057        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntitySpawnFn),
1058    pub on_player_place_block:
1059        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlaceBlockFn),
1060    pub on_player_death:
1061        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerDeathFn),
1062    pub on_player_respawn:
1063        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerRespawnFn),
1064    pub on_advancement:
1065        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogAdvancementFn),
1066    // ── ABI minor 8 ──────────────────────────────────────────────────────────
1067    pub on_entity_interact:
1068        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityInteractFn),
1069    pub on_item_craft: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogCraftFn),
1070    pub on_explosion: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogExplosionFn),
1071    // ── ABI minor 9 ──────────────────────────────────────────────────────────
1072    pub on_item_pickup: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogItemPickupFn),
1073    pub on_player_move: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerMoveFn),
1074    pub on_container_open:
1075        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogContainerOpenFn),
1076    pub on_container_close:
1077        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogContainerCloseFn),
1078    pub on_projectile_hit:
1079        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogProjectileHitFn),
1080    pub on_server_tick: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1081    pub on_server_started: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1082    pub on_server_stopping: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1083
1084    // ── networking ───────────────────────────────────────────────────────────
1085    pub on_packet:
1086        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, ud: *mut c_void, h: YogPacketFn),
1087    pub on_client_packet:
1088        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, ud: *mut c_void, h: YogPacketFn),
1089
1090    // ── commands ─────────────────────────────────────────────────────────────
1091    pub register_command:
1092        unsafe extern "C" fn(ctx: *mut c_void, name: YogStr, ud: *mut c_void, h: YogCommandFn),
1093    pub register_typed_command: unsafe extern "C" fn(
1094        ctx: *mut c_void,
1095        name: YogStr,
1096        schema: YogStr,
1097        ud: *mut c_void,
1098        h: YogCommandFn,
1099    ),
1100
1101    // ── recipes ──────────────────────────────────────────────────────────────
1102    /// Register a recipe by supplying Minecraft JSON (`data/` format).
1103    /// `namespace` + `name` form the file path: `data/{ns}/recipes/{name}.json`.
1104    pub register_recipe_json:
1105        unsafe extern "C" fn(ctx: *mut c_void, namespace: YogStr, name: YogStr, json: YogStr),
1106
1107    // ── content ──────────────────────────────────────────────────────────────
1108    pub register_item: unsafe extern "C" fn(ctx: *mut c_void, def: *const YogItemDef),
1109    pub register_block: unsafe extern "C" fn(ctx: *mut c_void, def: *const YogBlockDef),
1110
1111    // ── scheduler ────────────────────────────────────────────────────────────
1112    pub schedule_once: unsafe extern "C" fn(
1113        ctx: *mut c_void,
1114        delay_ticks: u64,
1115        ud: *mut c_void,
1116        h: YogScheduledFn,
1117    ),
1118    pub schedule_repeating: unsafe extern "C" fn(
1119        ctx: *mut c_void,
1120        period_ticks: u64,
1121        ud: *mut c_void,
1122        h: YogScheduledFn,
1123    ),
1124
1125    // ── ABI minor 10 — client-side events ────────────────────────────────────
1126    pub on_client_tick: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogClientFn),
1127    pub on_hud_render: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogHudRenderFn),
1128    pub on_key_press: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogKeyPressFn),
1129    pub on_screen_open: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogScreenFn),
1130    pub on_screen_close: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogScreenFn),
1131
1132    // ── ABI minor 14 — world render ──────────────────────────────────────────
1133    /// Register a handler that fires after world geometry is rendered.
1134    /// `gfx.view_proj` and `gfx.camera_pos` are filled; use them to project
1135    /// custom 3D geometry into clip space.
1136    pub on_world_render:
1137        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogWorldRenderFn),
1138
1139    // ── ABI minor 16 — startup grants ────────────────────────────────────────
1140    pub register_startup_grant:
1141        unsafe extern "C" fn(ctx: *mut c_void, grant: *const YogStartupGrantDef),
1142
1143    // ── ABI minor 18 — books ─────────────────────────────────────────────────
1144    pub register_book: unsafe extern "C" fn(ctx: *mut c_void, book_id: YogStr, book_json: YogStr),
1145
1146    // ── ABI minor 19 — UI system ──────────────────────────────────────────────
1147    /// Register a UI tree. `ui_id` is the unique identifier (e.g. "mymod:menu").
1148    /// `layout_json` is the serialized layout tree.
1149    /// `handler` is called when an interactive element is clicked/keyed.
1150    pub register_ui: unsafe extern "C" fn(
1151        ctx: *mut c_void,
1152        ui_id: YogStr,
1153        layout_json: YogStr,
1154        ud: *mut c_void,
1155        h: YogUIEventFn,
1156    ),
1157
1158    // ── ABI minor 20 — per-UI screen renderer ────────────────────────────────
1159    /// Register a render callback that fires during `YogUIScreen.render()` for a
1160    /// specific UI id — i.e. AFTER the screen darkening, unlike `on_hud_render`.
1161    /// Reuses `YogHudRenderFn` (`fn(ud, gfx)`); `gfx.screen_w/h` are set.
1162    /// Clicks are forwarded as `"click:X:Y"` events to the `register_ui` handler
1163    /// so mods can do their own hit-testing with their stored layout.
1164    pub on_ui_render:
1165        unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr, ud: *mut c_void, h: YogHudRenderFn),
1166
1167    // ── ABI minor 22 — menu entry registration ──────────────────────────────
1168    /// Register a button/link that the host renders on vanilla screens
1169    /// (TitleScreen on Fabric, ModListScreen on Forge/NeoForge).
1170    /// `label` is the human-readable button text (e.g. "Yog Mods").
1171    /// `ui_id` is the Yog UI to open when clicked (e.g. "yog:modlist").
1172    pub register_menu_entry: unsafe extern "C" fn(ctx: *mut c_void, label: YogStr, ui_id: YogStr),
1173
1174    // ── ABI minor 23 — installed mods listing ───────────────────────────────
1175    /// All installed mods as TSV, one per line:
1176    /// `source \t id \t name \t version \t authors \t description`
1177    /// where `source` is `yog` (a .yog mod) or `platform` (a loader mod, e.g.
1178    /// a Fabric/Forge jar). Tabs and newlines inside fields become spaces.
1179    /// Callable at any time after registration (also client-side).
1180    pub mods_list: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
1181    /// Free a `YogOwnedStr` returned by an api-table call (same allocator as
1182    /// `YogServer::free_str`).
1183    pub free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
1184
1185    // ── ABI minor 24 — open a Yog UI screen ─────────────────────────────────
1186    /// Open the Yog UI registered as `ui_id` on the client (schedules onto the
1187    /// render thread). Client-side only; a no-op on dedicated servers.
1188    /// `modal` blocks game input; `pause` pauses a singleplayer game.
1189    pub ui_open: unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr, modal: bool, pause: bool),
1190
1191    // ── ABI minor 25 — inventory framework (yog-inventory) ──────────────────
1192    /// Register a real Container/Menu inventory screen — see `yog_inventory`.
1193    pub register_inventory: unsafe extern "C" fn(ctx: *mut c_void, def: *const YogInventoryDef),
1194
1195    // ── ABI minor 26 — inter-mod communication ─────────────────────────────
1196    /// Export a function pointer under `symbol` for the calling mod (`mod_id`).
1197    /// Other mods can import it via `interop_import`.
1198    pub interop_export:
1199        unsafe extern "C" fn(ctx: *mut c_void, mod_id: YogStr, symbol: YogStr, ptr: *const c_void),
1200    /// Import a function pointer exported by `mod_id` under `symbol`.
1201    /// Returns null if the symbol is not (yet) registered.
1202    pub interop_import:
1203        unsafe extern "C" fn(ctx: *mut c_void, mod_id: YogStr, symbol: YogStr) -> *const c_void,
1204
1205    // ── ABI minor 29 — custom dimensions ────────────────────────────────────
1206    /// Register a dimension-type definition (id + `YogDimensionTypeDef` JSON).
1207    /// Must be called at mod-init time (same window as blocks/items) — the
1208    /// host registers a `LevelStem`/`DimensionType` entry before the
1209    /// registry freezes. See `yog-dimensions` crate.
1210    pub register_dimension_json: unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, json: YogStr),
1211    /// Register a chunk generator for a previously-declared dimension `id`.
1212    /// `handler` is called once per chunk column that needs generating —
1213    /// see `YogChunkWriterApi`/`YogChunkGeneratorFn`.
1214    pub register_chunk_generator:
1215        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, ud: *mut c_void, h: YogChunkGeneratorFn),
1216}
1217
1218unsafe impl Send for YogApi {}
1219unsafe impl Sync for YogApi {}