Skip to main content

yog_abi/
lib.rs

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