Skip to main content

yog_api/client/
world.rs

1//! The world as this client sees it: its blocks — found by kind, or an
2//! area of them for finding a way — and the entities near the player.
3
4use serde::Deserialize;
5use serde_json::json;
6use yog_ids::client_op as op;
7
8use super::op::ask;
9
10/// A block found by [`scan`]: where, and what.
11#[derive(Deserialize, Debug, Clone, PartialEq)]
12pub struct Found {
13    pub x: i32,
14    pub y: i32,
15    pub z: i32,
16    pub id: String,
17}
18
19/// The blocks among `ids` (`minecraft:diamond_ore`, `#minecraft:logs` for a tag) between `min` and `max`, in the loaded world.
20pub fn scan(min: [i32; 3], max: [i32; 3], ids: &[&str]) -> Vec<Found> {
21    ask(op::SCAN, json!({ "min": min, "max": max, "ids": ids })).unwrap_or_default()
22}
23
24/// One kind of block in an [`Area`].
25#[derive(Deserialize, Debug, Clone, PartialEq)]
26pub struct Kind {
27    pub id: String,
28    /// Can be stood on and can't be walked through.
29    pub solid: bool,
30    pub liquid: bool,
31    /// Ladders, vines: climbed.
32    pub climbable: bool,
33    /// Hurts to touch (fire, lava, cactus, berry bush…).
34    pub harmful: bool,
35}
36
37/// The blocks between `min` and `max`: a palette and an index into it per block, x fastest, then z, then y.
38#[derive(Deserialize, Debug, Clone, PartialEq)]
39pub struct Area {
40    pub min: [i32; 3],
41    pub max: [i32; 3],
42    pub palette: Vec<Kind>,
43    pub blocks: Vec<u16>,
44}
45
46impl Area {
47    /// The kind of block at `pos`, if it's inside.
48    pub fn at(&self, pos: [i32; 3]) -> Option<&Kind> {
49        let [w, _, d] = [self.max[0] - self.min[0] + 1, 0, self.max[2] - self.min[2] + 1];
50        let [x, y, z] = [pos[0] - self.min[0], pos[1] - self.min[1], pos[2] - self.min[2]];
51        let inside = (0..w).contains(&x) && (0..d).contains(&z) && (0..=self.max[1] - self.min[1]).contains(&y);
52        inside.then(|| self.blocks.get(((y * d + z) * w + x) as usize)).flatten().and_then(|i| self.palette.get(*i as usize))
53    }
54}
55
56/// The blocks between `min` and `max` of the loaded world, for finding a way.
57pub fn area(min: [i32; 3], max: [i32; 3]) -> Option<Area> {
58    ask(op::AREA, json!({ "min": min, "max": max }))
59}
60
61/// An entity near the player.
62#[derive(Deserialize, Debug, Clone, PartialEq)]
63pub struct Entity {
64    pub uuid: String,
65    #[serde(rename = "type")]
66    pub kind: String,
67    pub x: f64,
68    pub y: f64,
69    pub z: f64,
70    pub eye_height: f64,
71    /// Its motion, blocks a tick (0 from a host that doesn't tell it).
72    #[serde(default)]
73    pub vx: f64,
74    #[serde(default)]
75    pub vy: f64,
76    #[serde(default)]
77    pub vz: f64,
78    pub health: Option<f32>,
79    /// A player's name.
80    pub name: Option<String>,
81    /// A monster, hostile to players.
82    pub hostile: bool,
83}
84
85/// The entities within `radius` blocks of the player (not the player).
86pub fn entities(radius: f64) -> Vec<Entity> {
87    ask(op::ENTITIES, json!({ "radius": radius })).unwrap_or_default()
88}
89