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/// A local player's previous game mode.
37///
38/// This component is not present on non-local players. This is `None` if the
39/// server specifically told us that the player has no previous gamemode.
40///
41/// Also see [`GameMode`].
42#[derive(Clone, Component, Copy, Debug)]
43pub struct PreviousGameMode(pub Option<GameMode>);
44
45/// Level must be 0..=4
46#[derive(Clone, Component, Default, Deref, DerefMut)]
47pub struct PermissionLevel(pub u8);
48
49/// A component that contains a map of player UUIDs to their information in the
50/// tab list.
51///
52/// ```
53/// # use azalea_client::local_player::TabList;
54/// fn example(tab_list: &TabList) {
55///     println!("Online players:");
56///     for (uuid, player_info) in tab_list.iter() {
57///         println!("- {} ({}ms)", player_info.profile.name, player_info.latency);
58///     }
59/// }
60/// ```
61///
62/// For convenience, `TabList` is also a resource in the ECS as
63/// [`TabListResource`].
64///
65/// It's set to be the same as the tab list for the last client whose tab list
66/// was updated.
67/// This means you should avoid using `TabList` as a resource unless you know
68/// all of your clients will have the same tab list.
69#[derive(Clone, Debug, Default, Deref, DerefMut, Component)]
70pub struct TabList(HashMap<Uuid, PlayerInfo>);
71
72/// The `Resource` version of [`TabList`] (which is a `Component`).
73#[derive(Clone, Debug, Default, Deref, DerefMut, Resource)]
74pub struct TabListResource(HashMap<Uuid, PlayerInfo>);
75
76impl From<TabList> for TabListResource {
77    fn from(t: TabList) -> Self {
78        TabListResource(t.0)
79    }
80}
81impl From<TabListResource> for TabList {
82    fn from(t: TabListResource) -> Self {
83        TabList(t.0)
84    }
85}
86
87#[derive(Clone, Component, Debug)]
88pub struct Hunger {
89    /// The main hunger bar. This is typically in the range `0..=20`.
90    pub food: u32,
91    /// The amount of saturation the player has.
92    ///
93    /// This isn't displayed in the vanilla Minecraft GUI, but it's used
94    /// internally by the game. It's a decrementing counter, and the player's
95    /// [`Hunger::food`] only starts decreasing when their saturation reaches 0.
96    pub saturation: f32,
97}
98
99impl Default for Hunger {
100    fn default() -> Self {
101        Hunger {
102            food: 20,
103            saturation: 5.,
104        }
105    }
106}
107impl Hunger {
108    /// Returns true if we have enough food level to sprint.
109    ///
110    /// Note that this doesn't consider our gamemode or passenger status.
111    pub fn is_enough_to_sprint(&self) -> bool {
112        // hasEnoughFoodToSprint
113        self.food >= 6
114    }
115}
116
117/// The player's experience state.
118#[derive(Clone, Component, Debug)]
119pub struct Experience {
120    /// Progress towards the next level, in the range 0.0..1.0.
121    pub progress: f32,
122    /// The current experience level. You'll mostly be using this.
123    pub level: u32,
124    /// Total experience points accumulated.
125    pub total: u32,
126}
127
128impl Default for Experience {
129    fn default() -> Self {
130        Experience {
131            progress: 0.0,
132            level: 0,
133            total: 0,
134        }
135    }
136}
137
138impl WorldHolder {
139    /// Create a new `WorldHolder` for the given entity.
140    ///
141    /// The partial world will be created for you. The render distance will
142    /// be set to a default value, which you can change by creating a new
143    /// partial world.
144    pub fn new(entity: Entity, shared: Arc<RwLock<World>>) -> Self {
145        let client_information = ClientInformation::default();
146
147        WorldHolder {
148            shared,
149            partial: Arc::new(RwLock::new(PartialWorld::new(
150                azalea_world::chunk::calculate_chunk_storage_range(
151                    client_information.view_distance.into(),
152                ),
153                Some(entity),
154            ))),
155        }
156    }
157
158    /// Reset the [`World`] to be a reference to an empty world, but with
159    /// the same registries as the current one.
160    ///
161    /// This is used by Azalea when entering the config state.
162    pub fn reset(&mut self) {
163        let registries = self.shared.read().registries.clone();
164
165        let new_world = World {
166            registries,
167            ..Default::default()
168        };
169        self.shared = Arc::new(RwLock::new(new_world));
170
171        self.partial.write().reset();
172    }
173}