azalea_client/
local_player.rs

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