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}