Skip to main content

kojacoord_protocol/versions/v1_8/login/
mod.rs

1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::{Decode, Encode, PacketId};
4use crate::error::ProtocolError;
5use crate::types::VarInt;
6
7pub use packets::{
8    ClientboundEncryptionRequest, ClientboundLoginDisconnect, ClientboundLoginSuccess,
9    ClientboundSetCompression, ProfileProperty, ServerboundEncryptionResponse,
10    ServerboundLoginStart,
11};
12
13mod packets {
14    use super::*;
15
16    fn encode_str(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
17        let bytes = s.as_bytes();
18        VarInt(bytes.len() as i32).encode(dst)?;
19        dst.put_slice(bytes);
20        Ok(())
21    }
22
23    fn decode_str(src: &mut Bytes, ctx: &'static str) -> Result<String, ProtocolError> {
24        let len = VarInt::decode(src)?.0 as usize;
25        if src.remaining() < len {
26            return Err(ProtocolError::Io(std::io::Error::new(
27                std::io::ErrorKind::UnexpectedEof,
28                format!("Missing bytes for {ctx}"),
29            )));
30        }
31        let mut b = vec![0u8; len];
32        src.copy_to_slice(&mut b);
33        String::from_utf8(b).map_err(|_| {
34            ProtocolError::Io(std::io::Error::new(
35                std::io::ErrorKind::InvalidData,
36                format!("Invalid UTF-8 in {ctx}"),
37            ))
38        })
39    }
40
41    fn encode_byte_array(data: &[u8], dst: &mut BytesMut) -> Result<(), ProtocolError> {
42        VarInt(data.len() as i32).encode(dst)?;
43        dst.put_slice(data);
44        Ok(())
45    }
46
47    fn decode_byte_array(src: &mut Bytes, ctx: &'static str) -> Result<Vec<u8>, ProtocolError> {
48        let len = VarInt::decode(src)?.0 as usize;
49        if src.remaining() < len {
50            return Err(ProtocolError::Io(std::io::Error::new(
51                std::io::ErrorKind::UnexpectedEof,
52                format!("Missing bytes for {ctx}"),
53            )));
54        }
55        let mut b = vec![0u8; len];
56        src.copy_to_slice(&mut b);
57        Ok(b)
58    }
59
60    #[derive(Debug, Clone, PartialEq)]
61    pub struct ServerboundLoginStart {
62        pub username: String,
63    }
64
65    impl PacketId for ServerboundLoginStart {
66        fn packet_id(_ver: u32) -> u8 {
67            0x00
68        }
69    }
70
71    impl Encode for ServerboundLoginStart {
72        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
73            encode_str(&self.username, dst)
74        }
75    }
76
77    impl Decode for ServerboundLoginStart {
78        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
79            let username = decode_str(src, "ServerboundLoginStart username")?;
80            Ok(Self { username })
81        }
82    }
83
84    #[derive(Debug, Clone, PartialEq)]
85    pub struct ClientboundLoginDisconnect {
86        pub reason: String,
87    }
88
89    impl PacketId for ClientboundLoginDisconnect {
90        fn packet_id(_ver: u32) -> u8 {
91            0x00
92        }
93    }
94
95    impl Encode for ClientboundLoginDisconnect {
96        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
97            encode_str(&self.reason, dst)
98        }
99    }
100
101    impl Decode for ClientboundLoginDisconnect {
102        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
103            let reason = decode_str(src, "ClientboundLoginDisconnect reason")?;
104            Ok(Self { reason })
105        }
106    }
107
108    #[derive(Debug, Clone, PartialEq)]
109    pub struct ClientboundEncryptionRequest {
110        pub server_id: String,
111        pub public_key: Vec<u8>,
112        pub verify_token: Vec<u8>,
113    }
114
115    impl PacketId for ClientboundEncryptionRequest {
116        fn packet_id(_ver: u32) -> u8 {
117            0x01
118        }
119    }
120
121    impl Encode for ClientboundEncryptionRequest {
122        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
123            encode_str(&self.server_id, dst)?;
124            encode_byte_array(&self.public_key, dst)?;
125            encode_byte_array(&self.verify_token, dst)
126        }
127    }
128
129    impl Decode for ClientboundEncryptionRequest {
130        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
131            let server_id = decode_str(src, "ClientboundEncryptionRequest server_id")?;
132            let public_key = decode_byte_array(src, "ClientboundEncryptionRequest public_key")?;
133            let verify_token = decode_byte_array(src, "ClientboundEncryptionRequest verify_token")?;
134            Ok(Self {
135                server_id,
136                public_key,
137                verify_token,
138            })
139        }
140    }
141
142    #[derive(Debug, Clone, PartialEq)]
143    pub struct ProfileProperty {
144        pub name: String,
145        pub value: String,
146        pub signature: Option<String>,
147    }
148
149    impl Encode for ProfileProperty {
150        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
151            encode_str(&self.name, dst)?;
152            encode_str(&self.value, dst)?;
153            match &self.signature {
154                Some(sig) => {
155                    dst.put_u8(1);
156                    encode_str(sig, dst)?;
157                },
158                None => dst.put_u8(0),
159            }
160            Ok(())
161        }
162    }
163
164    impl Decode for ProfileProperty {
165        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
166            let name = decode_str(src, "ProfileProperty name")?;
167            let value = decode_str(src, "ProfileProperty value")?;
168            if src.remaining() < 1 {
169                return Err(ProtocolError::Io(std::io::Error::new(
170                    std::io::ErrorKind::UnexpectedEof,
171                    "Missing signature flag in ProfileProperty",
172                )));
173            }
174            let signature = if src.get_u8() != 0 {
175                Some(decode_str(src, "ProfileProperty signature")?)
176            } else {
177                None
178            };
179            Ok(Self {
180                name,
181                value,
182                signature,
183            })
184        }
185    }
186
187    #[derive(Debug, Clone, PartialEq)]
188    pub struct ClientboundLoginSuccess {
189        pub uuid: uuid::Uuid,
190        pub username: String,
191    }
192
193    impl PacketId for ClientboundLoginSuccess {
194        fn packet_id(_ver: u32) -> u8 {
195            0x02
196        }
197    }
198
199    impl Encode for ClientboundLoginSuccess {
200        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
201            encode_str(&self.uuid.hyphenated().to_string(), dst)?;
202            encode_str(&self.username, dst)?;
203            Ok(())
204        }
205    }
206
207    impl Decode for ClientboundLoginSuccess {
208        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
209            let uuid_str = decode_str(src, "ClientboundLoginSuccess uuid")?;
210            let uuid = uuid::Uuid::parse_str(&uuid_str).map_err(|_| {
211                ProtocolError::Io(std::io::Error::new(
212                    std::io::ErrorKind::InvalidData,
213                    "Invalid UUID in ClientboundLoginSuccess",
214                ))
215            })?;
216            let username = decode_str(src, "ClientboundLoginSuccess username")?;
217
218            Ok(Self { uuid, username })
219        }
220    }
221
222    #[derive(Debug, Clone, PartialEq)]
223    pub struct ClientboundSetCompression {
224        pub threshold: VarInt,
225    }
226
227    impl PacketId for ClientboundSetCompression {
228        fn packet_id(_ver: u32) -> u8 {
229            0x03
230        }
231    }
232
233    impl Encode for ClientboundSetCompression {
234        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
235            self.threshold.encode(dst)
236        }
237    }
238
239    impl Decode for ClientboundSetCompression {
240        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
241            let threshold = VarInt::decode(src)?;
242            Ok(Self { threshold })
243        }
244    }
245
246    #[derive(Debug, Clone, PartialEq)]
247    pub struct ServerboundEncryptionResponse {
248        pub shared_secret: Vec<u8>,
249        pub verify_token: Vec<u8>,
250    }
251
252    impl PacketId for ServerboundEncryptionResponse {
253        fn packet_id(_ver: u32) -> u8 {
254            0x01
255        }
256    }
257
258    impl Encode for ServerboundEncryptionResponse {
259        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
260            encode_byte_array(&self.shared_secret, dst)?;
261            encode_byte_array(&self.verify_token, dst)
262        }
263    }
264
265    impl Decode for ServerboundEncryptionResponse {
266        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
267            let shared_secret =
268                decode_byte_array(src, "ServerboundEncryptionResponse shared_secret")?;
269            let verify_token =
270                decode_byte_array(src, "ServerboundEncryptionResponse verify_token")?;
271            Ok(Self {
272                shared_secret,
273                verify_token,
274            })
275        }
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282
283    fn rt_login_start(p: ServerboundLoginStart) -> ServerboundLoginStart {
284        let mut buf = BytesMut::new();
285        p.encode(&mut buf).unwrap();
286        let mut b = buf.freeze();
287        ServerboundLoginStart::decode(&mut b).unwrap()
288    }
289
290    #[test]
291    fn login_start_roundtrip() {
292        let p = ServerboundLoginStart {
293            username: "Notch".to_string(),
294        };
295        assert_eq!(rt_login_start(p.clone()), p);
296    }
297
298    #[test]
299    fn login_disconnect_roundtrip() {
300        let p = ClientboundLoginDisconnect {
301            reason: r#"{"text":"banned"}"#.to_string(),
302        };
303        let mut buf = BytesMut::new();
304        p.encode(&mut buf).unwrap();
305        let mut b = buf.freeze();
306        assert_eq!(ClientboundLoginDisconnect::decode(&mut b).unwrap(), p);
307    }
308
309    #[test]
310    fn encryption_request_roundtrip() {
311        let p = ClientboundEncryptionRequest {
312            server_id: String::new(),
313            public_key: vec![1, 2, 3, 4],
314            verify_token: vec![5, 6, 7, 8],
315        };
316        let mut buf = BytesMut::new();
317        p.encode(&mut buf).unwrap();
318        let mut b = buf.freeze();
319        assert_eq!(ClientboundEncryptionRequest::decode(&mut b).unwrap(), p);
320    }
321
322    #[test]
323    fn encryption_response_roundtrip() {
324        let p = ServerboundEncryptionResponse {
325            shared_secret: vec![0xAA; 128],
326            verify_token: vec![0xBB; 128],
327        };
328        let mut buf = BytesMut::new();
329        p.encode(&mut buf).unwrap();
330        let mut b = buf.freeze();
331        assert_eq!(ServerboundEncryptionResponse::decode(&mut b).unwrap(), p);
332    }
333
334    #[test]
335    fn login_success_roundtrip() {
336        let p = ClientboundLoginSuccess {
337            uuid: uuid::Uuid::new_v4(),
338            username: "Notch".to_string(),
339        };
340        let mut buf = BytesMut::new();
341        p.encode(&mut buf).unwrap();
342        let mut b = buf.freeze();
343        let decoded = ClientboundLoginSuccess::decode(&mut b).unwrap();
344        assert_eq!(decoded.uuid, p.uuid);
345        assert_eq!(decoded.username, p.username);
346    }
347
348    #[test]
349    fn set_compression_roundtrip() {
350        let p = ClientboundSetCompression {
351            threshold: VarInt(256),
352        };
353        let mut buf = BytesMut::new();
354        p.encode(&mut buf).unwrap();
355        let mut b = buf.freeze();
356        assert_eq!(ClientboundSetCompression::decode(&mut b).unwrap(), p);
357    }
358}