[Protocol] Implement generic response with dyn (#56)

* Implement generic response as enum

* First draft of implementing into_common()

* Make common response type generic

* Use macros and generics to reduce repetition

* [Games] Add dynamically dispatched CommonResponse trait

This adds two traits: "CommonResponse", and "CommonPlayer", when the
generic game query function returns a response it returns a pointer to
its original response type that implements "CommonResponse".

Both common traits require that "as_original()" be implemented, this
returns an enum containing a pointer to the original type.

Both traits have a concrete method "as_json()" that returns a struct
containing data fetched from all of its methods as. This struct
implements serde and can hence be serialized as required.

The traits require a few other methods be implemented, those being the
fields that are common across all types. All other methods have a
default None implementation so that each response type only needs to
implement methods for fields that it has.

* [Game] Implement common traits for JCMP2 response

* [Fmt] Run cargo fmt

* Fix doctest failing

* Run cargo fmt
This commit is contained in:
Tom 2023-06-25 13:31:23 +00:00 committed by GitHub
parent bf14ecb4a4
commit b368877031
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 405 additions and 430 deletions

View file

@ -14,51 +14,124 @@ pub enum Protocol {
Minecraft(Option<minecraft::types::Server>),
Quake(quake::QuakeVersion),
Valve(valve::SteamApp),
#[cfg(not(feature = "no_games"))]
TheShip,
#[cfg(not(feature = "no_games"))]
FFOW,
JC2MP,
}
/// A generic version of a response
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
/// All response types
#[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,
pub enum GenericResponse<'a> {
GameSpy(gamespy::VersionedResponse<'a>),
Minecraft(minecraft::VersionedResponse<'a>),
Quake(quake::VersionedResponse<'a>),
Valve(&'a valve::Response),
#[cfg(not(feature = "no_games"))]
TheShip(&'a crate::games::ts::Response),
#[cfg(not(feature = "no_games"))]
FFOW(&'a crate::games::ffow::Response),
#[cfg(not(feature = "no_games"))]
JC2MP(&'a crate::games::jc2mp::Response),
}
/// A specific response containing extra data that isn't generic
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
/// All player types
#[derive(Debug, Clone, PartialEq)]
pub enum SpecificResponse {
Gamespy(gamespy::VersionedExtraResponse),
Minecraft(minecraft::VersionedExtraResponse),
Quake(quake::ExtraResponse),
Valve(valve::ExtraResponse),
pub enum GenericPlayer<'a> {
Valve(&'a valve::ServerPlayer),
QuakeOne(&'a quake::one::Player),
QuakeTwo(&'a quake::two::Player),
Minecraft(&'a minecraft::Player),
Gamespy(gamespy::VersionedPlayer<'a>),
#[cfg(not(feature = "no_games"))]
TheShip(crate::games::ts::ExtraResponse),
TheShip(&'a crate::games::ts::TheShipPlayer),
#[cfg(not(feature = "no_games"))]
FFOW(crate::games::ffow::ExtraResponse),
#[cfg(not(feature = "no_games"))]
JC2MP,
JCMP2(&'a crate::games::jc2mp::Player),
}
pub trait CommonResponse {
/// Get the original response type
fn as_original(&self) -> GenericResponse;
/// Get a struct that can be stored as JSON (you don't need to override
/// this)
fn as_json(&self) -> CommonResponseJson {
CommonResponseJson {
name: self.name(),
description: self.description(),
game: self.game(),
game_version: self.game_version(),
has_password: self.has_password(),
map: self.map(),
players_maximum: self.players_maximum(),
players_online: self.players_online(),
players_bots: self.players_bots(),
players: self
.players()
.map(|players| players.iter().map(|p| p.as_json()).collect()),
}
}
/// The name of the server
fn name(&self) -> Option<&str> { None }
/// Description of the server
fn description(&self) -> Option<&str> { None }
/// Name of the current game or game mode
fn game(&self) -> Option<&str> { None }
/// Version of the game being run on the server
fn game_version(&self) -> Option<&str> { None }
/// The current map name
fn map(&self) -> Option<&str> { None }
/// Maximum number of players allowed to connect
fn players_maximum(&self) -> u64;
/// Number of players currently connected
fn players_online(&self) -> u64;
/// Number of bots currently connected
fn players_bots(&self) -> Option<u64> { None }
/// Whether the server requires a password to join
fn has_password(&self) -> Option<bool> { None }
/// Currently connected players
fn players(&self) -> Option<Vec<&dyn CommonPlayer>> { None }
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct CommonResponseJson<'a> {
pub name: Option<&'a str>,
pub description: Option<&'a str>,
pub game: Option<&'a str>,
pub game_version: Option<&'a str>,
pub map: Option<&'a str>,
pub players_maximum: u64,
pub players_online: u64,
pub players_bots: Option<u64>,
pub has_password: Option<bool>,
pub players: Option<Vec<CommonPlayerJson<'a>>>,
}
pub trait CommonPlayer {
/// Get the original player type
fn as_original(&self) -> GenericPlayer;
/// Get a struct that can be stored as JSON (you don't need to override
/// this)
fn as_json(&self) -> CommonPlayerJson {
CommonPlayerJson {
name: self.name(),
score: self.score(),
}
}
/// Player name
fn name(&self) -> &str;
/// Player score
fn score(&self) -> Option<u32> { None }
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct CommonPlayerJson<'a> {
pub name: &'a str,
pub score: Option<u32>,
}
/// Timeout settings for socket operations