mc_ping/packets.rs
1use std::time::Duration;
2use anyhow::Context;
3use tokio::time::sleep;
4use crate::mc_text::ServerStatus;
5use crate::varint::VarInt;
6
7/// Represents the Minecraft client handshake packet.
8///
9/// This packet initiates the handshake with the server before status or login requests.
10///
11/// # Example
12/// ```
13/// let handshake = ClientHandshake::new("127.0.0.1".to_string(), 25565);
14/// let bytes = handshake.to_bytes();
15/// ```
16#[derive(Debug)]
17pub struct ClientHandshake {
18 /// Length of the entire packet, encoded as a VarInt.
19 pub len: VarInt,
20 /// Packet ID (0x00 for handshake).
21 pub packet_id: VarInt,
22 /// Protocol version number, e.g., 768 for Minecraft 1.21.
23 pub protocol_version: VarInt,
24 /// Server address as a string (domain or IP).
25 pub server_addr: String,
26 /// Server port number.
27 pub server_port: u16,
28 /// Next state after handshake: 1 = status, 2 = login.
29 pub next_state: VarInt,
30}
31
32impl ClientHandshake {
33 /// Creates a new ClientHandshake packet for the given server address and port.
34 ///
35 /// Automatically calculates packet length and uses default protocol version 768.
36 pub fn new(server_addr: String, server_port: u16) -> ClientHandshake {
37 let packet_id = VarInt::from(0x00);
38 let protocol_version = VarInt::from(768);
39 let next_state = VarInt::from(1);
40
41 // Calculate length of the packet payload:
42 // packet_id + protocol_version + length of server_addr string + server_addr bytes + port(2 bytes) + next_state
43 let len_val =
44 packet_id.size() +
45 protocol_version.size() +
46 VarInt::from(server_addr.len() as i32).size() +
47 server_addr.len() +
48 2 + // server_port is 2 bytes
49 next_state.size();
50
51 let len = VarInt::from(len_val as i32);
52
53 let handshake = ClientHandshake {
54 len,
55 packet_id,
56 protocol_version,
57 server_addr,
58 server_port,
59 next_state,
60 };
61
62
63 handshake
64 }
65
66 /// Serializes the handshake packet into a byte vector ready for sending over the network.
67 ///
68 /// The format follows Minecraft's VarInt and packet structure conventions.
69 pub fn to_bytes(&self) -> Vec<u8> {
70 let mut buf = Vec::new();
71
72 // Helper function to write VarInt bytes until continuation bit is zero.
73 fn write_varint_bytes(buf: &mut Vec<u8>, varint_inner: &[u8]) {
74 for &byte in varint_inner {
75 buf.push(byte);
76 if byte & 0b1000_0000 == 0 {
77 break;
78 }
79 }
80 }
81
82 // Write packet length
83 write_varint_bytes(&mut buf, &self.len.inner);
84 // Write packet ID
85 write_varint_bytes(&mut buf, &self.packet_id.inner);
86 // Write protocol version
87 write_varint_bytes(&mut buf, &self.protocol_version.inner);
88
89 // Write server address as Minecraft String: VarInt length + UTF-8 bytes
90 let addr_len = VarInt::from(self.server_addr.len() as i32);
91 write_varint_bytes(&mut buf, &addr_len.inner);
92 buf.extend(self.server_addr.as_bytes());
93
94 // Write server port as 2 bytes big-endian
95 buf.push((self.server_port >> 8) as u8);
96 buf.push(self.server_port as u8);
97
98 // Write next state VarInt
99 write_varint_bytes(&mut buf, &self.next_state.inner);
100
101 buf
102 }
103}
104
105/// Represents the status query packet.
106///
107/// This packet is sent after handshake to request the server status.
108pub struct StatusQuery {
109 len: VarInt,
110 packet_id: VarInt,
111}
112
113impl StatusQuery {
114 /// Creates a new status query packet.
115 ///
116 /// # Example
117 /// ```
118 /// let query = StatusQuery::new();
119 /// let bytes = query.to_bytes();
120 /// ```
121 pub fn new() -> StatusQuery {
122 let packet_id = VarInt::from(0x00);
123 let len = VarInt::from(1); // Packet length: 1 byte for packet_id
124 StatusQuery {
125 len,
126 packet_id,
127 }
128 }
129
130 /// Returns the serialized bytes of the status query packet.
131 ///
132 /// This packet is always 2 bytes: [0x01, 0x00]
133 pub fn to_bytes(&self) -> Vec<u8> {
134 vec![0x01, 0x00]
135 }
136}
137
138/// Represents the server's response to a status query.
139///
140/// Contains the raw JSON string with server information.
141#[derive(Debug)]
142pub struct ServerQueryResponse {
143 /// Length of the entire response packet.
144 pub len: VarInt,
145 /// Packet ID (should be 0x00).
146 pub packet_id: VarInt,
147 /// Length of the JSON string.
148 pub json_len: VarInt,
149 /// JSON string with server status information.
150 pub json: String,
151}
152
153impl ServerQueryResponse {
154 /// Parses a ServerQueryResponse from raw bytes.
155 ///
156 /// Reads VarInts for lengths and packet IDs, then extracts the JSON string.
157 ///
158 /// # Panics
159 /// If the byte slice is too short or malformed, this may panic.
160 pub async fn from(bytes: &[u8]) -> ServerQueryResponse {
161 sleep(Duration::from_millis(100)).await; // panic fix
162 // Helper to read a VarInt from a byte slice,
163 fn read_varint(data: &[u8]) -> (VarInt, usize) {
164 let mut val = VarInt::default();
165 let mut i = 0;
166 loop {
167 let byte = data[i];
168 val.inner[i] = byte;
169 i += 1;
170 if byte & 0x80 == 0 {
171 break;
172 }
173 }
174 (val, i)
175 }
176
177 let mut cursor = 0;
178
179 // 1. Read length VarInt
180 let (len, len_size) = read_varint(&bytes[cursor..]);
181 cursor += len_size;
182
183 // 2. Read packet_id VarInt
184 let (packet_id, packet_id_size) = read_varint(&bytes[cursor..]);
185 cursor += packet_id_size;
186
187 // 3. Read json_len VarInt
188 let (json_len, json_len_size) = read_varint(&bytes[cursor..]);
189 cursor += json_len_size;
190
191 // 4. Read JSON bytes using length from json_len
192 let json_bytes = &bytes[cursor..cursor + i32::from(json_len.clone()) as usize];
193 cursor += i32::from(json_len.clone()) as usize;
194
195 let json = String::from_utf8_lossy(json_bytes).to_string();
196
197 ServerQueryResponse {
198 len,
199 packet_id,
200 json_len,
201 json,
202 }
203 }
204
205 /// Parses the JSON string into a strongly-typed ServerStatus struct.
206 ///
207 /// Returns an error if JSON deserialization fails.
208 ///
209 /// # Example
210 /// ```
211 /// let response = ServerQueryResponse::from(&bytes);
212 /// let status = response.parse_status()?;
213 /// ```
214 pub fn parse_status(&self) -> anyhow::Result<ServerStatus> {
215 let status: ServerStatus = serde_json::from_str(&self.json)
216 .context("Failed to deserialize ServerQueryResponse.json into ServerStatus")?;
217 Ok(status)
218 }
219}