use a2s::A2SClient;
use crate::HLQueryError::{IOError,A2SError};
+macro_rules! write_datapoint {
+ ($formatter:expr,$pad:expr,$caption:expr,$var:expr) => {
+ writeln!($formatter, "{:<pad$} {}", $caption, $var, pad = $pad)
+ }
+}
+
+macro_rules! implement_displaydebug {
+ ($foreigntype:path,$localtype:ident) => {
+ struct $localtype<'a>(&'a $foreigntype);
+
+ impl Display for $localtype<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self.0)
+ }
+ }
+ }
+}
+
+implement_displaydebug!(a2s::info::ServerOS, ServerOS);
+implement_displaydebug!(a2s::info::ServerType, ServerType);
+implement_displaydebug!(Option<a2s::info::TheShip>, TheShip);
+implement_displaydebug!(Option<a2s::info::SourceTVInfo>, SourceTVInfo);
+implement_displaydebug!(Option<a2s::players::TheShipPlayer>, TheShipPlayer);
+
#[derive(Parser)]
#[command(name = "HLQuery")]
#[command(author = "MegaBrutal")]
#[arg(short, long)]
json: bool,
+ /// Print output in Rust debug format
+ #[arg(short, long)]
+ rust: bool,
+
/// Pretty-print JSON or Rust objects
#[arg(short, long)]
pretty: bool,
addresses: Vec<String>
}
-
#[derive(Debug, Serialize)]
struct HLQueryResult {
address: SocketAddrV4,
}
}
+const PAD_WIDTH: usize = 25;
+
+impl Display for HLQueryResult {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_datapoint!(f, PAD_WIDTH, "Address:", self.address)?;
+ match &self.info {
+ Ok(info) => {
+ writeln!(f, "\nServer info:")?;
+ write_datapoint!(f, PAD_WIDTH, "Server name:", info.name)?;
+ write_datapoint!(f, PAD_WIDTH, "Game:", info.game)?;
+ write_datapoint!(f, PAD_WIDTH, "Folder:", info.folder)?;
+ write_datapoint!(f, PAD_WIDTH, "Current map:", info.map)?;
+ write_datapoint!(f, PAD_WIDTH, "Protocol version:", info.protocol)?;
+ write_datapoint!(f, PAD_WIDTH, "Steam App ID:", info.app_id)?;
+ write_datapoint!(f, PAD_WIDTH, "Number of players:", format!("{}/{}", info.players, info.max_players))?;
+ write_datapoint!(f, PAD_WIDTH, "Number of bots:", info.bots)?;
+ write_datapoint!(f, PAD_WIDTH, "Server type:", ServerType(&info.server_type))?;
+ write_datapoint!(f, PAD_WIDTH, "Server OS:", ServerOS(&info.server_os))?;
+ write_datapoint!(f, PAD_WIDTH, "Password protected:", if info.visibility { "Private" } else { "Public" })?;
+ write_datapoint!(f, PAD_WIDTH, "VAC enabled:", info.vac)?;
+ write_datapoint!(f, PAD_WIDTH, "The Ship mode:", TheShip(&info.the_ship))?;
+ write_datapoint!(f, PAD_WIDTH, "Server version:", info.version)?;
+ write_datapoint!(f, PAD_WIDTH, "Extra Data Flag:", info.edf)?;
+ write_datapoint!(f, PAD_WIDTH, "Server port:", info.extended_server_info.port.map_or("Unknown".to_string(), |i| i.to_string()))?;
+ write_datapoint!(f, PAD_WIDTH, "Steam ID:", info.extended_server_info.steam_id.map_or("Unknown".to_string(), |i| i.to_string()))?;
+ write_datapoint!(f, PAD_WIDTH, "Keywords:", info.extended_server_info.keywords.clone().unwrap_or("Unknown".to_string()))?;
+ write_datapoint!(f, PAD_WIDTH, "Game ID:", info.extended_server_info.game_id.map_or("Unknown".to_string(), |i| i.to_string()))?;
+ write_datapoint!(f, PAD_WIDTH, "Source TV port:", SourceTVInfo(&info.source_tv))?;
+ },
+ Err(e) =>
+ writeln!(f, "Failed to query server info:\t{}", e)?
+ }
+
+ match &self.rules {
+ Ok(rules) => {
+ writeln!(f, "\nGame rules (CVARs):")?;
+ for rule in rules {
+ write_datapoint!(f, PAD_WIDTH, rule.name, format!("\"{}\"", rule.value))?;
+ }
+ }
+ Err(e) =>
+ writeln!(f, "Failed to query game rules:\t{}", e)?
+ }
+
+ match &self.players {
+ Ok(players) => {
+ writeln!(f, "\nPlayers:")?;
+ writeln!(f, "\t{:>3} players online.", players.len())?;
+ for player in players {
+ writeln!(f, "{}: {:<30} {:>4} {:>10} {}",
+ player.index,
+ player.name,
+ player.score,
+ player.duration,
+ TheShipPlayer(&player.the_ship))?;
+ }
+ Ok(())
+ }
+ Err(e) =>
+ writeln!(f, "Failed to query players:\t{}", e)
+ }
+ }
+}
+
#[derive(Debug, Serialize)]
struct HLQuery {
input: String,
}
}
+impl Display for HLQuery {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_datapoint!(f, PAD_WIDTH, "Input:", self.input)?;
+ match &self.result {
+ Ok(v) => {
+ write_datapoint!(f, PAD_WIDTH, "Number of addresses:", format!("{}\n", v.len()))?;
+ for r in v {
+ writeln!(f, "{r}\n")?;
+ };
+ Ok(())
+ },
+ Err(e) =>
+ writeln!(f, "Failed to execute query:\t{e}\n")
+ }
+ }
+}
+
#[derive(Debug)]
enum HLQueryError {
IOError(std::io::Error),
println!("{}", serde_json::to_string(&query_results).unwrap());
}
}
- else {
+ else if cli.rust {
if cli.pretty {
println!("{:#?}", query_results);
}
println!("{:?}", query_results);
}
}
+ else {
+ for query in query_results {
+ println!("{}", query);
+ }
+ }
}