kojacoord_protocol/versions/v1_7_10/status/
mod.rs1use 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}