mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
Added socket timeout capability and reduced PACKET_SIZE to 1400 as specified from protocol
This commit is contained in:
parent
d3b71fccf6
commit
caa7329a68
21 changed files with 125 additions and 49 deletions
|
|
@ -2,16 +2,20 @@
|
||||||
Who knows what the future holds...
|
Who knows what the future holds...
|
||||||
|
|
||||||
# 0.0.5 - ??/??/2022
|
# 0.0.5 - ??/??/2022
|
||||||
|
Added `SocketBind` error, regarding failing to bind a socket.
|
||||||
|
Socket custom timeout capability (with an error if provided durations are zero).
|
||||||
|
Because of this, a parameter similar to GatherSettings has been added on the Valve Protocol Query.
|
||||||
Support for GoldSrc split packets and obsolete A2S_INFO response.
|
Support for GoldSrc split packets and obsolete A2S_INFO response.
|
||||||
Changed the Valve Protocol app parameter to represent the engine responses.
|
Changed the Valve Protocol app parameter to represent the engine responses.
|
||||||
It is now an enum of:
|
It is now an enum of:
|
||||||
- `Source(Option<u32>)` - A Source response with optionally, the id (if the id is present and the response id is not the same, the query fails)
|
- `Source(Option<u32>)` - A Source response with optionally, the id (if the id is present and the response id is not the same, the query fails), if it isn't provided, find it.
|
||||||
- `GoldSrc(bool)` - A GoldSrc response with the option to enforce the obsolete A2S_INFO response.
|
- `GoldSrc(bool)` - A GoldSrc response with the option to enforce the obsolete A2S_INFO response.
|
||||||
|
|
||||||
|
Fixed Source multi-packet response crash due to when a certain app with a certain protocol doesn't have the Size field.
|
||||||
|
Reduced Valve Protocol `PACKET_SIZE` to be as specified from 2048 to 1400.
|
||||||
[Counter-Strike: Condition Zero](https://store.steampowered.com/app/80/CounterStrike_Condition_Zero/) implementation.
|
[Counter-Strike: Condition Zero](https://store.steampowered.com/app/80/CounterStrike_Condition_Zero/) implementation.
|
||||||
[Day of Defeat](https://store.steampowered.com/app/30/Day_of_Defeat/) implementation.
|
[Day of Defeat](https://store.steampowered.com/app/30/Day_of_Defeat/) implementation.
|
||||||
Games besides CSGO and TS now have the same response structure.
|
Games besides CSGO and TS now have the same response structure.
|
||||||
Fixed Source multipacket response crash due to when a certain app with a certain protocol doesnt have the Size field.
|
|
||||||
|
|
||||||
# 0.0.4 - 23/10/2022
|
# 0.0.4 - 23/10/2022
|
||||||
Queries now support DNS resolve.
|
Queries now support DNS resolve.
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ fn main() -> GDResult<()> {
|
||||||
"ts" => println!("{:?}", ts::query(ip, port)?),
|
"ts" => println!("{:?}", ts::query(ip, port)?),
|
||||||
"cscz" => println!("{:?}", cscz::query(ip, port)?),
|
"cscz" => println!("{:?}", cscz::query(ip, port)?),
|
||||||
"dod" => println!("{:?}", dod::query(ip, port)?),
|
"dod" => println!("{:?}", dod::query(ip, port)?),
|
||||||
"_src" => println!("{:?}", valve::query(ip, 27015, App::Source(None), None)?),
|
"_src" => println!("{:?}", valve::query(ip, 27015, App::Source(None), None, None)?),
|
||||||
"_gld" => println!("{:?}", valve::query(ip, 27015, App::GoldSrc(false), None)?),
|
"_gld" => println!("{:?}", valve::query(ip, 27015, App::GoldSrc(false), None, None)?),
|
||||||
"_gld_f" => println!("{:?}", valve::query(ip, 27015, App::GoldSrc(true), None)?),
|
"_gld_f" => println!("{:?}", valve::query(ip, 27015, App::GoldSrc(true), None, None)?),
|
||||||
_ => panic!("Undefined game: {}", args[1])
|
_ => panic!("Undefined game: {}", args[1])
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ pub enum GDError {
|
||||||
BadGame(String),
|
BadGame(String),
|
||||||
/// Problems occurred while dns resolving.
|
/// Problems occurred while dns resolving.
|
||||||
DnsResolve(String),
|
DnsResolve(String),
|
||||||
|
/// Couldn't bind a socket.
|
||||||
|
SocketBind(String),
|
||||||
|
/// Invalid input.
|
||||||
|
InvalidInput(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GDError {
|
impl fmt::Display for GDError {
|
||||||
|
|
@ -42,6 +46,8 @@ impl fmt::Display for GDError {
|
||||||
GDError::UnknownEnumCast => write!(f, "Unknown enum cast encountered."),
|
GDError::UnknownEnumCast => write!(f, "Unknown enum cast encountered."),
|
||||||
GDError::BadGame(details) => write!(f, "Queried another game that the supposed one: {details}"),
|
GDError::BadGame(details) => write!(f, "Queried another game that the supposed one: {details}"),
|
||||||
GDError::DnsResolve(details) => write!(f, "DNS Resolve: {details}"),
|
GDError::DnsResolve(details) => write!(f, "DNS Resolve: {details}"),
|
||||||
|
GDError::SocketBind(details) => write!(f, "Socket bind: {details}"),
|
||||||
|
GDError::InvalidInput(details) => write!(f, "Invalid input: {details}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::ALIENS.app(), None)?;
|
}, SteamID::ALIENS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::ASRD.app(), None)?;
|
}, SteamID::ASRD.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::CSCZ.app(), None)?;
|
}, SteamID::CSCZ.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::CSGO.app(), Some(GatheringSettings {
|
}, SteamID::CSGO.as_app(), Some(GatheringSettings {
|
||||||
players: true,
|
players: true,
|
||||||
rules: false // cause csgo doesnt reply with rules anymore
|
rules: false // cause csgo doesnt reply with rules anymore
|
||||||
}))?;
|
}), None)?;
|
||||||
|
|
||||||
Ok(Response::new_from_valve_response(valve_response))
|
Ok(Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::CSS.app(), None)?;
|
}, SteamID::CSS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::DOD.app(), None)?;
|
}, SteamID::DOD.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::DODS.app(), None)?;
|
}, SteamID::DODS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::GM.app(), None)?;
|
}, SteamID::GM.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::HL2DM.app(), None)?;
|
}, SteamID::HL2DM.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::INS.app(), None)?;
|
}, SteamID::INS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::INSMIC.app(), None)?;
|
}, SteamID::INSMIC.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27131,
|
None => 27131,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::INSS.app(), None)?;
|
}, SteamID::INSS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::L4D.app(), None)?;
|
}, SteamID::L4D.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::L4D2.app(), None)?;
|
}, SteamID::L4D2.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<game::Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::TF2.app(), None)?;
|
}, SteamID::TF2.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(game::Response::new_from_valve_response(valve_response))
|
Ok(game::Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ pub fn query(address: &str, port: Option<u16>) -> GDResult<Response> {
|
||||||
let valve_response = valve::query(address, match port {
|
let valve_response = valve::query(address, match port {
|
||||||
None => 27015,
|
None => 27015,
|
||||||
Some(port) => port
|
Some(port) => port
|
||||||
}, SteamID::TS.app(), None)?;
|
}, SteamID::TS.as_app(), None, None)?;
|
||||||
|
|
||||||
Ok(Response::new_from_valve_response(valve_response))
|
Ok(Response::new_from_valve_response(valve_response))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
use bzip2_rs::decoder::Decoder;
|
use bzip2_rs::decoder::Decoder;
|
||||||
use crate::{GDError, GDResult};
|
use crate::{GDError, GDResult};
|
||||||
use crate::protocols::valve::{App, ModData, SteamID};
|
use crate::protocols::valve::{App, ModData, SteamID, TimeoutSettings};
|
||||||
use crate::protocols::valve::types::{Environment, ExtraData, GatheringSettings, Request, Response, Server, ServerInfo, ServerPlayer, ServerRule, TheShip};
|
use crate::protocols::valve::types::{Environment, ExtraData, GatheringSettings, Request, Response, Server, ServerInfo, ServerPlayer, ServerRule, TheShip};
|
||||||
use crate::utils::{buffer, complete_address, u8_lower_upper};
|
use crate::utils::{buffer, complete_address, u8_lower_upper};
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ impl SplitPacket {
|
||||||
App::Source(_) => {
|
App::Source(_) => {
|
||||||
let total = buffer::get_u8(&buf, &mut pos)?;
|
let total = buffer::get_u8(&buf, &mut pos)?;
|
||||||
let number = buffer::get_u8(&buf, &mut pos)?;
|
let number = buffer::get_u8(&buf, &mut pos)?;
|
||||||
let size = match protocol == 7 && (*app == SteamID::CSS.app()) { //certain apps with protocol = 7 doesnt have this field
|
let size = match protocol == 7 && (*app == SteamID::CSS.as_app()) { //certain apps with protocol = 7 doesnt have this field
|
||||||
false => buffer::get_u16_le(&buf, &mut pos)?,
|
false => buffer::get_u16_le(&buf, &mut pos)?,
|
||||||
true => 1248
|
true => 1248
|
||||||
};
|
};
|
||||||
|
|
@ -143,12 +143,17 @@ struct ValveProtocol {
|
||||||
complete_address: String
|
complete_address: String
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_PACKET_SIZE: usize = 1400;
|
static PACKET_SIZE: usize = 1400;
|
||||||
|
|
||||||
impl ValveProtocol {
|
impl ValveProtocol {
|
||||||
fn new(address: &str, port: u16) -> GDResult<Self> {
|
fn new(address: &str, port: u16, timeout_settings: TimeoutSettings) -> GDResult<Self> {
|
||||||
|
let socket = UdpSocket::bind("0.0.0.0:0").map_err(|e| GDError::SocketBind(e.to_string()))?;
|
||||||
|
|
||||||
|
socket.set_read_timeout(timeout_settings.get_read()).unwrap(); //unwrapping because TimeoutSettings::new
|
||||||
|
socket.set_write_timeout(timeout_settings.get_write()).unwrap();//checks if these are 0 and throws an error
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
socket: UdpSocket::bind("0.0.0.0:0").unwrap(),
|
socket,
|
||||||
complete_address: complete_address(address, port)?
|
complete_address: complete_address(address, port)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +198,7 @@ impl ValveProtocol {
|
||||||
let request_initial_packet = Packet::initial(kind.clone()).to_bytes();
|
let request_initial_packet = Packet::initial(kind.clone()).to_bytes();
|
||||||
|
|
||||||
self.send(&request_initial_packet)?;
|
self.send(&request_initial_packet)?;
|
||||||
let packet = self.receive(app, protocol, DEFAULT_PACKET_SIZE)?;
|
let packet = self.receive(app, protocol, PACKET_SIZE)?;
|
||||||
|
|
||||||
if packet.kind != 0x41 { //'A'
|
if packet.kind != 0x41 { //'A'
|
||||||
return Ok(packet.payload.clone());
|
return Ok(packet.payload.clone());
|
||||||
|
|
@ -203,7 +208,7 @@ impl ValveProtocol {
|
||||||
let challenge_packet = Packet::challenge(kind.clone(), challenge).to_bytes();
|
let challenge_packet = Packet::challenge(kind.clone(), challenge).to_bytes();
|
||||||
|
|
||||||
self.send(&challenge_packet)?;
|
self.send(&challenge_packet)?;
|
||||||
Ok(self.receive(app, protocol, DEFAULT_PACKET_SIZE)?.payload)
|
Ok(self.receive(app, protocol, PACKET_SIZE)?.payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_goldsrc_server_info(buf: &[u8]) -> GDResult<ServerInfo> {
|
fn get_goldsrc_server_info(buf: &[u8]) -> GDResult<ServerInfo> {
|
||||||
|
|
@ -301,7 +306,7 @@ impl ValveProtocol {
|
||||||
};
|
};
|
||||||
let has_password = buffer::get_u8(&buf, &mut pos)? == 1;
|
let has_password = buffer::get_u8(&buf, &mut pos)? == 1;
|
||||||
let vac_secured = buffer::get_u8(&buf, &mut pos)? == 1;
|
let vac_secured = buffer::get_u8(&buf, &mut pos)? == 1;
|
||||||
let the_ship = match *app == SteamID::TS.app() {
|
let the_ship = match *app == SteamID::TS.as_app() {
|
||||||
false => None,
|
false => None,
|
||||||
true => Some(TheShip {
|
true => Some(TheShip {
|
||||||
mode: buffer::get_u8(&buf, &mut pos)?,
|
mode: buffer::get_u8(&buf, &mut pos)?,
|
||||||
|
|
@ -381,11 +386,11 @@ impl ValveProtocol {
|
||||||
name: buffer::get_string(&buf, &mut pos)?,
|
name: buffer::get_string(&buf, &mut pos)?,
|
||||||
score: buffer::get_u32_le(&buf, &mut pos)?,
|
score: buffer::get_u32_le(&buf, &mut pos)?,
|
||||||
duration: buffer::get_f32_le(&buf, &mut pos)?,
|
duration: buffer::get_f32_le(&buf, &mut pos)?,
|
||||||
deaths: match *app == SteamID::TS.app() {
|
deaths: match *app == SteamID::TS.as_app() {
|
||||||
false => None,
|
false => None,
|
||||||
true => Some(buffer::get_u32_le(&buf, &mut pos)?)
|
true => Some(buffer::get_u32_le(&buf, &mut pos)?)
|
||||||
},
|
},
|
||||||
money: match *app == SteamID::TS.app() {
|
money: match *app == SteamID::TS.as_app() {
|
||||||
false => None,
|
false => None,
|
||||||
true => Some(buffer::get_u32_le(&buf, &mut pos)?)
|
true => Some(buffer::get_u32_le(&buf, &mut pos)?)
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +402,7 @@ impl ValveProtocol {
|
||||||
|
|
||||||
/// Get the server rules's.
|
/// Get the server rules's.
|
||||||
fn get_server_rules(&self, app: &App, protocol: u8) -> GDResult<Option<Vec<ServerRule>>> {
|
fn get_server_rules(&self, app: &App, protocol: u8) -> GDResult<Option<Vec<ServerRule>>> {
|
||||||
if *app == SteamID::CSGO.app() { //cause csgo wont respond to this since feb 21 2014 update
|
if *app == SteamID::CSGO.as_app() { //cause csgo wont respond to this since feb 21 2014 update
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -418,10 +423,16 @@ impl ValveProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query a server by providing the address, the port, the app and the gather settings, the settings
|
/// Query a server by providing the address, the port, the app, gather and timeout settings.
|
||||||
/// being *None* means to also get the players and the rules.
|
/// Providing None to the settings results in using the default values for them (GatherSettings::[default](GatheringSettings::default), TimeoutSettings::[default](TimeoutSettings::default)).
|
||||||
pub fn query(address: &str, port: u16, app: App, gather_settings: Option<GatheringSettings>) -> GDResult<Response> {
|
pub fn query(address: &str, port: u16, app: App, gather_settings: Option<GatheringSettings>, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||||
let client = ValveProtocol::new(address, port)?;
|
let response_gather_settings = gather_settings.unwrap_or(GatheringSettings::default());
|
||||||
|
let response_timeout_settings = timeout_settings.unwrap_or(TimeoutSettings::default());
|
||||||
|
get_response(address, port, app, response_gather_settings, response_timeout_settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_response(address: &str, port: u16, app: App, gather_settings: GatheringSettings, timeout_settings: TimeoutSettings) -> GDResult<Response> {
|
||||||
|
let client = ValveProtocol::new(address, port, timeout_settings)?;
|
||||||
|
|
||||||
let info = client.get_server_info(&app)?;
|
let info = client.get_server_info(&app)?;
|
||||||
let protocol = info.protocol;
|
let protocol = info.protocol;
|
||||||
|
|
@ -434,21 +445,13 @@ pub fn query(address: &str, port: u16, app: App, gather_settings: Option<Gatheri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (gather_players, gather_rules) = match gather_settings.is_some() {
|
|
||||||
false => (true, true),
|
|
||||||
true => {
|
|
||||||
let settings = gather_settings.unwrap();
|
|
||||||
(settings.players, settings.rules)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Response {
|
Ok(Response {
|
||||||
info,
|
info,
|
||||||
players: match gather_players {
|
players: match gather_settings.players {
|
||||||
false => None,
|
false => None,
|
||||||
true => Some(client.get_server_players(&app, protocol)?)
|
true => Some(client.get_server_players(&app, protocol)?)
|
||||||
},
|
},
|
||||||
rules: match gather_rules {
|
rules: match gather_settings.rules {
|
||||||
false => None,
|
false => None,
|
||||||
true => client.get_server_rules(&app, protocol)?
|
true => client.get_server_rules(&app, protocol)?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
use crate::{GDError, GDResult};
|
||||||
|
|
||||||
/// The type of the server.
|
/// The type of the server.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -111,6 +113,7 @@ pub struct ExtraData {
|
||||||
pub game_id: Option<u64>
|
pub game_id: Option<u64>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data related to GoldSrc Mod response.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ModData {
|
pub struct ModData {
|
||||||
pub link: String,
|
pub link: String,
|
||||||
|
|
@ -141,6 +144,7 @@ pub enum Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supported steam apps id's
|
/// Supported steam apps id's
|
||||||
|
#[repr(u32)]
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
pub enum SteamID {
|
pub enum SteamID {
|
||||||
/// Day of Defeat
|
/// Day of Defeat
|
||||||
|
|
@ -178,10 +182,11 @@ pub enum SteamID {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SteamID {
|
impl SteamID {
|
||||||
pub fn app(self) -> App {
|
/// Get ID as App (the engine is specified).
|
||||||
|
pub fn as_app(&self) -> App {
|
||||||
match self {
|
match self {
|
||||||
SteamID::CSCZ | SteamID::DOD => App::GoldSrc(false),
|
SteamID::CSCZ | SteamID::DOD => App::GoldSrc(false),
|
||||||
x => App::Source(Some(x as u32))
|
x => App::Source(Some(x.clone() as u32))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +209,64 @@ pub struct GatheringSettings {
|
||||||
pub rules: bool
|
pub rules: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for GatheringSettings {
|
||||||
|
/// Default values are true for both the players and the rules.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
players: true,
|
||||||
|
rules: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Timeout settings for socket operations
|
||||||
|
pub struct TimeoutSettings {
|
||||||
|
read: Option<Duration>,
|
||||||
|
write: Option<Duration>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeoutSettings {
|
||||||
|
/// Construct new settings, passing None will block indefinitely. Passing zero Duration throws GDError::[InvalidInput](GDError::InvalidInput).
|
||||||
|
pub fn new(read: Option<Duration>, write: Option<Duration>) -> GDResult<Self> {
|
||||||
|
if let Some(read_duration) = read {
|
||||||
|
if read_duration == Duration::new(0, 0) {
|
||||||
|
return Err(GDError::InvalidInput("Can't pass duration 0 to timeout settings".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(write_duration) = write {
|
||||||
|
if write_duration == Duration::new(0, 0) {
|
||||||
|
return Err(GDError::InvalidInput("Can't pass duration 0 to timeout settings".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
read,
|
||||||
|
write
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the read timeout.
|
||||||
|
pub fn get_read(&self) -> Option<Duration> {
|
||||||
|
self.read
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the write timeout.
|
||||||
|
pub fn get_write(&self) -> Option<Duration> {
|
||||||
|
self.write
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TimeoutSettings {
|
||||||
|
/// Default values are 4 seconds for both read and write.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
read: Some(Duration::from_secs(4)),
|
||||||
|
write: Some(Duration::from_secs(4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generic response types that are used by many games, they are the protocol ones, but without the
|
/// Generic response types that are used by many games, they are the protocol ones, but without the
|
||||||
/// unnecessary bits (example: the **The Ship**-only fields)
|
/// unnecessary bits (example: the **The Ship**-only fields)
|
||||||
pub mod game {
|
pub mod game {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue