Skip to main content

yog_event/wire/
handles.rs

1//! Handles: one entity, player or dimension held by its key, each method
2//! the action of that domain with the key put in first (generated by
3//! `actions!` from the actions themselves — nothing here repeats them).
4//!
5//! ```ignore
6//! let me = Player::new(&e.player_name);
7//! me.give_item("minecraft:diamond", 1u32);
8//! if let Some(body) = me.entity() {
9//!     body.teleport((0.5, 100.0, 0.5));
10//! }
11//! ```
12
13use yog_core::{BlockPos, join_state, split_state};
14use yog_ids::sep::{PROPERTY_IS, PROPERTY_SEP};
15
16/// Which handle an action belongs to, by its first field: the actions
17/// whose first field is `uuid` are [`Entity`]'s, `player` [`Player`]'s,
18/// `dim` [`World`]'s. What other languages see of the handles (Yog
19/// Script's objects) reads this; `handle_method!`'s arms are the same
20/// three, as a macro must spell them.
21pub const SUBJECTS: &[(&str, &str)] = &[
22    (stringify!(uuid), stringify!(Entity)),
23    (stringify!(player), stringify!(Player)),
24    (stringify!(dim), stringify!(World)),
25];
26
27/// An entity, by its uuid: every action whose first field is `uuid`.
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct Entity {
30    pub uuid: String,
31}
32
33impl Entity {
34    pub fn new(uuid: impl Into<String>) -> Self {
35        Self { uuid: uuid.into() }
36    }
37}
38
39/// An online player, by name: every action whose first field is `player`.
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub struct Player {
42    pub name: String,
43}
44
45impl Player {
46    pub fn new(name: impl Into<String>) -> Self {
47        Self { name: name.into() }
48    }
49
50    /// The player as an entity (its uuid asked of the server); `None` offline.
51    pub fn entity(&self) -> Option<Entity> {
52        self.info().map(|i| Entity::new(i.uuid))
53    }
54}
55
56/// A dimension (`minecraft:overworld`): every action whose first field is `dim`.
57#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub struct World {
59    pub dim: String,
60}
61
62impl World {
63    pub fn new(dim: impl Into<String>) -> Self {
64        Self { dim: dim.into() }
65    }
66
67    pub fn overworld() -> Self {
68        Self::new(yog_ids::game::OVERWORLD)
69    }
70
71    /// Property `name` of the block at `pos` (`"energized"` → `"true"`).
72    pub fn property(&self, pos: BlockPos, name: &str) -> Option<String> {
73        let state = self.state(pos)?;
74        split_state(&state)
75            .1
76            .into_iter()
77            .find(|(k, _)| *k == name)
78            .map(|(_, v)| v.to_owned())
79    }
80
81    /// Sets some properties of the block at `pos` (`"energized=true,powered=false"`),
82    /// keeping the rest; `false` if there's no block or the state doesn't take.
83    pub fn set_properties(&self, pos: BlockPos, properties: &str) -> bool {
84        let Some(state) = self.state(pos) else {
85            return false;
86        };
87        let (id, mut props) = split_state(&state);
88        for pair in properties.split(PROPERTY_SEP).filter(|p| !p.is_empty()) {
89            let Some((k, v)) = pair.split_once(PROPERTY_IS) else {
90                return false;
91            };
92            match props.iter_mut().find(|(n, _)| *n == k) {
93                Some(p) => p.1 = v,
94                None => props.push((k, v)),
95            }
96        }
97        self.set_state(pos, join_state(id, &props))
98    }
99}