Skip to main content

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