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