use std::collections::HashMap; use std::io::Cursor; use azalea_buf::McBuf; use azalea_chat::FormattedText; use azalea_core::resource_location::ResourceLocation; use azalea_inventory::ItemStack; use azalea_protocol_macros::ClientboundGamePacket; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundUpdateAdvancements { pub reset: bool, pub added: Vec, pub removed: Vec, pub progress: HashMap, } #[derive(Clone, Debug, McBuf)] pub struct Advancement { pub parent_id: Option, pub display: Option, pub requirements: Vec>, pub sends_telemetry_event: bool, } #[derive(Clone, Debug)] pub struct DisplayInfo { pub title: FormattedText, pub description: FormattedText, pub icon: ItemStack, pub frame: FrameType, pub show_toast: bool, pub hidden: bool, pub background: Option, pub x: f32, pub y: f32, } impl azalea_buf::McBufWritable for DisplayInfo { fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { self.title.write_into(buf)?; self.description.write_into(buf)?; self.icon.write_into(buf)?; self.frame.write_into(buf)?; let mut data: u32 = 0; if self.background.is_some() { data |= 0b001; } if self.show_toast { data |= 0b010; } if self.hidden { data |= 0b100; } data.write_into(buf)?; if let Some(background) = &self.background { background.write_into(buf)?; } self.x.write_into(buf)?; self.y.write_into(buf)?; Ok(()) } } impl azalea_buf::McBufReadable for DisplayInfo { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let title = azalea_buf::McBufReadable::read_from(buf)?; let description = azalea_buf::McBufReadable::read_from(buf)?; let icon = azalea_buf::McBufReadable::read_from(buf)?; let frame = azalea_buf::McBufReadable::read_from(buf)?; let data = u32::read_from(buf)?; let has_background = (data & 0b1) != 0; let show_toast = (data & 0b10) != 0; let hidden = (data & 0b100) != 0; let background = if has_background { Some(ResourceLocation::read_from(buf)?) } else { None }; let x = azalea_buf::McBufReadable::read_from(buf)?; let y = azalea_buf::McBufReadable::read_from(buf)?; Ok(DisplayInfo { title, description, icon, frame, show_toast, hidden, background, x, y, }) } } #[derive(Clone, Debug, Copy, McBuf)] pub enum FrameType { Task = 0, Challenge = 1, Goal = 2, } pub type AdvancementProgress = HashMap; #[derive(Clone, Debug, McBuf)] pub struct CriterionProgress { pub date: Option, } #[derive(Clone, Debug, McBuf)] pub struct AdvancementHolder { pub id: ResourceLocation, pub value: Advancement, } #[cfg(test)] mod tests { use azalea_buf::{McBufReadable, McBufWritable}; use super::*; #[test] fn test() { let packet = ClientboundUpdateAdvancements { reset: true, added: [AdvancementHolder { id: ResourceLocation::new("minecraft:test"), value: Advancement { parent_id: None, display: Some(DisplayInfo { title: FormattedText::from("title".to_string()), description: FormattedText::from("description".to_string()), icon: ItemStack::Empty, frame: FrameType::Task, show_toast: true, hidden: false, background: None, x: 0.0, y: 0.0, }), requirements: Vec::new(), sends_telemetry_event: false, }, }] .into_iter() .collect(), removed: vec![ResourceLocation::new("minecraft:test2")], progress: [( ResourceLocation::new("minecraft:test3"), [( "minecraft:test4".to_string(), CriterionProgress { date: Some(123456789), }, )] .into_iter() .collect(), )] .into_iter() .collect(), }; let mut data = Vec::new(); packet.write_into(&mut data).unwrap(); let mut buf: Cursor<&[u8]> = Cursor::new(&data); let read_packet = ClientboundUpdateAdvancements::read_from(&mut buf).unwrap(); assert_eq!(packet.reset, read_packet.reset); assert_eq!(packet.removed, read_packet.removed); let advancement = packet .added .into_iter() .find_map(|a| { if a.id == ResourceLocation::new("minecraft:test") { Some(a.value) } else { None } }) .unwrap() .clone(); let read_advancement = read_packet .added .into_iter() .find_map(|a| { if a.id == ResourceLocation::new("minecraft:test") { Some(a.value) } else { None } }) .unwrap() .clone(); assert_eq!(advancement.parent_id, read_advancement.parent_id); let display = advancement.display.unwrap(); let read_display = read_advancement.display.unwrap(); assert_eq!(display.title, read_display.title); assert_eq!(display.description, read_display.description); } }