mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-05-06 15:27:28 +00:00
[Game] Add JC2MP support. (#54)
* [Game] Add JC2MP support. * [Game] Add game to changelog and games * [Games] Add generic support to JC2MP. * [Game] Add players_maximum and players_online
This commit is contained in:
parent
cb9384e474
commit
c3e2d948e8
9 changed files with 236 additions and 91 deletions
|
|
@ -12,6 +12,7 @@ Generic query:
|
|||
|
||||
Games:
|
||||
- [Halo: Combat Evolved](https://en.wikipedia.org/wiki/Halo:_Combat_Evolved) support.
|
||||
- [Just Cause 2: Multiplayer](https://store.steampowered.com/app/259080/Just_Cause_2_Multiplayer_Mod/) support.
|
||||
|
||||
### Breaking...
|
||||
Crate:
|
||||
|
|
|
|||
109
GAMES.md
109
GAMES.md
|
|
@ -2,60 +2,61 @@ A supported game is defined as a game that has been successfully tested, other g
|
|||
Beware of the `Notes` column, as it contains information about query port offsets or other query requirements.
|
||||
|
||||
# Supported games:
|
||||
| Game | Use name | Protocol | Notes |
|
||||
|------------------------------------|----------|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Team Fortress 2 | TF2 | Valve Protocol | |
|
||||
| The Ship | TS | Valve Protocol (*Altered) | |
|
||||
| Counter-Strike: Global Offensive | CSGO | Valve Protocol | The server must have the cvar `host_players_show` set to `2` to get the full player list. |
|
||||
| Counter-Strike: Source | CSS | Valve Protocol | |
|
||||
| Day of Defeat: Source | DODS | Valve Protocol | |
|
||||
| Left 4 Dead | L4D | Valve Protocol | |
|
||||
| Left 4 Dead 2 | L4D2 | Valve Protocol | |
|
||||
| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
|
||||
| Alien Swarm | ALIENS | Valve Protocol | |
|
||||
| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
|
||||
| Insurgency | INS | Valve Protocol | |
|
||||
| Insurgency: Sandstorm | INSS | Valve Protocol | Query port offset: 1. |
|
||||
| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
|
||||
| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
|
||||
| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
|
||||
| Minecraft | MC | Proprietary | Bedrock edition provides a different response compared to the Java edition, query specifically for bedrock to get them, otherwise, only matching fields will be provided. |
|
||||
| 7 Days To Die | SDTD | Valve Protocol | |
|
||||
| ARK: Survival Evolved | ASE | Valve Protocol | |
|
||||
| Unturned | UNTURNED | Valve Protocol | |
|
||||
| The Forest | TF | Valve Protocol (GoldSrc) | Query port offset: 1. |
|
||||
| Team Fortress Classic | TFC | Valve Protocol | |
|
||||
| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
|
||||
| Rust | RUST | Valve Protocol | |
|
||||
| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
|
||||
| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Query port offset: 1. |
|
||||
| Day of Infamy | DOI | Valve Protocol | |
|
||||
| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
|
||||
| Risk of Rain 2 | ROR2 | Valve Protocol | Query port offset: 1. |
|
||||
| Battalion 1944 | BAT1944 | Valve Protocol | Query port offset: 3. It is strongly recommended to also query the rules, as it sends basic server info in them. |
|
||||
| Black Mesa | BM | Valve Protocol | |
|
||||
| Project Zomboid | PZ | Valve Protocol | |
|
||||
| Age of Chivalry | AOC | Valve Protocol | |
|
||||
| Don't Starve Together | DST | Valve Protocol | Query port is 27016. |
|
||||
| Colony Survival | COLU | Valve Protocol | |
|
||||
| Onset | ONSET | Valve Protocol | Query port is 7776. |
|
||||
| Codename CURE | CCURE | Valve Protocol | |
|
||||
| Ballistic Overkill | BO | Valve Protocol | Query port is 27016. |
|
||||
| BrainBread 2 | BB2 | Valve Protocol | |
|
||||
| Avorion | AVORION | Valve Protocol | Query port is 27020. |
|
||||
| Operation: Harsh Doorstop | OHD | Valve Protocol | Query port is 27005. |
|
||||
| V Rising | VR | Valve Protocol | Query port is 27016. |
|
||||
| Unreal Tournament | UT | GameSpy 1 | Query Port offset: 1. |
|
||||
| Battlefield 1942 | BF1942 | GameSpy 1 | Query port is 23000. |
|
||||
| Serious Sam | SS | GameSpy 1 | Query Port offset: 1. |
|
||||
| Frontlines: Fuel of War | FFOW | Valve Protocol (Proprietary) | Query Port offset: 2. |
|
||||
| Crysis Wars | CW | GameSpy 3 | |
|
||||
| Quake 2 | QUAKE2 | Quake 2 | |
|
||||
| Quake 1 | QUAKE1 | Quake 1 | |
|
||||
| Quake 3: Arena | QUAKE3A | Quake 3 | |
|
||||
| Hell Let Loose | HLL | Valve Protocol | Query port is 26420. Note that on this port it might not send players data, as there might be another query port that does send players data. |
|
||||
| Soldier of Fortune 2 | SOF2 | Quake 3 | |
|
||||
| Halo: Combat Evolved | HALOCE | GameSpy 2 | |
|
||||
| Game | Use name | Protocol | Notes |
|
||||
|------------------------------------|----------|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Team Fortress 2 | TF2 | Valve Protocol | |
|
||||
| The Ship | TS | Valve Protocol (*Altered) | |
|
||||
| Counter-Strike: Global Offensive | CSGO | Valve Protocol | The server must have the cvar `host_players_show` set to `2` to get the full player list. |
|
||||
| Counter-Strike: Source | CSS | Valve Protocol | |
|
||||
| Day of Defeat: Source | DODS | Valve Protocol | |
|
||||
| Left 4 Dead | L4D | Valve Protocol | |
|
||||
| Left 4 Dead 2 | L4D2 | Valve Protocol | |
|
||||
| Half-Life 2 Deathmatch | HL2DM | Valve Protocol | |
|
||||
| Alien Swarm | ALIENS | Valve Protocol | |
|
||||
| Alien Swarm: Reactive Drop | ASRD | Valve Protocol | |
|
||||
| Insurgency | INS | Valve Protocol | |
|
||||
| Insurgency: Sandstorm | INSS | Valve Protocol | Query port offset: 1. |
|
||||
| Insurgency: Modern Infantry Combat | INSMIC | Valve Protocol | |
|
||||
| Counter-Strike: Condition Zero | CSCZ | Valve Protocol (GoldSrc) | |
|
||||
| Day of Defeat | DOD | Valve Protocol (GoldSrc) | |
|
||||
| Minecraft | MC | Proprietary | Bedrock edition provides a different response compared to the Java edition, query specifically for bedrock to get them, otherwise, only matching fields will be provided. |
|
||||
| 7 Days To Die | SDTD | Valve Protocol | |
|
||||
| ARK: Survival Evolved | ASE | Valve Protocol | |
|
||||
| Unturned | UNTURNED | Valve Protocol | |
|
||||
| The Forest | TF | Valve Protocol (GoldSrc) | Query port offset: 1. |
|
||||
| Team Fortress Classic | TFC | Valve Protocol | |
|
||||
| Sven Co-op | SC | Valve Protocol (GoldSrc) | |
|
||||
| Rust | RUST | Valve Protocol | |
|
||||
| Counter-Strike | CS | Valve Protocol (GoldSrc) | |
|
||||
| Arma 2: Operation Arrowhead | ARMA2OA | Valve Protocol | Query port offset: 1. |
|
||||
| Day of Infamy | DOI | Valve Protocol | |
|
||||
| Half-Life Deathmatch: Source | HLDMS | Valve Protocol | |
|
||||
| Risk of Rain 2 | ROR2 | Valve Protocol | Query port offset: 1. |
|
||||
| Battalion 1944 | BAT1944 | Valve Protocol | Query port offset: 3. It is strongly recommended to also query the rules, as it sends basic server info in them. |
|
||||
| Black Mesa | BM | Valve Protocol | |
|
||||
| Project Zomboid | PZ | Valve Protocol | |
|
||||
| Age of Chivalry | AOC | Valve Protocol | |
|
||||
| Don't Starve Together | DST | Valve Protocol | Query port is 27016. |
|
||||
| Colony Survival | COLU | Valve Protocol | |
|
||||
| Onset | ONSET | Valve Protocol | Query port is 7776. |
|
||||
| Codename CURE | CCURE | Valve Protocol | |
|
||||
| Ballistic Overkill | BO | Valve Protocol | Query port is 27016. |
|
||||
| BrainBread 2 | BB2 | Valve Protocol | |
|
||||
| Avorion | AVORION | Valve Protocol | Query port is 27020. |
|
||||
| Operation: Harsh Doorstop | OHD | Valve Protocol | Query port is 27005. |
|
||||
| V Rising | VR | Valve Protocol | Query port is 27016. |
|
||||
| Unreal Tournament | UT | GameSpy 1 | Query Port offset: 1. |
|
||||
| Battlefield 1942 | BF1942 | GameSpy 1 | Query port is 23000. |
|
||||
| Serious Sam | SS | GameSpy 1 | Query Port offset: 1. |
|
||||
| Frontlines: Fuel of War | FFOW | Valve Protocol (*Altered) | Query Port offset: 2. |
|
||||
| Crysis Wars | CW | GameSpy 3 | |
|
||||
| Quake 2 | QUAKE2 | Quake 2 | |
|
||||
| Quake 1 | QUAKE1 | Quake 1 | |
|
||||
| Quake 3: Arena | QUAKE3A | Quake 3 | |
|
||||
| Hell Let Loose | HLL | Valve Protocol | Query port is 26420. Note that on this port it might not send players data, as there might be another query port that does send players data. |
|
||||
| Soldier of Fortune 2 | SOF2 | Quake 3 | |
|
||||
| Halo: Combat Evolved | HALOCE | GameSpy 2 | |
|
||||
| Just Cause 2: Multiplayer | JC2MP | GameSpy 3 (*Altered) | |
|
||||
|
||||
## Planned to add support:
|
||||
_
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use gamedig::{
|
|||
ins,
|
||||
insmic,
|
||||
inss,
|
||||
jc2mp,
|
||||
l4d,
|
||||
l4d2,
|
||||
mc,
|
||||
|
|
@ -189,6 +190,7 @@ fn main() -> GDResult<()> {
|
|||
"sof2" => println!("{:#?}", sof2::query(ip, port)?),
|
||||
"_gamespy2" => println!("{:#?}", gamespy::two::query(address, None)),
|
||||
"haloce" => println!("{:#?}", haloce::query(ip, port)?),
|
||||
"jc2mp" => println!("{:#?}", jc2mp::query(ip, port)?),
|
||||
_ => panic!("Undefined game: {}", args[1]),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -81,4 +81,5 @@ pub static GAMES: Map<&'static str, Game> = phf_map! {
|
|||
"unturned" => game!("Unturned", 27015, Protocol::Valve(SteamApp::UNTURNED)),
|
||||
"ut" => game!("Unreal Tournament", 7778, Protocol::Gamespy(GameSpyVersion::One)),
|
||||
"vr" => game!("V Rising", 27016, Protocol::Valve(SteamApp::VR)),
|
||||
"jc2mp" => game!("Just Cause 2: Multiplayer", 7777, Protocol::JC2MP),
|
||||
};
|
||||
|
|
|
|||
106
src/games/jc2mp.rs
Normal file
106
src/games/jc2mp.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
use crate::bufferer::{Bufferer, Endianess};
|
||||
use crate::protocols::gamespy::common::has_password;
|
||||
use crate::protocols::gamespy::three::{data_to_map, GameSpy3};
|
||||
use crate::protocols::types::SpecificResponse;
|
||||
use crate::protocols::GenericResponse;
|
||||
use crate::{GDError, GDResult};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Player {
|
||||
name: String,
|
||||
steam_id: String,
|
||||
ping: u16,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Response {
|
||||
version: String,
|
||||
description: String,
|
||||
name: String,
|
||||
has_password: bool,
|
||||
players: Vec<Player>,
|
||||
players_maximum: usize,
|
||||
players_online: usize,
|
||||
}
|
||||
|
||||
impl From<Response> for GenericResponse {
|
||||
fn from(r: Response) -> Self {
|
||||
Self {
|
||||
name: Some(r.name),
|
||||
description: Some(r.description),
|
||||
game: None,
|
||||
game_version: Some(r.version),
|
||||
map: None,
|
||||
players_maximum: r.players_maximum as u64,
|
||||
players_online: r.players_online as u64,
|
||||
players_bots: None,
|
||||
has_password: Some(r.has_password),
|
||||
inner: SpecificResponse::JC2MP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_players_and_teams(packet: Vec<u8>) -> GDResult<Vec<Player>> {
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Big, &packet);
|
||||
|
||||
let count = buf.get_u16()?;
|
||||
let mut players = Vec::with_capacity(count as usize);
|
||||
|
||||
while buf.remaining_length() > 0 {
|
||||
players.push(Player {
|
||||
name: buf.get_string_utf8()?,
|
||||
steam_id: buf.get_string_utf8()?,
|
||||
ping: buf.get_u16()?,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(players)
|
||||
}
|
||||
|
||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<Response> {
|
||||
let mut client = GameSpy3::new_custom(
|
||||
&SocketAddr::new(*address, port.unwrap_or(7777)),
|
||||
None,
|
||||
[0xFF, 0xFF, 0xFF, 0x02],
|
||||
true,
|
||||
)?;
|
||||
|
||||
let packets = client.get_server_packets()?;
|
||||
let data = packets.get(0).ok_or(GDError::PacketBad)?;
|
||||
|
||||
let (mut server_vars, remaining_data) = data_to_map(data)?;
|
||||
let players = parse_players_and_teams(remaining_data)?;
|
||||
|
||||
let players_maximum = server_vars
|
||||
.remove("maxplayers")
|
||||
.ok_or(GDError::PacketBad)?
|
||||
.parse()
|
||||
.map_err(|_| GDError::TypeParse)?;
|
||||
let players_online = match server_vars.remove("numplayers") {
|
||||
None => players.len(),
|
||||
Some(v) => {
|
||||
let reported_players = v.parse().map_err(|_| GDError::TypeParse)?;
|
||||
match reported_players < players.len() {
|
||||
true => players.len(),
|
||||
false => reported_players,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Response {
|
||||
version: server_vars.remove("version").ok_or(GDError::PacketBad)?,
|
||||
description: server_vars
|
||||
.remove("description")
|
||||
.ok_or(GDError::PacketBad)?,
|
||||
name: server_vars.remove("hostname").ok_or(GDError::PacketBad)?,
|
||||
has_password: has_password(&mut server_vars)?,
|
||||
players,
|
||||
players_maximum,
|
||||
players_online,
|
||||
})
|
||||
}
|
||||
|
|
@ -65,6 +65,8 @@ pub mod ins;
|
|||
pub mod insmic;
|
||||
/// Insurgency: Sandstorm
|
||||
pub mod inss;
|
||||
/// Just Cause 2: Multiplayer
|
||||
pub mod jc2mp;
|
||||
/// Left 4 Dead
|
||||
pub mod l4d;
|
||||
/// Left 4 Dead 2
|
||||
|
|
@ -171,5 +173,6 @@ pub fn query(game: &Game, address: &IpAddr, port: Option<u16>) -> GDResult<proto
|
|||
}
|
||||
Protocol::TheShip => ts::query(address, port).map(|r| r.into())?,
|
||||
Protocol::FFOW => ffow::query(address, port).map(|r| r.into())?,
|
||||
Protocol::JC2MP => jc2mp::query(address, port).map(|r| r.into())?,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod common;
|
||||
pub(crate) mod common;
|
||||
/// The implementations.
|
||||
pub mod protocols;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,18 +36,41 @@ impl RequestPacket {
|
|||
}
|
||||
}
|
||||
|
||||
struct GameSpy3 {
|
||||
pub(crate) struct GameSpy3 {
|
||||
socket: UdpSocket,
|
||||
payload: [u8; 4],
|
||||
single_packets: bool,
|
||||
}
|
||||
|
||||
const PACKET_SIZE: usize = 2048;
|
||||
const DEFAULT_PAYLOAD: [u8; 4] = [0xFF, 0xFF, 0xFF, 0x01];
|
||||
|
||||
impl GameSpy3 {
|
||||
fn new(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Self> {
|
||||
let socket = UdpSocket::new(address)?;
|
||||
socket.apply_timeout(timeout_settings)?;
|
||||
|
||||
Ok(Self { socket })
|
||||
Ok(Self {
|
||||
socket,
|
||||
payload: DEFAULT_PAYLOAD,
|
||||
single_packets: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn new_custom(
|
||||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
payload: [u8; 4],
|
||||
single_packets: bool,
|
||||
) -> GDResult<Self> {
|
||||
let socket = UdpSocket::new(address)?;
|
||||
socket.apply_timeout(timeout_settings)?;
|
||||
|
||||
Ok(Self {
|
||||
socket,
|
||||
payload,
|
||||
single_packets,
|
||||
})
|
||||
}
|
||||
|
||||
fn receive(&mut self, size: Option<usize>, kind: u8) -> GDResult<Bufferer> {
|
||||
|
|
@ -97,54 +120,57 @@ impl GameSpy3 {
|
|||
kind: 0,
|
||||
session_id: THIS_SESSION_ID,
|
||||
challenge,
|
||||
payload: Some([0xff, 0xff, 0xff, 0x01]),
|
||||
payload: Some(self.payload),
|
||||
}
|
||||
.to_bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_server_packets(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Vec<Vec<u8>>> {
|
||||
let mut gs3 = GameSpy3::new(address, timeout_settings)?;
|
||||
pub(crate) fn get_server_packets(&mut self) -> GDResult<Vec<Vec<u8>>> {
|
||||
let challenge = self.make_initial_handshake()?;
|
||||
self.send_data_request(challenge)?;
|
||||
|
||||
let challenge = gs3.make_initial_handshake()?;
|
||||
gs3.send_data_request(challenge)?;
|
||||
let mut values: Vec<Vec<u8>> = Vec::new();
|
||||
|
||||
let mut values: Vec<Vec<u8>> = Vec::new();
|
||||
let mut expected_number_of_packets: Option<usize> = None;
|
||||
|
||||
let mut expected_number_of_packets: Option<usize> = None;
|
||||
while expected_number_of_packets.is_none() || values.len() != expected_number_of_packets.unwrap() {
|
||||
let mut buf = self.receive(None, 0)?;
|
||||
|
||||
while expected_number_of_packets.is_none() || values.len() != expected_number_of_packets.unwrap() {
|
||||
let mut buf = gs3.receive(None, 0)?;
|
||||
if self.single_packets {
|
||||
buf.move_position_ahead(11);
|
||||
return Ok(vec![buf.remaining_data_vec()]);
|
||||
}
|
||||
|
||||
if buf.get_string_utf8()? != "splitnum" {
|
||||
if buf.get_string_utf8()? != "splitnum" {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
let id = buf.get_u8()?;
|
||||
let is_last = (id & 0x80) > 0;
|
||||
let packet_id = (id & 0x7f) as usize;
|
||||
buf.move_position_ahead(1); //unknown byte regarding packet no.
|
||||
|
||||
if is_last {
|
||||
expected_number_of_packets = Some(packet_id + 1);
|
||||
}
|
||||
|
||||
while values.len() <= packet_id {
|
||||
values.push(Vec::new());
|
||||
}
|
||||
|
||||
values[packet_id] = buf.remaining_data_vec();
|
||||
}
|
||||
|
||||
if values.iter().any(|v| v.is_empty()) {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
let id = buf.get_u8()?;
|
||||
let is_last = (id & 0x80) > 0;
|
||||
let packet_id = (id & 0x7f) as usize;
|
||||
buf.move_position_ahead(1); //unknown byte regarding packet no.
|
||||
|
||||
if is_last {
|
||||
expected_number_of_packets = Some(packet_id + 1);
|
||||
}
|
||||
|
||||
while values.len() <= packet_id {
|
||||
values.push(Vec::new());
|
||||
}
|
||||
|
||||
values[packet_id] = buf.remaining_data_vec();
|
||||
Ok(values)
|
||||
}
|
||||
|
||||
if values.iter().any(|v| v.is_empty()) {
|
||||
return Err(GDError::PacketBad);
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
|
||||
fn data_to_map(packet: &[u8]) -> GDResult<(HashMap<String, String>, Vec<u8>)> {
|
||||
pub(crate) fn data_to_map(packet: &[u8]) -> GDResult<(HashMap<String, String>, Vec<u8>)> {
|
||||
let mut vars = HashMap::new();
|
||||
|
||||
let mut buf = Bufferer::new_with_data(Endianess::Big, packet);
|
||||
|
|
@ -168,7 +194,8 @@ pub fn query_vars(
|
|||
address: &SocketAddr,
|
||||
timeout_settings: Option<TimeoutSettings>,
|
||||
) -> GDResult<HashMap<String, String>> {
|
||||
let packets = get_server_packets(address, timeout_settings)?;
|
||||
let mut client = GameSpy3::new(address, timeout_settings)?;
|
||||
let packets = client.get_server_packets()?;
|
||||
|
||||
let mut vars = HashMap::new();
|
||||
|
||||
|
|
@ -308,7 +335,8 @@ fn parse_players_and_teams(packets: Vec<Vec<u8>>) -> GDResult<(Vec<Player>, Vec<
|
|||
/// Providing None to the timeout settings results in using the default values.
|
||||
/// (TimeoutSettings::[default](TimeoutSettings::default)).
|
||||
pub fn query(address: &SocketAddr, timeout_settings: Option<TimeoutSettings>) -> GDResult<Response> {
|
||||
let packets = get_server_packets(address, timeout_settings)?;
|
||||
let mut client = GameSpy3::new(address, timeout_settings)?;
|
||||
let packets = client.get_server_packets()?;
|
||||
|
||||
let (mut server_vars, remaining_data) = data_to_map(packets.get(0).ok_or(GDError::PacketBad)?)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub enum Protocol {
|
|||
Valve(valve::SteamApp),
|
||||
TheShip,
|
||||
FFOW,
|
||||
JC2MP,
|
||||
}
|
||||
|
||||
/// A generic version of a response
|
||||
|
|
@ -56,6 +57,8 @@ pub enum SpecificResponse {
|
|||
TheShip(crate::games::ts::ExtraResponse),
|
||||
#[cfg(not(feature = "no_games"))]
|
||||
FFOW(crate::games::ffow::ExtraResponse),
|
||||
#[cfg(not(feature = "no_games"))]
|
||||
JC2MP,
|
||||
}
|
||||
|
||||
/// Timeout settings for socket operations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue