1pub mod error;
30pub mod player_info;
31pub mod server_info;
32
33use crate::error::Q3Error;
34use crate::server_info::ServerInfo;
35
36use format_bytes::format_bytes;
37use std::net;
38
39#[derive(Debug)]
40pub struct Q3Tool {
41 password: Option<String>,
42 host: String,
43}
44
45impl Q3Tool {
46 pub fn new(host: &str, password: Option<String>) -> Self {
48 Self {
49 host: host.to_owned(),
50 password,
51 }
52 }
53
54 pub fn get_status(&self) -> Result<ServerInfo, Q3Error> {
56 let info = self.send_request()?;
57 let info = Self::parse_response(info)?;
58 Ok(info)
59 }
60
61 pub fn rcon(&self, command: &str) -> Result<String, Q3Error> {
64 let socket = self.create_socket()?;
65 let mut buffer = [0; 2048];
66
67 let request = format_bytes!(
68 b"\xFF\xFF\xFF\xFFrcon {} {}",
69 self.password.as_ref().unwrap().as_bytes(),
70 command.as_bytes()
71 );
72 socket.send(&request)?;
73 socket.recv(&mut buffer)?;
74
75 let response = String::from_utf8_lossy(&buffer).into_owned();
76
77 Ok(response)
78 }
79
80 fn create_socket(&self) -> Result<net::UdpSocket, Q3Error> {
81 let socket = net::UdpSocket::bind("0.0.0.0:0")?;
82 socket.connect(&self.host)?;
83 Ok(socket)
84 }
85
86 fn send_request(&self) -> Result<String, Q3Error> {
87 let socket = self.create_socket()?;
88 let mut buffer = [0; 2048];
89
90 socket.send(b"\xFF\xFF\xFF\xFFgetstatus")?;
91 socket.recv(&mut buffer)?;
92
93 let info = String::from_utf8_lossy(&buffer).into_owned();
94
95 Ok(info)
96 }
97
98 fn parse_response(raw_info: String) -> Result<ServerInfo, Q3Error> {
99 if let Some((_header, info)) = raw_info.split_once('\n') {
100 Ok(ServerInfo::new(info.to_string())?)
101 } else {
102 Err(Q3Error::InvalidResponse)
103 }
104 }
105}