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