Skip to main content

yog_api/
client.rs

1//! Client-side helpers (ABI minor 1): opening a screen with flags, sounds
2//! only the local player hears, the local player's held items and the
3//! keyboard state. All are no-ops (or `None`/`false`) on a dedicated server
4//! and before the mod registered.
5//!
6//! ```ignore
7//! use yog_api::client::{self, ui_flags};
8//! client::open_ui_flags("mymod:overlay", ui_flags::MODAL | ui_flags::NO_BACKGROUND);
9//! let loop_ = client::play_sound("mymod:hum", 1.0, 1.0, true);
10//! client::stop_sound(loop_);
11//! ```
12
13use yog_abi::YogStr;
14pub use yog_abi::ui_flags;
15
16use crate::registry::global_api;
17
18/// Open the Yog UI `ui_id` with [`ui_flags`] bits.
19pub fn open_ui_flags(ui_id: &str, flags: u32) {
20    let api = global_api();
21    if api.is_null() {
22        return;
23    }
24    unsafe { ((*api).ui_open_flags)((*api).ctx, YogStr::from_str(ui_id), flags) }
25}
26
27/// A sound started by [`play_sound`]; `SoundHandle(0)` is "none".
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29pub struct SoundHandle(pub u32);
30
31/// Play sound event `sound_id` for the local player only, at their
32/// position; `looping` keeps it playing (following the player) until
33/// [`stop_sound`].
34pub fn play_sound(sound_id: &str, volume: f32, pitch: f32, looping: bool) -> SoundHandle {
35    let api = global_api();
36    if api.is_null() {
37        return SoundHandle(0);
38    }
39    SoundHandle(unsafe {
40        ((*api).client_play_sound)(
41            (*api).ctx,
42            YogStr::from_str(sound_id),
43            volume,
44            pitch,
45            looping,
46        )
47    })
48}
49
50/// Stop a sound from [`play_sound`].
51pub fn stop_sound(handle: SoundHandle) {
52    let api = global_api();
53    if api.is_null() || handle.0 == 0 {
54        return;
55    }
56    unsafe { ((*api).client_stop_sound)((*api).ctx, handle.0) }
57}
58
59/// Item id in the local player's main hand (or `off_hand`), `None` for an
60/// empty hand.
61pub fn held_item(off_hand: bool) -> Option<String> {
62    let api = global_api();
63    if api.is_null() {
64        return None;
65    }
66    let owned = unsafe { ((*api).client_held_item)((*api).ctx, off_hand) };
67    crate::registry::take_owned(unsafe { (*api).free_str }, owned)
68}
69
70/// Attribute `attribute_id`'s value on this client's player — its base
71/// changed by every modifier (effects, equipment); `None` without a player
72/// or such an attribute.
73pub fn attribute_value(attribute_id: &str) -> Option<f64> {
74    let api = global_api();
75    if api.is_null() {
76        return None;
77    }
78    let v = unsafe { ((*api).client_attribute_value)((*api).ctx, YogStr::from_str(attribute_id)) };
79    (!v.is_nan()).then_some(v)
80}
81
82/// Whether this client's player has advancement `id`.
83pub fn advancement_done(id: &str) -> bool {
84    let api = global_api();
85    !api.is_null() && unsafe { ((*api).client_advancement_done)((*api).ctx, YogStr::from_str(id)) }
86}
87
88/// Tints the block at `pos` in `dimension` on this client — the faces of
89/// its model with a `tint` index — `0xRRGGBB`; `None` clears it. The mod
90/// sends what to tint (its own packet); the tint lasts till cleared or the
91/// game closes.
92pub fn set_block_tint(dimension: &str, pos: yog_core::BlockPos, color: Option<u32>) {
93    let api = global_api();
94    if api.is_null() {
95        return;
96    }
97    let p = yog_abi::YogBlockPos { x: pos.x, y: pos.y, z: pos.z };
98    let c = color.map_or(-1, |c| (c & 0xFF_FFFF) as i64);
99    unsafe { ((*api).client_set_block_tint)((*api).ctx, YogStr::from_str(dimension), p, c) }
100}
101
102/// GLFW key codes for [`key_down`].
103pub mod keys {
104    pub const LEFT_SHIFT: i32 = 340;
105    pub const LEFT_CONTROL: i32 = 341;
106    pub const LEFT_ALT: i32 = 342;
107    pub const RIGHT_SHIFT: i32 = 344;
108    pub const RIGHT_CONTROL: i32 = 345;
109    pub const RIGHT_ALT: i32 = 346;
110}
111
112/// Whether GLFW key `key` is held right now.
113pub fn key_down(key: i32) -> bool {
114    let api = global_api();
115    if api.is_null() {
116        return false;
117    }
118    unsafe { ((*api).client_key_down)((*api).ctx, key) }
119}
120
121/// Either Control key is held (Minecraft's `Screen.hasControlDown()`; on
122/// macOS Minecraft uses Command — not covered here).
123pub fn control_down() -> bool {
124    key_down(keys::LEFT_CONTROL) || key_down(keys::RIGHT_CONTROL)
125}