Skip to main content

steel_core/player/
abilities.rs

1//! Player abilities (flight, invulnerability, etc.)
2
3use steel_protocol::packets::game::{CPlayerAbilities, SPlayerAbilities, ability_flags};
4use steel_utils::types::GameType;
5
6use crate::player::Player;
7
8/// Default flying speed in vanilla Minecraft
9pub const DEFAULT_FLYING_SPEED: f32 = 0.05;
10/// Default walking speed in vanilla Minecraft
11pub const DEFAULT_WALKING_SPEED: f32 = 0.1;
12
13/// Player abilities that control flight, invulnerability, and other special states.
14/// This mirrors vanilla's `Abilities` class.
15#[derive(Debug, Clone)]
16pub struct Abilities {
17    /// Whether the player is invulnerable to damage
18    pub invulnerable: bool,
19    /// Whether the player is currently flying (creative/spectator flight)
20    pub flying: bool,
21    /// Whether the player is allowed to fly
22    pub may_fly: bool,
23    /// Whether the player can instantly break blocks (creative mode)
24    pub instabuild: bool,
25    /// Whether the player can place/break blocks
26    pub may_build: bool,
27    /// Flying speed (default 0.05)
28    pub flying_speed: f32,
29    /// Walking speed (default 0.1)
30    pub walking_speed: f32,
31}
32
33impl Default for Abilities {
34    fn default() -> Self {
35        Self {
36            invulnerable: false,
37            flying: false,
38            may_fly: false,
39            instabuild: false,
40            may_build: true,
41            flying_speed: DEFAULT_FLYING_SPEED,
42            walking_speed: DEFAULT_WALKING_SPEED,
43        }
44    }
45}
46
47impl Abilities {
48    /// Creates default abilities for survival mode
49    #[must_use]
50    pub fn survival() -> Self {
51        Self::default()
52    }
53
54    /// Creates abilities for creative mode
55    #[must_use]
56    pub fn creative() -> Self {
57        Self {
58            invulnerable: true,
59            flying: false,
60            may_fly: true,
61            instabuild: true,
62            may_build: true,
63            ..Self::default()
64        }
65    }
66
67    /// Creates abilities for adventure mode
68    #[must_use]
69    pub fn adventure() -> Self {
70        Self {
71            may_build: false,
72            ..Self::default()
73        }
74    }
75
76    /// Updates abilities based on the given game mode.
77    /// This mirrors vanilla's `GameType.updatePlayerAbilities()`.
78    pub const fn update_for_game_mode(&mut self, game_mode: GameType) {
79        match game_mode {
80            GameType::Survival => {
81                self.invulnerable = false;
82                self.may_fly = false;
83                self.instabuild = false;
84                self.flying = false;
85                self.may_build = true;
86            }
87            GameType::Creative => {
88                self.invulnerable = true;
89                self.may_fly = true;
90                self.instabuild = true;
91                // flying state is preserved
92                self.may_build = true;
93            }
94            GameType::Adventure => {
95                self.invulnerable = false;
96                self.may_fly = false;
97                self.instabuild = false;
98                self.flying = false;
99                self.may_build = false;
100            }
101            GameType::Spectator => {
102                self.invulnerable = true;
103                self.may_fly = true;
104                self.instabuild = false;
105                self.flying = true;
106                self.may_build = false;
107            }
108        }
109    }
110
111    /// Converts abilities to a clientbound packet
112    #[must_use]
113    pub const fn to_packet(&self) -> CPlayerAbilities {
114        let mut flags: u8 = 0;
115
116        if self.invulnerable {
117            flags |= ability_flags::INVULNERABLE;
118        }
119        if self.flying {
120            flags |= ability_flags::FLYING;
121        }
122        if self.may_fly {
123            flags |= ability_flags::MAY_FLY;
124        }
125        if self.instabuild {
126            flags |= ability_flags::INSTABUILD;
127        }
128
129        CPlayerAbilities {
130            flags,
131            flying_speed: self.flying_speed,
132            walking_speed: self.walking_speed,
133        }
134    }
135}
136
137impl Player {
138    /// Sends the player abilities packet to the client.
139    /// This tells the client about flight, invulnerability, speeds, etc.
140    pub fn send_abilities(&self) {
141        let packet = self.abilities.lock().to_packet();
142        self.send_packet(packet);
143    }
144
145    /// Returns true if the player is flying (creative/spectator flight).
146    #[must_use]
147    pub fn is_flying(&self) -> bool {
148        self.abilities.lock().flying
149    }
150
151    /// Sets the player's flying state.
152    pub fn set_flying(&self, flying: bool) {
153        self.abilities.lock().flying = flying;
154    }
155
156    /// Returns the player's flying speed.
157    #[must_use]
158    pub fn get_flying_speed(&self) -> f32 {
159        self.abilities.lock().flying_speed
160    }
161
162    /// Sets the player's flying speed.
163    pub fn set_flying_speed(&self, speed: f32) {
164        self.abilities.lock().flying_speed = speed;
165    }
166
167    /// Returns a copy of the player's abilities.
168    #[must_use]
169    pub fn get_abilities(&self) -> Abilities {
170        self.abilities.lock().clone()
171    }
172
173    /// Handles the player abilities packet from the client.
174    /// This is sent when the player starts or stops flying.
175    pub fn handle_player_abilities(&self, packet: SPlayerAbilities) {
176        let mut abilities = self.abilities.lock();
177
178        if abilities.may_fly {
179            abilities.flying = packet.is_flying();
180        } else if packet.is_flying() {
181            // Client tried to fly but isn't allowed - resync abilities
182            drop(abilities);
183            self.send_abilities();
184        }
185    }
186}