Almost completed the valve protocol

This commit is contained in:
CosminPerRam 2022-10-16 02:42:17 +03:00
parent 8098136d09
commit c9eb725a51
6 changed files with 135 additions and 43 deletions

View file

@ -2,13 +2,9 @@
use gamedig::TF2;
fn main() {
let response = TF2::query("5.15.202.107", None);
let response = TF2::query("91.216.250.10", None);
match response {
Err(_) => println!("fuck"),
Ok(r) => {
println!("{:?}", r);
()
}
Err(error) => println!("Couldn't query, error: {}", error),
Ok(r) => println!("{:?}", r)
}
}

View file

@ -1,5 +1,4 @@
use crate::errors::GDError;
use crate::protocol::Protocol;
use crate::protocols::valve::{Response, ValveProtocol};
pub struct TF2;
@ -9,6 +8,6 @@ impl TF2 {
ValveProtocol::query(address, match port {
None => 27015,
Some(port) => port
})
}, false)
}
}

View file

@ -1,6 +1,5 @@
mod errors;
mod protocol;
mod protocols;
mod utils;
pub mod games;

View file

@ -1,7 +0,0 @@
use crate::errors::GDError;
pub trait Protocol {
type Response;
fn query(address: &str, port: u16) -> Result<Self::Response, GDError>;
}

View file

