diff --git a/GAMES.md b/GAMES.md index 4b97517..6c0af95 100644 --- a/GAMES.md +++ b/GAMES.md @@ -95,6 +95,7 @@ requirements/information. | Nova-Life: Amboise | NLA | Valve | | | Abiotic Factor | ABIOTICFACTOR | Valve | | | Soulmask | SOULMASK | Valve | | +| Minetest | MINETEST | Proprietary | Available on the 'tls', 'serde' and 'services' feature | ## Planned to add support: diff --git a/RESPONSES.md b/RESPONSES.md index 9827130..14a7948 100644 --- a/RESPONSES.md +++ b/RESPONSES.md @@ -6,57 +6,73 @@ annotated in brackets. # Response table -| Field | Generic | GameSpy(1) | GameSpy(2) | GameSpy(3) | Minecraft(Java) | Minecraft(Bedrock) | Valve | Quake | Unreal2 | Epic | Proprietary: FFOW | Proprietary: TheShip | Proprietary: JC2MP | Proprietary: Savage 2 | -|----------------------|----------|------------|------------|------------|-----------------|--------------------|---------------|-----------|------------|----------|-------------------|----------------------|--------------------|-----------------------| -| name | `Option` | `String` | `String` | `String` | | `String` | `String` | `String` | `String` | `String` | `String` | `String` | `String` | `String` | -| description | `Option` | | | | `String` | | | | | | `String` | | `String` | | -| game_mode | `Option` | `String` | | `String` | | `Option` | `String` | | `String` | | `String` | `String` | | `String` | -| game_version | `Option` | `String` | | `String` | `String` | | `String` | `String` | | `String` | `String` | `String` | `String` | | -| map | `Option` | `String` | `String` | `String` | | `Option` | `String` | `String` | `String` | `String` | `String` | `String` | | `String` | -| players_maxmimum | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | -| players_online | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | -| players_bots | `Option` | | | | | | `u8` | | | | | `u8` | | | -| has_password | `Option` | `bool` | `bool` | `bool` | | | `bool` | | | `bool` | `bool` | `bool` | `bool` | | -| players_minimum | | `Option` | `Option` | `Option` | | | | | | | | | | `u8` | -| players | | `Vec` | `Vec` | `Vec` | `Option>` | | `Option>` | `Vec ` | `Vec` | `Vec` | | `Vec` | `Vec` | | -| tournament | | `bool` | | `bool` | | | | | | | | | | | -| unused_entries | | `Hashmap` | | `HashMap` | | | | `HashMap` | | | | | | | -| teams | | | `Vec` | `Vec` | | | | | | | | | | | -| protocol_version | | | | | `i32` | `String` | `u8` | | | | `u8` | `u8` | | `String` | -| server_type | | | | | `Server` | `Server` | `Server` | | | | | `Server` | | | -| rules | | | | | | | `Option>` | | `HashMap>` | | | `HashMap` | | | -| environment_type | | | | | | | `Environment` | | | | `Environment` | | | | -| vac_secured | | | | | | | `bool` | | | | `bool` | `bool` | | | -| map_title | | `Option` | | | | | | | | | | | | | -| admin_contact | | `Option` | | | | | | | | | | | | | -| admin_name | | `Option` | | | | | | | | | | | | | -| favicon | | | | | `Option` | | | | | | | | | | -| previews_chat | | | | | `Option` | | | | | | | | | | -| enforces_secure_chat | | | | | `Option` | | | | | | | | | | -| edition | | | | | | `String` | | | | | | | | | -| id | | | | | | `String` | | | `String` | | | | | | -| the_ship | | | | | | | `Option` | | | | | | | | -| is_mod | | | | | | | `bool` | | | | | | | | -| extra_data | | | | | | | `Option` | | | | | | | | -| mod_data | | | | | | | `Option` | | | | | | | | -| folder | | | | | | | `String` | | | | | | | | -| appid | | | | | | | `u32` | | | | | | | | -| active_mod | | | | | | | | | | | `String` | | | | -| round | | | | | | | | | | | `u8` | | | | -| rounds_maximum | | | | | | | | | | | `u8` | | | | -| time_left | | | | | | | | | | | `u16` | | | | -| port | | | | | | | | | `u32` | | | `Option` | | | -| steam_id | | | | | | | | | | | | `Option` | | | -| tv_port | | | | | | | | | | | | `Option` | | | -| tv_name | | | | | | | | | | | | `Option` | | | -| keywords | | | | | | | | | | | | `Option` | | | -| mode | | | | | | | | | | | | `u8` | | | -| witnesses | | | | | | | | | | | | `u8` | | | -| duration | | | | | | | | | | | | `u8` | | | -| query_port | | | | | | | | | `u32` | | | | | | -| ip | | | | | | | | | `String` | | | | | | -| mutators | | | | | | | | | `HashSet` | | | | | | -| next_map | | | | | | | | | | | | | | `String` | -| location | | | | | | | | | | | | | | `String` | -| level_minimum | | | | | | | | | | | | | | `String` | -| time | | | | | | | | | | | | | | `String` | +| Field | Generic | GameSpy(1) | GameSpy(2) | GameSpy(3) | Minecraft(Java) | Minecraft(Bedrock) | Valve | Quake | Unreal2 | Epic | Proprietary: FFOW | Proprietary: TheShip | Proprietary: JC2MP | Proprietary: Savage 2 | Proprietary: Minetest | +|----------------------|----------|------------|------------|------------|-----------------|--------------------|---------------|-----------|------------|----------|-------------------|----------------------|--------------------|-----------------------|-----------------------| +| name | `Option` | `String` | `String` | `String` | | `String` | `String` | `String` | `String` | `String` | `String` | `String` | `String` | `String` | `String` | +| description | `Option` | | | | `String` | | | | | | `String` | | `String` | | `String` | +| game_mode | `Option` | `String` | | `String` | | `Option` | `String` | | `String` | | `String` | `String` | | `String` | | +| game_version | `Option` | `String` | | `String` | `String` | | `String` | `String` | | `String` | `String` | `String` | `String` | | `String` | +| map | `Option` | `String` | `String` | `String` | | `Option` | `String` | `String` | `String` | `String` | `String` | `String` | | `String` | | +| players_maxmimum | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u32` | +| players_online | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u32` | +| players_bots | `Option` | | | | | | `u8` | | | | | `u8` | | | | +| has_password | `Option` | `bool` | `bool` | `bool` | | | `bool` | | | `bool` | `bool` | `bool` | `bool` | | `Option` | +| players_minimum | | `Option` | `Option` | `Option` | | | | | | | | | | `u8` | | +| players | | `Vec` | `Vec` | `Vec` | `Option>` | | `Option>` | `Vec ` | `Vec` | `Vec` | | `Vec` | `Vec` | | `Vec` | +| tournament | | `bool` | | `bool` | | | | | | | | | | | | +| unused_entries | | `Hashmap` | | `HashMap` | | | | `HashMap` | | | | | | | | +| teams | | | `Vec` | `Vec` | | | | | | | | | | | | +| protocol_version | | | | | `i32` | `String` | `u8` | | | | `u8` | `u8` | | `String` | | +| server_type | | | | | `Server` | `Server` | `Server` | | | | | `Server` | | | | +| rules | | | | | | | `Option>` | | `HashMap>` | | | `HashMap` | | | | +| environment_type | | | | | | | `Environment` | | | | `Environment` | | | | | +| vac_secured | | | | | | | `bool` | | | | `bool` | `bool` | | | | +| map_title | | `Option` | | | | | | | | | | | | | | +| admin_contact | | `Option` | | | | | | | | | | | | | | +| admin_name | | `Option` | | | | | | | | | | | | | | +| favicon | | | | | `Option` | | | | | | | | | | | +| previews_chat | | | | | `Option` | | | | | | | | | | | +| enforces_secure_chat | | | | | `Option` | | | | | | | | | | | +| edition | | | | | | `String` | | | | | | | | | | +| id | | | | | | `String` | | | `String` | | | | | | `String` | +| the_ship | | | | | | | `Option` | | | | | | | | | +| is_mod | | | | | | | `bool` | | | | | | | | | +| extra_data | | | | | | | `Option` | | | | | | | | | +| mod_data | | | | | | | `Option` | | | | | | | | | +| folder | | | | | | | `String` | | | | | | | | | +| appid | | | | | | | `u32` | | | | | | | | | +| active_mod | | | | | | | | | | | `String` | | | | | +| round | | | | | | | | | | | `u8` | | | | | +| rounds_maximum | | | | | | | | | | | `u8` | | | | | +| time_left | | | | | | | | | | | `u16` | | | | | +| port | | | | | | | | | `u32` | | | `Option` | | | `u32` | +| steam_id | | | | | | | | | | | | `Option` | | | | +| tv_port | | | | | | | | | | | | `Option` | | | | +| tv_name | | | | | | | | | | | | `Option` | | | | +| keywords | | | | | | | | | | | | `Option` | | | | +| mode | | | | | | | | | | | | `u8` | | | | +| witnesses | | | | | | | | | | | | `u8` | | | | +| duration | | | | | | | | | | | | `u8` | | | | +| query_port | | | | | | | | | `u32` | | | | | | | +| ip | | | | | | | | | `String` | | | | | | `String` | +| mutators | | | | | | | | | `HashSet` | | | | | | | +| next_map | | | | | | | | | | | | | | `String` | | +| location | | | | | | | | | | | | | | `String` | | +| level_minimum | | | | | | | | | | | | | | `String` | | +| time | | | | | | | | | | | | | | `String` | | +| creative | | | | | | | | | | | | | | | `Option` | +| damage | | | | | | | | | | | | | | | `bool` | +| game_time | | | | | | | | | | | | | | | `u32` | +| lag | | | | | | | | | | | | | | | `Option` | +| proto_max | | | | | | | | | | | | | | | `u16` | +| proto_min | | | | | | | | | | | | | | | `u16` | +| pvp | | | | | | | | | | | | | | | `bool` | +| uptime | | | | | | | | | | | | | | | `u32` | +| url | | | | | | | | | | | | | | | `Option` | +| update_time | | | | | | | | | | | | | | | `u32` | +| start | | | | | | | | | | | | | | | `u32` | +| clients_top | | | | | | | | | | | | | | | `u32` | +| updates | | | | | | | | | | | | | | | `u32` | +| pop_v | | | | | | | | | | | | | | | `f32` | +| geo_continent | | | | | | | | | | | | | | | `Option` | +| ping | | | | | | | | | | | | | | | `f32` | diff --git a/crates/lib/CHANGELOG.md b/crates/lib/CHANGELOG.md index 59fc206..84a5b09 100644 --- a/crates/lib/CHANGELOG.md +++ b/crates/lib/CHANGELOG.md @@ -2,7 +2,10 @@ Who knows what the future holds... # 0.X.Y - DD/MM/YYYY -To be made... +Games: + +- [Minetest](https://www.minetest.net/) support (available on the `tls`, `serde` and `services` features) (#218 by + @CosminPerRam). # 0.5.2 - 20/10/2024 diff --git a/crates/lib/src/games/minetest/mod.rs b/crates/lib/src/games/minetest/mod.rs new file mode 100644 index 0000000..bf33372 --- /dev/null +++ b/crates/lib/src/games/minetest/mod.rs @@ -0,0 +1,8 @@ +/// The implementation. +/// Reference: [Node-GameGig](https://github.com/gamedig/node-gamedig/blob/master/protocols/minetest.js) +pub mod protocol; +/// All types used by the implementation. +pub mod types; + +pub use protocol::*; +pub use types::*; diff --git a/crates/lib/src/games/minetest/protocol.rs b/crates/lib/src/games/minetest/protocol.rs new file mode 100644 index 0000000..a3bb138 --- /dev/null +++ b/crates/lib/src/games/minetest/protocol.rs @@ -0,0 +1,23 @@ +use crate::minetest::Response; +use crate::{minetest_master_server, GDErrorKind, GDResult, TimeoutSettings}; +use std::net::IpAddr; + +pub fn query(address: &IpAddr, port: Option) -> GDResult { query_with_timeout(address, port, &None) } + +pub fn query_with_timeout( + address: &IpAddr, + port: Option, + timeout_settings: &Option, +) -> GDResult { + let address = address.to_string(); + let port = port.unwrap_or(30000); + + let servers = minetest_master_server::query(timeout_settings.unwrap_or_default())?; + for server in servers.list { + if server.ip == address && server.port == port { + return Ok(server.into()); + } + } + + Err(GDErrorKind::AutoQuery.context("Server not found in the master query list.")) +} diff --git a/crates/lib/src/games/minetest/types.rs b/crates/lib/src/games/minetest/types.rs new file mode 100644 index 0000000..3055c07 --- /dev/null +++ b/crates/lib/src/games/minetest/types.rs @@ -0,0 +1,108 @@ +use crate::minetest_master_server::Server; +use crate::protocols::types::{CommonPlayer, CommonResponse, GenericPlayer}; +use crate::protocols::GenericResponse; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Player { + pub name: String, +} + +impl CommonPlayer for Player { + fn as_original(&self) -> GenericPlayer { GenericPlayer::Minetest(self) } + + fn name(&self) -> &str { &self.name } +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Response { + pub name: String, + pub description: String, + pub game_version: String, + pub players_maximum: u32, + pub players_online: u32, + pub has_password: Option, + pub players: Vec, + pub id: String, + pub ip: String, + pub port: u16, + pub creative: Option, + pub damage: bool, + pub game_time: u32, + pub lag: Option, + pub proto_max: u16, + pub proto_min: u16, + pub pvp: bool, + pub uptime: u32, + pub url: Option, + pub update_time: u32, + pub start: u32, + pub clients_top: u32, + pub updates: u32, + pub pop_v: f32, + pub geo_continent: Option, + pub ping: f32, +} + +impl From for Response { + fn from(server: Server) -> Self { + Self { + name: server.name, + description: server.description, + game_version: server.version, + players_maximum: server.clients_max, + players_online: server.total_clients, + has_password: server.password, + players: server + .clients_list + .unwrap_or_default() + .into_iter() + .map(|name| Player { name }) + .collect(), + ip: server.address, + creative: server.creative, + damage: server.damage, + game_time: server.game_time, + id: server.gameid, + lag: server.lag, + port: server.port, + proto_max: server.proto_max, + proto_min: server.proto_min, + pvp: server.pvp, + uptime: server.uptime, + url: server.url, + update_time: server.update_time, + start: server.start, + clients_top: server.clients_top, + updates: server.updates, + pop_v: server.pop_v, + geo_continent: server.geo_continent, + ping: server.ping, + } + } +} + +impl CommonResponse for Response { + fn as_original(&self) -> GenericResponse { GenericResponse::Minetest(self) } + + fn name(&self) -> Option<&str> { Some(&self.name) } + + fn description(&self) -> Option<&str> { Some(&self.description) } + + fn game_version(&self) -> Option<&str> { Some(&self.game_version) } + + fn players_maximum(&self) -> u32 { self.players_maximum } + + fn players_online(&self) -> u32 { self.players_online } + + fn has_password(&self) -> Option { self.has_password } + + fn players(&self) -> Option> { + Some( + self.players + .iter() + .map(|p| p as &dyn CommonPlayer) + .collect(), + ) + } +} diff --git a/crates/lib/src/games/mod.rs b/crates/lib/src/games/mod.rs index 1d25506..1be8b5d 100644 --- a/crates/lib/src/games/mod.rs +++ b/crates/lib/src/games/mod.rs @@ -7,6 +7,9 @@ pub mod quake; pub mod unreal2; pub mod valve; +#[cfg(all(feature = "tls", feature = "serde", feature = "services"))] +pub mod minetest; + #[cfg(feature = "tls")] pub use epic::*; pub use gamespy::*; @@ -14,6 +17,9 @@ pub use quake::*; pub use unreal2::*; pub use valve::*; +#[cfg(all(feature = "tls", feature = "serde", feature = "services"))] +pub use minetest::*; + /// Battalion 1944 pub mod battalion1944; /// Eco diff --git a/crates/lib/src/games/query.rs b/crates/lib/src/games/query.rs index d87a631..8968683 100644 --- a/crates/lib/src/games/query.rs +++ b/crates/lib/src/games/query.rs @@ -2,6 +2,8 @@ use std::net::{IpAddr, SocketAddr}; +#[cfg(all(feature = "services", feature = "tls", feature = "serde"))] +use crate::games::minetest; use crate::games::types::Game; use crate::games::{eco, ffow, jc2m, mindustry, minecraft, savage2, theship}; use crate::protocols; @@ -125,6 +127,10 @@ pub fn query_with_timeout_and_extra_settings( ) .map(Box::new)? } + #[cfg(all(feature = "services", feature = "tls", feature = "serde"))] + ProprietaryProtocol::Minetest => { + minetest::query_with_timeout(address, port, &timeout_settings).map(Box::new)? + } } } }) diff --git a/crates/lib/src/protocols/types.rs b/crates/lib/src/protocols/types.rs index bd614fd..a86b1c6 100644 --- a/crates/lib/src/protocols/types.rs +++ b/crates/lib/src/protocols/types.rs @@ -23,6 +23,8 @@ pub enum ProprietaryProtocol { Savage2, Eco, Mindustry, + #[cfg(all(feature = "services", feature = "tls", feature = "serde"))] + Minetest, } /// Enumeration of all valid protocol types @@ -63,6 +65,13 @@ pub enum GenericResponse<'a> { Savage2(&'a crate::games::savage2::Response), #[cfg(feature = "games")] Eco(&'a crate::games::eco::Response), + #[cfg(all( + feature = "services", + feature = "tls", + feature = "serde", + feature = "games" + ))] + Minetest(&'a crate::games::minetest::Response), } /// All player types @@ -84,6 +93,13 @@ pub enum GenericPlayer<'a> { JCMP2(&'a crate::games::jc2m::Player), #[cfg(feature = "games")] Eco(&'a crate::games::eco::Player), + #[cfg(all( + feature = "services", + feature = "tls", + feature = "serde", + feature = "games" + ))] + Minetest(&'a crate::games::minetest::Player), } pub trait CommonResponse { diff --git a/crates/lib/src/services/minetest_master_server/types.rs b/crates/lib/src/services/minetest_master_server/types.rs index c9842ff..88486d7 100644 --- a/crates/lib/src/services/minetest_master_server/types.rs +++ b/crates/lib/src/services/minetest_master_server/types.rs @@ -1,19 +1,19 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct Server { pub address: String, pub clients: u32, - pub clients_list: Vec, + pub clients_list: Option>, pub clients_max: u32, - pub creative: bool, + pub creative: Option, pub damage: bool, pub description: String, pub game_time: u32, pub gameid: String, pub lag: Option, pub name: String, - pub password: bool, + pub password: Option, pub port: u16, pub proto_max: u16, pub proto_min: u16,