use std::{error::Error, fmt::Display}; use palette::{Hsl, IntoColor, Srgb}; use serde::{Deserialize, Serialize}; use rust_mc_proto::ProtocolError; use serde_with::skip_serializing_none; // Ошибки сервера #[derive(Debug)] pub enum ServerError { UnknownPacket(String), Protocol(ProtocolError), ConnectionClosed, SerTextComponent, DeTextComponent } impl Display for ServerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&format!("{:?}", self)) } } impl Error for ServerError {} // Делаем чтобы ProtocolError мог переделываться в наш ServerError impl From for ServerError { fn from(error: ProtocolError) -> ServerError { match error { // Если просто закрыто соединение, переделываем в нашу ошибку этого ProtocolError::ConnectionClosedError => { ServerError::ConnectionClosed }, // Все остальное просто засовываем в обертку error => { ServerError::Protocol(error) }, } } } #[derive(Debug, Serialize, Deserialize, Clone)] #[skip_serializing_none] pub struct TextComponent { pub text: String, pub color: Option, pub bold: Option, pub italic: Option, pub underlined: Option, pub strikethrough: Option, pub obfuscated: Option, pub extra: Option>, } impl TextComponent { pub fn new(text: String) -> Self { Self { text, color: None, bold: None, italic: None, underlined: None, strikethrough: None, obfuscated: None, extra: None } } pub fn rainbow(text: String) -> TextComponent { if text.is_empty() { return TextComponent::new(text); } let children = text.char_indices() .map(|(i, c)| { let hue = (i as f32) / (text.chars().count() as f32) * 360.0; let hsl = Hsl::new(hue, 1.0, 0.5); let rgb: Srgb = hsl.into_color(); let r = (rgb.red * 255.0).round() as u8; let g = (rgb.green * 255.0).round() as u8; let b = (rgb.blue * 255.0).round() as u8; let mut component = TextComponent::new(c.to_string()); component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); component }) .collect::>(); let mut parent = children[0].clone(); parent.extra = Some(children[1..].to_vec()); parent } pub fn builder() -> TextComponentBuilder { TextComponentBuilder::new() } pub fn as_nbt(self) -> Result, ServerError> { fastnbt::to_bytes(&self) .map_err(|_| ServerError::SerTextComponent) } pub fn from_nbt(bytes: &[u8]) -> Result { fastnbt::from_bytes(bytes) .map_err(|_| ServerError::DeTextComponent) } pub fn as_json(self) -> Result { serde_json::to_string(&self) .map_err(|_| ServerError::SerTextComponent) } pub fn from_json(text: &str) -> Result { serde_json::from_str(text) .map_err(|_| ServerError::DeTextComponent) } } pub struct TextComponentBuilder { text: String, color: Option, bold: Option, italic: Option, underlined: Option, strikethrough: Option, obfuscated: Option, extra: Option>, } impl TextComponentBuilder { pub fn new() -> Self { Self { text: String::new(), color: None, bold: None, italic: None, underlined: None, strikethrough: None, obfuscated: None, extra: None, } } pub fn text(mut self, text: &str) -> Self { self.text = text.to_string(); self } pub fn color(mut self, color: &str) -> Self { self.color = Some(color.to_string()); self } pub fn bold(mut self, bold: bool) -> Self { self.bold = Some(bold); self } pub fn italic(mut self, italic: bool) -> Self { self.italic = Some(italic); self } pub fn underlined(mut self, underlined: bool) -> Self { self.underlined = Some(underlined); self } pub fn strikethrough(mut self, strikethrough: bool) -> Self { self.strikethrough = Some(strikethrough); self } pub fn obfuscated(mut self, obfuscated: bool) -> Self { self.obfuscated = Some(obfuscated); self } pub fn extra(mut self, extra: Vec) -> Self { self.extra = Some(extra); self } pub fn build(self) -> TextComponent { TextComponent { text: self.text, color: self.color, bold: self.bold, italic: self.italic, underlined: self.underlined, strikethrough: self.strikethrough, obfuscated: self.obfuscated, extra: self.extra } } }