use tokio::io::{AsyncWriteExt, AsyncReadExt};
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
use crate::mc_text::ServerStatus;
use crate::packets::{ClientHandshake, ServerQueryResponse, StatusQuery};
use anyhow::{anyhow, Result};
use tokio::net::lookup_host;
fn is_domain(addr: &str) -> bool {
addr.parse::<std::net::IpAddr>().is_err()
}
pub struct Connection {
pub stream: TcpStream,
pub addr: (String, u16),
}
impl Connection {
pub async fn connect(addr: (String, u16)) -> Result<Self> {
#[cfg(not(feature = "resolve"))]
{
if is_domain(&addr.0) {
return Err(anyhow!(r#"Enable feature "resolve" to enable domain resolving"#))
}
let stream = TcpStream::connect(addr.clone()).await?;
Ok(Self {
stream,
addr,
})
}
#[cfg(feature = "resolve")]
{
let host_port = format!("{}:{}", addr.0.clone(), addr.1);
let mut addrs = lookup_host(host_port.clone()).await?;
if let Some(sock_addr) = addrs.next() {
let stream = TcpStream::connect(sock_addr).await?;
Ok(Self {
stream,
addr: (addr.0, sock_addr.port()),
})
} else {
Err(anyhow::anyhow!("Could not resolve address: {}", host_port))
}
}
}
pub async fn connect_timeout(addr: (String, u16), _timeout: Duration) -> Result<Self> {
let _conn = timeout(_timeout, Self::connect(addr)).await;
match _conn {
Ok(Ok(conn)) => Ok(conn),
Err(err) => Err(anyhow::anyhow!("Could not connect: {} (timeout)", err))?,
Ok(Err(err)) => Err(anyhow::anyhow!("Could not connect: {}", err))?,
}
}
pub async fn send_handshake(&mut self) -> Result<()> {
let _ip = self.addr.0.clone();
let _port = self.addr.1;
let handshake = ClientHandshake::new(_ip, _port);
let bytes = handshake.to_bytes();
self.stream.write_all(bytes.as_slice()).await?;
Ok(())
}
async fn __send_query_packet(&mut self) -> Result<()> {
let query = StatusQuery::new();
let bytes = query.to_bytes();
self.stream.write_all(bytes.as_slice()).await?;
Ok(())
}
async fn __read_status_packet(&mut self) -> Result<ServerQueryResponse> {
let mut buf = [0u8; 4096];
self.stream.read(&mut buf).await?;
let status_packet = ServerQueryResponse::from(&buf[..]);
Ok(status_packet)
}
pub async fn get_status(&mut self) -> Result<ServerStatus> {
self.__send_query_packet().await?;
let _status = self.__read_status_packet().await?;
Ok(_status.parse_status()?)
}
pub async fn ping(&mut self) -> Result<ServerStatus> {
self.send_handshake().await?;
self.__send_query_packet().await?;
let status = self.__read_status_packet().await?;
status.parse_status()
}
}