From c9eb725a51d73c4de4e2f6fe4e505b2838220d3f Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Sun, 16 Oct 2022 02:42:17 +0300 Subject: [PATCH] Almost completed the valve protocol --- examples/tf2.rs | 10 +-- src/games/tf2.rs | 3 +- src/lib.rs | 1 - src/protocol.rs | 7 -- src/protocols/valve.rs | 147 ++++++++++++++++++++++++++++++++++------- src/utils.rs | 10 ++- 6 files changed, 135 insertions(+), 43 deletions(-) delete mode 100644 src/protocol.rs diff --git a/examples/tf2.rs b/examples/tf2.rs index 99c3741..1f8b659 100644 --- a/examples/tf2.rs +++ b/examples/tf2.rs @@ -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) } } diff --git a/src/games/tf2.rs b/src/games/tf2.rs index 83c1452..d349f0f 100644 --- a/src/games/tf2.rs +++ b/src/games/tf2.rs @@ -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) } } diff --git a/src/lib.rs b/src/lib.rs index 775b4e0..482d0df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ mod errors; -mod protocol; mod protocols; mod utils; pub mod games; diff --git a/src/protocol.rs b/src/protocol.rs deleted file mode 100644 index cadc7cc..0000000 --- a/src/protocol.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::errors::GDError; - -pub trait Protocol { - type Response; - - fn query(address: &str, port: u16) -> Result; -} diff --git a/src/protocols/valve.rs b/src/protocols/valve.rs index 5fa00cd..64f9fcc 100644 --- a/src/protocols/valve.rs +++ b/src/protocols/valve.rs @@ -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, + pub version: String, + pub extra_data: Option +} + +#[derive(Debug)] +pub struct TheShip { + pub mode: u8, + pub witnesses: u8, + pub duration: u8 +} + +#[derive(Debug)] +pub struct ExtraData { + pub port: Option, + pub steam_id: Option, + pub tv_port: Option, + pub tv_name: Option, + pub keywords: Option, + pub game_id: Option } 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 { +impl ValveProtocol { + pub(crate) fn query(address: &str, port: u16, has_the_ship: bool) -> Result { 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 }) } } diff --git a/src/utils.rs b/src/utils.rs index c32b35c..2428578 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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]) }