144 lines
4.6 KiB
Rust
144 lines
4.6 KiB
Rust
use std::io::{Error, ErrorKind, Read, Write, Result};
|
|
use std::net::{SocketAddr, TcpStream};
|
|
|
|
use crate::leb128::LEB128;
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct MinecraftVersion {
|
|
pub name: String,
|
|
pub protocol: u32,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct MinecraftPlayer {
|
|
pub name: String,
|
|
pub id: String,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct MinecraftPlayers {
|
|
pub online: u32,
|
|
pub max: u32,
|
|
pub sample: Vec<MinecraftPlayer>,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
#[serde(untagged)]
|
|
enum MinecraftDescriptionExtra {
|
|
Extra(MinecraftDescription),
|
|
String(String),
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct MinecraftDescription {
|
|
text: String,
|
|
extra: Option<Vec<MinecraftDescriptionExtra>>,
|
|
bold: Option<bool>,
|
|
color: Option<String>,
|
|
}
|
|
|
|
#[derive(serde::Serialize, serde::Deserialize)]
|
|
pub struct MinecraftStatus {
|
|
pub version: MinecraftVersion,
|
|
pub players: MinecraftPlayers,
|
|
description: MinecraftDescription,
|
|
pub favicon: Option<String>,
|
|
#[serde(alias = "enforcesSecureChat")]
|
|
enforces_secure_chat: Option<bool>,
|
|
}
|
|
|
|
impl MinecraftStatus {
|
|
pub fn fetch(address: SocketAddr) -> Result<MinecraftStatus> {
|
|
// println!("Connecting to {address}...");
|
|
|
|
let mut stream = TcpStream::connect(address.to_string()).unwrap();
|
|
// println!("Connected!");
|
|
|
|
let mut send_buffer: Vec<u8> = Vec::new();
|
|
|
|
// println!("Sending payload...");
|
|
send_buffer.push(0x00);
|
|
LEB128::write_leb128(&mut send_buffer, 769); // 1.21.4
|
|
LEB128::write_leb128(&mut send_buffer, address.ip().to_string().len().try_into().unwrap());
|
|
send_buffer.extend_from_slice(address.ip().to_string().as_bytes());
|
|
send_buffer.extend_from_slice(&address.port().to_be_bytes());
|
|
LEB128::write_leb128(&mut send_buffer, 1);
|
|
send_packet(&mut stream, &send_buffer).unwrap();
|
|
|
|
send_packet(&mut stream, &[0x00]).unwrap();
|
|
|
|
let mut data: Vec<u8> = Vec::new();
|
|
let mut len: usize = 0;
|
|
let mut msg_len: usize = 0;
|
|
let mut object_len: usize = 0;
|
|
let mut offset: usize = 0;
|
|
|
|
loop {
|
|
let mut recv_buffer: [u8; 10240] = [0; 10240];
|
|
len += stream.read(&mut recv_buffer)?;
|
|
|
|
if len > 0 {
|
|
if msg_len == 0 {
|
|
let mut val: u32;
|
|
(val, offset) = LEB128::read_leb128(&recv_buffer);
|
|
msg_len = val as usize;
|
|
|
|
if recv_buffer[offset] != 0x00 {
|
|
return Err(Error::new(ErrorKind::InvalidData, format!("Expected packet type 0x00, but got 0x{:02x?}!", recv_buffer[offset])));
|
|
}
|
|
offset += 1; // skip message type bit
|
|
|
|
let offset2: usize;
|
|
(val, offset2) = LEB128::read_leb128(&recv_buffer[offset..]);
|
|
object_len = val as usize;
|
|
offset += offset2;
|
|
}
|
|
data.extend_from_slice(&recv_buffer);
|
|
if len >= offset + object_len {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let msg = std::str::from_utf8(&data[offset..]).unwrap().trim();
|
|
let sanitised: String = msg.chars().filter(|&c| c >= '\u{20}' || c == '\n' || c == '\r' || c == '\t').collect();
|
|
// println!("{sanitised}");
|
|
let status: MinecraftStatus = serde_json::from_slice(sanitised.as_bytes()).unwrap();
|
|
|
|
Ok(status)
|
|
}
|
|
|
|
pub fn parse_description(&self) -> String {
|
|
_description(&self.description)
|
|
}
|
|
|
|
pub fn enforces_secure_chat(&self) -> bool {
|
|
self.enforces_secure_chat.unwrap_or(false)
|
|
}
|
|
}
|
|
|
|
fn _description(description: &MinecraftDescription) -> String {
|
|
if description.extra.is_some() {
|
|
let mut extras = String::new();
|
|
for extra in description.extra.as_ref().unwrap() {
|
|
match extra {
|
|
MinecraftDescriptionExtra::Extra(description) => {
|
|
extras += &_description(&description);
|
|
}
|
|
MinecraftDescriptionExtra::String(string) => {
|
|
extras += &string;
|
|
}
|
|
}
|
|
}
|
|
return description.text.clone() + &extras;
|
|
}
|
|
description.text.clone()
|
|
}
|
|
|
|
fn send_packet(stream: &mut TcpStream, data: &[u8]) -> Result<()> {
|
|
let mut packet: Vec<u8> = Vec::new();
|
|
LEB128::write_leb128(&mut packet, data.len() as u64);
|
|
packet.extend_from_slice(&data);
|
|
|
|
stream.write(&packet).unwrap();
|
|
Ok(())
|
|
}
|