1use std::io::{Cursor, Write};
2
3use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, BufReadError};
4use azalea_chat::translatable_component::TranslatableComponent;
5use tracing::debug;
6
7#[derive(Hash, Copy, Clone, Debug, Default, Eq, PartialEq)]
9pub enum GameMode {
10 #[default]
11 Survival,
12 Creative,
13 Adventure,
14 Spectator,
15}
16
17impl GameMode {
18 pub fn to_id(&self) -> u8 {
19 match self {
20 GameMode::Survival => 0,
21 GameMode::Creative => 1,
22 GameMode::Adventure => 2,
23 GameMode::Spectator => 3,
24 }
25 }
26
27 pub fn to_optional_id<T: Into<Option<GameMode>>>(game_type: T) -> i8 {
29 match game_type.into() {
30 Some(game_type) => game_type.to_id() as i8,
31 None => -1,
32 }
33 }
34
35 pub fn from_id(id: u8) -> Option<GameMode> {
36 Some(match id {
37 0 => GameMode::Survival,
38 1 => GameMode::Creative,
39 2 => GameMode::Adventure,
40 3 => GameMode::Spectator,
41 _ => return None,
42 })
43 }
44
45 pub fn from_optional_id(id: i8) -> Option<OptionalGameType> {
46 Some(
47 match id {
48 -1 => None,
49 id => Some(GameMode::from_id(id as u8)?),
50 }
51 .into(),
52 )
53 }
54
55 pub fn short_name(&self) -> TranslatableComponent {
57 TranslatableComponent::new(format!("selectWorld.gameMode.{}", self.name()), vec![])
58 }
59
60 pub fn long_name(&self) -> TranslatableComponent {
63 TranslatableComponent::new(format!("gameMode.{}", self.name()), vec![])
64 }
65
66 pub fn from_name(name: &str) -> GameMode {
67 match name {
68 "survival" => GameMode::Survival,
69 "creative" => GameMode::Creative,
70 "adventure" => GameMode::Adventure,
71 "spectator" => GameMode::Spectator,
72 _ => panic!("Unknown game type name: {name}"),
73 }
74 }
75
76 pub fn name(&self) -> &'static str {
78 match self {
79 GameMode::Survival => "survival",
80 GameMode::Creative => "creative",
81 GameMode::Adventure => "adventure",
82 GameMode::Spectator => "spectator",
83 }
84 }
85}
86
87impl GameMode {
88 pub fn is_block_placing_restricted(&self) -> bool {
92 matches!(self, GameMode::Adventure | GameMode::Spectator)
93 }
94}
95
96impl AzaleaRead for GameMode {
97 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
98 let id = u32::azalea_read_var(buf)?;
99 let id = id.try_into().unwrap_or_else(|_| {
100 debug!("Unknown game mode id {id}, defaulting to survival");
101 0
102 });
103 Ok(GameMode::from_id(id).unwrap_or_else(|| {
104 debug!("Unknown game mode id {id}, defaulting to survival");
105 GameMode::Survival
106 }))
107 }
108}
109
110impl AzaleaWrite for GameMode {
111 fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
112 u8::azalea_write(&self.to_id(), buf)
113 }
114}
115
116#[derive(Hash, Copy, Clone, Debug)]
119pub struct OptionalGameType(pub Option<GameMode>);
120
121impl From<Option<GameMode>> for OptionalGameType {
122 fn from(game_type: Option<GameMode>) -> Self {
123 OptionalGameType(game_type)
124 }
125}
126
127impl From<OptionalGameType> for Option<GameMode> {
128 fn from(optional_game_type: OptionalGameType) -> Self {
129 optional_game_type.0
130 }
131}
132
133impl AzaleaRead for OptionalGameType {
134 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
135 let id = i8::azalea_read(buf)?;
136 GameMode::from_optional_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
137 }
138}
139
140impl AzaleaWrite for OptionalGameType {
141 fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
142 GameMode::to_optional_id(*self).azalea_write(buf)
143 }
144}