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