Reduce game implementation repetition (#122)

* [Games] Add macro to replace valve game query implementations

This somewhat reduces repeated code (#120), and also adds auto-generated
doc comments to all valve game query functions.

* [Games] Add macro to replace gamespy game query implementations

This somewhat reduces repeated code (#120), and also adds auto-generated
doc comments to all gamespy game query functions.

* [Games] Add macro to replace quake game query implementations

This somewhat reduces repeated code (#120), and also adds auto-generated
doc comments to all quake game query functions.

* [Games] Move all valve game modules into a single file using macros

Vastly reduces the number of files. However does break the game
definition-per-file test, so that was removed.

* [Games] Move all quake game modules into a single file using macros

* [Games] Move all gamespy game modules into a single file using macros

* [Docs] Update CHANGELOG

* [Docs] Improve game query function generation macro documentation

* [Games] Add missed Halo: Combat Evolved to gamespy
This commit is contained in:
Tom 2023-10-10 06:26:35 +00:00 committed by GitHub
parent c8a93357cf
commit 3b9c784e70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 250 additions and 872 deletions

View file

@ -31,3 +31,56 @@ pub enum VersionedPlayer<'a> {
Two(&'a two::Player),
Three(&'a three::Player),
}
/// Generate a module containing a query function for a gamespy game.
///
/// * `mod_name` - The name to be given to the game module (see ID naming
/// conventions in CONTRIBUTING.md).
/// * `pretty_name` - The full name of the game, will be used as the
/// documentation for the created module.
/// * `gamespy_ver`, `default_port` - Passed through to [game_query_fn].
macro_rules! game_query_mod {
($mod_name: ident, $pretty_name: expr, $gamespy_ver: ident, $default_port: literal) => {
#[doc = $pretty_name]
pub mod $mod_name {
crate::protocols::gamespy::game_query_fn!($gamespy_ver, $default_port);
}
};
}
pub(crate) use game_query_mod;
// Allow generating doc comments:
// https://users.rust-lang.org/t/macros-filling-text-in-comments/20473
/// Generate a query function for a gamespy game.
///
/// * `gamespy_ver` - The name of the [module](crate::protocols::gamespy) for
/// the gamespy version the game uses.
/// * `default_port` - The default port the game uses.
///
/// ```rust,ignore
/// use crate::protocols::gamespy::game_query_fn;
/// game_query_fn!(one, 7778);
/// ```
macro_rules! game_query_fn {
($gamespy_ver: ident, $default_port: literal) => {
crate::protocols::gamespy::game_query_fn! {@gen $gamespy_ver, $default_port, concat!(
"Make a gamespy ", stringify!($gamespy_ver), " query with default timeout settings.\n\n",
"If port is `None`, then the default port (", stringify!($default_port), ") will be used.")}
};
(@gen $gamespy_ver: ident, $default_port: literal, $doc: expr) => {
#[doc = $doc]
pub fn query(
address: &std::net::IpAddr,
port: Option<u16>,
) -> crate::GDResult<crate::protocols::gamespy::$gamespy_ver::Response> {
crate::protocols::gamespy::$gamespy_ver::query(
&std::net::SocketAddr::new(*address, port.unwrap_or($default_port)),
None,
)
}
};
}
pub(crate) use game_query_fn;

View file

@ -18,3 +18,57 @@ pub enum QuakeVersion {
Two,
Three,
}
/// Generate a module containing a query function for a quake game.
///
/// * `mod_name` - The name to be given to the game module (see ID naming
/// conventions in CONTRIBUTING.md).
/// * `pretty_name` - The full name of the game, will be used as the
/// documentation for the created module.
/// * `quake_ver`, `default_port` - Passed through to [game_query_fn].
macro_rules! game_query_mod {
($mod_name: ident, $pretty_name: expr, $quake_ver: ident, $default_port: literal) => {
#[doc = $pretty_name]
pub mod $mod_name {
crate::protocols::quake::game_query_fn!($quake_ver, $default_port);
}
};
}
pub(crate) use game_query_mod;
// Allow generating doc comments:
// https://users.rust-lang.org/t/macros-filling-text-in-comments/20473
/// Generate a query function for a quake game.
///
/// * `quake_ver` - The name of the [module](crate::protocols::quake) for the
/// quake version the game uses.
/// * `default_port` - The default port the game uses.
///
/// ```rust,ignore
/// use crate::protocols::quake::game_query_fn;
/// game_query_fn!(one, 27500);
/// ```
macro_rules! game_query_fn {
($quake_ver: ident, $default_port: literal) => {
use crate::protocols::quake::$quake_ver::Player;
crate::protocols::quake::game_query_fn! {@gen $quake_ver, Player, $default_port, concat!(
"Make a quake ", stringify!($quake_ver), " query with default timeout settings.\n\n",
"If port is `None`, then the default port (", stringify!($default_port), ") will be used.")}
};
(@gen $quake_ver: ident, $player_type: ty, $default_port: literal, $doc: expr) => {
#[doc = $doc]
pub fn query(
address: &std::net::IpAddr,
port: Option<u16>,
) -> crate::GDResult<crate::protocols::quake::Response<$player_type>> {
crate::protocols::quake::$quake_ver::query(
&std::net::SocketAddr::new(*address, port.unwrap_or($default_port)),
None,
)
}
};
}
pub(crate) use game_query_fn;

View file

@ -1,11 +1,13 @@
use crate::protocols::quake::client::{client_query, QuakeClient};
use crate::protocols::quake::two::{Player, QuakeTwo};
use crate::protocols::quake::two::QuakeTwo;
use crate::protocols::quake::Response;
use crate::protocols::types::TimeoutSettings;
use crate::GDResult;
use std::net::SocketAddr;
use std::slice::Iter;
pub use crate::protocols::quake::two::Player;
struct QuakeThree;
impl QuakeClient for QuakeThree {
type Player = Player;

View file

@ -5,3 +5,56 @@ pub mod types;
pub use protocol::*;
pub use types::*;
/// Generate a module containing a query function for a valve game.
///
/// * `mod_name` - The name to be given to the game module (see ID naming
/// conventions in CONTRIBUTING.md).
/// * `pretty_name` - The full name of the game, will be used as the
/// documentation for the created module.
/// * `steam_app`, `default_port` - Passed through to [game_query_fn].
macro_rules! game_query_mod {
($mod_name: ident, $pretty_name: expr, $steam_app: ident, $default_port: literal) => {
#[doc = $pretty_name]
pub mod $mod_name {
crate::protocols::valve::game_query_fn!($steam_app, $default_port);
}
};
}
pub(crate) use game_query_mod;
// Allow generating doc comments:
// https://users.rust-lang.org/t/macros-filling-text-in-comments/20473
/// Generate a query function for a valve game.
///
/// * `steam_app` - The entry in the [SteamApp] enum that the game uses.
/// * `default_port` - The default port the game uses.
///
/// ```rust,ignore
/// use crate::protocols::valve::game_query_fn;
/// game_query_fn!(TEAMFORTRESS2, 27015);
/// ```
macro_rules! game_query_fn {
($steam_app: ident, $default_port: literal) => {
crate::protocols::valve::game_query_fn!{@gen $steam_app, $default_port, concat!(
"Make a valve query for ", stringify!($steam_app), " with default timeout settings and default extra request settings.\n\n",
"If port is `None`, then the default port (", stringify!($default_port), ") will be used.")}
};
(@gen $steam_app: ident, $default_port: literal, $doc: expr) => {
#[doc = $doc]
pub fn query(address: &std::net::IpAddr, port: Option<u16>) -> crate::GDResult<crate::protocols::valve::game::Response> {
let valve_response = crate::protocols::valve::query(
&std::net::SocketAddr::new(*address, port.unwrap_or($default_port)),
crate::protocols::valve::SteamApp::$steam_app.as_engine(),
None,
None,
)?;
Ok(crate::protocols::valve::game::Response::new_from_valve_response(valve_response))
}
};
}
pub(crate) use game_query_fn;