mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
[Games] Programmatic games by storing information as data (#45)
* Define games as structs * Create table of response types * Ensure serde is always included * Remove server_ prefix in GenericResponse * Make players online/max non-optional in generic response * Use already existing minecraft server enum * Implement ExtraResponses to prevent cloning when creating generic * Add game definitions * Add doc comments to generic types * Include players in gamespy extra responses * Add custom response types for TheShip and FFOW * Cargo format differing files * Final cleanup
This commit is contained in:
parent
26ad1f5d19
commit
d853189e06
16 changed files with 806 additions and 102 deletions
|
|
@ -17,9 +17,10 @@ keywords = ["server", "query", "game", "check", "status"]
|
||||||
rust-version = "1.60.0"
|
rust-version = "1.60.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["game_defs"]
|
||||||
no_games = []
|
no_games = []
|
||||||
no_services = []
|
no_services = []
|
||||||
|
game_defs = ["dep:phf"]
|
||||||
serde = ["dep:serde", "serde/derive"]
|
serde = ["dep:serde", "serde/derive"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -29,3 +30,6 @@ crc32fast = "1.3.2"
|
||||||
serde_json = "1.0.91"
|
serde_json = "1.0.91"
|
||||||
|
|
||||||
serde = { version = "1.0.155", optional = true }
|
serde = { version = "1.0.155", optional = true }
|
||||||
|
|
||||||
|
phf = { version = "0.11", optional = true, features = ["macros"] }
|
||||||
|
|
||||||
|
|
|
||||||
54
RESPONSES.md
Normal file
54
RESPONSES.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
Every protocol has its own response type(s), below is a listing of the overlapping fields on these responses.
|
||||||
|
|
||||||
|
If a cell is blank it doesn't exist, otherwise it contains the type of that data in the current column's response type.
|
||||||
|
In the case that a field that performs the same function exists in the current column's response type that name is annotated in brackets.
|
||||||
|
|
||||||
|
# Response table
|
||||||
|
|
||||||
|
| Field | Generic | GameSpy(1) | GameSpy(3) | Minecraft(Java) | Minecraft(Bedrock) | Valve | Quake | FFOW | TheShip |
|
||||||
|
| :--------------- | -------------- | ---------- | ---------- | --------------- | ------------------ | ------ | ------ | ------ | ------- |
|
||||||
|
| name | Option<String> | String | String | | String | String | String | String | String |
|
||||||
|
| description | Option<String> | | | String | | | | String | |
|
||||||
|
| game | Option<String> | String (game_type) | String (game_type) | | Option<GameMode> (game_mode) | String | | String (game_mode) | String |
|
||||||
|
| game_version | Option<String> | String | String | String (version_name) | | String (version) | String (version) | String (version) | String (version) |
|
||||||
|
| map | Option<String> | String | String | | Option<String> | String | String | String | String |
|
||||||
|
| players_maxmimum | u64 | usize | usize | u32 | u32 | u8 | u8 | u8 | u8 (max_players) |
|
||||||
|
| players_online | u64 | usize | usize | u32 | u32 | u8 | u8 | u8 | u8 (players) |
|
||||||
|
| players_bots | Option<u64> | | | | | u8 | | | u8 (bots) |
|
||||||
|
| has_password | Option<bool> | bool | bool | | | bool | | bool | bool |
|
||||||
|
| map_title | | Option<String> | | | | | | | |
|
||||||
|
| admin_contact | | Option<String> | | | | | | | |
|
||||||
|
| admin_name | | Option<String> | | | | | | | |
|
||||||
|
| players_minimum | | Option<u8> | Option<u8> | | | | | | |
|
||||||
|
| players | | Vec<Player> | Vec<Player> | | | Option<Vec<ServerPlayer>> | Vec<P> | | Vec<TheShipPlayer> (player_details) |
|
||||||
|
| tournament | | bool | bool | | | | | | |
|
||||||
|
| unused_entries | | Hashmap<String, String> | HashMap<String, String> | | | Option<ExtraData> (extra_data) | HashMap<String, String> | | |
|
||||||
|
| teams | | | Vec<Team> | | | | | | |
|
||||||
|
| version_protocol | | | | i32 | String | u8 (protocol) | | u8 (protocol) | u8 (protocol) |
|
||||||
|
| players_sample | | | | Option<Vec<Player>> | | | | | |
|
||||||
|
| favicon | | | | Option<String> | | | | | |
|
||||||
|
| previews_chat | | | | Option<bool> | | | | | |
|
||||||
|
| enforces_secure_chat | | | | Option<bool> | | | | | |
|
||||||
|
| server_type | | | | Server | Server | Server | | | Server |
|
||||||
|
| edition | | | | | String | | | | |
|
||||||
|
| id | | | | | String | | | | |
|
||||||
|
| rules | | | | | | Option<HashMap<String,String>> | | | HashMap<String,String> |
|
||||||
|
| folder | | | | | | String | | | |
|
||||||
|
| appid | | | | | | u32 | | | |
|
||||||
|
| environment_type | | | | | | Environment | | Environment | |
|
||||||
|
| vac_secured | | | | | | bool | | bool | bool |
|
||||||
|
| the_ship | | | | | | Option<TheShip> | | | |
|
||||||
|
| is_mod | | | | | | bool | | | |
|
||||||
|
| mod_data | | | | | | Option<ModData> | | | |
|
||||||
|
| active_mod | | | | | | | | String | |
|
||||||
|
| round | | | | | | | | u8 | |
|
||||||
|
| rounds_maximum | | | | | | | | u8 | |
|
||||||
|
| time_left | | | | | | | | u16 | |
|
||||||
|
| port | | | | | | | | | Option<u16> |
|
||||||
|
| steam_id | | | | | | | | | Option<u64> |
|
||||||
|
| tv_port | | | | | | | | | Option<u16> |
|
||||||
|
| tv_name | | | | | | | | | Option<String> |
|
||||||
|
| keywords | | | | | | | | | Option<string> |
|
||||||
|
| mode | | | | | | | | | u8 |
|
||||||
|
| witnesses | | | | | | | | | u8 |
|
||||||
|
| duration | | | | | | | | | u8 |
|
||||||
61
examples/generic.rs
Normal file
61
examples/generic.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use gamedig::{protocols::GenericResponse, query, GDResult, GAMES};
|
||||||
|
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
fn generic_query(game_name: &str, addr: &IpAddr, port: Option<u16>) -> GDResult<GenericResponse> {
|
||||||
|
let game = GAMES.get(game_name).expect("Game doesn't exist");
|
||||||
|
|
||||||
|
println!("Querying {:?} with {:?}", addr, game);
|
||||||
|
|
||||||
|
let response = query(game, addr, port)?;
|
||||||
|
|
||||||
|
println!("{:?}", response);
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = std::env::args().skip(1);
|
||||||
|
|
||||||
|
let game_name = args.next().expect("Must provide a game name");
|
||||||
|
let addr: IpAddr = args
|
||||||
|
.next()
|
||||||
|
.map(|s| s.parse().unwrap())
|
||||||
|
.expect("Must provide address");
|
||||||
|
let port: Option<u16> = args.next().map(|s| s.parse().unwrap());
|
||||||
|
|
||||||
|
generic_query(&game_name, &addr, port).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use gamedig::GAMES;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
use super::generic_query;
|
||||||
|
|
||||||
|
const ADDR: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
|
||||||
|
|
||||||
|
fn test_game(game_name: &str) {
|
||||||
|
assert!(generic_query(game_name, &ADDR, None).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn battlefield() { test_game("bf1942"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minecraft() { test_game("mc"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tf2() { test_game("tf2"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quake() { test_game("quake3a"); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all_games() {
|
||||||
|
for game_name in GAMES.keys() {
|
||||||
|
test_game(game_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/games/definitions.rs
Normal file
83
src/games/definitions.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
//! Static definitions of currently supported games
|
||||||
|
|
||||||
|
use crate::protocols::{
|
||||||
|
gamespy::GameSpyVersion,
|
||||||
|
minecraft::{LegacyGroup, Server},
|
||||||
|
quake::QuakeVersion,
|
||||||
|
valve::SteamApp,
|
||||||
|
Protocol,
|
||||||
|
};
|
||||||
|
use crate::Game;
|
||||||
|
|
||||||
|
use phf::{phf_map, Map};
|
||||||
|
|
||||||
|
macro_rules! game {
|
||||||
|
($name: literal, $default_port: literal, $protocol: expr) => {
|
||||||
|
Game {
|
||||||
|
name: $name,
|
||||||
|
default_port: $default_port,
|
||||||
|
protocol: $protocol,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map of all currently supported games
|
||||||
|
pub static GAMES: Map<&'static str, Game> = phf_map! {
|
||||||
|
"mc" => game!("Minecraft", 25565, Protocol::Minecraft(None)),
|
||||||
|
"mc-java" => game!("Minecraft (java)", 25565, Protocol::Minecraft(Some(Server::Java))),
|
||||||
|
"mc-bedrock" => game!("Minecraft (bedrock)", 19132, Protocol::Minecraft(Some(Server::Bedrock))),
|
||||||
|
"mc-legacy-1.6" => game!("Minecraft (legacy v1.6)", 25565, Protocol::Minecraft(Some(Server::Legacy(LegacyGroup::V1_6)))),
|
||||||
|
"mc-legacy-1.4" => game!("Minecraft (legacy v1.4-1.5)", 25565, Protocol::Minecraft(Some(Server::Legacy(LegacyGroup::V1_4)))),
|
||||||
|
"mc-legacy-b1.8" => game!("Minecraft (legacy vB1.8-1.3)", 25565, Protocol::Minecraft(Some(Server::Legacy(LegacyGroup::VB1_8)))),
|
||||||
|
"aliens" => game!("Alien Swarm", 27015, Protocol::Valve(SteamApp::ALIENS)),
|
||||||
|
"aoc" => game!("Age of Chivalry", 27015, Protocol::Valve(SteamApp::AOC)),
|
||||||
|
"arma2oa" => game!("ARMA 2: Operation Arrowhead", 2304, Protocol::Valve(SteamApp::ARMA2OA)),
|
||||||
|
"ase" => game!("ARK: Survival Evolved", 27015, Protocol::Valve(SteamApp::ASE)),
|
||||||
|
"asrd" => game!("Alien Swarm: Reactive Drop", 2304, Protocol::Valve(SteamApp::ASRD)),
|
||||||
|
"avorion" => game!("Avorion", 27020, Protocol::Valve(SteamApp::AVORION)),
|
||||||
|
"bat1944" => game!("Battalion 1944", 7780, Protocol::Valve(SteamApp::BAT1944)),
|
||||||
|
"bb2" => game!("BrainBread 2", 27015, Protocol::Valve(SteamApp::BB2)),
|
||||||
|
"bf1942" => game!("Battlefield 1942", 23000, Protocol::Gamespy(GameSpyVersion::One)),
|
||||||
|
"bm" => game!("Black Mesa", 27015, Protocol::Valve(SteamApp::BM)),
|
||||||
|
"bo" => game!("Ballistic Overkill", 27016, Protocol::Valve(SteamApp::BO)),
|
||||||
|
"ccure" => game!("Codename CURE", 27015, Protocol::Valve(SteamApp::CCURE)),
|
||||||
|
"cosu" => game!("Colony Survival", 27004, Protocol::Valve(SteamApp::COSU)),
|
||||||
|
"cs" => game!("Counter-Strike", 27015, Protocol::Valve(SteamApp::CS)),
|
||||||
|
"cscz" => game!("Counter Strike: Condition Zero", 27015, Protocol::Valve(SteamApp::CSCZ)),
|
||||||
|
"csgo" => game!("Counter-Strike: Global Offensive", 27015, Protocol::Valve(SteamApp::CSGO)),
|
||||||
|
"css" => game!("Counter-Strike: Source", 27015, Protocol::Valve(SteamApp::CSS)),
|
||||||
|
"cw" => game!("Crysis Wars", 64100, Protocol::Gamespy(GameSpyVersion::Three)),
|
||||||
|
"dod" => game!("Day of Defeat", 27015, Protocol::Valve(SteamApp::DOD)),
|
||||||
|
"dods" => game!("Day of Defeat: Source", 27015, Protocol::Valve(SteamApp::DODS)),
|
||||||
|
"doi" => game!("Day of Infamy", 27015, Protocol::Valve(SteamApp::DOI)),
|
||||||
|
"dst" => game!("Don't Starve Together", 27016, Protocol::Valve(SteamApp::DST)),
|
||||||
|
"ffow" => game!("Frontlines: Fuel of War", 5478, Protocol::FFOW),
|
||||||
|
"gm" => game!("Garry's Mod", 27016, Protocol::Valve(SteamApp::GM)),
|
||||||
|
"hl2dm" => game!("Half-Life 2 Deathmatch", 27015, Protocol::Valve(SteamApp::HL2DM)),
|
||||||
|
"hldms" => game!("Half-Life Deathmatch: Source", 27015, Protocol::Valve(SteamApp::HLDMS)),
|
||||||
|
"hll" => game!("Hell Let Loose", 26420, Protocol::Valve(SteamApp::HLL)),
|
||||||
|
"ins" => game!("Insurgency", 27015, Protocol::Valve(SteamApp::INS)),
|
||||||
|
"insmic" => game!("Insurgency: Modern Infantry Combat", 27015, Protocol::Valve(SteamApp::INSMIC)),
|
||||||
|
"inss" => game!("Insurgency: Sandstorm", 27131, Protocol::Valve(SteamApp::INSS)),
|
||||||
|
"l4d" => game!("Left 4 Dead", 27015, Protocol::Valve(SteamApp::L4D)),
|
||||||
|
"l4d2" => game!("Left 4 Dead 2", 27015, Protocol::Valve(SteamApp::L4D2)),
|
||||||
|
"ohd" => game!("Operation: Harsh Doorstop", 27005, Protocol::Valve(SteamApp::OHD)),
|
||||||
|
"onset" => game!("Onset", 7776, Protocol::Valve(SteamApp::ONSET)),
|
||||||
|
"pz" => game!("Project Zomboid", 16261, Protocol::Valve(SteamApp::PZ)),
|
||||||
|
"quake1" => game!("Quake 1", 27500, Protocol::Quake(QuakeVersion::One)),
|
||||||
|
"quake2" => game!("Quake 2", 27910, Protocol::Quake(QuakeVersion::Two)),
|
||||||
|
"quake3a" => game!("Quake 3: Arena", 27960, Protocol::Quake(QuakeVersion::Three)),
|
||||||
|
"ror2" => game!("Risk of Rain 2", 27016, Protocol::Valve(SteamApp::ROR2)),
|
||||||
|
"rust" => game!("Rust", 27015, Protocol::Valve(SteamApp::RUST)),
|
||||||
|
"sc" => game!("Sven Co-op", 27015, Protocol::Valve(SteamApp::SC)),
|
||||||
|
"sdtd" => game!("7 Days To Die", 26900, Protocol::Valve(SteamApp::SDTD)),
|
||||||
|
"sof2" => game!("Soldier of Fortune 2", 20100, Protocol::Quake(QuakeVersion::Three)),
|
||||||
|
"ss" => game!("Serious Sam", 25601, Protocol::Gamespy(GameSpyVersion::One)),
|
||||||
|
"tf" => game!("The Forest", 27016, Protocol::Valve(SteamApp::TF)),
|
||||||
|
"tf2" => game!("Team Fortress 2", 27015, Protocol::Valve(SteamApp::TF2)),
|
||||||
|
"tfc" => game!("Team Fortress Classic", 27015, Protocol::Valve(SteamApp::TFC)),
|
||||||
|
"ts" => game!("The Ship", 27015, Protocol::TheShip),
|
||||||
|
"unturned" => game!("Unturned", 27015, Protocol::Valve(SteamApp::UNTURNED)),
|
||||||
|
"ut" => game!("Unreal Tournament", 7778, Protocol::Gamespy(GameSpyVersion::One)),
|
||||||
|
"vr" => game!("V Rising", 27016, Protocol::Valve(SteamApp::VR)),
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::protocols::types::TimeoutSettings;
|
use crate::protocols::types::{SpecificResponse, TimeoutSettings};
|
||||||
use crate::protocols::valve::{Engine, Environment, Server, ValveProtocol};
|
use crate::protocols::valve::{Engine, Environment, Server, ValveProtocol};
|
||||||
|
use crate::protocols::GenericResponse;
|
||||||
use crate::GDResult;
|
use crate::GDResult;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -43,6 +44,53 @@ pub struct Response {
|
||||||
pub time_left: u16,
|
pub time_left: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
/// Protocol used by the server.
|
||||||
|
pub protocol: u8,
|
||||||
|
/// Map name.
|
||||||
|
pub active_mod: String,
|
||||||
|
/// Dedicated, NonDedicated or SourceTV
|
||||||
|
pub server_type: Server,
|
||||||
|
/// The Operating System that the server is on.
|
||||||
|
pub environment_type: Environment,
|
||||||
|
/// Indicates whether the server uses VAC.
|
||||||
|
pub vac_secured: bool,
|
||||||
|
/// Current round index.
|
||||||
|
pub round: u8,
|
||||||
|
/// Maximum amount of rounds.
|
||||||
|
pub rounds_maximum: u8,
|
||||||
|
/// Time left for the current round in seconds.
|
||||||
|
pub time_left: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for GenericResponse {
|
||||||
|
fn from(r: Response) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: Some(r.description),
|
||||||
|
game: Some(r.game_mode),
|
||||||
|
game_version: Some(r.version),
|
||||||
|
map: Some(r.map),
|
||||||
|
players_maximum: r.players_maximum.into(),
|
||||||
|
players_online: r.players_online.into(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: Some(r.has_password),
|
||||||
|
inner: SpecificResponse::FFOW(ExtraResponse {
|
||||||
|
protocol: r.protocol,
|
||||||
|
active_mod: r.active_mod,
|
||||||
|
server_type: r.server_type,
|
||||||
|
environment_type: r.environment_type,
|
||||||
|
vac_secured: r.vac_secured,
|
||||||
|
round: r.round,
|
||||||
|
rounds_maximum: r.rounds_maximum,
|
||||||
|
time_left: r.time_left,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<Response> {
|
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<Response> {
|
||||||
query_with_timeout(address, port, TimeoutSettings::default())
|
query_with_timeout(address, port, TimeoutSettings::default())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
//! Currently supported games.
|
//! Currently supported games.
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Alien Swarm
|
/// Alien Swarm
|
||||||
pub mod aliens;
|
pub mod aliens;
|
||||||
/// Age of Chivalry
|
/// Age of Chivalry
|
||||||
|
|
@ -106,3 +109,61 @@ pub mod unturned;
|
||||||
pub mod ut;
|
pub mod ut;
|
||||||
/// V Rising
|
/// V Rising
|
||||||
pub mod vr;
|
pub mod vr;
|
||||||
|
|
||||||
|
use crate::protocols::gamespy::GameSpyVersion;
|
||||||
|
use crate::protocols::quake::QuakeVersion;
|
||||||
|
use crate::protocols::{self, Protocol};
|
||||||
|
use crate::GDResult;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Game {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub default_port: u16,
|
||||||
|
pub protocol: Protocol,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "game_defs")]
|
||||||
|
mod definitions;
|
||||||
|
|
||||||
|
#[cfg(feature = "game_defs")]
|
||||||
|
pub use definitions::GAMES;
|
||||||
|
|
||||||
|
pub fn query(game: &Game, address: &IpAddr, port: Option<u16>) -> GDResult<protocols::GenericResponse> {
|
||||||
|
let socket_addr = SocketAddr::new(*address, port.unwrap_or(game.default_port));
|
||||||
|
Ok(match &game.protocol {
|
||||||
|
Protocol::Valve(steam_app) => {
|
||||||
|
protocols::valve::query(&socket_addr, steam_app.as_engine(), None, None).map(|r| r.into())?
|
||||||
|
}
|
||||||
|
Protocol::Minecraft(version) => {
|
||||||
|
match version {
|
||||||
|
Some(protocols::minecraft::Server::Java) => {
|
||||||
|
protocols::minecraft::query_java(&socket_addr, None).map(|r| r.into())?
|
||||||
|
}
|
||||||
|
Some(protocols::minecraft::Server::Bedrock) => {
|
||||||
|
protocols::minecraft::query_bedrock(&socket_addr, None).map(|r| r.into())?
|
||||||
|
}
|
||||||
|
Some(protocols::minecraft::Server::Legacy(group)) => {
|
||||||
|
protocols::minecraft::query_legacy_specific(*group, &socket_addr, None).map(|r| r.into())?
|
||||||
|
}
|
||||||
|
None => protocols::minecraft::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Protocol::Gamespy(version) => {
|
||||||
|
match version {
|
||||||
|
GameSpyVersion::One => protocols::gamespy::one::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
GameSpyVersion::Three => protocols::gamespy::three::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Protocol::Quake(version) => {
|
||||||
|
match version {
|
||||||
|
QuakeVersion::One => protocols::quake::one::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
QuakeVersion::Two => protocols::quake::two::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
QuakeVersion::Three => protocols::quake::three::query(&socket_addr, None).map(|r| r.into())?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Protocol::TheShip => ts::query(address, port).map(|r| r.into())?,
|
||||||
|
Protocol::FFOW => ffow::query(address, port).map(|r| r.into())?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
protocols::valve::{self, get_optional_extracted_data, Server, ServerPlayer, SteamApp},
|
protocols::{
|
||||||
|
types::SpecificResponse,
|
||||||
|
valve::{self, get_optional_extracted_data, Server, ServerPlayer, SteamApp},
|
||||||
|
GenericResponse,
|
||||||
|
},
|
||||||
GDResult,
|
GDResult,
|
||||||
};
|
};
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
@ -57,6 +61,55 @@ pub struct Response {
|
||||||
pub duration: u8,
|
pub duration: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
pub protocol: u8,
|
||||||
|
pub player_details: Vec<TheShipPlayer>,
|
||||||
|
pub server_type: Server,
|
||||||
|
pub vac_secured: bool,
|
||||||
|
pub port: Option<u16>,
|
||||||
|
pub steam_id: Option<u64>,
|
||||||
|
pub tv_port: Option<u16>,
|
||||||
|
pub tv_name: Option<String>,
|
||||||
|
pub keywords: Option<String>,
|
||||||
|
pub rules: HashMap<String, String>,
|
||||||
|
pub mode: u8,
|
||||||
|
pub witnesses: u8,
|
||||||
|
pub duration: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for GenericResponse {
|
||||||
|
fn from(r: Response) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: None,
|
||||||
|
game: Some(r.game),
|
||||||
|
game_version: Some(r.version),
|
||||||
|
map: Some(r.map),
|
||||||
|
players_maximum: r.max_players.into(),
|
||||||
|
players_online: r.players.into(),
|
||||||
|
players_bots: Some(r.bots.into()),
|
||||||
|
has_password: Some(r.has_password),
|
||||||
|
inner: SpecificResponse::TheShip(ExtraResponse {
|
||||||
|
protocol: r.protocol,
|
||||||
|
player_details: r.players_details,
|
||||||
|
server_type: r.server_type,
|
||||||
|
vac_secured: r.vac_secured,
|
||||||
|
steam_id: r.steam_id,
|
||||||
|
port: r.port,
|
||||||
|
tv_port: r.tv_port,
|
||||||
|
tv_name: r.tv_name,
|
||||||
|
keywords: r.keywords,
|
||||||
|
rules: r.rules,
|
||||||
|
mode: r.mode,
|
||||||
|
witnesses: r.witnesses,
|
||||||
|
duration: r.duration,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
pub fn new_from_valve_response(response: valve::Response) -> Self {
|
pub fn new_from_valve_response(response: valve::Response) -> Self {
|
||||||
let (port, steam_id, tv_port, tv_name, keywords) = get_optional_extracted_data(response.info.extra_data);
|
let (port, steam_id, tv_port, tv_name, keywords) = get_optional_extracted_data(response.info.extra_data);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
/// The implementations.
|
/// The implementations.
|
||||||
pub mod protocols;
|
pub mod protocols;
|
||||||
|
|
||||||
pub use protocols::*;
|
pub use protocols::*;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum GameSpyVersion {
|
||||||
|
One,
|
||||||
|
Three,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum VersionedExtraResponse {
|
||||||
|
One(protocols::one::ExtraResponse),
|
||||||
|
Three(protocols::three::ExtraResponse),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ use std::collections::HashMap;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::protocols::gamespy::VersionedExtraResponse;
|
||||||
|
use crate::protocols::{types::SpecificResponse, GenericResponse};
|
||||||
|
|
||||||
/// A player’s details.
|
/// A player’s details.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
@ -39,3 +42,41 @@ pub struct Response {
|
||||||
pub tournament: bool,
|
pub tournament: bool,
|
||||||
pub unused_entries: HashMap<String, String>,
|
pub unused_entries: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-generic query response
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
pub map_title: Option<String>,
|
||||||
|
pub admin_contact: Option<String>,
|
||||||
|
pub admin_name: Option<String>,
|
||||||
|
pub players_minimum: Option<u8>,
|
||||||
|
pub tournament: bool,
|
||||||
|
pub unused_entries: HashMap<String, String>,
|
||||||
|
pub players: Vec<Player>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for GenericResponse {
|
||||||
|
fn from(r: Response) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: None,
|
||||||
|
game: Some(r.game_type),
|
||||||
|
game_version: Some(r.game_version),
|
||||||
|
map: Some(r.map),
|
||||||
|
players_maximum: r.players_maximum.try_into().unwrap(), // FIXME: usize to u64 may fail
|
||||||
|
players_online: r.players_online.try_into().unwrap(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: Some(r.has_password),
|
||||||
|
inner: SpecificResponse::Gamespy(VersionedExtraResponse::One(ExtraResponse {
|
||||||
|
map_title: r.map_title,
|
||||||
|
admin_contact: r.admin_contact,
|
||||||
|
admin_name: r.admin_name,
|
||||||
|
players_minimum: r.players_minimum,
|
||||||
|
tournament: r.tournament,
|
||||||
|
unused_entries: r.unused_entries,
|
||||||
|
players: r.players,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::protocols::gamespy::VersionedExtraResponse;
|
||||||
|
use crate::protocols::{types::SpecificResponse, GenericResponse};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
|
@ -40,3 +42,37 @@ pub struct Response {
|
||||||
pub tournament: bool,
|
pub tournament: bool,
|
||||||
pub unused_entries: HashMap<String, String>,
|
pub unused_entries: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-generic query response
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
pub players_minimum: Option<u8>,
|
||||||
|
pub teams: Vec<Team>,
|
||||||
|
pub tournament: bool,
|
||||||
|
pub unused_entries: HashMap<String, String>,
|
||||||
|
pub players: Vec<Player>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for GenericResponse {
|
||||||
|
fn from(r: Response) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: None,
|
||||||
|
game: Some(r.game_type),
|
||||||
|
game_version: Some(r.game_version),
|
||||||
|
map: Some(r.map),
|
||||||
|
players_maximum: r.players_maximum.try_into().unwrap(), // FIXME: usize to u64 may fail
|
||||||
|
players_online: r.players_online.try_into().unwrap(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: Some(r.has_password),
|
||||||
|
inner: SpecificResponse::Gamespy(VersionedExtraResponse::Three(ExtraResponse {
|
||||||
|
players_minimum: r.players_minimum,
|
||||||
|
teams: r.teams,
|
||||||
|
tournament: r.tournament,
|
||||||
|
unused_entries: r.unused_entries,
|
||||||
|
players: r.players,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bufferer::Bufferer,
|
bufferer::Bufferer,
|
||||||
|
protocols::{types::SpecificResponse, GenericResponse},
|
||||||
GDError::{PacketBad, UnknownEnumCast},
|
GDError::{PacketBad, UnknownEnumCast},
|
||||||
GDResult,
|
GDResult,
|
||||||
};
|
};
|
||||||
|
|
@ -43,6 +44,13 @@ pub struct Player {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub enum VersionedExtraResponse {
|
||||||
|
Bedrock(BedrockExtraResponse),
|
||||||
|
Java(JavaExtraResponse),
|
||||||
|
}
|
||||||
|
|
||||||
/// A Java query response.
|
/// A Java query response.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
@ -70,6 +78,48 @@ pub struct JavaResponse {
|
||||||
pub server_type: Server,
|
pub server_type: Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct JavaExtraResponse {
|
||||||
|
/// Version protocol, example: 760 (for 1.19.2). Note that for versions
|
||||||
|
/// below 1.6 this field is always -1.
|
||||||
|
pub version_protocol: i32,
|
||||||
|
/// Some online players (can be missing).
|
||||||
|
pub players_sample: Option<Vec<Player>>,
|
||||||
|
/// The favicon (can be missing).
|
||||||
|
pub favicon: Option<String>,
|
||||||
|
/// Tells if the chat preview is enabled (can be missing).
|
||||||
|
pub previews_chat: Option<bool>,
|
||||||
|
/// Tells if secure chat is enforced (can be missing).
|
||||||
|
pub enforces_secure_chat: Option<bool>,
|
||||||
|
/// Tell's the server type.
|
||||||
|
pub server_type: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JavaResponse> for GenericResponse {
|
||||||
|
fn from(r: JavaResponse) -> Self {
|
||||||
|
Self {
|
||||||
|
name: None,
|
||||||
|
description: Some(r.description),
|
||||||
|
game: Some(String::from("Minecraft")),
|
||||||
|
game_version: Some(r.version_name),
|
||||||
|
map: None,
|
||||||
|
players_maximum: r.players_maximum.into(),
|
||||||
|
players_online: r.players_online.into(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: None,
|
||||||
|
inner: SpecificResponse::Minecraft(VersionedExtraResponse::Java(JavaExtraResponse {
|
||||||
|
version_protocol: r.version_protocol,
|
||||||
|
players_sample: r.players_sample,
|
||||||
|
favicon: r.favicon,
|
||||||
|
previews_chat: r.previews_chat,
|
||||||
|
enforces_secure_chat: r.enforces_secure_chat,
|
||||||
|
server_type: r.server_type,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A Bedrock Edition query response.
|
/// A Bedrock Edition query response.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
@ -96,6 +146,44 @@ pub struct BedrockResponse {
|
||||||
pub server_type: Server,
|
pub server_type: Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct BedrockExtraResponse {
|
||||||
|
/// Server's edition.
|
||||||
|
pub edition: String,
|
||||||
|
/// Version protocol, example: 760 (for 1.19.2).
|
||||||
|
pub version_protocol: String,
|
||||||
|
/// Server id.
|
||||||
|
pub id: Option<String>,
|
||||||
|
/// Current game mode.
|
||||||
|
pub game_mode: Option<GameMode>,
|
||||||
|
/// Tells the server type.
|
||||||
|
pub server_type: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BedrockResponse> for GenericResponse {
|
||||||
|
fn from(r: BedrockResponse) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: None,
|
||||||
|
game: None,
|
||||||
|
game_version: Some(r.version_name),
|
||||||
|
map: r.map,
|
||||||
|
players_maximum: r.players_maximum.into(),
|
||||||
|
players_online: r.players_online.into(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: None,
|
||||||
|
inner: SpecificResponse::Minecraft(VersionedExtraResponse::Bedrock(BedrockExtraResponse {
|
||||||
|
edition: r.edition,
|
||||||
|
version_protocol: r.version_protocol,
|
||||||
|
id: r.id,
|
||||||
|
game_mode: r.game_mode,
|
||||||
|
server_type: r.server_type,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl JavaResponse {
|
impl JavaResponse {
|
||||||
pub fn from_bedrock_response(response: BedrockResponse) -> Self {
|
pub fn from_bedrock_response(response: BedrockResponse) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,5 @@ pub mod quake;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
/// Reference: [Server Query](https://developer.valvesoftware.com/wiki/Server_queries)
|
/// Reference: [Server Query](https://developer.valvesoftware.com/wiki/Server_queries)
|
||||||
pub mod valve;
|
pub mod valve;
|
||||||
|
|
||||||
|
pub use types::{GenericResponse, Protocol};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod one;
|
pub mod one;
|
||||||
pub mod three;
|
pub mod three;
|
||||||
pub mod two;
|
pub mod two;
|
||||||
|
|
@ -7,3 +10,11 @@ pub mod types;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum QuakeVersion {
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::protocols::{types::SpecificResponse, GenericResponse};
|
||||||
|
|
||||||
/// General server information's.
|
/// General server information's.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -21,3 +23,30 @@ pub struct Response<P> {
|
||||||
/// Other server entries that weren't used.
|
/// Other server entries that weren't used.
|
||||||
pub unused_entries: HashMap<String, String>,
|
pub unused_entries: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
/// Other server entries that weren't used.
|
||||||
|
pub unused_entries: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Response<T>> for GenericResponse {
|
||||||
|
fn from(r: Response<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(r.name),
|
||||||
|
description: None,
|
||||||
|
game: None,
|
||||||
|
game_version: Some(r.version),
|
||||||
|
map: Some(r.map),
|
||||||
|
players_maximum: r.players_maximum.into(),
|
||||||
|
players_online: r.players_online.into(),
|
||||||
|
players_bots: None,
|
||||||
|
has_password: None,
|
||||||
|
inner: SpecificResponse::Quake(ExtraResponse {
|
||||||
|
// TODO: Players
|
||||||
|
unused_entries: r.unused_entries,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,63 @@
|
||||||
|
use crate::protocols::{gamespy, minecraft, quake, valve};
|
||||||
use crate::{GDError::InvalidInput, GDResult};
|
use crate::{GDError::InvalidInput, GDResult};
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Enumeration of all valid protocol types
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Protocol {
|
||||||
|
Gamespy(gamespy::GameSpyVersion),
|
||||||
|
Minecraft(Option<minecraft::types::Server>),
|
||||||
|
Quake(quake::QuakeVersion),
|
||||||
|
Valve(valve::SteamApp),
|
||||||
|
TheShip,
|
||||||
|
FFOW,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A generic version of a response
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct GenericResponse {
|
||||||
|
/// The name of the server
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// Description of the server
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Name of the current game or game mode
|
||||||
|
pub game: Option<String>,
|
||||||
|
/// Version of the game being run on the server
|
||||||
|
pub game_version: Option<String>,
|
||||||
|
/// The current map name
|
||||||
|
pub map: Option<String>,
|
||||||
|
/// Maximum number of players allowed to connect
|
||||||
|
pub players_maximum: u64,
|
||||||
|
/// Number of players currently connected
|
||||||
|
pub players_online: u64,
|
||||||
|
/// Number of bots currently connected
|
||||||
|
pub players_bots: Option<u64>,
|
||||||
|
/// Whether the server requires a password to join
|
||||||
|
pub has_password: Option<bool>,
|
||||||
|
/// Data specific to non-generic responses
|
||||||
|
pub inner: SpecificResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A specific response containing extra data that isn't generic
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SpecificResponse {
|
||||||
|
Gamespy(gamespy::VersionedExtraResponse),
|
||||||
|
Minecraft(minecraft::VersionedExtraResponse),
|
||||||
|
Quake(quake::ExtraResponse),
|
||||||
|
Valve(valve::ExtraResponse),
|
||||||
|
#[cfg(not(feature = "no_games"))]
|
||||||
|
TheShip(crate::games::ts::ExtraResponse),
|
||||||
|
#[cfg(not(feature = "no_games"))]
|
||||||
|
FFOW(crate::games::ffow::ExtraResponse),
|
||||||
|
}
|
||||||
|
|
||||||
/// Timeout settings for socket operations
|
/// Timeout settings for socket operations
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TimeoutSettings {
|
pub struct TimeoutSettings {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::bufferer::Bufferer;
|
|
||||||
use crate::GDError::UnknownEnumCast;
|
use crate::GDError::UnknownEnumCast;
|
||||||
use crate::GDResult;
|
use crate::GDResult;
|
||||||
|
use crate::{
|
||||||
|
bufferer::Bufferer,
|
||||||
|
protocols::{types::SpecificResponse, GenericResponse},
|
||||||
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -55,6 +58,63 @@ pub struct Response {
|
||||||
pub rules: Option<HashMap<String, String>>,
|
pub rules: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ExtraResponse {
|
||||||
|
pub players: Option<Vec<ServerPlayer>>,
|
||||||
|
pub rules: Option<HashMap<String, String>>,
|
||||||
|
/// Protocol used by the server.
|
||||||
|
pub protocol: u8,
|
||||||
|
/// Name of the folder containing the game files.
|
||||||
|
pub folder: String,
|
||||||
|
/// [Steam Application ID](https://developer.valvesoftware.com/wiki/Steam_Application_ID) of game.
|
||||||
|
pub appid: u32,
|
||||||
|
/// Dedicated, NonDedicated or SourceTV
|
||||||
|
pub server_type: Server,
|
||||||
|
/// The Operating System that the server is on.
|
||||||
|
pub environment_type: Environment,
|
||||||
|
/// Indicates whether the server uses VAC.
|
||||||
|
pub vac_secured: bool,
|
||||||
|
/// [The ship](https://developer.valvesoftware.com/wiki/The_Ship) extra data
|
||||||
|
pub the_ship: Option<TheShip>,
|
||||||
|
/// Some extra data that the server might provide or not.
|
||||||
|
pub extra_data: Option<ExtraData>,
|
||||||
|
/// GoldSrc only: Indicates whether the hosted game is a mod.
|
||||||
|
pub is_mod: bool,
|
||||||
|
/// GoldSrc only: If the game is a mod, provide additional data.
|
||||||
|
pub mod_data: Option<ModData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Response> for GenericResponse {
|
||||||
|
fn from(r: Response) -> Self {
|
||||||
|
GenericResponse {
|
||||||
|
name: Some(r.info.name),
|
||||||
|
description: None,
|
||||||
|
game: Some(r.info.game),
|
||||||
|
game_version: Some(r.info.version),
|
||||||
|
map: Some(r.info.map),
|
||||||
|
players_maximum: r.info.players_maximum.into(),
|
||||||
|
players_online: r.info.players_online.into(),
|
||||||
|
players_bots: Some(r.info.players_bots.into()),
|
||||||
|
has_password: Some(r.info.has_password),
|
||||||
|
inner: SpecificResponse::Valve(ExtraResponse {
|
||||||
|
players: r.players,
|
||||||
|
rules: r.rules,
|
||||||
|
protocol: r.info.protocol,
|
||||||
|
folder: r.info.folder,
|
||||||
|
appid: r.info.appid,
|
||||||
|
server_type: r.info.server_type,
|
||||||
|
environment_type: r.info.environment_type,
|
||||||
|
vac_secured: r.info.vac_secured,
|
||||||
|
the_ship: r.info.the_ship,
|
||||||
|
extra_data: r.info.extra_data,
|
||||||
|
is_mod: r.info.is_mod,
|
||||||
|
mod_data: r.info.mod_data,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// General server information's.
|
/// General server information's.
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue