mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 15:27:28 +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
|
|
@ -1,5 +1,22 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod common;
|
||||
/// The implementations.
|
||||
pub mod 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")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::protocols::gamespy::VersionedExtraResponse;
|
||||
use crate::protocols::{types::SpecificResponse, GenericResponse};
|
||||
|
||||
/// A player’s details.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
|
@ -39,3 +42,41 @@ pub struct Response {
|
|||
pub tournament: bool,
|
||||
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;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
|
|
@ -40,3 +42,37 @@ pub struct Response {
|
|||
pub tournament: bool,
|
||||
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::{
|
||||
bufferer::Bufferer,
|
||||
protocols::{types::SpecificResponse, GenericResponse},
|
||||
GDError::{PacketBad, UnknownEnumCast},
|
||||
GDResult,
|
||||
};
|
||||
|
|
@ -43,6 +44,13 @@ pub struct Player {
|
|||
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.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
|
@ -70,6 +78,48 @@ pub struct JavaResponse {
|
|||
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.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
|
@ -96,6 +146,44 @@ pub struct BedrockResponse {
|
|||
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 {
|
||||
pub fn from_bedrock_response(response: BedrockResponse) -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -14,3 +14,5 @@ pub mod quake;
|
|||
pub mod types;
|
||||
/// Reference: [Server Query](https://developer.valvesoftware.com/wiki/Server_queries)
|
||||
pub mod valve;
|
||||
|
||||
pub use types::{GenericResponse, Protocol};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod one;
|
||||
pub mod three;
|
||||
pub mod two;
|
||||
|
|
@ -7,3 +10,11 @@ pub mod types;
|
|||
pub use types::*;
|
||||
|
||||
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 std::collections::HashMap;
|
||||
|
||||
use crate::protocols::{types::SpecificResponse, GenericResponse};
|
||||
|
||||
/// General server information's.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -21,3 +23,30 @@ pub struct Response<P> {
|
|||
/// Other server entries that weren't used.
|
||||
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,98 +1,154 @@
|
|||
use crate::{GDError::InvalidInput, GDResult};
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
/// Timeout settings for socket operations
|
||||
#[derive(Clone, Debug)]
|
||||
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](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(InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(write_duration) = write {
|
||||
if write_duration == Duration::new(0, 0) {
|
||||
return Err(InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
// Test creating new TimeoutSettings with valid durations
|
||||
#[test]
|
||||
fn test_new_with_valid_durations() -> GDResult<()> {
|
||||
// Define valid read and write durations
|
||||
let read_duration = Duration::from_secs(1);
|
||||
let write_duration = Duration::from_secs(2);
|
||||
|
||||
// Create new TimeoutSettings with the valid durations
|
||||
let timeout_settings = TimeoutSettings::new(Some(read_duration), Some(write_duration))?;
|
||||
|
||||
// Verify that the get_read and get_write methods return the expected values
|
||||
assert_eq!(timeout_settings.get_read(), Some(read_duration));
|
||||
assert_eq!(timeout_settings.get_write(), Some(write_duration));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Test creating new TimeoutSettings with a zero duration
|
||||
#[test]
|
||||
fn test_new_with_zero_duration() {
|
||||
// Define a zero read duration and a valid write duration
|
||||
let read_duration = Duration::new(0, 0);
|
||||
let write_duration = Duration::from_secs(2);
|
||||
|
||||
// Try to create new TimeoutSettings with the zero read duration (this should
|
||||
// fail)
|
||||
let result = TimeoutSettings::new(Some(read_duration), Some(write_duration));
|
||||
|
||||
// Verify that the function returned an error and that the error type is
|
||||
// InvalidInput
|
||||
assert!(result.is_err());
|
||||
assert_eq!(result.unwrap_err(), InvalidInput);
|
||||
}
|
||||
|
||||
// Test that the default TimeoutSettings values are correct
|
||||
#[test]
|
||||
fn test_default_values() {
|
||||
// Get the default TimeoutSettings values
|
||||
let default_settings = TimeoutSettings::default();
|
||||
|
||||
// Verify that the get_read and get_write methods return the expected default
|
||||
// values
|
||||
assert_eq!(default_settings.get_read(), Some(Duration::from_secs(4)));
|
||||
assert_eq!(default_settings.get_write(), Some(Duration::from_secs(4)));
|
||||
}
|
||||
}
|
||||
use crate::protocols::{gamespy, minecraft, quake, valve};
|
||||
use crate::{GDError::InvalidInput, GDResult};
|
||||
|
||||
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
|
||||
#[derive(Clone, Debug)]
|
||||
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](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(InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(write_duration) = write {
|
||||
if write_duration == Duration::new(0, 0) {
|
||||
return Err(InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
// Test creating new TimeoutSettings with valid durations
|
||||
#[test]
|
||||
fn test_new_with_valid_durations() -> GDResult<()> {
|
||||
// Define valid read and write durations
|
||||
let read_duration = Duration::from_secs(1);
|
||||
let write_duration = Duration::from_secs(2);
|
||||
|
||||
// Create new TimeoutSettings with the valid durations
|
||||
let timeout_settings = TimeoutSettings::new(Some(read_duration), Some(write_duration))?;
|
||||
|
||||
// Verify that the get_read and get_write methods return the expected values
|
||||
assert_eq!(timeout_settings.get_read(), Some(read_duration));
|
||||
assert_eq!(timeout_settings.get_write(), Some(write_duration));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Test creating new TimeoutSettings with a zero duration
|
||||
#[test]
|
||||
fn test_new_with_zero_duration() {
|
||||
// Define a zero read duration and a valid write duration
|
||||
let read_duration = Duration::new(0, 0);
|
||||
let write_duration = Duration::from_secs(2);
|
||||
|
||||
// Try to create new TimeoutSettings with the zero read duration (this should
|
||||
// fail)
|
||||
let result = TimeoutSettings::new(Some(read_duration), Some(write_duration));
|
||||
|
||||
// Verify that the function returned an error and that the error type is
|
||||
// InvalidInput
|
||||
assert!(result.is_err());
|
||||
assert_eq!(result.unwrap_err(), InvalidInput);
|
||||
}
|
||||
|
||||
// Test that the default TimeoutSettings values are correct
|
||||
#[test]
|
||||
fn test_default_values() {
|
||||
// Get the default TimeoutSettings values
|
||||
let default_settings = TimeoutSettings::default();
|
||||
|
||||
// Verify that the get_read and get_write methods return the expected default
|
||||
// values
|
||||
assert_eq!(default_settings.get_read(), Some(Duration::from_secs(4)));
|
||||
assert_eq!(default_settings.get_write(), Some(Duration::from_secs(4)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::bufferer::Bufferer;
|
||||
use crate::GDError::UnknownEnumCast;
|
||||
use crate::GDResult;
|
||||
use crate::{
|
||||
bufferer::Bufferer,
|
||||
protocols::{types::SpecificResponse, GenericResponse},
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -55,6 +58,63 @@ pub struct Response {
|
|||
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.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue