use std::{ pin::Pin, sync::{ Arc, atomic::{AtomicBool, AtomicI8, Ordering}, }, }; use super::BlockEntity; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_util::math::position::BlockPos; use tokio::sync::Mutex; #[derive(Clone, Copy, Default, Debug, PartialEq, Eq)] #[repr(i8)] pub enum DyeColor { White = 0, Orange = 1, Magenta = 2, LightBlue = 3, Yellow = 4, Lime = 5, Pink = 6, Gray = 7, LightGray = 8, Cyan = 9, Purple = 10, Blue = 11, Brown = 12, Green = 13, Red = 14, #[default] Black = 15, } impl From for String { fn from(value: DyeColor) -> Self { match value { DyeColor::White => "white".to_string(), DyeColor::Orange => "orange".to_string(), DyeColor::Magenta => "magenta".to_string(), DyeColor::LightBlue => "light_blue".to_string(), DyeColor::Yellow => "yellow".to_string(), DyeColor::Lime => "lime".to_string(), DyeColor::Pink => "pink".to_string(), DyeColor::Gray => "gray".to_string(), DyeColor::LightGray => "light_gray".to_string(), DyeColor::Cyan => "cyan".to_string(), DyeColor::Purple => "purple".to_string(), DyeColor::Blue => "blue".to_string(), DyeColor::Brown => "brown".to_string(), DyeColor::Green => "green".to_string(), DyeColor::Red => "red".to_string(), DyeColor::Black => "black".to_string(), } } } impl From<&str> for DyeColor { fn from(s: &str) -> Self { match s { "white" => Self::White, "orange" => Self::Orange, "magenta" => Self::Magenta, "light_blue" => Self::LightBlue, "yellow" => Self::Yellow, "lime" => Self::Lime, "pink" => Self::Pink, "gray" => Self::Gray, "light_gray" => Self::LightGray, "cyan" => Self::Cyan, "purple" => Self::Purple, "blue" => Self::Blue, "brown" => Self::Brown, "green" => Self::Green, "red" => Self::Red, "black" => Self::Black, _ => Self::default(), } } } impl From for DyeColor { fn from(s: i8) -> Self { match s { 0 => Self::White, 1 => Self::Orange, 2 => Self::Magenta, 3 => Self::LightBlue, 4 => Self::Yellow, 5 => Self::Lime, 6 => Self::Pink, 7 => Self::Gray, 8 => Self::LightGray, 9 => Self::Cyan, 10 => Self::Purple, 11 => Self::Blue, 12 => Self::Brown, 13 => Self::Green, 14 => Self::Red, 15 => Self::Black, _ => Self::default(), } } } impl From for NbtTag { fn from(value: DyeColor) -> Self { Self::Byte(value as i8) } } pub struct SignBlockEntity { pub front_text: Text, pub back_text: Text, pub is_waxed: AtomicBool, position: BlockPos, pub currently_editing_player: Arc>>, } pub struct Text { pub has_glowing_text: AtomicBool, color: AtomicI8, pub messages: Arc; 4]>>, } impl Clone for Text { fn clone(&self) -> Self { Self { has_glowing_text: AtomicBool::new(self.has_glowing_text.load(Ordering::Relaxed)), color: AtomicI8::new(self.color.load(Ordering::Relaxed)), messages: self.messages.clone(), } } } impl Default for Text { fn default() -> Self { Self { has_glowing_text: AtomicBool::new(false), color: AtomicI8::new(DyeColor::default() as i8), messages: Arc::default(), } } } #[allow(clippy::fallible_impl_from)] impl From for NbtTag { fn from(value: Text) -> Self { let mut nbt = NbtCompound::new(); nbt.put_bool( "has_glowing_text", value.has_glowing_text.load(Ordering::Relaxed), ); nbt.put_string("color", value.get_color().into()); nbt.put_list( "messages", value .messages .lock() .unwrap() .iter() .map(|s| Self::String(s.clone())) .collect(), ); Self::Compound(nbt) } } #[allow(clippy::fallible_impl_from)] impl From for Text { fn from(tag: NbtTag) -> Self { let nbt = tag.extract_compound().unwrap(); let has_glowing_text = nbt.get_bool("has_glowing_text").unwrap_or(false); let color = nbt.get_string("color").unwrap(); let messages: Vec> = nbt .get_list("messages") .unwrap() .iter() .filter_map(|tag| tag.extract_string().map(Box::from)) .collect(); let get_message = |i: usize| -> Box { messages.get(i).cloned().unwrap_or_else(|| Box::from("")) }; Self { has_glowing_text: AtomicBool::new(has_glowing_text), color: AtomicI8::new(DyeColor::from(color) as i8), messages: Arc::new(std::sync::Mutex::new([ get_message(0), get_message(1), get_message(2), get_message(3), ])), } } } impl Text { fn new(messages: [Box; 4]) -> Self { Self { has_glowing_text: AtomicBool::new(false), color: AtomicI8::new(DyeColor::default() as i8), messages: Arc::new(std::sync::Mutex::new(messages)), } } pub fn get_color(&self) -> DyeColor { self.color.load(Ordering::Relaxed).into() } pub fn set_color(&self, color: DyeColor) { self.color.store(color as i8, Ordering::Relaxed); } } impl BlockEntity for SignBlockEntity { fn resource_location(&self) -> &'static str { Self::ID } fn get_position(&self) -> BlockPos { self.position } fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self where Self: Sized, { let front_text = Text::from(nbt.get("front_text").unwrap().clone()); let back_text = Text::from(nbt.get("back_text").unwrap().clone()); let is_waxed = nbt.get_bool("is_waxed").unwrap_or(false); Self { position, front_text, back_text, is_waxed: AtomicBool::new(is_waxed), currently_editing_player: Arc::new(Mutex::new(None)), } } fn write_nbt<'a>( &'a self, nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { nbt.put("front_text", self.front_text.clone()); nbt.put("back_text", self.back_text.clone()); nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed)); }) } fn chunk_data_nbt(&self) -> Option { let mut nbt = NbtCompound::new(); nbt.put("front_text", self.front_text.clone()); nbt.put("back_text", self.back_text.clone()); nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed)); Some(nbt) } fn as_any(&self) -> &dyn std::any::Any { self } } impl SignBlockEntity { pub const ID: &'static str = "minecraft:sign"; #[must_use] pub fn new(position: BlockPos, is_front: bool, messages: [Box; 4]) -> Self { Self { position, is_waxed: AtomicBool::new(false), front_text: if is_front { Text::new(messages.clone()) } else { Text::default() }, back_text: if is_front { Text::default() } else { Text::new(messages) }, currently_editing_player: Arc::new(Mutex::new(None)), } } #[must_use] pub fn empty(position: BlockPos) -> Self { Self { position, is_waxed: AtomicBool::new(false), front_text: Text::default(), back_text: Text::default(), currently_editing_player: Arc::new(Mutex::new(None)), } } }