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