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