Skip to main content

kojacoord_protocol/versions/v1_8/status/
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::{ClientboundPongResponse, ClientboundStatusResponse};
8pub use serverbound::{ServerboundPingRequest, ServerboundStatusRequest};
9
10pub const PROTOCOL_VERSION: u32 = 47;
11
12mod serverbound {
13    use super::*;
14
15    #[derive(Debug, Clone, PartialEq)]
16    pub struct ServerboundStatusRequest;
17
18    impl PacketId for ServerboundStatusRequest {
19        fn packet_id(_ver: u32) -> u8 {
20            0x00
21        }
22    }
23
24    impl Encode for ServerboundStatusRequest {
25        fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
26            Ok(())
27        }
28    }
29
30    impl Decode for ServerboundStatusRequest {
31        fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
32            Ok(Self)
33        }
34    }
35
36    #[derive(Debug, Clone, PartialEq)]
37    pub struct ServerboundPingRequest {
38        pub payload: i64,
39    }
40
41    impl PacketId for ServerboundPingRequest {
42        fn packet_id(_ver: u32) -> u8 {
43            0x01
44        }
45    }
46
47    impl Encode for ServerboundPingRequest {
48        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
49            dst.put_i64(self.payload);
50            Ok(())
51        }
52    }
53
54    impl Decode for ServerboundPingRequest {
55        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
56            Ok(Self {
57                payload: src.get_i64(),
58            })
59        }
60    }
61}
62
63mod clientbound {
64    use super::*;
65
66    #[derive(Debug, Clone, PartialEq)]
67    pub struct ClientboundStatusResponse {
68        pub json_response: String,
69    }
70
71    impl PacketId for ClientboundStatusResponse {
72        fn packet_id(_ver: u32) -> u8 {
73            0x00
74        }
75    }
76
77    impl Encode for ClientboundStatusResponse {
78        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
79            let json_bytes = self.json_response.as_bytes();
80            VarInt(json_bytes.len() as i32).encode(dst)?;
81            dst.put_slice(json_bytes);
82            Ok(())
83        }
84    }
85
86    impl Decode for ClientboundStatusResponse {
87        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
88            let json_len = VarInt::decode(src)?.0 as usize;
89
90            if src.remaining() < json_len {
91                return Err(ProtocolError::Io(std::io::Error::new(
92                    std::io::ErrorKind::UnexpectedEof,
93                    "Missing bytes while reading ClientboundStatusResponse JSON payload",
94                )));
95            }
96
97            let mut json_bytes = vec![0u8; json_len];
98            src.copy_to_slice(&mut json_bytes);
99
100            let json_response = String::from_utf8(json_bytes).map_err(|_| {
101                ProtocolError::Io(std::io::Error::new(
102                    std::io::ErrorKind::InvalidData,
103                    "Invalid UTF-8 string in ClientboundStatusResponse",
104                ))
105            })?;
106
107            Ok(Self { json_response })
108        }
109    }
110
111    #[derive(Debug, Clone, PartialEq)]
112    pub struct ClientboundPongResponse {
113        pub payload: i64,
114    }
115
116    impl PacketId for ClientboundPongResponse {
117        fn packet_id(_ver: u32) -> u8 {
118            0x01
119        }
120    }
121
122    impl Encode for ClientboundPongResponse {
123        fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
124            dst.put_i64(self.payload);
125            Ok(())
126        }
127    }
128
129    impl Decode for ClientboundPongResponse {
130        fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
131            if src.remaining() < 8 {
132                return Err(ProtocolError::Io(std::io::Error::new(
133                    std::io::ErrorKind::UnexpectedEof,
134                    "Missing bytes for ClientboundPongResponse payload",
135                )));
136            }
137            Ok(Self {
138                payload: src.get_i64(),
139            })
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn status_request_roundtrip() {
150        let mut buf = BytesMut::new();
151        ServerboundStatusRequest.encode(&mut buf).unwrap();
152        let mut bytes = buf.freeze();
153        let _ = ServerboundStatusRequest::decode(&mut bytes).unwrap();
154    }
155
156    #[test]
157    fn status_response_roundtrip() {
158        let p = ClientboundStatusResponse {
159            json_response: r#"{"version":{"name":"1.8","protocol":47},"players":{"max":20,"online":0},"description":{"text":"A Minecraft 1.8 server"}}"#.to_string(),
160        };
161        let mut buf = BytesMut::new();
162        p.encode(&mut buf).unwrap();
163        let mut bytes = buf.freeze();
164        assert_eq!(ClientboundStatusResponse::decode(&mut bytes).unwrap(), p);
165    }
166
167    #[test]
168    fn ping_roundtrip() {
169        let p = ServerboundPingRequest { payload: 123456789 };
170        let mut buf = BytesMut::new();
171        p.encode(&mut buf).unwrap();
172        let mut bytes = buf.freeze();
173        assert_eq!(ServerboundPingRequest::decode(&mut bytes).unwrap(), p);
174    }
175
176    #[test]
177    fn pong_roundtrip() {
178        let p = ClientboundPongResponse { payload: -99 };
179        let mut buf = BytesMut::new();
180        p.encode(&mut buf).unwrap();
181        let mut bytes = buf.freeze();
182        assert_eq!(ClientboundPongResponse::decode(&mut bytes).unwrap(), p);
183    }
184
185    #[test]
186    fn packet_ids_are_correct_for_1_8() {
187        assert_eq!(ServerboundStatusRequest::packet_id(PROTOCOL_VERSION), 0x00);
188        assert_eq!(ServerboundPingRequest::packet_id(PROTOCOL_VERSION), 0x01);
189        assert_eq!(ClientboundStatusResponse::packet_id(PROTOCOL_VERSION), 0x00);
190        assert_eq!(ClientboundPongResponse::packet_id(PROTOCOL_VERSION), 0x01);
191    }
192}