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