pub mod data;
mod packet;
use crate::{
errors::MinecraftProtocolError,
socket::{ReadWriteMinecraftString, ReadWriteVarInt},
varint::VarInt,
};
use tokio::{
io::{self, AsyncWriteExt, Interest},
net::TcpStream,
};
use self::{
data::StatusResponse,
packet::{Packet, PacketId},
};
pub async fn status(host: &str, port: u16) -> io::Result<StatusResponse> {
let mut socket = TcpStream::connect(format!("{host}:{port}")).await?;
socket
.ready(Interest::READABLE | Interest::WRITABLE)
.await?;
let handshake = Packet::builder(PacketId::Handshake)
.add_varint(&VarInt::from(-1))
.add_string(host)
.add_u16(port)
.add_varint(&VarInt::from(PacketId::Status))
.build();
socket.write_all(&handshake.bytes()).await?;
let status_request = Packet::builder(PacketId::Handshake).build();
socket.write_all(&status_request.bytes()).await?;
let _len = socket.read_varint().await?;
let id = socket.read_varint().await?;
if id != 0 {
return Err(MinecraftProtocolError::InvalidStatusResponse.into());
}
let data = socket.read_mc_string().await?;
socket.shutdown().await?;
serde_json::from_str::<StatusResponse>(&data)
.map_err(|_| MinecraftProtocolError::InvalidStatusResponse.into())
}
create_timeout!(status, StatusResponse);
#[cfg(test)]
mod tests {
use super::status;
use tokio::io::Result;
#[tokio::test]
async fn test_hypixel_status() -> Result<()> {
let data = status("mc.hypixel.net", 25565).await?;
println!("{data:#?}");
Ok(())
}
#[tokio::test]
async fn test_local_status() -> Result<()> {
let data = status("localhost", 25565).await?;
println!("{data:#?}");
Ok(())
}
}