use std::time::{Duration, SystemTime};
use tokio::net::UdpSocket;
use tokio::time::timeout;
use crate::error::McError;
use crate::models::{BedrockStatus, PingResult, ServerData};
use crate::proxy::ProxyConfig;
#[cfg(feature = "proxy")]
use crate::proxy::UdpSupport;
use super::{PingProtocol, ResolvedTarget};
use crate::core::time::{start_timer, elapsed_ms};
const READ_BUFFER_SIZE: usize = 4096;
const BEDROCK_PING_PACKET_SIZE: usize = 35;
const BEDROCK_MIN_RESPONSE_SIZE: usize = 35;
#[derive(Debug, Clone, Copy, Default)]
pub struct BedrockProtocol;
impl PingProtocol for BedrockProtocol {
fn name(&self) -> &'static str { "bedrock" }
async fn ping(
&self,
target: &ResolvedTarget,
tout: Duration,
proxy: Option<&ProxyConfig>,
) -> Result<PingResult, McError> {
#[cfg(feature = "proxy")]
if let Some(cfg) = proxy
&& cfg.udp_support() != UdpSupport::Yes {
return Err(McError::proxy_udp_unsupported(cfg.addr().to_string()));
}
let _ = proxy;
let start = start_timer();
let socket = UdpSocket::bind("0.0.0.0:0").await.map_err(McError::Io)?;
timeout(tout, socket.send_to(&build_ping(), target.addr))
.await
.map_err(|_| McError::timeout())?
.map_err(McError::Io)?;
let mut buf = [0u8; READ_BUFFER_SIZE];
let (len, _) = timeout(tout, socket.recv_from(&mut buf))
.await
.map_err(|_| McError::timeout())?
.map_err(McError::Io)?;
if len < BEDROCK_MIN_RESPONSE_SIZE {
return Err(McError::invalid_response("response too short"));
}
let latency = elapsed_ms(start);
let pong = String::from_utf8(buf[BEDROCK_PING_PACKET_SIZE..len].to_vec())
.unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned());
Ok(PingResult::new(ServerData::Bedrock(BedrockStatus::parse(&pong)?), latency))
}
}
fn build_ping() -> Vec<u8> {
let mut p = Vec::with_capacity(BEDROCK_PING_PACKET_SIZE);
p.push(0x01);
let ts = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
p.extend_from_slice(&ts.to_be_bytes());
p.extend_from_slice(&[
0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE,
0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78,
]);
p.extend_from_slice(&[0u8; 8]);
p
}