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; the runtime loads a mod
10//! only when its `ABI_VERSION` is exactly the runtime's.
11
12use std::os::raw::c_void;
13
14// ── Version ──────────────────────────────────────────────────────────────────
15
16pub const ABI_MAJOR: u32 = 3;
17pub const ABI_MINOR: u32 = 2;
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    /// Borrow `s` — not `FromStr`, which can't return a borrowing value.
39    #[allow(clippy::should_implement_trait)]
40    #[inline]
41    pub fn from_str(s: &str) -> Self {
42        Self {
43            ptr: s.as_ptr(),
44            len: s.len() as u32,
45        }
46    }
47
48    #[inline]
49    pub fn is_empty(self) -> bool {
50        self.len == 0 || self.ptr.is_null()
51    }
52
53    /// # Safety
54    ///
55    /// `ptr` must be valid UTF-8 of `len` bytes for at least the duration of
56    /// the call that provided this `YogStr`.
57    #[inline]
58    pub unsafe fn as_str<'a>(self) -> &'a str {
59        unsafe {
60            if self.ptr.is_null() || self.len == 0 {
61                return "";
62            }
63            std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.ptr, self.len as usize))
64        }
65    }
66}
67
68/// Heap-allocated UTF-8 string owned by the RUNTIME.
69/// `ptr == null` encodes `None` / "not found".
70/// When `ptr` is non-null the caller MUST free it via `YogServer::free_str`.
71#[repr(C)]
72#[derive(Copy, Clone)]
73pub struct YogOwnedStr {
74    pub ptr: *mut u8,
75    pub len: u32,
76}
77
78impl YogOwnedStr {
79    pub const NONE: Self = Self {
80        ptr: std::ptr::null_mut(),
81        len: 0,
82    };
83
84    /// Allocate a new `YogOwnedStr` from a Rust `String`.
85    pub fn from_string(s: String) -> Self {
86        let len = s.len() as u32;
87        let ptr = Box::into_raw(s.into_bytes().into_boxed_slice()) as *mut u8;
88        Self { ptr, len }
89    }
90
91    #[inline]
92    pub fn is_none(self) -> bool {
93        self.ptr.is_null()
94    }
95}
96
97/// Integer 3-D block position.
98#[repr(C)]
99#[derive(Copy, Clone)]
100pub struct YogBlockPos {
101    pub x: i32,
102    pub y: i32,
103    pub z: i32,
104}
105
106/// Float 3-D vector (position, velocity, …).
107#[repr(C)]
108#[derive(Copy, Clone)]
109pub struct YogVec3 {
110    pub x: f64,
111    pub y: f64,
112    pub z: f64,
113}
114
115// ── Event structs (Java → Rust) ───────────────────────────────────────────────
116
117#[repr(C)]
118pub struct YogBlockBreakEvent {
119    pub player: YogStr,
120    pub block: YogStr,
121    pub pos: YogBlockPos,
122    /// Dimension the block was broken in, e.g. `minecraft:overworld`.
123    pub dimension: YogStr,
124}
125
126#[repr(C)]
127pub struct YogChatEvent {
128    pub player: YogStr,
129    pub message: YogStr,
130    pub dimension: YogStr,
131}
132
133/// Shared by player_join and player_leave.
134#[repr(C)]
135pub struct YogPlayerEvent {
136    pub player: YogStr,
137    pub uuid: YogStr,
138    pub dimension: YogStr,
139}
140
141#[repr(C)]
142pub struct YogUseItemEvent {
143    pub player: YogStr,
144    pub item: YogStr,
145    /// ABI minor 25: whether the player was sneaking (shift) during use.
146    pub sneaking: bool,
147    pub dimension: YogStr,
148}
149
150#[repr(C)]
151pub struct YogUseBlockEvent {
152    pub player: YogStr,
153    pub block: YogStr,
154    pub pos: YogBlockPos,
155    pub dimension: YogStr,
156    /// Whether the player was sneaking (shift).
157    pub sneaking: bool,
158}
159
160#[repr(C)]
161pub struct YogAttackEntityEvent {
162    pub player: YogStr,
163    pub target_type: YogStr,
164    pub target_uuid: YogStr,
165    pub dimension: YogStr,
166}
167
168#[repr(C)]
169pub struct YogEntityDamageEvent {
170    pub entity_type: YogStr,
171    pub uuid: YogStr,
172    pub amount: f32,
173    pub source: YogStr,
174    pub dimension: YogStr,
175}
176
177#[repr(C)]
178pub struct YogEntityDeathEvent {
179    pub entity_type: YogStr,
180    pub uuid: YogStr,
181    pub source: YogStr,
182    pub dimension: YogStr,
183}
184
185#[repr(C)]
186pub struct YogEntitySpawnEvent {
187    pub entity_type: YogStr,
188    pub uuid: YogStr,
189    pub dimension: YogStr,
190}
191
192/// Fired when a player dies (Pre: before death is processed; Post: after death).
193/// Pre phase — return false to prevent death (keep entity alive at 0.5 HP).
194#[repr(C)]
195pub struct YogPlayerDeathEvent {
196    pub player: YogStr,
197    pub uuid: YogStr,
198    /// Damage source identifier, e.g. `"player"`, `"fall"`.
199    pub source: YogStr,
200    pub dimension: YogStr,
201}
202
203/// Fired when a player respawns after death.
204#[repr(C)]
205pub struct YogPlayerRespawnEvent {
206    pub player: YogStr,
207    pub uuid: YogStr,
208    /// True if respawning at a bed or anchor, false at world spawn.
209    pub at_anchor: bool,
210    /// Dimension respawned into (may differ from where the player died).
211    pub dimension: YogStr,
212}
213
214/// Fired when a player earns an advancement (Post only).
215#[repr(C)]
216pub struct YogAdvancementEvent {
217    pub player: YogStr,
218    pub uuid: YogStr,
219    /// Namespaced id, e.g. `"minecraft:story/mine_stone"`.
220    pub advancement: YogStr,
221    pub dimension: YogStr,
222}
223
224/// Fired when a player right-clicks (interacts with) an entity (Pre: before; Post: after).
225/// Pre phase — return false to cancel the interaction.
226#[repr(C)]
227pub struct YogEntityInteractEvent {
228    pub player: YogStr,
229    pub player_uuid: YogStr,
230    pub entity_type: YogStr,
231    pub entity_uuid: YogStr,
232    /// `"main_hand"` or `"off_hand"`.
233    pub hand: YogStr,
234    pub dimension: YogStr,
235}
236
237/// Fired when a player takes a crafted item from a crafting output slot (Post only).
238#[repr(C)]
239pub struct YogCraftEvent {
240    pub player: YogStr,
241    pub player_uuid: YogStr,
242    pub result_item: YogStr,
243    pub result_count: u32,
244    pub dimension: YogStr,
245}
246
247/// A machine (an inventory's `.yui` `<RecipeArea>`) and the recipe its
248/// slots make. Pre: whether it may — the result shows only if every
249/// handler allows it (a workbench short of a resource says no); `player`
250/// empty. Post: a player took the result — the mod takes its share (the
251/// host takes the ingredients).
252#[repr(C)]
253pub struct YogMachineCraftEvent {
254    /// The machine's inventory, e.g. `mymod:press`.
255    pub machine: YogStr,
256    /// The recipe's kind and id.
257    pub kind: YogStr,
258    pub recipe: YogStr,
259    pub result_item: YogStr,
260    pub result_count: u32,
261    /// The recipe's values (`{ "energy": 4 }`), as JSON.
262    pub values: YogStr,
263    pub pos: YogBlockPos,
264    pub dimension: YogStr,
265    pub player: YogStr,
266}
267
268/// Fired when an explosion occurs in a world (Pre: before block destruction; Post: after).
269/// Pre phase — return false to cancel the explosion (block and entity damage suppressed).
270#[repr(C)]
271pub struct YogExplosionEvent {
272    pub dimension: YogStr,
273    pub x: f64,
274    pub y: f64,
275    pub z: f64,
276    pub power: f32,
277    /// UUID of the entity that caused the explosion, or empty if world/tnt.
278    pub cause_uuid: YogStr,
279}
280
281// ── ABI minor 9–10 event structs ──────────────────────────────────────────────
282
283/// A hopper or dropper puts an item into a block that accepts items
284/// (Pre: would it take it; Post: it takes it).
285#[repr(C)]
286pub struct YogItemInsertEvent {
287    pub block: YogStr,
288    pub pos: YogBlockPos,
289    pub dimension: YogStr,
290    pub item_id: YogStr,
291    pub count: u32,
292    /// The item's data as SNBT (`{}` for none).
293    pub nbt: YogStr,
294}
295
296/// Fired when a player picks up an item entity (Pre: cancellable; Post: informational).
297#[repr(C)]
298pub struct YogItemPickupEvent {
299    pub player: YogStr,
300    pub player_uuid: YogStr,
301    /// Registry id of the item, e.g. `"minecraft:diamond"`.
302    pub item_id: YogStr,
303    pub item_count: u32,
304    /// UUID of the item entity that was picked up.
305    pub entity_uuid: YogStr,
306    pub dimension: YogStr,
307}
308
309/// Fired when a player sends a movement packet (Post only; very high frequency).
310#[repr(C)]
311pub struct YogPlayerMoveEvent {
312    pub player: YogStr,
313    pub player_uuid: YogStr,
314    pub x: f64,
315    pub y: f64,
316    pub z: f64,
317    pub yaw: f32,
318    pub pitch: f32,
319    pub dimension: YogStr,
320}
321
322/// Fired when a player opens a container screen (Pre: cancellable; Post: informational).
323/// `container_type` is the screen handler registry id, e.g. `"minecraft:chest"`.
324/// Empty string if the type is not in the registry (e.g. the player inventory).
325#[repr(C)]
326pub struct YogContainerOpenEvent {
327    pub player: YogStr,
328    pub player_uuid: YogStr,
329    pub container_type: YogStr,
330    pub dimension: YogStr,
331}
332
333/// Fired when a player closes a container screen (Post only).
334#[repr(C)]
335pub struct YogContainerCloseEvent {
336    pub player: YogStr,
337    pub player_uuid: YogStr,
338    pub dimension: YogStr,
339}
340
341/// Fired when a persistent projectile (arrow, trident) hits a target (Pre: cancellable).
342/// Pre phase — return false to cancel the hit (projectile passes through).
343#[repr(C)]
344pub struct YogProjectileHitEvent {
345    /// Registry id of the projectile, e.g. `"minecraft:arrow"`.
346    pub projectile_type: YogStr,
347    pub projectile_uuid: YogStr,
348    /// UUID of the entity that shot/threw this projectile, or empty.
349    pub shooter_uuid: YogStr,
350    /// `"block"` or `"entity"`.
351    pub hit_type: YogStr,
352    /// UUID of the entity that was hit (empty for block hits).
353    pub hit_entity_uuid: YogStr,
354    pub x: f64,
355    pub y: f64,
356    pub z: f64,
357    pub dimension: YogStr,
358}
359
360/// Fired when a player places a block (Pre: before placement; Post: after).
361#[repr(C)]
362pub struct YogPlaceBlockEvent {
363    pub player: YogStr,
364    pub block: YogStr,
365    pub pos: YogBlockPos,
366    pub dimension: YogStr,
367    /// Whether the player was sneaking (shift).
368    pub sneaking: bool,
369}
370
371#[repr(C)]
372pub struct YogPacketEvent {
373    pub channel: YogStr,
374    pub player: YogStr, // empty string on client-received packets
375    pub payload: *const u8,
376    pub payload_len: u32,
377}
378
379#[repr(C)]
380pub struct YogCommandEvent {
381    pub name: YogStr,
382    pub args: YogStr,
383    pub source: YogStr,
384    pub uuid: YogStr,
385    /// Empty for console-run commands (no world).
386    pub dimension: YogStr,
387}
388
389// ── ABI minor 10 client event structs ─────────────────────────────────────────
390
391/// Key press / release / repeat from the keyboard (client-side only).
392#[repr(C)]
393#[derive(Copy, Clone)]
394pub struct YogKeyPressEvent {
395    /// GLFW key code (e.g. `GLFW_KEY_E = 69`).
396    pub key_code: i32,
397    pub scan_code: i32,
398    /// 0 = release, 1 = press, 2 = repeat.
399    pub action: i32,
400    /// Modifier bitmask: 1=shift, 2=ctrl, 4=alt, 8=super.
401    pub modifiers: i32,
402}
403
404// ── Handler function-pointer types ────────────────────────────────────────────
405//
406// All event handlers receive a `phase: u8` argument:
407//   0 = Pre  — fires before the action; return value matters (false = cancel).
408//   1 = Post — fires after the action; return value is ignored.
409//
410// This unified signature lets one registered closure handle both phases.
411//
412// Client-side handlers (minor 10) do NOT receive a `YogServer*` — they run on
413// the render thread and have no server context.
414
415pub type YogBlockBreakFn =
416    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogBlockBreakEvent, u8) -> bool;
417pub type YogChatFn =
418    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogChatEvent, u8) -> bool;
419pub type YogPlayerFn =
420    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerEvent, u8) -> bool;
421pub type YogUseItemFn =
422    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogUseItemEvent, u8) -> bool;
423pub type YogUseBlockFn =
424    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogUseBlockEvent, u8) -> bool;
425pub type YogAttackEntityFn =
426    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogAttackEntityEvent, u8) -> bool;
427pub type YogEntityDamageFn =
428    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityDamageEvent, u8) -> bool;
429pub type YogEntityDeathFn =
430    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityDeathEvent, u8) -> bool;
431pub type YogEntitySpawnFn =
432    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntitySpawnEvent, u8) -> bool;
433pub type YogPlaceBlockFn =
434    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlaceBlockEvent, u8) -> bool;
435pub type YogPlayerDeathFn =
436    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerDeathEvent, u8) -> bool;
437pub type YogPlayerRespawnFn =
438    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerRespawnEvent, u8) -> bool;
439pub type YogAdvancementFn =
440    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogAdvancementEvent, u8) -> bool;
441pub type YogEntityInteractFn =
442    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogEntityInteractEvent, u8) -> bool;
443pub type YogCraftFn =
444    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogCraftEvent, u8) -> bool;
445pub type YogMachineCraftFn =
446    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogMachineCraftEvent, u8) -> bool;
447pub type YogExplosionFn =
448    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogExplosionEvent, u8) -> bool;
449pub type YogItemInsertFn =
450    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogItemInsertEvent, u8) -> bool;
451pub type YogItemPickupFn =
452    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogItemPickupEvent, u8) -> bool;
453pub type YogPlayerMoveFn =
454    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPlayerMoveEvent, u8) -> bool;
455pub type YogContainerOpenFn =
456    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogContainerOpenEvent, u8) -> bool;
457pub type YogContainerCloseFn =
458    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogContainerCloseEvent, u8) -> bool;
459pub type YogProjectileHitFn =
460    unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogProjectileHitEvent, u8) -> bool;
461
462/// Packet events — always Post, no phase.
463pub type YogPacketFn = unsafe extern "C" fn(*mut c_void, *const YogServer, *const YogPacketEvent);
464/// Server lifecycle / tick — no event struct, always fires.
465pub type YogServerFn = unsafe extern "C" fn(*mut c_void, *const YogServer);
466/// Command handler.
467pub type YogCommandFn = unsafe extern "C" fn(
468    ud: *mut c_void,
469    srv: *const YogServer,
470    ev: *const YogCommandEvent,
471    reply_buf: *mut u8,
472    reply_cap: u32,
473    reply_len: *mut u32,
474);
475/// Scheduler handler (once or repeating).
476pub type YogScheduledFn = unsafe extern "C" fn(*mut c_void, *const YogServer);
477
478// ── ABI minor 10 — client-side function pointer types ────────────────────────
479
480/// Client tick — no event, no server context.
481pub type YogClientFn = unsafe extern "C" fn(ud: *mut c_void);
482/// UI event for a `register_ui` screen. `event_id` is one of
483/// `click:X:Y`, `drag:X:Y`, `release:X:Y`, `scroll:DY` and (ABI minor 33)
484/// `key:KEY:MODS` — a GLFW key code pressed or repeating, with GLFW modifier
485/// bits — and `char:CODEPOINT` — a typed character (Unicode scalar value).
486/// Call `YogApi::ui_consume_event` while handling `key:`/`char:` to stop the
487/// host's default handling of that key (Esc closing the screen, ...).
488pub type YogUIEventFn = unsafe extern "C" fn(ud: *mut c_void, ui_id: YogStr, event_id: YogStr);
489/// HUD render — `gfx` is the graphics context for this frame; only valid for
490/// the call duration.  `draw2d_*` functions in `gfx` work here.
491pub type YogHudRenderFn = unsafe extern "C" fn(ud: *mut c_void, gfx: *const YogGfxApi);
492/// A book page's picture: `data`/`len` is the page's `data` (Yog Data,
493/// binary), `x, y, w, h` its box in screen pixels; `gfx` is valid for the
494/// call.
495pub type YogPageDrawFn = unsafe extern "C" fn(
496    ud: *mut c_void,
497    gfx: *const YogGfxApi,
498    data: *const u8,
499    len: usize,
500    x: f32,
501    y: f32,
502    w: f32,
503    h: f32,
504);
505/// Whether a stack of `item` with custom data `snbt` gets a tooltip
506/// picture; if so its size in GUI pixels goes to `w`, `h`.
507pub type YogTooltipSizeFn =
508    unsafe extern "C" fn(ud: *mut c_void, item: YogStr, snbt: YogStr, w: *mut f32, h: *mut f32) -> bool;
509/// Draw the tooltip picture of a stack of `item` (custom data `snbt`) in
510/// the box `x, y, w, h` (screen pixels); `gfx` is valid for the call.
511pub type YogTooltipDrawFn = unsafe extern "C" fn(
512    ud: *mut c_void,
513    gfx: *const YogGfxApi,
514    item: YogStr,
515    snbt: YogStr,
516    x: f32,
517    y: f32,
518    w: f32,
519    h: f32,
520);
521/// Where a callback writes text it gives back: `write(sink, text)`, the
522/// text copied by the caller during the call.
523pub type YogTextSink = unsafe extern "C" fn(sink: *mut c_void, text: YogStr);
524/// The name and tooltip lines of a stack of `item` with custom data
525/// `snbt`: `false` for the item's own; else one `write` of the name (empty:
526/// the item's own) and the lines that stand instead of its description,
527/// separated by `\n`.
528/// Takes bytes a handler writes (copies them; valid for the call).
529pub type YogBytesSink = unsafe extern "C" fn(sink: *mut c_void, data: *const u8, len: usize);
530/// A body frame (`yog_body::Frame`, its wire form): the handler writes its
531/// ops (`yog_body::Ops`) to `sink`.
532pub type YogBodyFn = unsafe extern "C" fn(ud: *mut c_void, frame: *const u8, len: usize, sink: *mut c_void, write: YogBytesSink);
533/// The camera this frame; the handler changes `out` (each handler sees the
534/// last one's result).
535pub type YogCameraFn = unsafe extern "C" fn(ud: *mut c_void, frame: *const YogCameraFrame, out: *mut YogCameraOut);
536/// The local player's travel step: `true` and `velocity` set replaces it.
537pub type YogTravelFn = unsafe extern "C" fn(ud: *mut c_void, frame: *const YogTravelFrame, velocity: *mut [f32; 3]) -> bool;
538
539/// See `yog_body::CameraFrame`.
540#[repr(C)]
541#[derive(Copy, Clone)]
542pub struct YogCameraFrame {
543    pub uuid: YogStr,
544    pub partial: f32,
545    pub pos: [f64; 3],
546    pub yaw: f32,
547    pub pitch: f32,
548    pub detached: bool,
549    pub flags: u32,
550}
551
552/// See `yog_body::CameraOut`.
553#[repr(C)]
554#[derive(Copy, Clone)]
555pub struct YogCameraOut {
556    pub local_offset: [f32; 3],
557    pub world_offset: [f32; 3],
558    pub yaw: f32,
559    pub pitch: f32,
560    pub roll: f32,
561    pub fov: f32,
562}
563
564/// See `yog_body::TravelFrame`.
565#[repr(C)]
566#[derive(Copy, Clone)]
567pub struct YogTravelFrame {
568    pub uuid: YogStr,
569    pub velocity: [f32; 3],
570    pub look: [f32; 3],
571    pub yaw: f32,
572    pub pitch: f32,
573    pub forward: f32,
574    pub strafe: f32,
575    pub jumping: bool,
576    pub sneaking: bool,
577    pub gravity: f32,
578    pub flags: u32,
579}
580
581pub type YogItemTextFn =
582    unsafe extern "C" fn(ud: *mut c_void, item: YogStr, snbt: YogStr, sink: *mut c_void, write: YogTextSink) -> bool;
583/// World render — `gfx` contains `view_proj` and `camera_pos` for 3D rendering.
584/// Valid only for the call duration.
585pub type YogWorldRenderFn = unsafe extern "C" fn(ud: *mut c_void, gfx: *const YogGfxApi);
586/// Key press — return `false` to cancel (prevent Minecraft from processing the key).
587pub type YogKeyPressFn = unsafe extern "C" fn(ud: *mut c_void, ev: *const YogKeyPressEvent) -> bool;
588/// Screen event — `screen_class` is the simple class name (e.g. `"InventoryScreen"`).
589/// For `on_screen_open` return `false` to prevent the screen from opening.
590pub type YogScreenFn = unsafe extern "C" fn(ud: *mut c_void, screen_class: YogStr) -> bool;
591
592// ── ABI minor 14 — low-level GPU pipeline ────────────────────────────────────
593
594/// Low-level GPU context passed to render handlers.
595///
596/// Provides direct access to the OpenGL pipeline: buffer objects, vertex arrays,
597/// shader programs, textures, draw calls, and render state.
598///
599/// The per-frame fields (`screen_w/h`, `delta_tick`, `view_proj`, `camera_pos`)
600/// are filled by the runtime before calling the handler; the function pointers
601/// point to statically-allocated implementations.
602///
603/// Valid only for the duration of the render callback — never store the pointer.
604/// GPU resource **handles** (`u32`) may be stored between frames.
605///
606/// Colors are `0xAARRGGBB` (Minecraft convention).
607#[repr(C)]
608#[derive(Copy, Clone)]
609pub struct YogGfxApi {
610    // ── Per-frame context ─────────────────────────────────────────────────────
611    /// GUI-pixel screen width.
612    pub screen_w: i32,
613    /// GUI-pixel screen height.
614    pub screen_h: i32,
615    /// Partial-tick interpolation factor (0.0–1.0).
616    pub delta_tick: f32,
617    /// GUI scale factor: physical pixels per GUI pixel (e.g. 2.0 for 2× GUI scale).
618    /// Useful for converting GUI-pixel coordinates to physical pixels for OpenGL calls.
619    pub scale_factor: f32,
620    /// View-projection matrix in camera-relative space (column-major, 16 × f32).
621    /// All zeros during `on_hud_render`; filled during `on_world_render`.
622    pub view_proj: [f32; 16],
623    /// Camera world-space position.  All zeros during `on_hud_render`.
624    pub camera_pos: [f32; 3],
625    /// Local player world-space position (eye height).  All zeros during `on_hud_render`.
626    /// Differs from `camera_pos` in third-person view.
627    pub player_pos: [f32; 3],
628    pub _pad1: f32,
629
630    // ── GPU buffers ───────────────────────────────────────────────────────────
631    /// Allocate a new GPU buffer (VBO / EBO). Returns 0 on failure.
632    pub buf_create: unsafe extern "C" fn() -> u32,
633    /// Delete a buffer created by `buf_create`.
634    pub buf_delete: unsafe extern "C" fn(handle: u32),
635    /// Upload `len` bytes from `bytes` into `handle`.
636    /// `dynamic`: hints frequent updates (`GL_DYNAMIC_DRAW` vs `GL_STATIC_DRAW`).
637    pub buf_data: unsafe extern "C" fn(handle: u32, bytes: *const u8, len: u32, dynamic: bool),
638    /// Overwrite `len` bytes at `offset` in `handle`.
639    pub buf_subdata: unsafe extern "C" fn(handle: u32, offset: u32, bytes: *const u8, len: u32),
640
641    // ── Vertex arrays ─────────────────────────────────────────────────────────
642    /// Create a vertex array object. Returns 0 on failure.
643    pub vao_create: unsafe extern "C" fn() -> u32,
644    /// Delete a VAO created by `vao_create`.
645    pub vao_delete: unsafe extern "C" fn(handle: u32),
646    /// Declare one vertex attribute in `vao`, sourced from `vbo`.
647    /// `dtype`: 0=f32, 1=u8, 2=i32, 3=u32.
648    pub vao_attrib: unsafe extern "C" fn(
649        vao: u32,
650        vbo: u32,
651        index: u32,
652        components: u8,
653        dtype: u8,
654        normalized: bool,
655        stride: u32,
656        offset: u32,
657    ),
658    /// Bind an index buffer (EBO) to `vao`.
659    pub vao_set_ebo: unsafe extern "C" fn(vao: u32, ebo: u32),
660
661    // ── Shader programs ───────────────────────────────────────────────────────
662    /// Compile + link `vert_src` / `frag_src` (GLSL 150 core).
663    /// Writes the program handle to `*out`. Returns false and logs on error.
664    pub prog_create: unsafe extern "C" fn(vert: YogStr, frag: YogStr, out: *mut u32) -> bool,
665    /// Delete a shader program.
666    pub prog_delete: unsafe extern "C" fn(handle: u32),
667    pub prog_uniform_1i: unsafe extern "C" fn(prog: u32, name: YogStr, v: i32),
668    pub prog_uniform_1f: unsafe extern "C" fn(prog: u32, name: YogStr, v: f32),
669    pub prog_uniform_2f: unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32),
670    pub prog_uniform_3f: unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32, z: f32),
671    pub prog_uniform_4f:
672        unsafe extern "C" fn(prog: u32, name: YogStr, x: f32, y: f32, z: f32, w: f32),
673    /// Set a mat4 uniform from 16 column-major floats.
674    pub prog_uniform_mat4: unsafe extern "C" fn(prog: u32, name: YogStr, col_major: *const f32),
675
676    // ── Textures ──────────────────────────────────────────────────────────────
677    /// Upload RGBA8 pixel data as a new texture.
678    /// `linear`: `GL_LINEAR` if true, `GL_NEAREST` if false.
679    pub tex_create: unsafe extern "C" fn(w: u32, h: u32, rgba: *const u8, linear: bool) -> u32,
680    /// Delete a texture created by `tex_create`.
681    pub tex_delete: unsafe extern "C" fn(handle: u32),
682    /// Bind `handle` to texture unit `unit` (0–7).
683    pub tex_bind: unsafe extern "C" fn(unit: u32, handle: u32),
684    /// Return the GL texture handle Minecraft uses for a namespaced resource
685    /// (e.g. `"minecraft:textures/gui/icons.png"`). Returns 0 if not found.
686    /// Do **not** delete handles obtained this way — Minecraft owns them.
687    pub tex_from_mc: unsafe extern "C" fn(id: YogStr) -> u32,
688
689    // ── Draw calls ────────────────────────────────────────────────────────────
690    /// Draw `count` primitives from `vao`, using shader `prog`.
691    /// `mode`: 0=Triangles, 1=Lines, 2=LineStrip, 3=TriangleStrip, 4=TriangleFan.
692    pub draw_arrays: unsafe extern "C" fn(vao: u32, prog: u32, mode: u8, first: u32, count: u32),
693    /// Draw indexed primitives.  `ebo` must be bound to `vao` via `vao_set_ebo`.
694    /// `u32_idx`: `true` for `u32` indices, `false` for `u16` indices.
695    pub draw_elements:
696        unsafe extern "C" fn(vao: u32, ebo: u32, prog: u32, mode: u8, count: u32, u32_idx: bool),
697
698    // ── Render state ──────────────────────────────────────────────────────────
699    /// Enable/disable blending. `src`/`dst` are raw GL blend factor enum values.
700    pub set_blend: unsafe extern "C" fn(enabled: bool, src: u32, dst: u32),
701    /// Enable/disable depth testing and depth writes.
702    pub set_depth: unsafe extern "C" fn(test: bool, write: bool),
703    /// Enable the GL scissor: `x, y, w, h` in framebuffer pixels, from the
704    /// bottom-left corner — for the GPU drawing here, not `draw2d_*` (the
705    /// game batches 2D drawing; clip it by hand, as yog-ui does).
706    pub set_scissor: unsafe extern "C" fn(x: i32, y: i32, w: i32, h: i32),
707    /// Disable scissor clipping.
708    pub clear_scissor: unsafe extern "C" fn(),
709    /// Set the GL viewport (physical pixel coordinates).
710    pub set_viewport: unsafe extern "C" fn(x: i32, y: i32, w: i32, h: i32),
711
712    // ── 2D convenience (HUD-render only — uses MC's DrawContext) ─────────────
713    /// Filled rectangle. Only valid during `on_hud_render`.
714    pub draw2d_rect: unsafe extern "C" fn(x1: f32, y1: f32, x2: f32, y2: f32, color: u32),
715    /// Vertical-gradient rectangle. Only valid during `on_hud_render`.
716    pub draw2d_gradient:
717        unsafe extern "C" fn(x1: f32, y1: f32, x2: f32, y2: f32, top: u32, bottom: u32),
718    /// MC text renderer string. Only valid during `on_hud_render`.
719    pub draw2d_text: unsafe extern "C" fn(text: YogStr, x: f32, y: f32, color: u32, shadow: bool),
720
721    // ── appended in ABI minor 21 ──────────────────────────────────────────────
722    /// Render an item stack (3D block models included) via MC's item renderer,
723    /// like Patchouli's `renderItemStack`. `id` is a registry item id
724    /// ("minecraft:crafting_table"); `size` is the on-screen size in GUI px
725    /// (16 = inventory size). Only valid during `on_hud_render`.
726    pub draw2d_item: unsafe extern "C" fn(id: YogStr, x: f32, y: f32, size: f32),
727
728    // ── appended in ABI minor 33 ──────────────────────────────────────────────
729    /// `draw2d_text` in the font `font` — a font id such as
730    /// `"minecraft:default"`, `"minecraft:uniform"`, `"minecraft:alt"` or a
731    /// resource-pack font (`"mymod:fancy"`); empty = the default font. Only
732    /// valid during a render callback.
733    pub draw2d_text_font:
734        unsafe extern "C" fn(text: YogStr, font: YogStr, x: f32, y: f32, color: u32, shadow: bool),
735    /// Width in GUI pixels of `text` drawn in `font` (empty = default), as the
736    /// host's text renderer measures it. Callable from the render thread at
737    /// any time (render callbacks and UI event handlers); the pointer stays
738    /// valid for the life of the process.
739    pub text_width: unsafe extern "C" fn(text: YogStr, font: YogStr) -> f32,
740
741    // ── appended in ABI minor 34 ──────────────────────────────────────────────
742    /// Draw the image `source` — a Yog image source (`item:apple`,
743    /// `block:stone`, `sprite:button`, `mymod:gui/logo`, ...; see
744    /// `yog_gfx::image`), resolved by the host for its Minecraft version —
745    /// scaled into `(x, y, w, h)` (GUI pixels, nearest-neighbour). `(u, v,
746    /// uw, vh)` is the part of the image to draw, in the image's own pixels;
747    /// `uw <= 0` or `vh <= 0` = the whole image (items and sprites are
748    /// always whole). `tint` multiplies the colors (`0xAARRGGBB`;
749    /// `0xFFFFFFFF` = none; items ignore it). Only valid during a render
750    /// callback.
751    pub draw2d_image: unsafe extern "C" fn(
752        source: YogStr,
753        x: f32,
754        y: f32,
755        w: f32,
756        h: f32,
757        u: f32,
758        v: f32,
759        uw: f32,
760        vh: f32,
761        tint: u32,
762    ),
763    /// Natural size in GUI pixels of the image `source` (16×16 for items, a
764    /// texture's pixel size, ...) into `*w`/`*h`. `false` (outputs untouched)
765    /// when the source can't be resolved. Callable from the render thread at
766    /// any time; the pointer stays valid for the life of the process.
767    pub image_size: unsafe extern "C" fn(source: YogStr, w: *mut f32, h: *mut f32) -> bool,
768
769    // ── ABI 1.4 ──────────────────────────────────────────────────────────────
770    /// Draws the SVG document `data` scaled into `(x, y, w, h)` GUI pixels
771    /// (rasterized by the host at the screen's resolution, cached).
772    pub draw2d_svg: unsafe extern "C" fn(data: YogStr, x: f32, y: f32, w: f32, h: f32),
773    /// Draws a living entity of type `entity` (`minecraft:zombie`) standing
774    /// in `(x, y, w, h)` GUI pixels, as big as fits, turned `yaw` degrees
775    /// (0 = facing the viewer) and tilted `pitch` degrees.
776    pub draw2d_entity:
777        unsafe extern "C" fn(entity: YogStr, x: f32, y: f32, w: f32, h: f32, yaw: f32, pitch: f32),
778
779    // ── ABI 1.5 ──────────────────────────────────────────────────────────────
780    /// Draws `text` in `font` (empty = default) at `(x, y)`, `scale` times
781    /// its size.
782    pub draw2d_text_scaled: unsafe extern "C" fn(
783        text: YogStr,
784        font: YogStr,
785        x: f32,
786        y: f32,
787        scale: f32,
788        color: u32,
789        shadow: bool,
790    ),
791    /// Draws blocks in 3D, as big as fits in `(x, y, w, h)`: `blocks` is one
792    /// `x y z state` line per block (`0 0 0 minecraft:furnace[facing=north]`),
793    /// turned `yaw` degrees about the vertical and tilted `pitch` degrees.
794    pub draw2d_blocks:
795        unsafe extern "C" fn(blocks: YogStr, x: f32, y: f32, w: f32, h: f32, yaw: f32, pitch: f32),
796}
797
798unsafe impl Send for YogGfxApi {}
799unsafe impl Sync for YogGfxApi {}
800
801// ── Server action table (runtime → mod direction is wrong; it's mod → runtime) ─
802
803/// All Minecraft-mutating calls available inside a handler.
804///
805/// `ctx` is an opaque pointer to the runtime's JNI state.  Every function takes
806/// it as its first argument.  The pointer is valid for the lifetime of the process.
807///
808/// Strings **returned** by functions in this table are heap-allocated by the
809/// runtime and must be freed with `free_str` after the caller has read them.
810#[repr(C)]
811pub struct YogServer {
812    pub ctx: *mut c_void,
813    pub abi_version: u32,
814    /// `sizeof(YogServer)` at build time — allows mods compiled against an older
815    /// table to detect and skip fields they don't know about.
816    pub size: u32,
817
818    /// Free a string returned by any function in this table.
819    pub free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
820
821    // ── chat ─────────────────────────────────────────────────────────────────
822    pub broadcast: unsafe extern "C" fn(ctx: *mut c_void, msg: YogStr),
823
824    // ── world ────────────────────────────────────────────────────────────────
825    pub get_block:
826        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
827    pub set_block: unsafe extern "C" fn(
828        ctx: *mut c_void,
829        dim: YogStr,
830        pos: YogBlockPos,
831        block: YogStr,
832    ) -> bool,
833    pub world_time: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, out: *mut i64) -> bool,
834    pub set_time: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, time: i64) -> bool,
835    pub is_raining: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr) -> bool,
836    pub set_weather:
837        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, raining: bool, dur: i32) -> bool,
838
839    // ── player ───────────────────────────────────────────────────────────────
840    pub give_item:
841        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, item: YogStr, count: u32) -> bool,
842    pub player_teleport:
843        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, pos: YogVec3) -> bool,
844    pub send_to_player: unsafe extern "C" fn(
845        ctx: *mut c_void,
846        player: YogStr,
847        channel: YogStr,
848        data: *const u8,
849        len: u32,
850    ) -> bool,
851    pub send_to_server:
852        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, data: *const u8, len: u32) -> bool,
853    pub kick_player: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, reason: YogStr) -> bool,
854    pub set_gamemode: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, mode: YogStr) -> bool,
855    pub send_title: unsafe extern "C" fn(
856        ctx: *mut c_void,
857        player: YogStr,
858        title: YogStr,
859        sub: YogStr,
860        fi: i32,
861        stay: i32,
862        fo: i32,
863    ) -> bool,
864    pub send_actionbar: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, msg: YogStr) -> bool,
865    pub play_sound: unsafe extern "C" fn(
866        ctx: *mut c_void,
867        dim: YogStr,
868        pos: YogVec3,
869        sound: YogStr,
870        vol: f32,
871        pitch: f32,
872    ) -> bool,
873    pub play_sound_player: unsafe extern "C" fn(
874        ctx: *mut c_void,
875        player: YogStr,
876        sound: YogStr,
877        vol: f32,
878        pitch: f32,
879    ) -> bool,
880
881    // ── entity ───────────────────────────────────────────────────────────────
882    pub entity_teleport: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, pos: YogVec3) -> bool,
883    pub entity_position:
884        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
885    pub entity_health: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut f32) -> bool,
886    pub entity_set_health: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, hp: f32) -> bool,
887    pub entity_kill: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> bool,
888    pub spawn_entity: unsafe extern "C" fn(
889        ctx: *mut c_void,
890        type_id: YogStr,
891        dim: YogStr,
892        pos: YogVec3,
893    ) -> YogOwnedStr,
894    pub entity_add_effect: unsafe extern "C" fn(
895        ctx: *mut c_void,
896        uuid: YogStr,
897        fx: YogStr,
898        dur: i32,
899        amp: u8,
900        particles: bool,
901    ) -> bool,
902    pub entity_remove_effect:
903        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, fx: YogStr) -> bool,
904    pub entity_clear_effects: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> bool,
905    pub entity_velocity:
906        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
907    pub entity_set_velocity:
908        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, vel: YogVec3) -> bool,
909    pub entity_add_velocity:
910        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, vel: YogVec3) -> bool,
911
912    // ── tags & loot ──────────────────────────────────────────────────────────
913    pub has_item_tag: unsafe extern "C" fn(ctx: *mut c_void, item: YogStr, tag: YogStr) -> bool,
914    pub has_block_tag: unsafe extern "C" fn(ctx: *mut c_void, block: YogStr, tag: YogStr) -> bool,
915    pub drop_loot:
916        unsafe extern "C" fn(ctx: *mut c_void, table: YogStr, dim: YogStr, pos: YogVec3) -> bool,
917
918    // ── scoreboard ───────────────────────────────────────────────────────────
919    pub scoreboard_get:
920        unsafe extern "C" fn(ctx: *mut c_void, obj: YogStr, player: YogStr, out: *mut i32) -> bool,
921    pub scoreboard_set:
922        unsafe extern "C" fn(ctx: *mut c_void, obj: YogStr, player: YogStr, score: i32) -> bool,
923    pub scoreboard_add: unsafe extern "C" fn(
924        ctx: *mut c_void,
925        obj: YogStr,
926        player: YogStr,
927        delta: i32,
928        out: *mut i32,
929    ) -> bool,
930
931    // ── boss bar ─────────────────────────────────────────────────────────────
932    pub bossbar_create: unsafe extern "C" fn(
933        ctx: *mut c_void,
934        id: YogStr,
935        title: YogStr,
936        color: YogStr,
937        style: YogStr,
938    ) -> bool,
939    pub bossbar_remove: unsafe extern "C" fn(ctx: *mut c_void, id: YogStr) -> bool,
940    pub bossbar_set_title:
941        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, title: YogStr) -> bool,
942    pub bossbar_set_progress:
943        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, progress: f32) -> bool,
944    pub bossbar_set_color:
945        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, color: YogStr) -> bool,
946    pub bossbar_add_player:
947        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, player: YogStr) -> bool,
948    pub bossbar_remove_player:
949        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, player: YogStr) -> bool,
950    pub bossbar_set_visible:
951        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, visible: bool) -> bool,
952
953    // ── misc ─────────────────────────────────────────────────────────────────
954    pub game_dir: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
955
956    // ── player query (ABI minor 4) ────────────────────────────────────────────
957    /// Newline-separated list of online player names, or NONE if server not up.
958    pub online_players: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
959
960    // ── block entity (NBT, ABI minor 3) ──────────────────────────────────────
961    /// SNBT string of the block entity at `pos`, or NONE if there is none.
962    pub get_block_nbt:
963        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
964    /// Write SNBT into the block entity at `pos`. Returns false if no block entity exists.
965    pub set_block_nbt:
966        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, snbt: YogStr) -> bool,
967
968    // ── inventory (ABI minor 3) ───────────────────────────────────────────────
969    /// Tab/newline-encoded inventory: one line per occupied slot, `slot\titem_id\tcount`.
970    pub player_inventory: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
971    /// Set (or clear when count==0) a specific inventory slot.
972    pub player_set_slot: unsafe extern "C" fn(
973        ctx: *mut c_void,
974        player: YogStr,
975        slot: u32,
976        item_id: YogStr,
977        count: u32,
978    ) -> bool,
979
980    // ── inventory-backed block slots (ABI minor 25, yog-inventory) ──────────
981    /// `"item_id\tcount\tsnbt"` for the given slot of the block container
982    /// at `pos`, or NONE if the slot is empty / there's no container there.
983    pub get_inventory_slot: unsafe extern "C" fn(
984        ctx: *mut c_void,
985        dim: YogStr,
986        pos: YogBlockPos,
987        slot: u32,
988    ) -> YogOwnedStr,
989    /// Set (or clear when count==0) a slot of the block container at `pos`;
990    /// `item_id` may carry the item's SNBT after a tab. Returns false if
991    /// there's no container there.
992    pub set_inventory_slot: unsafe extern "C" fn(
993        ctx: *mut c_void,
994        dim: YogStr,
995        pos: YogBlockPos,
996        slot: u32,
997        item_id: YogStr,
998        count: u32,
999    ) -> bool,
1000
1001    // ── cross-dimension teleport (ABI minor 3) ────────────────────────────────
1002    pub player_teleport_dim:
1003        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, dim: YogStr, pos: YogVec3) -> bool,
1004    pub entity_teleport_dim:
1005        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, dim: YogStr, pos: YogVec3) -> bool,
1006
1007    // ── entity counting ───────────────────────────────────────────────────────
1008    /// Count loaded instances of `entity_type` in `dimension`. Returns -1 on error.
1009    pub world_entity_count:
1010        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, entity_type: YogStr) -> i32,
1011
1012    // ── entity NBT (ABI minor 6) ──────────────────────────────────────────────
1013    /// SNBT of the entity's persistent data, or NONE if entity not found.
1014    pub entity_get_nbt: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
1015    /// Merge SNBT data into the entity. Returns false if entity not found.
1016    pub entity_set_nbt: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, snbt: YogStr) -> bool,
1017
1018    // ── particles (ABI minor 6) ───────────────────────────────────────────────
1019    /// Spawn `count` particles at `pos` in `dim`.
1020    /// `dx/dy/dz` control spread, `speed` controls particle speed.
1021    /// Returns false if the dimension or particle type is unknown.
1022    pub spawn_particles: unsafe extern "C" fn(
1023        ctx: *mut c_void,
1024        dim: YogStr,
1025        pos: YogVec3,
1026        particle_type: YogStr,
1027        count: i32,
1028        dx: f64,
1029        dy: f64,
1030        dz: f64,
1031        speed: f64,
1032    ) -> bool,
1033
1034    // ── attributes (ABI minor 7) ──────────────────────────────────────────────
1035    /// Get the base value of an attribute on a living entity.
1036    /// `attribute_id` is a registry id, e.g. `"minecraft:generic.max_health"`.
1037    /// Returns `f64::NAN` if entity or attribute is not found.
1038    pub entity_attribute_get:
1039        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, attribute_id: YogStr) -> f64,
1040    /// Set the base value of an attribute. Returns false if entity or attribute is not found.
1041    pub entity_attribute_set: unsafe extern "C" fn(
1042        ctx: *mut c_void,
1043        uuid: YogStr,
1044        attribute_id: YogStr,
1045        value: f64,
1046    ) -> bool,
1047
1048    // ── held item NBT (ABI minor 11) ─────────────────────────────────────────
1049    /// SNBT of the item currently held in the player's main hand.
1050    /// Returns NONE if the player is offline or holding air.
1051    pub get_held_item_nbt: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
1052    /// Merge `snbt` data into the NBT of the player's held main-hand item in-place.
1053    /// Returns false if the player is offline or holding air.
1054    pub set_held_item_nbt:
1055        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, snbt: YogStr) -> bool,
1056
1057    // ── item stack query (ABI minor 12) ──────────────────────────────────────
1058    /// SNBT of the item in the player's off hand, or NONE if offline / holding air.
1059    pub get_offhand_item_nbt: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
1060    /// Merge `snbt` into the NBT of the player's off-hand item.
1061    /// Returns false if offline or holding air.
1062    pub set_offhand_item_nbt:
1063        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, snbt: YogStr) -> bool,
1064    /// Full item stack at inventory `slot`: tab-separated `item_id\tcount\tsnbt`.
1065    /// `snbt` is `{}` when the item has no NBT. Returns NONE if offline or slot empty.
1066    pub get_slot_item:
1067        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, slot: u32) -> YogOwnedStr,
1068    /// Replace inventory `slot` with an item stack. `snbt` may be empty to clear NBT.
1069    /// Pass `count == 0` to clear the slot (ignores `item_id` and `snbt`).
1070    pub set_slot_item: unsafe extern "C" fn(
1071        ctx: *mut c_void,
1072        player: YogStr,
1073        slot: u32,
1074        item_id: YogStr,
1075        count: u32,
1076        snbt: YogStr,
1077    ) -> bool,
1078
1079    // ── entity rotation (ABI minor 25) ───────────────────────────────────────
1080    /// Yaw and pitch (degrees) of an entity by UUID. Returns false if the
1081    /// entity does not exist. `out` receives x=yaw, y=pitch, z=0.
1082    pub entity_rotation:
1083        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, out: *mut YogVec3) -> bool,
1084
1085    // ── world queries & effects (ABI minor 1) ────────────────────────────────
1086    // Records come back as one tab-separated line; `yog_core`'s `EntityInfo`,
1087    // `BlockHit`, `BlockInfo` and `PlayerInfo` document and decode them.
1088    /// `EntityInfo` line of a loaded entity, or NONE.
1089    pub entity_info: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
1090    /// Newline-separated uuids of the entities touching the box.
1091    pub entities_in_box: unsafe extern "C" fn(
1092        ctx: *mut c_void,
1093        dim: YogStr,
1094        min: YogVec3,
1095        max: YogVec3,
1096    ) -> YogOwnedStr,
1097    /// `BlockHit` line of the first block on the segment, or NONE.
1098    pub raycast_block: unsafe extern "C" fn(
1099        ctx: *mut c_void,
1100        dim: YogStr,
1101        from: YogVec3,
1102        to: YogVec3,
1103    ) -> YogOwnedStr,
1104    /// Uuid of the nearest entity on the segment (not `exclude`), or NONE.
1105    pub raycast_entity: unsafe extern "C" fn(
1106        ctx: *mut c_void,
1107        dim: YogStr,
1108        from: YogVec3,
1109        to: YogVec3,
1110        exclude: YogStr,
1111    ) -> YogOwnedStr,
1112    /// `BlockInfo` line of the block at `pos`, or NONE.
1113    pub block_info:
1114        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
1115    pub break_block: unsafe extern "C" fn(
1116        ctx: *mut c_void,
1117        dim: YogStr,
1118        pos: YogBlockPos,
1119        drop: bool,
1120        breaker: YogStr,
1121    ) -> bool,
1122    pub place_block_from_slot: unsafe extern "C" fn(
1123        ctx: *mut c_void,
1124        player: YogStr,
1125        slot: u32,
1126        dim: YogStr,
1127        pos: YogBlockPos,
1128    ) -> bool,
1129    pub bonemeal: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> bool,
1130    pub explode: unsafe extern "C" fn(
1131        ctx: *mut c_void,
1132        dim: YogStr,
1133        pos: YogVec3,
1134        power: f32,
1135        fire: bool,
1136        source: YogStr,
1137    ) -> bool,
1138    /// `json` is a JSON text component (the `/tellraw` format).
1139    pub tellraw: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, json: YogStr) -> bool,
1140    pub entity_damage: unsafe extern "C" fn(
1141        ctx: *mut c_void,
1142        uuid: YogStr,
1143        amount: f32,
1144        damage_type: YogStr,
1145        attacker: YogStr,
1146    ) -> bool,
1147    pub entity_set_fire_ticks:
1148        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, ticks: i32) -> bool,
1149    pub player_add_xp: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, amount: i32) -> bool,
1150    pub player_set_may_fly:
1151        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, may_fly: bool) -> bool,
1152    /// `PlayerInfo` line of an online player, or NONE.
1153    pub player_info: unsafe extern "C" fn(ctx: *mut c_void, player: YogStr) -> YogOwnedStr,
1154    /// Uuid of the dropped item entity, or NONE.
1155    pub spawn_item: unsafe extern "C" fn(
1156        ctx: *mut c_void,
1157        dim: YogStr,
1158        pos: YogVec3,
1159        vel: YogVec3,
1160        item_id: YogStr,
1161        count: u32,
1162        snbt: YogStr,
1163    ) -> YogOwnedStr,
1164    /// `item_id\tcount\tsnbt` of a dropped item entity, or NONE.
1165    pub item_entity_stack: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
1166    pub item_entity_set_stack: unsafe extern "C" fn(
1167        ctx: *mut c_void,
1168        uuid: YogStr,
1169        item_id: YogStr,
1170        count: u32,
1171        snbt: YogStr,
1172    ) -> bool,
1173    // ── ABI minor 2 ──
1174    /// `json` is a JSON text component.
1175    pub actionbar_json:
1176        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, json: YogStr) -> bool,
1177    /// `face` is a `Direction` ordinal (down, up, north, south, west, east).
1178    pub use_item_on_block: unsafe extern "C" fn(
1179        ctx: *mut c_void,
1180        dim: YogStr,
1181        pos: YogBlockPos,
1182        face: u8,
1183        item_id: YogStr,
1184        player: YogStr,
1185    ) -> bool,
1186    pub pour_fluid: unsafe extern "C" fn(
1187        ctx: *mut c_void,
1188        dim: YogStr,
1189        pos: YogBlockPos,
1190        bucket_item_id: YogStr,
1191    ) -> bool,
1192    pub has_entity_tag:
1193        unsafe extern "C" fn(ctx: *mut c_void, entity_type: YogStr, tag_id: YogStr) -> bool,
1194    /// The block id a block item places, or NONE.
1195    pub item_block: unsafe extern "C" fn(ctx: *mut c_void, item_id: YogStr) -> YogOwnedStr,
1196    pub place_feature: unsafe extern "C" fn(
1197        ctx: *mut c_void,
1198        dim: YogStr,
1199        pos: YogBlockPos,
1200        feature_id: YogStr,
1201    ) -> bool,
1202
1203    // ── ABI minor 3 ──────────────────────────────────────────────────────────
1204    pub set_item_cooldown:
1205        unsafe extern "C" fn(ctx: *mut c_void, player: YogStr, item_id: YogStr, ticks: i32) -> bool,
1206    /// The UUID of what an entity rides, or NONE.
1207    pub entity_vehicle: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
1208    /// The UUIDs of what rides an entity directly, one a line (empty: none).
1209    pub entity_passengers: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> YogOwnedStr,
1210    /// Seats `rider` on `vehicle`; `force`: even one that wouldn't take it.
1211    pub entity_mount:
1212        unsafe extern "C" fn(ctx: *mut c_void, rider: YogStr, vehicle: YogStr, force: bool) -> bool,
1213    /// Gets an entity off what it rides.
1214    pub entity_dismount: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr) -> bool,
1215    /// Received redstone power 0–15, or -1 (no such dimension).
1216    pub redstone_power: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32,
1217    /// Sets how far an entity has fallen (fall damage counts it on landing).
1218    pub entity_set_fall_distance: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, distance: f32) -> bool,
1219    /// The strongest signal the block gives off 0–15, or -1 (not a source).
1220    pub redstone_signal: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> i32,
1221    pub set_redstone_output:
1222        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, level: i32) -> bool,
1223    /// `"ns:id[prop=value,...]"`, or NONE.
1224    pub get_block_state: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos) -> YogOwnedStr,
1225    pub set_block_state:
1226        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, state: YogStr) -> bool,
1227    pub world_is_thundering: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr) -> bool,
1228    pub world_set_thunder:
1229        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, thundering: bool, duration_ticks: i32) -> bool,
1230    /// Sets every block of the box `from..=to` to `state`; how many changed, or -1.
1231    pub fill_region: unsafe extern "C" fn(
1232        ctx: *mut c_void,
1233        dim: YogStr,
1234        from: YogBlockPos,
1235        to: YogBlockPos,
1236        state: YogStr,
1237    ) -> i32,
1238    /// The non-air blocks of the box `from..=to`: `"x y z state"` lines, or NONE.
1239    pub scan_region:
1240        unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, from: YogBlockPos, to: YogBlockPos) -> YogOwnedStr,
1241
1242    // ── ABI 3.1 — entity dimensions ─────────────────────────────────────────
1243    /// An entity's own hitbox and eye height (blocks), or vanilla's again
1244    /// (`width` < 0); `false` if there's no such entity.
1245    pub entity_set_dimensions:
1246        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, width: f32, height: f32, eye: f32) -> bool,
1247
1248    // ── ABI 3.2 — entity data ───────────────────────────────────────────────
1249    /// Sets (`flags` & 1) or removes a value an entity keeps under `key`;
1250    /// `flags` & 2 saves it with the entity. The players who see the entity
1251    /// get it. `false` for an empty uuid or key.
1252    pub entity_data_set:
1253        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, key: YogStr, data: *const u8, len: u32, flags: u32) -> bool,
1254    /// The value under `key`, written to `sink`; `false` if there's none.
1255    pub entity_data_get:
1256        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, key: YogStr, sink: *mut c_void, write: YogBytesSink) -> bool,
1257}
1258
1259// ctx = *mut JavaVM which is global/stable. All fn ptrs are pure C-ABI.
1260unsafe impl Send for YogServer {}
1261unsafe impl Sync for YogServer {}
1262
1263// ── Chunk generator callback (ABI minor 29) ───────────────────────────────────
1264
1265/// Per-callback context handed to a registered chunk generator (see
1266/// `YogApi::register_chunk_generator`) — mirrors `YogGfxApi`'s pattern of
1267/// bundling per-call context with the function pointer(s) needed to act on
1268/// it. Valid only for the duration of the generator callback.
1269#[repr(C)]
1270#[derive(Copy, Clone)]
1271pub struct YogChunkWriterApi {
1272    /// Chunk X coordinate (in chunks, not blocks).
1273    pub chunk_x: i32,
1274    /// Chunk Z coordinate (in chunks, not blocks).
1275    pub chunk_z: i32,
1276    /// Minimum build height (world Y) for this dimension.
1277    pub min_y: i32,
1278    /// Total world height for this dimension.
1279    pub height: i32,
1280    /// Opaque host-side handle for the chunk currently being generated.
1281    pub ctx: *mut c_void,
1282    /// Set a block within this chunk column. `x`/`z` are local (0..16)
1283    /// chunk-relative coordinates; `y` is world-absolute.
1284    pub set_block:
1285        unsafe extern "C" fn(ctx: *mut c_void, x: i32, y: i32, z: i32, block_id: YogStr) -> bool,
1286}
1287
1288unsafe impl Send for YogChunkWriterApi {}
1289unsafe impl Sync for YogChunkWriterApi {}
1290
1291/// Called by the host once per chunk column that needs generating in the
1292/// registered dimension. `writer` is valid only for the duration of the call.
1293pub type YogChunkGeneratorFn =
1294    unsafe extern "C" fn(ud: *mut c_void, writer: *const YogChunkWriterApi);
1295
1296// ── Registration table (passed to yog_mod_register) ──────────────────────────
1297
1298/// Passed to `yog_mod_register`. Call the function pointers here to register
1299/// handlers, commands, content, and schedulers.
1300///
1301/// When mods compiled against ABI `N` load on a runtime with ABI `M > N`:
1302/// fields beyond `size` are not present in the mod's view — check `size` before
1303/// accessing fields added in later minor versions.
1304#[repr(C)]
1305pub struct YogApi {
1306    pub abi_version: u32,
1307    /// `sizeof(YogApi)` at the runtime's build time.
1308    pub size: u32,
1309    /// Opaque pointer to runtime handler storage.
1310    pub ctx: *mut c_void,
1311    /// Stable server action table — pass to handlers.
1312    pub server: *const YogServer,
1313
1314    // ── events — all handlers receive (ud, srv, event, phase: u8) → bool ────────
1315    // phase 0 = Pre (return false to cancel), phase 1 = Post (return ignored).
1316    pub on_block_break: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogBlockBreakFn),
1317    pub on_chat: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogChatFn),
1318    pub on_player_join: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerFn),
1319    pub on_player_leave: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerFn),
1320    pub on_use_item: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogUseItemFn),
1321    pub on_use_block: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogUseBlockFn),
1322    pub on_attack_entity:
1323        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogAttackEntityFn),
1324    pub on_entity_damage:
1325        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityDamageFn),
1326    pub on_entity_death:
1327        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityDeathFn),
1328    pub on_entity_spawn:
1329        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntitySpawnFn),
1330    pub on_player_place_block:
1331        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlaceBlockFn),
1332    pub on_player_death:
1333        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerDeathFn),
1334    pub on_player_respawn:
1335        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerRespawnFn),
1336    pub on_advancement:
1337        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogAdvancementFn),
1338    // ── ABI minor 8 ──────────────────────────────────────────────────────────
1339    pub on_entity_interact:
1340        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogEntityInteractFn),
1341    pub on_item_craft: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogCraftFn),
1342    pub on_explosion: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogExplosionFn),
1343    // ── ABI minor 9 ──────────────────────────────────────────────────────────
1344    pub on_item_pickup: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogItemPickupFn),
1345    pub on_item_insert: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogItemInsertFn),
1346    pub on_player_move: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogPlayerMoveFn),
1347    pub on_container_open:
1348        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogContainerOpenFn),
1349    pub on_container_close:
1350        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogContainerCloseFn),
1351    pub on_projectile_hit:
1352        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogProjectileHitFn),
1353    pub on_server_tick: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1354    pub on_server_started: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1355    pub on_server_stopping: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogServerFn),
1356
1357    // ── networking ───────────────────────────────────────────────────────────
1358    pub on_packet:
1359        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, ud: *mut c_void, h: YogPacketFn),
1360    pub on_client_packet:
1361        unsafe extern "C" fn(ctx: *mut c_void, channel: YogStr, ud: *mut c_void, h: YogPacketFn),
1362
1363    // ── commands ─────────────────────────────────────────────────────────────
1364    pub register_command:
1365        unsafe extern "C" fn(ctx: *mut c_void, name: YogStr, ud: *mut c_void, h: YogCommandFn),
1366    pub register_typed_command: unsafe extern "C" fn(
1367        ctx: *mut c_void,
1368        name: YogStr,
1369        schema: YogStr,
1370        ud: *mut c_void,
1371        h: YogCommandFn,
1372    ),
1373
1374    // ── content ──────────────────────────────────────────────────────────────
1375    /// Register content of `kind` (`item`, `block`, `inventory`, `recipe`,
1376    /// `book`, `dimension`, `startup_grant`) named `id`: its definition as
1377    /// a Yog Data blob (`yog_data::binary`, `len` bytes at `data`), the
1378    /// shape of `yog_registry`'s / `yog_inventory`'s definitions, a recipe
1379    /// in Yog's format, a book, a dimension. One call for every kind of
1380    /// content — new kinds need no new ABI.
1381    pub register_content: unsafe extern "C" fn(ctx: *mut c_void, kind: YogStr, id: YogStr, data: *const u8, len: usize),
1382
1383    // ── scheduler ────────────────────────────────────────────────────────────
1384    pub schedule_once: unsafe extern "C" fn(
1385        ctx: *mut c_void,
1386        delay_ticks: u64,
1387        ud: *mut c_void,
1388        h: YogScheduledFn,
1389    ),
1390    pub schedule_repeating: unsafe extern "C" fn(
1391        ctx: *mut c_void,
1392        period_ticks: u64,
1393        ud: *mut c_void,
1394        h: YogScheduledFn,
1395    ),
1396
1397    // ── ABI minor 10 — client-side events ────────────────────────────────────
1398    pub on_client_tick: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogClientFn),
1399    pub on_hud_render: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogHudRenderFn),
1400    pub on_key_press: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogKeyPressFn),
1401    pub on_screen_open: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogScreenFn),
1402    pub on_screen_close: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogScreenFn),
1403
1404    // ── ABI minor 14 — world render ──────────────────────────────────────────
1405    /// Register a handler that fires after world geometry is rendered.
1406    /// `gfx.view_proj` and `gfx.camera_pos` are filled; use them to project
1407    /// custom 3D geometry into clip space.
1408    pub on_world_render:
1409        unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, h: YogWorldRenderFn),
1410
1411    // ── ABI minor 19 — UI system ──────────────────────────────────────────────
1412    /// Register a UI tree. `ui_id` is the unique identifier (e.g. "mymod:menu").
1413    /// `layout_json` is the serialized layout tree.
1414    /// `handler` is called when an interactive element is clicked/keyed.
1415    pub register_ui: unsafe extern "C" fn(
1416        ctx: *mut c_void,
1417        ui_id: YogStr,
1418        layout_json: YogStr,
1419        ud: *mut c_void,
1420        h: YogUIEventFn,
1421    ),
1422
1423    // ── ABI minor 20 — per-UI screen renderer ────────────────────────────────
1424    /// Register a render callback that fires during `YogUIScreen.render()` for a
1425    /// specific UI id — i.e. AFTER the screen darkening, unlike `on_hud_render`.
1426    /// Reuses `YogHudRenderFn` (`fn(ud, gfx)`); `gfx.screen_w/h` are set.
1427    /// Clicks are forwarded as `"click:X:Y"` events to the `register_ui` handler
1428    /// so mods can do their own hit-testing with their stored layout.
1429    pub on_ui_render:
1430        unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr, ud: *mut c_void, h: YogHudRenderFn),
1431
1432    // ── ABI minor 22 — menu entry registration ──────────────────────────────
1433    /// Register a button/link that the host renders on vanilla screens
1434    /// (TitleScreen on Fabric, ModListScreen on Forge/NeoForge).
1435    /// `label` is the human-readable button text (e.g. "Yog Mods").
1436    /// `ui_id` is the Yog UI to open when clicked (e.g. "example:menu").
1437    pub register_menu_entry: unsafe extern "C" fn(ctx: *mut c_void, label: YogStr, ui_id: YogStr),
1438
1439    // ── ABI minor 23 — installed mods listing ───────────────────────────────
1440    /// All installed mods as TSV, one per line:
1441    /// `source \t id \t name \t version \t authors \t description`
1442    /// where `source` is `yog` (a .yog mod) or `platform` (a loader mod, e.g.
1443    /// a Fabric/Forge jar). Tabs and newlines inside fields become spaces.
1444    /// Callable at any time after registration (also client-side).
1445    pub mods_list: unsafe extern "C" fn(ctx: *mut c_void) -> YogOwnedStr,
1446    /// Free a `YogOwnedStr` returned by an api-table call (same allocator as
1447    /// `YogServer::free_str`).
1448    pub free_str: unsafe extern "C" fn(ptr: *mut u8, len: u32),
1449
1450    // ── ABI minor 24 — open a Yog UI screen ─────────────────────────────────
1451    /// Open the Yog UI registered as `ui_id` on the client (schedules onto the
1452    /// render thread). Client-side only; a no-op on dedicated servers.
1453    /// `modal` blocks game input; `pause` pauses a singleplayer game.
1454    pub ui_open: unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr, modal: bool, pause: bool),
1455
1456    // ── ABI minor 26 — inter-mod communication ─────────────────────────────
1457    /// Export a function pointer under `symbol` for the calling mod (`mod_id`).
1458    /// Other mods can import it via `interop_import`.
1459    pub interop_export:
1460        unsafe extern "C" fn(ctx: *mut c_void, mod_id: YogStr, symbol: YogStr, ptr: *const c_void),
1461    /// Import a function pointer exported by `mod_id` under `symbol`.
1462    /// Returns null if the symbol is not (yet) registered.
1463    pub interop_import:
1464        unsafe extern "C" fn(ctx: *mut c_void, mod_id: YogStr, symbol: YogStr) -> *const c_void,
1465
1466    // ── ABI minor 29 — custom dimensions ────────────────────────────────────
1467    /// Register a chunk generator for a previously-declared dimension `id`.
1468    /// `handler` is called once per chunk column that needs generating —
1469    /// see `YogChunkWriterApi`/`YogChunkGeneratorFn`.
1470    pub register_chunk_generator:
1471        unsafe extern "C" fn(ctx: *mut c_void, id: YogStr, ud: *mut c_void, h: YogChunkGeneratorFn),
1472
1473    // ── ABI minor 33 — closing Yog UI screens, consuming UI key events ──────
1474    /// Close the Yog UI screen showing `ui_id` (empty = whichever Yog UI
1475    /// screen is open). Scheduled onto the render thread, so it's safe to
1476    /// call from that screen's own event handler. Client-side only.
1477    pub ui_close: unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr),
1478    /// Called from inside a `register_ui` handler while it handles a `key:`
1479    /// or `char:` event: the mod used the key, so the host skips its default
1480    /// handling (Esc closing the screen, Tab focus navigation, ...).
1481    pub ui_consume_event: unsafe extern "C" fn(ctx: *mut c_void),
1482
1483    // ── ABI minor 33 — translations ─────────────────────────────────────────
1484    /// The text of translation key `key` (e.g. `mymod.screen.title`) in the
1485    /// game's current language, from the loaded lang files — resource packs
1486    /// and `.yog` mods' `assets/<ns>/lang/*.json` (compiled from `.yres`).
1487    /// `YogOwnedStr::NONE` when no language defines the key. Format
1488    /// specifiers (`%s`, `%1$s`) come back as-is. Free with `free_str`.
1489    /// Client-side; a dedicated server only knows `en_us`.
1490    pub translate: unsafe extern "C" fn(ctx: *mut c_void, key: YogStr) -> YogOwnedStr,
1491
1492    // ── ABI minor 1 — client: screen flags, sounds, held items, keys ────────
1493    /// Like `ui_open`, with [`ui_flags`] bits: `MODAL`, `PAUSE`,
1494    /// `NO_BACKGROUND` (no dimmed/blurred world behind the screen).
1495    /// Client-side only.
1496    pub ui_open_flags: unsafe extern "C" fn(ctx: *mut c_void, ui_id: YogStr, flags: u32),
1497    /// Play sound event `sound_id` for the local player only: at the
1498    /// player's position, or (`looping`) as a loop that follows the player
1499    /// until `client_stop_sound`. Returns a handle (0 = failed).
1500    pub client_play_sound: unsafe extern "C" fn(
1501        ctx: *mut c_void,
1502        sound_id: YogStr,
1503        volume: f32,
1504        pitch: f32,
1505        looping: bool,
1506    ) -> u32,
1507    /// Stop a sound started by `client_play_sound` (0 or unknown: no-op).
1508    pub client_stop_sound: unsafe extern "C" fn(ctx: *mut c_void, handle: u32),
1509    /// Item id in the local player's main hand (`off_hand` false) or off
1510    /// hand, `YogOwnedStr::NONE` for air or no player. Free with `free_str`.
1511    pub client_held_item: unsafe extern "C" fn(ctx: *mut c_void, off_hand: bool) -> YogOwnedStr,
1512    /// Tints the block at `pos` in `dim` on this client (its faces with a
1513    /// tint index), `color` 0xRRGGBB; -1 clears it.
1514    pub client_set_block_tint: unsafe extern "C" fn(ctx: *mut c_void, dim: YogStr, pos: YogBlockPos, color: i64),
1515    /// Whether GLFW key `key` is held down right now (client window).
1516    pub client_key_down: unsafe extern "C" fn(ctx: *mut c_void, key: i32) -> bool,
1517
1518    // ── ABI 2.1 — book pages drawn by mods ──────────────────────────────────
1519    /// Draw the pictures of book pages of type `drawn` whose `drawer` is
1520    /// `drawer` (`hexcasting:pattern`) — see [`YogPageDrawFn`].
1521    pub on_book_page:
1522        unsafe extern "C" fn(ctx: *mut c_void, drawer: YogStr, ud: *mut c_void, f: YogPageDrawFn),
1523
1524    // ── ABI 2.2 — pictures in item tooltips ─────────────────────────────────
1525    /// A picture under the tooltip of item `item` (Hex Casting's scrolls
1526    /// show their pattern): `size` says whether a stack gets one and how
1527    /// big, `draw` draws it — see [`YogTooltipSizeFn`], [`YogTooltipDrawFn`].
1528    pub on_item_tooltip: unsafe extern "C" fn(
1529        ctx: *mut c_void,
1530        item: YogStr,
1531        ud: *mut c_void,
1532        size: YogTooltipSizeFn,
1533        draw: YogTooltipDrawFn,
1534    ),
1535
1536    // ── ABI 2.3 — other mods ────────────────────────────────────────────────
1537    /// Whether mod `id` is loaded — a Yog mod or a Java one (Fabric's,
1538    /// Forge's or NeoForge's mod list).
1539    pub is_mod_loaded: unsafe extern "C" fn(ctx: *mut c_void, id: YogStr) -> bool,
1540    /// Calls into a Java mod's API by reflection: `expr` is a chain from a
1541    /// class — `virtuoel.pehkui.api.ScaleTypes.BASE.getScaleData($0).getScale()`
1542    /// — `$n` the `n`-th of `args`, a list in Yog Data text (an entity is
1543    /// `@entity:<uuid>`). The result in Yog Data text, or
1544    /// [`YogOwnedStr::NONE`] when the call failed (said in the log).
1545    /// Free with `free_str`.
1546    pub java_call: unsafe extern "C" fn(ctx: *mut c_void, expr: YogStr, args: YogStr) -> YogOwnedStr,
1547
1548    // ── ABI 2.4 — a stack's own name and tooltip ────────────────────────────
1549    /// The name and tooltip lines of stacks of `item` from its custom data
1550    /// (a written blueprint shows its design) — see [`YogItemTextFn`].
1551    pub on_item_text: unsafe extern "C" fn(ctx: *mut c_void, item: YogStr, ud: *mut c_void, f: YogItemTextFn),
1552
1553    // ── ABI 2.5 — machines ──────────────────────────────────────────────────
1554    /// A machine making a recipe — see [`YogMachineCraftEvent`].
1555    pub on_machine_craft: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, f: YogMachineCraftFn),
1556
1557    // ── ABI 3.0 — bodies, camera, movement (yog-body) ───────────────────────
1558    /// Asks the mods about entity `uuid`'s body each frame (`on`) or not.
1559    pub client_body_track: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, on: bool),
1560    /// The same for every entity of type `entity_type`.
1561    pub client_body_track_type: unsafe extern "C" fn(ctx: *mut c_void, entity_type: YogStr, on: bool),
1562    /// The body of a tracked entity, each frame — see [`YogBodyFn`].
1563    pub on_body: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, f: YogBodyFn),
1564    /// The camera, each frame — see [`YogCameraFn`].
1565    pub on_camera: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, f: YogCameraFn),
1566    /// The local player's travel step, each tick — see [`YogTravelFn`].
1567    pub on_travel: unsafe extern "C" fn(ctx: *mut c_void, ud: *mut c_void, f: YogTravelFn),
1568
1569    // ── ABI 3.1 — entity dimensions (client) ────────────────────────────────
1570    /// This client's side of `entity_set_dimensions` (the local player
1571    /// collides with it); `width` < 0 gives vanilla's back.
1572    pub client_set_dimensions: unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, width: f32, height: f32, eye: f32),
1573
1574    // ── ABI 3.2 — entity data (client) ──────────────────────────────────────
1575    /// What this client was told the entity keeps under `key`, written to
1576    /// `sink`; `false` if nothing.
1577    pub client_entity_data:
1578        unsafe extern "C" fn(ctx: *mut c_void, uuid: YogStr, key: YogStr, sink: *mut c_void, write: YogBytesSink) -> bool,
1579}
1580
1581/// Flag bits for [`YogApi::ui_open_flags`].
1582pub mod ui_flags {
1583    /// Blocks game input (what `ui_open`'s `modal` does).
1584    pub const MODAL: u32 = 1;
1585    /// Pauses a singleplayer game.
1586    pub const PAUSE: u32 = 1 << 1;
1587    /// Draws no background behind the screen: the world stays visible.
1588    pub const NO_BACKGROUND: u32 = 1 << 2;
1589}
1590
1591unsafe impl Send for YogApi {}
1592unsafe impl Sync for YogApi {}
1593
1594#[cfg(test)]
1595mod tests {
1596    use super::*;
1597    use std::mem::{align_of, size_of};
1598
1599    /// Sizes and alignments of every type crossing the ABI, folded together.
1600    fn layout_fingerprint() -> u64 {
1601        macro_rules! layouts {
1602            ($($t:ty),* $(,)?) => { [$((stringify!($t), size_of::<$t>(), align_of::<$t>())),*] };
1603        }
1604        let all = layouts!(
1605            YogStr, YogOwnedStr, YogBlockPos, YogVec3, YogBlockBreakEvent, YogChatEvent, YogPlayerEvent,
1606            YogUseItemEvent, YogUseBlockEvent, YogAttackEntityEvent, YogEntityDamageEvent, YogEntityDeathEvent,
1607            YogEntitySpawnEvent, YogPlayerDeathEvent, YogPlayerRespawnEvent, YogAdvancementEvent,
1608            YogEntityInteractEvent, YogCraftEvent, YogExplosionEvent, YogItemPickupEvent, YogItemInsertEvent, YogPlayerMoveEvent,
1609            YogContainerOpenEvent, YogContainerCloseEvent, YogProjectileHitEvent, YogPlaceBlockEvent,
1610            YogPacketEvent, YogCommandEvent,
1611            YogKeyPressEvent, YogGfxApi, YogServer, YogChunkWriterApi, YogApi,
1612        );
1613        // FNV-1a over (name, size, align).
1614        let mut h: u64 = 0xcbf2_9ce4_8422_2325;
1615        for (name, size, align) in all {
1616            for b in name.bytes().chain((size as u64).to_le_bytes()).chain((align as u64).to_le_bytes()) {
1617                h = (h ^ b as u64).wrapping_mul(0x0100_0000_01b3);
1618            }
1619        }
1620        h
1621    }
1622
1623    /// A type crossing the ABI changed without `ABI_VERSION` changing: bump
1624    /// `ABI_MINOR` (fields appended at the end) or `ABI_MAJOR` (anything
1625    /// else), then record the new fingerprint here.
1626    #[test]
1627    #[cfg(target_pointer_width = "64")]
1628    fn the_layout_matches_its_version() {
1629        assert_eq!((ABI_VERSION, layout_fingerprint()), (30002, 0x73ec_636f_fee7_29d4), "layout fingerprint: {:#x}", layout_fingerprint());
1630    }
1631
1632    #[test]
1633    fn strings_cross_and_come_back() {
1634        let s = YogStr::from_str("Ünïcode ✓");
1635        assert_eq!(unsafe { s.as_str() }, "Ünïcode ✓");
1636        assert!(YogStr::EMPTY.is_empty());
1637        assert_eq!(unsafe { YogStr::EMPTY.as_str() }, "");
1638        let owned = YogOwnedStr::from_string("runtime".to_string());
1639        assert!(!owned.is_none() && owned.len == 7);
1640        assert!(YogOwnedStr::NONE.is_none());
1641        // What `free_str` does with it.
1642        drop(unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(owned.ptr, owned.len as usize)) });
1643    }
1644}