mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +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
|
|
@ -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())?,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue