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