Skip to main content

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