Skip to main content

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