@ -1,7 +1,6 @@
use std::net::UdpSocket;
use crate::errors::GDError;
use crate::protocol::Protocol;
use crate::utils::{combine_two_u8, complete_address, concat_u8, find_null_in_array};
use crate::utils::{combine_two_u8, complete_address, concat_u8, find_null_in_array, get_u64_from_buf};
#[derive(Debug)]
pub enum Server {
@ -31,7 +30,27 @@ pub struct Response {
pub server_type: Server,
pub environment_type: Environment,
pub has_password: bool,
pub vac_secured: bool
pub vac_secured: bool,
pub the_ship: Option<TheShip>,
pub version: String,
pub extra_data: Option<ExtraData>
}
#[derive(Debug)]
pub struct TheShip {
pub mode: u8,
pub witnesses: u8,
pub duration: u8
}
#[derive(Debug)]
pub struct ExtraData {
pub port: Option<u16>,
pub steam_id: Option<u64>,
pub tv_port: Option<u16>,
pub tv_name: Option<String>,
pub keywords: Option<String>,
pub game_id: Option<u64>
}
pub enum Request {
@ -43,6 +62,8 @@ pub struct ValveProtocol {
complete_address: String
}
static default_packet_size: usize = 256;
impl ValveProtocol {
fn new(address: &str, port: u16) -> Self {
Self {
@ -85,53 +106,129 @@ impl ValveProtocol {
}
}
impl Protocol for ValveProtocol {
type Response = Response;
fn query(address: &str, port: u16) -> Result<Response, GDError> {
impl ValveProtocol {
pub(crate) fn query(address: &str, port: u16, has_the_ship: bool) -> Result<Response, GDError> {
let client = ValveProtocol::new(address, port);
client.do_request(Request::A2sInfo(None), None);
let mut buf = client.receive();
let mut buf = client.receive_with_size(default_packet_size);
if buf[4] == 0x41 {
client.do_request(Request::A2sInfo(Some([buf[5], buf[6], buf[7], buf[8]])), None);
buf = client.receive_with_size(default_packet_size);
}
buf = client.receive_with_size(256);
println!("{:x?}", &buf);
let name_nul_pos = find_null_in_array(&mut buf);
let map_nul_pos = name_nul_pos + 1 + find_null_in_array(&mut buf[name_nul_pos + 1..]);
let folder_nul_pos = map_nul_pos + 1 + find_null_in_array(&mut buf[map_nul_pos + 1..]);
let game_nul_pos = folder_nul_pos + 1 + find_null_in_array(&mut buf[folder_nul_pos + 1..]);
let name_null_pos = find_null_in_array(&mut buf);
let map_null_pos = name_null_pos + 1 + find_null_in_array(&mut buf[name_null_pos + 1..]);
let folder_null_pos = map_null_pos + 1 + find_null_in_array(&mut buf[map_null_pos + 1..]);
let game_null_pos = folder_null_pos + 1 + find_null_in_array(&mut buf[folder_null_pos + 1..]);
let server_type = match buf[game_nul_pos + 6] as char {
let server_type = match buf[game_null_pos + 6] as char {
'd' => Server::Dedicated,
'l' => Server::NonDedicated,
_ => Server::SourceTV
};
let environment_type = match buf[game_nul_pos + 7] as char {
let environment_type = match buf[game_null_pos + 7] as char {
'l' => Environment::Linux,
'w' => Environment::Windows,
_ => Environment::Mac
};
let mut the_ship_index = game_null_pos + 10;
let the_ship = match has_the_ship {
false => None,
true => {
let ship = TheShip {
mode: buf[the_ship_index],
witnesses: buf[the_ship_index + 1],
duration: buf[the_ship_index + 2]
};
the_ship_index = the_ship_index + 3;
Some(ship)
}
};
let version_null_pos = the_ship_index + find_null_in_array(&mut buf[the_ship_index..]);
let extra_data = match buf.get(version_null_pos + 1) {
None => None,
Some(value) => {
let mut last_edf_position = version_null_pos + 2;
let edf_port = match (value & 0x80) > 0 {
false => None,
true => {
let p = combine_two_u8(buf[last_edf_position + 1], buf[last_edf_position]);
last_edf_position = last_edf_position + 2;
Some(p)
}
};
let steam_id = match (value & 0x10) > 0 { //doesnt work?
false => None,
true => {
let p = get_u64_from_buf(&buf[last_edf_position..]);
last_edf_position = last_edf_position + 8;
Some(p)
}
};
let (tv_port, tv_name) = match (value & 0x40) > 0 {
false => (None, None),
true => {
let port = combine_two_u8(buf[last_edf_position + 1], buf[last_edf_position]);
last_edf_position = last_edf_position + 2;
let tv_name_null_pos = last_edf_position + find_null_in_array(&buf[last_edf_position..]);
let tv_name = String::from_utf8(Vec::from(&buf[last_edf_position..tv_name_null_pos])).expect("cacat");
last_edf_position = tv_name_null_pos + 1;
(Some(port), Some(tv_name))
}
};
let keywords = match (value & 0x20) > 0 {
false => None,
true => {
let kws_null_pos = last_edf_position + find_null_in_array(&buf[last_edf_position..]);
let kws = String::from_utf8(Vec::from(&buf[last_edf_position..kws_null_pos])).expect("cacat");
last_edf_position = kws_null_pos + 1;
Some(kws)
}
};
let game_id = match (value & 0x01) > 0 {
false => None,
true => Some(get_u64_from_buf(&buf[last_edf_position..]))
};
Some(ExtraData {
port: edf_port,
steam_id,
tv_port,
tv_name,
keywords,
game_id
})
}
};
Ok(Response {
protocol: buf[5],
name: String::from_utf8(Vec::from(&mut buf[6..name_nul_pos])).expect("cacat"),
map: String::from_utf8(Vec::from(&mut buf[name_nul_pos + 1..map_nul_pos])).expect("cacat"),
folder: String::from_utf8(Vec::from(&mut buf[map_nul_pos + 1..folder_nul_pos])).expect("cacat"),
game: String::from_utf8(Vec::from(&mut buf[folder_nul_pos + 1..game_nul_pos])).expect("cacat"),
id: combine_two_u8(buf[game_nul_pos + 2], buf[game_nul_pos + 1]),
players: buf[game_nul_pos + 3],
max_players: buf[game_nul_pos + 4],
bots: buf[game_nul_pos + 5],
name: String::from_utf8(Vec::from(&mut buf[6..name_null_pos])).expect("cacat"),
map: String::from_utf8(Vec::from(&mut buf[name_null_pos + 1..map_null_pos])).expect("cacat"),
folder: String::from_utf8(Vec::from(&mut buf[map_null_pos + 1..folder_null_pos])).expect("cacat"),
game: String::from_utf8(Vec::from(&mut buf[folder_null_pos + 1..game_null_pos])).expect("cacat"),
id: combine_two_u8(buf[game_null_pos + 2], buf[game_null_pos + 1]),
players: buf[game_null_pos + 3],
max_players: buf[game_null_pos + 4],
bots: buf[game_null_pos + 5],
server_type,
environment_type,
has_password: buf[game_nul_pos + 8] == 1,
vac_secured: buf[game_nul_pos + 9] != 0
has_password: buf[game_null_pos + 8] == 1,
vac_secured: buf[game_null_pos + 9] != 0,
the_ship,
version: String::from_utf8(Vec::from(&mut buf[the_ship_index..version_null_pos])).expect("cacat"),
extra_data
})
}
}

View file

@ -16,5 +16,13 @@ pub fn complete_address(address: &str, port: u16) -> String {
}
pub fn combine_two_u8(high: u8, low: u8) -> u16 {
((high as u16) << 8) | low as u16
u16::from_be_bytes([high, low])
}
pub fn combine_eight_u8(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8, g: u8, h: u8) -> u64 {
u64::from_be_bytes([a, b, c, d, e, f, g, h])
}
pub fn get_u64_from_buf(buf: &[u8]) -> u64 {
combine_eight_u8(buf[7], buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0])
}