azalea_protocol/packets/game/
c_update_advancements.rs1use std::collections::HashMap;
2use std::io::Cursor;
3
4use azalea_buf::AzBuf;
5use azalea_chat::FormattedText;
6use azalea_core::resource_location::ResourceLocation;
7use azalea_inventory::ItemStack;
8use azalea_protocol_macros::ClientboundGamePacket;
9
10#[derive(Clone, Debug, AzBuf, ClientboundGamePacket)]
11pub struct ClientboundUpdateAdvancements {
12 pub reset: bool,
13 pub added: Vec<AdvancementHolder>,
14 pub removed: Vec<ResourceLocation>,
15 pub progress: HashMap<ResourceLocation, AdvancementProgress>,
16}
17
18#[derive(Clone, Debug, AzBuf)]
19pub struct Advancement {
20 pub parent_id: Option<ResourceLocation>,
21 pub display: Option<DisplayInfo>,
22 pub requirements: Vec<Vec<String>>,
23 pub sends_telemetry_event: bool,
24}
25
26#[derive(Clone, Debug)]
27pub struct DisplayInfo {
28 pub title: FormattedText,
29 pub description: FormattedText,
30 pub icon: ItemStack,
31 pub frame: FrameType,
32 pub show_toast: bool,
33 pub hidden: bool,
34 pub background: Option<ResourceLocation>,
35 pub x: f32,
36 pub y: f32,
37}
38
39impl azalea_buf::AzaleaWrite for DisplayInfo {
40 fn azalea_write(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
41 self.title.azalea_write(buf)?;
42 self.description.azalea_write(buf)?;
43 self.icon.azalea_write(buf)?;
44 self.frame.azalea_write(buf)?;
45
46 let mut data: u32 = 0;
47 if self.background.is_some() {
48 data |= 0b001;
49 }
50 if self.show_toast {
51 data |= 0b010;
52 }
53 if self.hidden {
54 data |= 0b100;
55 }
56 data.azalea_write(buf)?;
57
58 if let Some(background) = &self.background {
59 background.azalea_write(buf)?;
60 }
61 self.x.azalea_write(buf)?;
62 self.y.azalea_write(buf)?;
63 Ok(())
64 }
65}
66impl azalea_buf::AzaleaRead for DisplayInfo {
67 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
68 let title = azalea_buf::AzaleaRead::azalea_read(buf)?;
69 let description = azalea_buf::AzaleaRead::azalea_read(buf)?;
70 let icon = azalea_buf::AzaleaRead::azalea_read(buf)?;
71 let frame = azalea_buf::AzaleaRead::azalea_read(buf)?;
72
73 let data = u32::azalea_read(buf)?;
74 let has_background = (data & 0b1) != 0;
75 let show_toast = (data & 0b10) != 0;
76 let hidden = (data & 0b100) != 0;
77
78 let background = if has_background {
79 Some(ResourceLocation::azalea_read(buf)?)
80 } else {
81 None
82 };
83 let x = azalea_buf::AzaleaRead::azalea_read(buf)?;
84 let y = azalea_buf::AzaleaRead::azalea_read(buf)?;
85 Ok(DisplayInfo {
86 title,
87 description,
88 icon,
89 frame,
90 show_toast,
91 hidden,
92 background,
93 x,
94 y,
95 })
96 }
97}
98
99#[derive(Clone, Debug, Copy, AzBuf)]
100pub enum FrameType {
101 Task = 0,
102 Challenge = 1,
103 Goal = 2,
104}
105
106pub type AdvancementProgress = HashMap<String, CriterionProgress>;
107
108#[derive(Clone, Debug, AzBuf)]
109pub struct CriterionProgress {
110 pub date: Option<u64>,
111}
112
113#[derive(Clone, Debug, AzBuf)]
114pub struct AdvancementHolder {
115 pub id: ResourceLocation,
116 pub value: Advancement,
117}
118
119#[cfg(test)]
120mod tests {
121 use azalea_buf::{AzaleaRead, AzaleaWrite};
122
123 use super::*;
124
125 #[test]
126 fn test() {
127 let packet = ClientboundUpdateAdvancements {
128 reset: true,
129 added: [AdvancementHolder {
130 id: ResourceLocation::new("minecraft:test"),
131 value: Advancement {
132 parent_id: None,
133 display: Some(DisplayInfo {
134 title: FormattedText::from("title".to_string()),
135 description: FormattedText::from("description".to_string()),
136 icon: ItemStack::Empty,
137 frame: FrameType::Task,
138 show_toast: true,
139 hidden: false,
140 background: None,
141 x: 0.0,
142 y: 0.0,
143 }),
144 requirements: Vec::new(),
145 sends_telemetry_event: false,
146 },
147 }]
148 .into_iter()
149 .collect(),
150 removed: vec![ResourceLocation::new("minecraft:test2")],
151 progress: [(
152 ResourceLocation::new("minecraft:test3"),
153 [(
154 "minecraft:test4".to_string(),
155 CriterionProgress {
156 date: Some(123456789),
157 },
158 )]
159 .into_iter()
160 .collect(),
161 )]
162 .into_iter()
163 .collect(),
164 };
165
166 let mut data = Vec::new();
167 packet.azalea_write(&mut data).unwrap();
168 let mut buf: Cursor<&[u8]> = Cursor::new(&data);
169
170 let read_packet = ClientboundUpdateAdvancements::azalea_read(&mut buf).unwrap();
171 assert_eq!(packet.reset, read_packet.reset);
172 assert_eq!(packet.removed, read_packet.removed);
173
174 let advancement = packet
175 .added
176 .into_iter()
177 .find_map(|a| {
178 if a.id == ResourceLocation::new("minecraft:test") {
179 Some(a.value)
180 } else {
181 None
182 }
183 })
184 .unwrap()
185 .clone();
186 let read_advancement = read_packet
187 .added
188 .into_iter()
189 .find_map(|a| {
190 if a.id == ResourceLocation::new("minecraft:test") {
191 Some(a.value)
192 } else {
193 None
194 }
195 })
196 .unwrap()
197 .clone();
198 assert_eq!(advancement.parent_id, read_advancement.parent_id);
199
200 let display = advancement.display.unwrap();
201 let read_display = read_advancement.display.unwrap();
202 assert_eq!(display.title, read_display.title);
203 assert_eq!(display.description, read_display.description);
204 }
205}