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