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