Skip to main content

azalea_protocol/packets/game/
c_update_advancements.rs

1use 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}