azalea_protocol/packets/configuration/
serverbound_client_information_packet.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
use azalea_buf::{McBuf, McBufReadable, McBufWritable};
use azalea_core::bitset::FixedBitSet;
use azalea_protocol_macros::ServerboundConfigurationPacket;
use bevy_ecs::component::Component;

#[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket, PartialEq, Eq)]
pub struct ServerboundClientInformationPacket {
    pub information: ClientInformation,
}

/// A component that contains some of the "settings" for this client that are
/// sent to the server, such as render distance. This is only present on local
/// players.
#[derive(Clone, Debug, McBuf, PartialEq, Eq, Component)]
pub struct ClientInformation {
    /// The locale of the client.
    pub language: String,
    /// The view distance of the client in chunks, same as the render distance
    /// in-game.
    pub view_distance: u8,
    /// The types of chat messages the client wants to receive. Note that many
    /// servers ignore this.
    pub chat_visibility: ChatVisibility,
    /// Whether the messages sent from the server should have colors. Note that
    /// many servers ignore this and always send colored messages.
    pub chat_colors: bool,
    pub model_customization: ModelCustomization,
    pub main_hand: HumanoidArm,
    pub text_filtering_enabled: bool,
    /// Whether the client should show up as "Anonymous Player" in the server
    /// list.
    pub allows_listing: bool,
    pub particle_status: ParticleStatus,
}

impl Default for ClientInformation {
    fn default() -> Self {
        Self {
            language: "en_us".to_string(),
            view_distance: 8,
            chat_visibility: ChatVisibility::default(),
            chat_colors: true,
            model_customization: ModelCustomization::default(),
            main_hand: HumanoidArm::Right,
            text_filtering_enabled: false,
            allows_listing: false,
            particle_status: ParticleStatus::default(),
        }
    }
}

#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ChatVisibility {
    /// All chat messages should be sent to the client.
    #[default]
    Full = 0,
    /// Chat messages from other players should be not sent to the client, only
    /// messages from the server like "Player joined the game" should be sent.
    System = 1,
    /// No chat messages should be sent to the client.
    Hidden = 2,
}

#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum HumanoidArm {
    Left = 0,
    #[default]
    Right = 1,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ModelCustomization {
    pub cape: bool,
    pub jacket: bool,
    pub left_sleeve: bool,
    pub right_sleeve: bool,
    pub left_pants: bool,
    pub right_pants: bool,
    pub hat: bool,
}

#[derive(McBuf, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ParticleStatus {
    #[default]
    All,
    Decreased,
    Minimal,
}

impl Default for ModelCustomization {
    fn default() -> Self {
        Self {
            cape: true,
            jacket: true,
            left_sleeve: true,
            right_sleeve: true,
            left_pants: true,
            right_pants: true,
            hat: true,
        }
    }
}

impl McBufReadable for ModelCustomization {
    fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> {
        let set = FixedBitSet::<7>::read_from(buf)?;
        Ok(Self {
            cape: set.index(0),
            jacket: set.index(1),
            left_sleeve: set.index(2),
            right_sleeve: set.index(3),
            left_pants: set.index(4),
            right_pants: set.index(5),
            hat: set.index(6),
        })
    }
}

impl McBufWritable for ModelCustomization {
    fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
        let mut set = FixedBitSet::<7>::new();
        if self.cape {
            set.set(0);
        }
        if self.jacket {
            set.set(1);
        }
        if self.left_sleeve {
            set.set(2);
        }
        if self.right_sleeve {
            set.set(3);
        }
        if self.left_pants {
            set.set(4);
        }
        if self.right_pants {
            set.set(5);
        }
        if self.hat {
            set.set(6);
        }
        set.write_into(buf)
    }
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::*;

    #[test]
    fn test_client_information_packet() {
        {
            let data = ClientInformation::default();
            let mut buf = Vec::new();
            data.write_into(&mut buf).unwrap();
            let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);

            let read_data = ClientInformation::read_from(&mut data_cursor).unwrap();
            assert_eq!(read_data, data);
        }

        {
            let data = ClientInformation {
                language: "en_gb".to_string(),
                view_distance: 24,
                chat_visibility: ChatVisibility::Hidden,
                chat_colors: false,
                model_customization: ModelCustomization {
                    cape: false,
                    jacket: false,
                    left_sleeve: true,
                    right_sleeve: false,
                    left_pants: true,
                    right_pants: false,
                    hat: true,
                },
                main_hand: HumanoidArm::Left,
                text_filtering_enabled: true,
                allows_listing: true,
                particle_status: ParticleStatus::Decreased,
            };
            let mut buf = Vec::new();
            data.write_into(&mut buf).unwrap();
            let mut data_cursor: Cursor<&[u8]> = Cursor::new(&buf);

            let read_data = ClientInformation::read_from(&mut data_cursor).unwrap();
            assert_eq!(read_data, data);
        }
    }
}