Skip to main content

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