Skip to main content

kojacoord_protocol/versions/v1_12_2/login/
mod.rs

1use crate::codec::{Decode, Encode, PacketId};
2use crate::error::ProtocolError;
3use crate::types::VarInt;
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5
6fn encode_string(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
7    let bytes = s.as_bytes();
8    VarInt(bytes.len() as i32).encode(dst)?;
9    dst.put_slice(bytes);
10    Ok(())
11}
12
13fn decode_string(src: &mut Bytes, context: &'static str) -> Result<String, ProtocolError> {
14    let len = VarInt::decode(src)?.0 as usize;
15    if src.remaining() < len {
16        return Err(ProtocolError::Io(std::io::Error::new(
17            std::io::ErrorKind::UnexpectedEof,
18            format!("Missing bytes for {context}"),
19        )));
20    }
21    let mut b = vec![0u8; len];
22    src.copy_to_slice(&mut b);
23    String::from_utf8(b).map_err(|_| {
24        ProtocolError::Io(std::io::Error::new(
25            std::io::ErrorKind::InvalidData,
26            format!("Invalid UTF-8 in {context}"),
27        ))
28    })
29}
30
31fn encode_byte_array(data: &[u8], dst: &mut BytesMut) -> Result<(), ProtocolError> {
32    VarInt(data.len() as i32).encode(dst)?;
33    dst.put_slice(data);
34    Ok(())
35}
36
37fn decode_byte_array(src: &mut Bytes, context: &'static str) -> Result<Vec<u8>, ProtocolError> {
38    let len = VarInt::decode(src)?.0 as usize;
39    if src.remaining() < len {
40        return Err(ProtocolError::Io(std::io::Error::new(
41            std::io::ErrorKind::UnexpectedEof,
42            format!("Missing bytes for {context}"),
43        )));
44    }
45    let mut b = vec![0u8; len];
46    src.copy_to_slice(&mut b);
47    Ok(b)
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct ServerboundLoginStart {
52    pub username: String,
53}
54
55impl PacketId for ServerboundLoginStart {
56    fn packet_id(_ver: u32) -> u8 {
57        0x00
58    }
59}
60
61impl Encode for ServerboundLoginStart {
62    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
63        encode_string(&self.username, dst)
64    }
65}
66
67impl Decode for ServerboundLoginStart {
68    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
69        let username = decode_string(src, "ServerboundLoginStart username")?;
70        Ok(Self { username })
71    }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct ClientboundLoginDisconnect {
76    pub reason: String,
77}
78
79impl PacketId for ClientboundLoginDisconnect {
80    fn packet_id(_ver: u32) -> u8 {
81        0x00
82    }
83}
84
85impl Encode for ClientboundLoginDisconnect {
86    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
87        encode_string(&self.reason, dst)
88    }
89}
90
91impl Decode for ClientboundLoginDisconnect {
92    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
93        let reason = decode_string(src, "ClientboundLoginDisconnect reason")?;
94        Ok(Self { reason })
95    }
96}
97
98#[derive(Debug, Clone, PartialEq)]
99pub struct ClientboundEncryptionRequest {
100    pub server_id: String,
101    pub public_key: Vec<u8>,
102    pub verify_token: Vec<u8>,
103}
104
105impl PacketId for ClientboundEncryptionRequest {
106    fn packet_id(_ver: u32) -> u8 {
107        0x01
108    }
109}
110
111impl Encode for ClientboundEncryptionRequest {
112    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
113        let id_bytes = self.server_id.as_bytes();
114        VarInt(id_bytes.len() as i32).encode(dst)?;
115        dst.put_slice(id_bytes);
116
117        VarInt(self.public_key.len() as i32).encode(dst)?;
118        dst.put_slice(&self.public_key);
119
120        VarInt(self.verify_token.len() as i32).encode(dst)?;
121        dst.put_slice(&self.verify_token);
122
123        Ok(())
124    }
125}
126
127impl Decode for ClientboundEncryptionRequest {
128    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
129        let id_len = VarInt::decode(src)?.0 as usize;
130        if src.remaining() < id_len {
131            return Err(ProtocolError::UnexpectedEof);
132        }
133        let mut id_bytes = vec![0u8; id_len];
134        src.copy_to_slice(&mut id_bytes);
135        let server_id = String::from_utf8(id_bytes).map_err(|_| ProtocolError::UnexpectedEof)?;
136
137        let key_len = VarInt::decode(src)?.0 as usize;
138        if src.remaining() < key_len {
139            return Err(ProtocolError::UnexpectedEof);
140        }
141        let mut public_key = vec![0u8; key_len];
142        src.copy_to_slice(&mut public_key);
143
144        let token_len = VarInt::decode(src)?.0 as usize;
145        if src.remaining() < token_len {
146            return Err(ProtocolError::UnexpectedEof);
147        }
148        let mut verify_token = vec![0u8; token_len];
149        src.copy_to_slice(&mut verify_token);
150
151        let remaining_trailing_bytes = src.remaining();
152        src.advance(remaining_trailing_bytes);
153
154        Ok(Self {
155            server_id,
156            public_key,
157            verify_token,
158        })
159    }
160}
161
162#[derive(Debug, Clone, PartialEq)]
163pub struct ServerboundEncryptionResponse {
164    pub shared_secret: Vec<u8>,
165    pub verify_token: Vec<u8>,
166}
167
168impl PacketId for ServerboundEncryptionResponse {
169    fn packet_id(_ver: u32) -> u8 {
170        0x01
171    }
172}
173
174impl Encode for ServerboundEncryptionResponse {
175    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
176        encode_byte_array(&self.shared_secret, dst)?;
177        encode_byte_array(&self.verify_token, dst)
178    }
179}
180
181impl Decode for ServerboundEncryptionResponse {
182    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
183        let shared_secret = decode_byte_array(src, "ServerboundEncryptionResponse shared_secret")?;
184        let verify_token = decode_byte_array(src, "ServerboundEncryptionResponse verify_token")?;
185        Ok(Self {
186            shared_secret,
187            verify_token,
188        })
189    }
190}
191
192#[derive(Debug, Clone, PartialEq)]
193pub struct ProfileProperty {
194    pub name: String,
195    pub value: String,
196    pub signature: Option<String>,
197}
198
199impl Encode for ProfileProperty {
200    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
201        encode_string(&self.name, dst)?;
202        encode_string(&self.value, dst)?;
203        match &self.signature {
204            Some(sig) => {
205                dst.put_u8(1);
206                encode_string(sig, dst)?;
207            },
208            None => dst.put_u8(0),
209        }
210        Ok(())
211    }
212}
213
214impl Decode for ProfileProperty {
215    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
216        let name = decode_string(src, "ProfileProperty name")?;
217        let value = decode_string(src, "ProfileProperty value")?;
218        if src.remaining() < 1 {
219            return Err(ProtocolError::Io(std::io::Error::new(
220                std::io::ErrorKind::UnexpectedEof,
221                "Missing signature flag in ProfileProperty",
222            )));
223        }
224        let signature = if src.get_u8() != 0 {
225            Some(decode_string(src, "ProfileProperty signature")?)
226        } else {
227            None
228        };
229        Ok(Self {
230            name,
231            value,
232            signature,
233        })
234    }
235}
236
237#[derive(Debug, Clone, PartialEq)]
238pub struct ClientboundLoginSuccess {
239    pub uuid: uuid::Uuid,
240    pub username: String,
241    pub properties: Vec<ProfileProperty>,
242}
243
244impl PacketId for ClientboundLoginSuccess {
245    fn packet_id(_ver: u32) -> u8 {
246        0x02
247    }
248}
249
250impl Encode for ClientboundLoginSuccess {
251    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
252        let uuid_str = self.uuid.hyphenated().to_string();
253        let uuid_bytes = uuid_str.as_bytes();
254        VarInt(uuid_bytes.len() as i32).encode(dst)?;
255        dst.put_slice(uuid_bytes);
256
257        let user_bytes = self.username.as_bytes();
258        VarInt(user_bytes.len() as i32).encode(dst)?;
259        dst.put_slice(user_bytes);
260
261        Ok(())
262    }
263}
264
265impl Decode for ClientboundLoginSuccess {
266    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
267        let uuid_len = VarInt::decode(src)?.0 as usize;
268        if src.remaining() < uuid_len {
269            return Err(ProtocolError::Io(std::io::Error::new(
270                std::io::ErrorKind::UnexpectedEof,
271                "Missing bytes for LoginSuccess UUID",
272            )));
273        }
274        let mut uuid_bytes = vec![0u8; uuid_len];
275        src.copy_to_slice(&mut uuid_bytes);
276        let uuid_str = String::from_utf8(uuid_bytes).map_err(|_| {
277            ProtocolError::Io(std::io::Error::new(
278                std::io::ErrorKind::InvalidData,
279                "Invalid UTF-8 inside LoginSuccess UUID",
280            ))
281        })?;
282
283        let uuid = uuid::Uuid::parse_str(&uuid_str).map_err(|_| {
284            ProtocolError::Io(std::io::Error::new(
285                std::io::ErrorKind::InvalidData,
286                "Invalid string format for UUID parsing",
287            ))
288        })?;
289
290        let user_len = VarInt::decode(src)?.0 as usize;
291        if src.remaining() < user_len {
292            return Err(ProtocolError::Io(std::io::Error::new(
293                std::io::ErrorKind::UnexpectedEof,
294                "Missing bytes for LoginSuccess Username",
295            )));
296        }
297        let mut user_bytes = vec![0u8; user_len];
298        src.copy_to_slice(&mut user_bytes);
299        let username = String::from_utf8(user_bytes).map_err(|_| {
300            ProtocolError::Io(std::io::Error::new(
301                std::io::ErrorKind::InvalidData,
302                "Invalid UTF-8 inside LoginSuccess Username",
303            ))
304        })?;
305
306        Ok(Self {
307            uuid,
308            username,
309            properties: Vec::new(),
310        })
311    }
312}
313
314#[derive(Debug, Clone, PartialEq)]
315pub struct ClientboundSetCompression {
316    pub threshold: VarInt,
317}
318
319impl PacketId for ClientboundSetCompression {
320    fn packet_id(_ver: u32) -> u8 {
321        0x03
322    }
323}
324
325impl Encode for ClientboundSetCompression {
326    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
327        self.threshold.encode(dst)
328    }
329}
330
331impl Decode for ClientboundSetCompression {
332    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
333        let threshold = VarInt::decode(src)?;
334        Ok(Self { threshold })
335    }
336}
337
338#[derive(Debug, Clone, PartialEq)]
339pub struct ClientboundLoginPluginRequest {
340    pub message_id: VarInt,
341    pub channel: String,
342    pub data: Vec<u8>,
343}
344
345impl PacketId for ClientboundLoginPluginRequest {
346    fn packet_id(ver: u32) -> u8 {
347        if ver == 340 {
348            0xFF
349        } else {
350            0x04
351        }
352    }
353}
354
355impl Encode for ClientboundLoginPluginRequest {
356    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
357        self.message_id.encode(dst)?;
358        encode_string(&self.channel, dst)?;
359        dst.put_slice(&self.data);
360        Ok(())
361    }
362}
363
364impl Decode for ClientboundLoginPluginRequest {
365    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
366        let message_id = VarInt::decode(src)?;
367        let channel = decode_string(src, "ClientboundLoginPluginRequest channel")?;
368        let remaining = src.remaining();
369        let mut data = vec![0u8; remaining];
370        src.copy_to_slice(&mut data);
371        Ok(Self {
372            message_id,
373            channel,
374            data,
375        })
376    }
377}
378
379#[derive(Debug, Clone, PartialEq)]
380pub struct ServerboundLoginPluginResponse {
381    pub message_id: VarInt,
382    pub successful: bool,
383    pub data: Option<Vec<u8>>,
384}
385
386impl PacketId for ServerboundLoginPluginResponse {
387    fn packet_id(ver: u32) -> u8 {
388        if ver == 340 {
389            0xFF
390        } else {
391            0x02
392        }
393    }
394}
395
396impl Encode for ServerboundLoginPluginResponse {
397    fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
398        self.message_id.encode(dst)?;
399        dst.put_u8(if self.successful { 1 } else { 0 });
400        if self.successful {
401            if let Some(ref data) = self.data {
402                dst.put_slice(data);
403            }
404        }
405        Ok(())
406    }
407}
408
409impl Decode for ServerboundLoginPluginResponse {
410    fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
411        let message_id = VarInt::decode(src)?;
412        if src.remaining() < 1 {
413            return Err(ProtocolError::Io(std::io::Error::new(
414                std::io::ErrorKind::UnexpectedEof,
415                "Missing successful flag in ServerboundLoginPluginResponse",
416            )));
417        }
418        let successful = src.get_u8() != 0;
419        let data = if successful && src.remaining() > 0 {
420            let mut d = vec![0u8; src.remaining()];
421            src.copy_to_slice(&mut d);
422            Some(d)
423        } else {
424            None
425        };
426        Ok(Self {
427            message_id,
428            successful,
429            data,
430        })
431    }
432}
433
434#[derive(Debug, Clone, PartialEq)]
435pub struct ServerboundLoginAcknowledged;
436
437impl PacketId for ServerboundLoginAcknowledged {
438    fn packet_id(ver: u32) -> u8 {
439        if ver == 340 {
440            0xFF
441        } else {
442            0x03
443        }
444    }
445}
446
447impl Encode for ServerboundLoginAcknowledged {
448    fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
449        Ok(())
450    }
451}
452
453impl Decode for ServerboundLoginAcknowledged {
454    fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
455        Ok(Self)
456    }
457}
458
459#[derive(Debug, Clone, PartialEq)]
460pub struct AcknowledgeFinishConfiguration;
461
462impl PacketId for AcknowledgeFinishConfiguration {
463    fn packet_id(ver: u32) -> u8 {
464        if ver == 340 {
465            0xFF
466        } else {
467            0x02
468        }
469    }
470}
471
472impl Encode for AcknowledgeFinishConfiguration {
473    fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
474        Ok(())
475    }
476}
477
478impl Decode for AcknowledgeFinishConfiguration {
479    fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
480        Ok(Self)
481    }
482}
483
484#[cfg(test)]
485mod tests {
486    use super::*;
487
488    #[test]
489    fn login_start_roundtrip() {
490        let p = ServerboundLoginStart {
491            username: "Steve".to_string(),
492        };
493        let mut buf = BytesMut::new();
494        p.encode(&mut buf).unwrap();
495        let mut b = buf.freeze();
496        assert_eq!(ServerboundLoginStart::decode(&mut b).unwrap(), p);
497    }
498
499    #[test]
500    fn login_disconnect_roundtrip() {
501        let p = ClientboundLoginDisconnect {
502            reason: "Banned".to_string(),
503        };
504        let mut buf = BytesMut::new();
505        p.encode(&mut buf).unwrap();
506        let mut b = buf.freeze();
507        assert_eq!(ClientboundLoginDisconnect::decode(&mut b).unwrap(), p);
508    }
509
510    #[test]
511    fn encryption_roundtrip() {
512        let p = ClientboundEncryptionRequest {
513            server_id: String::new(),
514            public_key: vec![0xDE, 0xAD],
515            verify_token: vec![0xBE, 0xEF],
516        };
517        let mut buf = BytesMut::new();
518        p.encode(&mut buf).unwrap();
519        let mut b = buf.freeze();
520        assert_eq!(ClientboundEncryptionRequest::decode(&mut b).unwrap(), p);
521    }
522
523    #[test]
524    fn login_success_roundtrip() {
525        let p = ClientboundLoginSuccess {
526            uuid: uuid::Uuid::new_v4(),
527            username: "Steve".to_string(),
528            properties: vec![ProfileProperty {
529                name: "textures".to_string(),
530                value: "abc123".to_string(),
531                signature: Some("sig".to_string()),
532            }],
533        };
534        let mut buf = BytesMut::new();
535        p.encode(&mut buf).unwrap();
536        let mut b = buf.freeze();
537        let d = ClientboundLoginSuccess::decode(&mut b).unwrap();
538        assert_eq!(d.uuid, p.uuid);
539        assert_eq!(d.username, p.username);
540    }
541
542    #[test]
543    fn set_compression_roundtrip() {
544        let p = ClientboundSetCompression {
545            threshold: VarInt(512),
546        };
547        let mut buf = BytesMut::new();
548        p.encode(&mut buf).unwrap();
549        let mut b = buf.freeze();
550        assert_eq!(ClientboundSetCompression::decode(&mut b).unwrap(), p);
551    }
552
553    #[test]
554    fn plugin_request_roundtrip() {
555        let p = ClientboundLoginPluginRequest {
556            message_id: VarInt(1),
557            channel: "my:channel".to_string(),
558            data: vec![1, 2, 3],
559        };
560        let mut buf = BytesMut::new();
561        p.encode(&mut buf).unwrap();
562        let mut b = buf.freeze();
563        assert_eq!(ClientboundLoginPluginRequest::decode(&mut b).unwrap(), p);
564    }
565
566    #[test]
567    fn plugin_response_roundtrip() {
568        let p = ServerboundLoginPluginResponse {
569            message_id: VarInt(1),
570            successful: true,
571            data: Some(vec![0xAB, 0xCD]),
572        };
573        let mut buf = BytesMut::new();
574        p.encode(&mut buf).unwrap();
575        let mut b = buf.freeze();
576        assert_eq!(ServerboundLoginPluginResponse::decode(&mut b).unwrap(), p);
577    }
578}