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/// Draws only `blocks` (ids, `minecraft:diamond_ore`) of the world on this
84/// client: every other block isn't drawn and doesn't hide what's behind it
85/// (an x-ray). Empty: the world drawn as usual again. The world's drawing
86/// is made anew either way.
87pub fn only_blocks(blocks: &[&str]) {
88    call(&client::OnlyBlocks {
89        blocks: blocks.iter().map(|b| (*b).to_owned()).collect(),
90    });
91}
92
93/// GLFW key codes for [`key_down`].
94pub mod keys {
95    /// No key: a key binding left unbound (GLFW's `GLFW_KEY_UNKNOWN`).
96    pub const UNBOUND: i32 = -1;
97    /// Letters: GLFW codes them as their capital's ASCII.
98    pub const A: i32 = 65;
99    pub const B: i32 = 66;
100    pub const C: i32 = 67;
101    pub const D: i32 = 68;
102    pub const E: i32 = 69;
103    pub const F: i32 = 70;
104    pub const G: i32 = 71;
105    pub const H: i32 = 72;
106    pub const I: i32 = 73;
107    pub const J: i32 = 74;
108    pub const K: i32 = 75;
109    pub const L: i32 = 76;
110    pub const M: i32 = 77;
111    pub const N: i32 = 78;
112    pub const O: i32 = 79;
113    pub const P: i32 = 80;
114    pub const Q: i32 = 81;
115    pub const R: i32 = 82;
116    pub const S: i32 = 83;
117    pub const T: i32 = 84;
118    pub const U: i32 = 85;
119    pub const V: i32 = 86;
120    pub const W: i32 = 87;
121    pub const X: i32 = 88;
122    pub const Y: i32 = 89;
123    pub const Z: i32 = 90;
124    pub const HOME: i32 = 268;
125    pub const LEFT_SHIFT: i32 = 340;
126    pub const LEFT_CONTROL: i32 = 341;
127    pub const LEFT_ALT: i32 = 342;
128    pub const RIGHT_SHIFT: i32 = 344;
129    pub const RIGHT_CONTROL: i32 = 345;
130    pub const RIGHT_ALT: i32 = 346;
131}
132
133/// Either Control key is held (Minecraft's `Screen.hasControlDown()`; on
134/// macOS Minecraft uses Command — not covered here).
135pub fn control_down() -> bool {
136    key_down(keys::LEFT_CONTROL) || key_down(keys::RIGHT_CONTROL)
137}