mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-18 09:35:50 +00:00
feat: add savage 2 support (#169)
* feat: savage 2 support * fix: add savage 2 to definitions * chore: run rustfmt * fix: config serde use * fix: remove needless borrow * docs: add savage 2 to protocols.md
This commit is contained in:
parent
dd037daa04
commit
af8e1e9b1a
10 changed files with 148 additions and 57 deletions
|
|
@ -9,6 +9,7 @@ Games:
|
|||
- [Conan Exiles](https://store.steampowered.com/app/440900/Conan_Exiles/) support.
|
||||
- [Post Scriptum](https://store.steampowered.com/app/736220/Post_Scriptum/) support.
|
||||
- [Squad](https://store.steampowered.com/app/393380/Squad/) support.
|
||||
- [Savage 2](https://savage2.net/) support.
|
||||
- Added a valve protocol query example.
|
||||
|
||||
Protocols:
|
||||
|
|
|
|||
1
GAMES.md
1
GAMES.md
|
|
@ -73,6 +73,7 @@ Beware of the `Notes` column, as it contains information about query port offset
|
|||
| Unreal Tournament 2004 | UT2004 | Unreal2 | Query port offset: 1 |
|
||||
| Post Scriptum | POSTSCRIPTUM | Valve | |
|
||||
| Squad | SQUAD | Valve | |
|
||||
| Savage 2 | SAVAGE2 | Proprietary | |
|
||||
|
||||
## Planned to add support:
|
||||
_
|
||||
|
|
|
|||
15
PROTOCOLS.md
15
PROTOCOLS.md
|
|
@ -1,13 +1,14 @@
|
|||
A protocol is defined as proprietary if it is being used only for a single scope (or series, like Minecraft).
|
||||
|
||||
# Supported protocols:
|
||||
| Name | For | Proprietary? | Documentation reference | Notes |
|
||||
|----------------|-------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Valve Protocol | Games | No | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | In some cases, the players details query might contain some 0-length named players. Multi-packet decompression not tested. |
|
||||
| Minecraft | Games | Yes | Java: [List Server Protocol](https://wiki.vg/Server_List_Ping) <br> Bedrock: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js) | |
|
||||
| GameSpy | Games | No | One: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy1.js) Two: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy2.js) Three: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy3.js) | These protocols are not really standardized, gamedig tries to get the most common fields amongst its supported games, if there are parsing problems, use the `query_vars` function. |
|
||||
| Quake | Games | No | One: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake1.js) Two: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake2.js) Three: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake3.js) | |
|
||||
| Unreal2 | Games | Yes | [Node-GameDig Source](https://github.com/gamedig/node-gamedig) | Sometimes servers send strings that node-gamedig would treat as latin1 that are UTF-8 encoded, when this happens the remove color code breaks because latin1 decodes the colour sequences differently. Some games provide additional info at the end of the server info packet, this is not currently handled (see the node implementation). Some games use a bot player to denote the team names, this is not currently handled. |
|
||||
| Name | For | Proprietary? | Documentation reference | Notes |
|
||||
|----------------|-------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Valve Protocol | Games | No | [Server Queries](https://developer.valvesoftware.com/wiki/Server_queries) | In some cases, the players details query might contain some 0-length named players. Multi-packet decompression not tested. |
|
||||
| Minecraft | Games | Yes | Java: [List Server Protocol](https://wiki.vg/Server_List_Ping) <br> Bedrock: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/minecraftbedrock.js) | |
|
||||
| GameSpy | Games | No | One: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy1.js) Two: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy2.js) Three: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/gamespy3.js) | These protocols are not really standardized, gamedig tries to get the most common fields amongst its supported games, if there are parsing problems, use the `query_vars` function. |
|
||||
| Quake | Games | No | One: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake1.js) Two: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake2.js) Three: [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/quake3.js) | |
|
||||
| Unreal2 | Games | Yes | [Node-GameDig Source](https://github.com/gamedig/node-gamedig) | Sometimes servers send strings that node-gamedig would treat as latin1 that are UTF-8 encoded, when this happens the remove color code breaks because latin1 decodes the colour sequences differently. Some games provide additional info at the end of the server info packet, this is not currently handled (see the node implementation). Some games use a bot player to denote the team names, this is not currently handled. |
|
||||
| Savage 2 | Games | Yes | [Node-GameDig Source](https://github.com/gamedig/node-gamedig/blob/master/protocols/savage2.js) | |
|
||||
|
||||
## Planned to add support:
|
||||
_
|
||||
|
|
|
|||
104
RESPONSES.md
104
RESPONSES.md
|
|
@ -5,53 +5,57 @@ In the case that a field that performs the same function exists in the current c
|
|||
|
||||
# Response table
|
||||
|
||||
| Field | Generic | GameSpy(1) | GameSpy(2) | GameSpy(3) | Minecraft(Java) | Minecraft(Bedrock) | Valve | Quake | Unreal2 | Proprietary: FFOW | Proprietary: TheShip | Proprietary: JC2MP |
|
||||
|----------------------|------------------|---------------------------|---------------|---------------------------|-----------------------|--------------------|-----------------------------------|---------------------------|--------------------------------|-------------------|---------------------------|--------------------|
|
||||
| name | `Option<String>` | `String` | `String` | `String` | | `String` | `String` | `String` | `String` | `String` | `String` | `String` |
|
||||
| description | `Option<String>` | | | | `String` | | | | | `String` | | `String` |
|
||||
| game_mode | `Option<String>` | `String` | | `String` | | `Option<GameMode>` | `String` | | `String` | `String` | `String` | |
|
||||
| game_version | `Option<String>` | `String` | | `String` | `String` | | `String` | `String` | | `String` | `String` | `String` |
|
||||
| map | `Option<String>` | `String` | `String` | `String` | | `Option<String>` | `String` | `String` | `String` | `String` | `String` | |
|
||||
| players_maxmimum | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u8` | `u32` |
|
||||
| players_online | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u8` | `u32` |
|
||||
| players_bots | `Option<u32>` | | | | | | `u8` | | | | `u8` | |
|
||||
| has_password | `Option<bool>` | `bool` | `bool` | `bool` | | | `bool` | | | `bool` | `bool` | `bool` |
|
||||
| players_minimum | | `Option<u8>` | `Option<u8>` | `Option<u8>` | | | | | | | | |
|
||||
| players | | `Vec<Player>` | `Vec<Player>` | `Vec<Player>` | `Option<Vec<Player>>` | | `Option<Vec<ServerPlayer>>` | `Vec<P>` | `Vec<Player>` | | `Vec<TheShipPlayer>` | `Vec<Player>` |
|
||||
| tournament | | `bool` | | `bool` | | | | | | | | |
|
||||
| unused_entries | | `Hashmap<String, String>` | | `HashMap<String, String>` | | | | `HashMap<String, String>` | | | | |
|
||||
| teams | | | `Vec<Team>` | `Vec<Team>` | | | | | | | | |
|
||||
| protocol_version | | | | | `i32` | `String` | `u8` | | | `u8` | `u8` | |
|
||||
| server_type | | | | | `Server` | `Server` | `Server` | | | | `Server` | |
|
||||
| rules | | | | | | | `Option<HashMap<String, String>>` | | `HashMap<String, Vec<String>>` | | `HashMap<String, String>` | |
|
||||
| environment_type | | | | | | | `Environment` | | | `Environment` | | |
|
||||
| vac_secured | | | | | | | `bool` | | | `bool` | `bool` | |
|
||||
| map_title | | `Option<String>` | | | | | | | | | | |
|
||||
| admin_contact | | `Option<String>` | | | | | | | | | | |
|
||||
| admin_name | | `Option<String>` | | | | | | | | | | |
|
||||
| favicon | | | | | `Option<String>` | | | | | | | |
|
||||
| previews_chat | | | | | `Option<bool>` | | | | | | | |
|
||||
| enforces_secure_chat | | | | | `Option<bool>` | | | | | | | |
|
||||
| edition | | | | | | `String` | | | | | | |
|
||||
| id | | | | | | `String` | | | `String` | | | |
|
||||
| the_ship | | | | | | | `Option<TheShip>` | | | | | |
|
||||
| is_mod | | | | | | | `bool` | | | | | |
|
||||
| extra_data | | | | | | | `Option<ExtraData>` | | | | | |
|
||||
| mod_data | | | | | | | `Option<ModData>` | | | | | |
|
||||
| folder | | | | | | | `String` | | | | | |
|
||||
| appid | | | | | | | `u32` | | | | | |
|
||||
| active_mod | | | | | | | | | | `String` | | |
|
||||
| round | | | | | | | | | | `u8` | | |
|
||||
| rounds_maximum | | | | | | | | | | `u8` | | |
|
||||
| time_left | | | | | | | | | | `u16` | | |
|
||||
| port | | | | | | | | | `u32` | | `Option<u16>` | |
|
||||
| steam_id | | | | | | | | | | | `Option<u64>` | |
|
||||
| tv_port | | | | | | | | | | | `Option<u16>` | |
|
||||
| tv_name | | | | | | | | | | | `Option<String>` | |
|
||||
| keywords | | | | | | | | | | | `Option<String>` | |
|
||||
| mode | | | | | | | | | | | `u8` | |
|
||||
| witnesses | | | | | | | | | | | `u8` | |
|
||||
| duration | | | | | | | | | | | `u8` | |
|
||||
| query_port | | | | | | | | | `u32` | | | |
|
||||
| ip | | | | | | | | | `String` | | | |
|
||||
| mutators | | | | | | | | | `HashSet<String>` | | | |
|
||||
| Field | Generic | GameSpy(1) | GameSpy(2) | GameSpy(3) | Minecraft(Java) | Minecraft(Bedrock) | Valve | Quake | Unreal2 | Proprietary: FFOW | Proprietary: TheShip | Proprietary: JC2MP | Proprietary: Savage 2 |
|
||||
|----------------------|----------|------------|------------|------------|-----------------|--------------------|---------------|-----------|------------|-------------------|----------------------|--------------------|-----------------------|
|
||||
| name | `Option` | `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` | |
|
||||
| map | `Option` | `String` | `String` | `String` | | `Option` | `String` | `String` | `String` | `String` | `String` | | `String` |
|
||||
| players_maxmimum | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u8` | `u32` | `u8` |
|
||||
| players_online | `u32` | `u32` | `u32` | `u32` | `u32` | `u32` | `u8` | `u8` | `u32` | `u8` | `u8` | `u32` | `u8` |
|
||||
| players_bots | `Option` | | | | | | `u8` | | | | `u8` | | |
|
||||
| has_password | `Option` | `bool` | `bool` | `bool` | | | `bool` | | | `bool` | `bool` | `bool` | |
|
||||
| players_minimum | | `Option` | `Option` | `Option` | | | | | | | | | `u8` |
|
||||
| players | | `Vec` | `Vec` | `Vec` | `Option>` | | `Option>` | `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` |
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ pub static GAMES: Map<&'static str, Game> = phf_map! {
|
|||
"q3a" => game!("Quake 3 Arena", 27960, Protocol::Quake(QuakeVersion::Three)),
|
||||
"ror2" => game!("Risk of Rain 2", 27016, Protocol::Valve(Engine::new(632_360))),
|
||||
"rust" => game!("Rust", 27015, Protocol::Valve(Engine::new(252_490))),
|
||||
"savage2" => game!("Savage 2", 11235, Protocol::PROPRIETARY(ProprietaryProtocol::Savage2)),
|
||||
"sco" => game!("Sven Co-op", 27015, Protocol::Valve(Engine::new_gold_src(false))),
|
||||
"sdtd" => game!("7 Days to Die", 26900, Protocol::Valve(Engine::new(251_570))),
|
||||
"sof2" => game!("Soldier of Fortune 2", 20100, Protocol::Quake(QuakeVersion::Three)),
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ pub mod ffow;
|
|||
pub mod jc2m;
|
||||
/// Minecraft
|
||||
pub mod minecraft;
|
||||
/// Savage 2
|
||||
pub mod savage2;
|
||||
/// The Ship
|
||||
pub mod theship;
|
||||
|
||||
|
|
@ -117,6 +119,9 @@ pub fn query_with_timeout_and_extra_settings(
|
|||
}
|
||||
Protocol::PROPRIETARY(protocol) => {
|
||||
match protocol {
|
||||
ProprietaryProtocol::Savage2 => {
|
||||
savage2::query_with_timeout(address, port, timeout_settings).map(Box::new)?
|
||||
}
|
||||
ProprietaryProtocol::TheShip => {
|
||||
theship::query_with_timeout(address, port, timeout_settings).map(Box::new)?
|
||||
}
|
||||
|
|
|
|||
8
crates/lib/src/games/savage2/mod.rs
Normal file
8
crates/lib/src/games/savage2/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/// The implementation.
|
||||
/// Reference: [Node-GameGig](https://github.com/gamedig/node-gamedig/blob/master/protocols/savage2.js)
|
||||
pub mod protocol;
|
||||
/// All types used by the implementation.
|
||||
pub mod types;
|
||||
|
||||
pub use protocol::*;
|
||||
pub use types::*;
|
||||
37
crates/lib/src/games/savage2/protocol.rs
Normal file
37
crates/lib/src/games/savage2/protocol.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use crate::buffer::{Buffer, Utf8Decoder};
|
||||
use crate::games::savage2::types::Response;
|
||||
use crate::protocols::types::TimeoutSettings;
|
||||
use crate::socket::{Socket, UdpSocket};
|
||||
use crate::GDResult;
|
||||
use byteorder::LittleEndian;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<Response> { query_with_timeout(address, port, None) }
|
||||
|
||||
pub fn query_with_timeout(
|
||||
address: &IpAddr,
|
||||
port: Option<u16>,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
) -> GDResult<Response> {
|
||||
let addr = &SocketAddr::new(*address, port.unwrap_or(11235));
|
||||
let mut socket = UdpSocket::new(addr, &timeout_settings)?;
|
||||
socket.send(&[0x01])?;
|
||||
let data = socket.receive(None)?;
|
||||
let mut buffer = Buffer::<LittleEndian>::new(&data);
|
||||
|
||||
buffer.move_cursor(12)?;
|
||||
|
||||
Ok(Response {
|
||||
name: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
players_online: buffer.read::<u8>()?,
|
||||
players_maximum: buffer.read::<u8>()?,
|
||||
time: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
map: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
next_map: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
location: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
players_minimum: buffer.read::<u8>()?,
|
||||
game_mode: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
protocol_version: buffer.read_string::<Utf8Decoder>(None)?,
|
||||
level_minimum: buffer.read::<u8>()?,
|
||||
})
|
||||
}
|
||||
30
crates/lib/src/games/savage2/types.rs
Normal file
30
crates/lib/src/games/savage2/types.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use crate::protocols::types::CommonResponse;
|
||||
use crate::protocols::GenericResponse;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Response {
|
||||
pub name: String,
|
||||
pub players_online: u8,
|
||||
pub players_maximum: u8,
|
||||
pub players_minimum: u8,
|
||||
pub time: String,
|
||||
pub map: String,
|
||||
pub next_map: String,
|
||||
pub location: String,
|
||||
pub game_mode: String,
|
||||
pub protocol_version: String,
|
||||
pub level_minimum: u8,
|
||||
}
|
||||
|
||||
impl CommonResponse for Response {
|
||||
fn as_original(&self) -> GenericResponse { GenericResponse::Savage2(self) }
|
||||
|
||||
fn name(&self) -> Option<&str> { Some(&self.name) }
|
||||
fn game_mode(&self) -> Option<&str> { Some(&self.game_mode) }
|
||||
fn map(&self) -> Option<&str> { Some(&self.map) }
|
||||
fn players_maximum(&self) -> u32 { self.players_maximum.into() }
|
||||
fn players_online(&self) -> u32 { self.players_online.into() }
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ pub enum ProprietaryProtocol {
|
|||
Minecraft(Option<minecraft::types::Server>),
|
||||
FFOW,
|
||||
JC2M,
|
||||
Savage2,
|
||||
}
|
||||
|
||||
/// Enumeration of all valid protocol types
|
||||
|
|
@ -48,6 +49,8 @@ pub enum GenericResponse<'a> {
|
|||
FFOW(&'a crate::games::ffow::Response),
|
||||
#[cfg(feature = "games")]
|
||||
JC2M(&'a crate::games::jc2m::Response),
|
||||
#[cfg(feature = "games")]
|
||||
Savage2(&'a crate::games::savage2::Response),
|
||||
}
|
||||
|
||||
/// All player types
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue