Skip to main content

steel_utils/types/
gameplay.rs

1use std::io::{self, Cursor, Write};
2
3use bitflags::bitflags;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    codec::VarInt,
8    serial::{ReadFrom, WriteTo},
9};
10
11/// The game type.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[expect(missing_docs, reason = "variant names are self-explanatory")]
14pub enum GameType {
15    Survival = 0,
16    Creative = 1,
17    Adventure = 2,
18    Spectator = 3,
19}
20
21impl GameType {
22    /// Returns the name of the game type.
23    #[must_use]
24    pub const fn name(self) -> &'static str {
25        match self {
26            GameType::Survival => "survival",
27            GameType::Creative => "creative",
28            GameType::Adventure => "adventure",
29            GameType::Spectator => "spectator",
30        }
31    }
32}
33
34impl ReadFrom for GameType {
35    fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
36        let value = VarInt::read(data)?.0;
37        match value {
38            0 => Ok(GameType::Survival),
39            1 => Ok(GameType::Creative),
40            2 => Ok(GameType::Adventure),
41            3 => Ok(GameType::Spectator),
42            _ => Err(io::Error::new(
43                io::ErrorKind::InvalidData,
44                "Invalid GameType",
45            )),
46        }
47    }
48}
49
50impl From<GameType> for i8 {
51    fn from(value: GameType) -> Self {
52        value as i8
53    }
54}
55
56impl From<GameType> for i32 {
57    fn from(value: GameType) -> Self {
58        value as i32
59    }
60}
61
62impl From<GameType> for f32 {
63    fn from(value: GameType) -> Self {
64        f32::from(value as i8)
65    }
66}
67
68impl From<i8> for GameType {
69    fn from(value: i8) -> Self {
70        match value {
71            1 => GameType::Creative,
72            2 => GameType::Adventure,
73            3 => GameType::Spectator,
74            _ => GameType::Survival,
75        }
76    }
77}
78
79impl From<i32> for GameType {
80    fn from(value: i32) -> Self {
81        match value {
82            1 => GameType::Creative,
83            2 => GameType::Adventure,
84            3 => GameType::Spectator,
85            _ => GameType::Survival,
86        }
87    }
88}
89
90impl From<f32> for GameType {
91    fn from(value: f32) -> Self {
92        match value {
93            1. => GameType::Creative,
94            2. => GameType::Adventure,
95            3. => GameType::Spectator,
96            _ => GameType::Survival,
97        }
98    }
99}
100
101/// World difficulty level.
102///
103/// Controls starvation damage thresholds, mob spawning behavior,
104/// and various other gameplay tweaks.
105#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
106#[repr(u8)]
107pub enum Difficulty {
108    /// No hostile mobs, no starvation, health regenerates quickly.
109    Peaceful = 0,
110    /// Hostile mobs deal less damage, starvation stops at 10 HP.
111    Easy = 1,
112    /// Default difficulty, starvation stops at 1 HP.
113    #[default]
114    Normal = 2,
115    /// Hostile mobs deal more damage, starvation can kill.
116    Hard = 3,
117}
118
119#[expect(clippy::match_same_arms, reason = "cause it looks better")]
120impl From<u8> for Difficulty {
121    fn from(value: u8) -> Self {
122        match value {
123            0 => Difficulty::Peaceful,
124            1 => Difficulty::Easy,
125            2 => Difficulty::Normal,
126            3 => Difficulty::Hard,
127            _ => Difficulty::Normal,
128        }
129    }
130}
131
132impl From<Difficulty> for u8 {
133    fn from(value: Difficulty) -> Self {
134        value as u8
135    }
136}
137
138impl ReadFrom for Difficulty {
139    fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
140        let value = <u8 as ReadFrom>::read(data)?;
141        match value {
142            0 => Ok(Difficulty::Peaceful),
143            1 => Ok(Difficulty::Easy),
144            2 => Ok(Difficulty::Normal),
145            3 => Ok(Difficulty::Hard),
146            _ => Err(io::Error::new(
147                io::ErrorKind::InvalidData,
148                format!("Invalid Difficulty: {value}"),
149            )),
150        }
151    }
152}
153
154impl WriteTo for Difficulty {
155    fn write(&self, writer: &mut impl Write) -> io::Result<()> {
156        (*self as u8).write(writer)
157    }
158}
159
160impl Serialize for Difficulty {
161    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162    where
163        S: serde::Serializer,
164    {
165        serializer.serialize_u8(*self as u8)
166    }
167}
168
169impl<'de> Deserialize<'de> for Difficulty {
170    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
171    where
172        D: serde::Deserializer<'de>,
173    {
174        let id = u8::deserialize(deserializer)?;
175        Ok(Self::from(id))
176    }
177}
178
179/// Represents the hand used for an interaction.
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
181pub enum InteractionHand {
182    /// The main hand.
183    MainHand,
184    /// The off hand.
185    OffHand,
186}
187
188impl ReadFrom for InteractionHand {
189    fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
190        let id = VarInt::read(data)?.0;
191        match id {
192            0 => Ok(InteractionHand::MainHand),
193            1 => Ok(InteractionHand::OffHand),
194            _ => Err(io::Error::other("Invalid InteractionHand id")),
195        }
196    }
197}
198
199/// Flags that control how a block update is processed.
200#[derive(Clone, Copy, Debug, PartialEq, Eq)]
201pub struct UpdateFlags(u16);
202
203bitflags! {
204    impl UpdateFlags: u16 {
205        const UPDATE_NEIGHBORS = 1;
206        const UPDATE_CLIENTS = 1 << 1;
207        const UPDATE_INVISIBLE = 1 << 2;
208        const UPDATE_IMMEDIATE = 1 << 3;
209        const UPDATE_KNOWN_SHAPE = 1 << 4;
210        const UPDATE_SUPPRESS_DROPS = 1 << 5;
211        const UPDATE_MOVE_BY_PISTON = 1 << 6;
212        const UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE = 1 << 7;
213        const UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS = 1 << 8;
214        const UPDATE_SKIP_ON_PLACE = 1 << 9;
215
216        const UPDATE_NONE = Self::UPDATE_INVISIBLE.bits() | Self::UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS.bits();
217        const UPDATE_ALL = Self::UPDATE_NEIGHBORS.bits() | Self::UPDATE_CLIENTS.bits();
218        const UPDATE_ALL_IMMEDIATE = Self::UPDATE_ALL.bits() | Self::UPDATE_IMMEDIATE.bits();
219    }
220}