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_event::client;
14pub use yog_event::client::*;
15pub use yog_event::ui::flags as ui_flags;
16
17use crate::call;
18
19/// Open the Yog UI `ui_id` with [`ui_flags`] bits.
20pub fn open_ui_flags(ui_id: &str, flags: u32) {
21    call(&yog_event::ui::OpenWithFlags {
22        ui_id: ui_id.to_owned(),
23        flags: i32::try_from(flags).unwrap_or(i32::MAX),
24    });
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 handle = call(&client::PlaySound {
36        sound: sound_id.to_owned(),
37        volume,
38        pitch,
39        looping,
40    })
41    .flatten();
42    SoundHandle(handle.and_then(|h| u32::try_from(h).ok()).unwrap_or(0))
43}
44
45/// Stop a sound from [`play_sound`].
46pub fn stop_sound(handle: SoundHandle) {
47    if handle.0 == 0 {
48        return;
49    }
50    call(&client::StopSound {
51        handle: i32::try_from(handle.0).unwrap_or(i32::MAX),
52    });
53}
54
55/// Tints the block at `pos` in `dimension` on this client — the faces of
56/// its model with a `tint` index — `0xRRGGBB`; `None` clears it. The mod
57/// sends what to tint (its own packet); the tint lasts till cleared or the
58/// game closes.
59pub fn set_block_tint(dimension: &str, pos: yog_core::BlockPos, color: Option<u32>) {
60    /// What the host reads as "no tint".
61    const CLEAR: i32 = -1;
62    let color = color.map_or(CLEAR, |c| (c & 0xFF_FFFF) as i32);
63    call(&client::SetBlockTint {
64        dim: dimension.to_owned(),
65        pos,
66        color,
67    });
68}
69
70/// GLFW key codes for [`key_down`].
71pub mod keys {
72    /// No key: a key binding left unbound (GLFW's `GLFW_KEY_UNKNOWN`).
73    pub const UNBOUND: i32 = -1;
74    /// Letters: GLFW codes them as their capital's ASCII.
75    pub const A: i32 = 65;
76    pub const B: i32 = 66;
77    pub const C: i32 = 67;
78    pub const D: i32 = 68;
79    pub const E: i32 = 69;
80    pub const F: i32 = 70;
81    pub const G: i32 = 71;
82    pub const H: i32 = 72;
83    pub const I: i32 = 73;
84    pub const J: i32 = 74;
85    pub const K: i32 = 75;
86    pub const L: i32 = 76;
87    pub const M: i32 = 77;
88    pub const N: i32 = 78;
89    pub const O: i32 = 79;
90    pub const P: i32 = 80;
91    pub const Q: i32 = 81;
92    pub const R: i32 = 82;
93    pub const S: i32 = 83;
94    pub const T: i32 = 84;
95    pub const U: i32 = 85;
96    pub const V: i32 = 86;
97    pub const W: i32 = 87;
98    pub const X: i32 = 88;
99    pub const Y: i32 = 89;
100    pub const Z: i32 = 90;
101    pub const HOME: i32 = 268;
102    pub const LEFT_SHIFT: i32 = 340;
103    pub const LEFT_CONTROL: i32 = 341;
104    pub const LEFT_ALT: i32 = 342;
105    pub const RIGHT_SHIFT: i32 = 344;
106    pub const RIGHT_CONTROL: i32 = 345;
107    pub const RIGHT_ALT: i32 = 346;
108}
109
110/// Either Control key is held (Minecraft's `Screen.hasControlDown()`; on
111/// macOS Minecraft uses Command — not covered here).
112pub fn control_down() -> bool {
113    key_down(keys::LEFT_CONTROL) || key_down(keys::RIGHT_CONTROL)
114}