Skip to main content

azalea_client/
local_player.rs

1use std::{collections::HashMap, sync::Arc};
2
3use azalea_core::game_type::GameMode;
4use azalea_world::{PartialWorld, World};
5use bevy_ecs::{component::Component, prelude::*};
6use derive_more::{Deref, DerefMut};
7use parking_lot::RwLock;
8use uuid::Uuid;
9
10use crate::{ClientInformation, player::PlayerInfo};
11
12/// A component that keeps strong references to our [`PartialWorld`] and
13/// [`World`] for local players.
14///
15/// This can also act as a convenient way to access the player's `World`, since
16/// the alternative is to look up the player's [`WorldName`] in the [`Worlds`]
17/// resource.
18///
19/// [`Worlds`]: azalea_world::Worlds
20/// [`WorldName`]: azalea_world::WorldName
21#[derive(Clone, Component)]
22pub struct WorldHolder {
23    /// The slice of the world that this client actually has loaded, based on
24    /// its render distance.
25    pub partial: Arc<RwLock<PartialWorld>>,
26    /// The combined [`PartialWorld`]s of all clients in the same world.
27    ///
28    /// The distinction between this and `partial` is mostly only relevant if
29    /// you're using a shared world (i.e. a swarm). If in doubt, prefer to use
30    /// the shared world.
31    pub shared: Arc<RwLock<World>>,
32}
33#[deprecated = "renamed to `WorldHolder`."]
34pub type InstanceHolder = WorldHolder;
35
36/// The gamemode of a local player. For a non-local player, you can look up the
37/// player in the [`TabList`].
38#[derive(Clone, Component, Copy, Debug)]
39pub struct LocalGameMode {
40    pub current: GameMode,
41    pub previous: Option<GameMode>,
42}
43impl From<GameMode> for LocalGameMode {
44    fn from(current: GameMode) -> Self {
45        LocalGameMode {
46            current,
47            previous: None,
48        }
49    }
50}
51
52/// Level must be 0..=4
53#[derive(Clone, Component, Default, Deref, DerefMut)]
54pub struct PermissionLevel(pub u8);
55
56/// A component that contains a map of player UUIDs to their information in the
57/// tab list.
58///
59/// ```
60/// # use azalea_client::local_player::TabList;
61/// fn example(tab_list: &TabList) {
62///     println!("Online players:");
63///     for (uuid, player_info) in tab_list.iter() {
64///         println!("- {} ({}ms)", player_info.profile.name, player_info.latency);
65///     }
66/// }
67/// ```
68///
69/// For convenience, `TabList` is also a resource in the ECS.
70/// It's set to be the same as the tab list for the last client whose tab list
71/// was updated.
72/// This means you should avoid using `TabList` as a resource unless you know
73/// all of your clients will have the same tab list.
74#[derive(Clone, Component, Debug, Default, Deref, DerefMut, Resource)]
75pub struct TabList(HashMap<Uuid, PlayerInfo>);
76
77#[derive(Clone, Component, Debug)]
78pub struct Hunger {
79    /// The main hunger bar. This is typically in the range `0..=20`.
80    pub food: u32,
81    /// The amount of saturation the player has.
82    ///
83    /// This isn't displayed in the vanilla Minecraft GUI, but it's used
84    /// internally by the game. It's a decrementing counter, and the player's
85    /// [`Hunger::food`] only starts decreasing when their saturation reaches 0.
86    pub saturation: f32,
87}
88
89impl Default for Hunger {
90    fn default() -> Self {
91        Hunger {
92            food: 20,
93            saturation: 5.,
94        }
95    }
96}
97impl Hunger {
98    /// Returns true if we have enough food level to sprint.
99    ///
100    /// Note that this doesn't consider our gamemode or passenger status.
101    pub fn is_enough_to_sprint(&self) -> bool {
102        // hasEnoughFoodToSprint
103        self.food >= 6
104    }
105}
106
107/// The player's experience state.
108#[derive(Clone, Component, Debug)]
109pub struct Experience {
110    /// Progress towards the next level, in the range 0.0..1.0.
111    pub progress: f32,
112    /// The current experience level. You'll mostly be using this.
113    pub level: u32,
114    /// Total experience points accumulated.
115    pub total: u32,
116}
117
118impl Default for Experience {
119    fn default() -> Self {
120        Experience {
121            progress: 0.0,
122            level: 0,
123            total: 0,
124        }
125    }
126}
127
128impl WorldHolder {
129    /// Create a new `WorldHolder` for the given entity.
130    ///
131    /// The partial world will be created for you. The render distance will
132    /// be set to a default value, which you can change by creating a new
133    /// partial world.
134    pub fn new(entity: Entity, shared: Arc<RwLock<World>>) -> Self {
135        let client_information = ClientInformation::default();
136
137        WorldHolder {
138            shared,
139            partial: Arc::new(RwLock::new(PartialWorld::new(
140                azalea_world::chunk_storage::calculate_chunk_storage_range(
141                    client_information.view_distance.into(),
142                ),
143                Some(entity),
144            ))),
145        }
146    }
147
148    /// Reset the [`World`] to be a reference to an empty world, but with
149    /// the same registries as the current one.
150    ///
151    /// This is used by Azalea when entering the config state.
152    pub fn reset(&mut self) {
153        let registries = self.shared.read().registries.clone();
154
155        let new_world = World {
156            registries,
157            ..Default::default()
158        };
159        self.shared = Arc::new(RwLock::new(new_world));
160
161        self.partial.write().reset();
162    }
163}