mirror of
https://github.com/tribufu/rust-gamedig
synced 2026-06-01 09:42:41 +00:00
feat: initial http and eco support
This commit is contained in:
parent
bd3727d7fe
commit
285bd7fe6e
8 changed files with 263 additions and 0 deletions
|
|
@ -29,7 +29,9 @@ byteorder = "1.5"
|
|||
bzip2-rs = "0.1"
|
||||
crc32fast = "1.3"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
encoding_rs = "0.8"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
|
|
|
|||
10
crates/lib/examples/test_eco.rs
Normal file
10
crates/lib/examples/test_eco.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
use gamedig::games::eco;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn main() {
|
||||
let ip = IpAddr::from_str("142.132.154.69").unwrap();
|
||||
let port = 31111;
|
||||
let r = eco::query(&ip, Some(port));
|
||||
println!("{:#?}", r);
|
||||
}
|
||||
8
crates/lib/src/games/eco/mod.rs
Normal file
8
crates/lib/src/games/eco/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/// The implementation.
|
||||
/// Reference: [Node-GameGig](https://github.com/gamedig/node-gamedig/blob/master/protocols/ffow.js)
|
||||
pub mod protocol;
|
||||
/// All types used by the implementation.
|
||||
pub mod types;
|
||||
|
||||
pub use protocol::*;
|
||||
pub use types::*;
|
||||
13
crates/lib/src/games/eco/protocol.rs
Normal file
13
crates/lib/src/games/eco/protocol.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use crate::eco::{Response, Root};
|
||||
use crate::http::HttpClient;
|
||||
use crate::{GDResult, TimeoutSettings};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
pub fn query(address: &IpAddr, port: Option<u16>) -> GDResult<Response> {
|
||||
let address = &SocketAddr::new(*address, port.unwrap_or(3001));
|
||||
let mut client = HttpClient::new(address, &Some(TimeoutSettings::default()))?;
|
||||
|
||||
let response = client.request::<Root>("/frontpage")?;
|
||||
|
||||
Ok(response.into())
|
||||
}
|
||||
190
crates/lib/src/games/eco/types.rs
Normal file
190
crates/lib/src/games/eco/types.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
use serde_derive::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Root {
|
||||
#[serde(rename = "Info")]
|
||||
pub info: Info,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Info {
|
||||
#[serde(rename = "External")]
|
||||
pub external: bool,
|
||||
#[serde(rename = "GamePort")]
|
||||
pub game_port: u32,
|
||||
#[serde(rename = "WebPort")]
|
||||
pub web_port: u32,
|
||||
#[serde(rename = "IsLAN")]
|
||||
pub is_lan: bool,
|
||||
#[serde(rename = "Description")]
|
||||
pub description: String,
|
||||
#[serde(rename = "DetailedDescription")]
|
||||
pub detailed_description: String,
|
||||
#[serde(rename = "Category")]
|
||||
pub category: String,
|
||||
#[serde(rename = "OnlinePlayers")]
|
||||
pub online_players: u32,
|
||||
#[serde(rename = "TotalPlayers")]
|
||||
pub total_players: u32,
|
||||
#[serde(rename = "OnlinePlayersNames")]
|
||||
pub online_players_names: Vec<String>,
|
||||
#[serde(rename = "AdminOnline")]
|
||||
pub admin_online: bool,
|
||||
#[serde(rename = "TimeSinceStart")]
|
||||
pub time_since_start: f64,
|
||||
#[serde(rename = "TimeLeft")]
|
||||
pub time_left: f64,
|
||||
#[serde(rename = "Animals")]
|
||||
pub animals: u32,
|
||||
#[serde(rename = "Plants")]
|
||||
pub plants: u32,
|
||||
#[serde(rename = "Laws")]
|
||||
pub laws: u32,
|
||||
#[serde(rename = "WorldSize")]
|
||||
pub world_size: String,
|
||||
#[serde(rename = "Version")]
|
||||
pub version: String,
|
||||
#[serde(rename = "EconomyDesc")]
|
||||
pub economy_desc: String,
|
||||
#[serde(rename = "SkillSpecializationSetting")]
|
||||
pub skill_specialization_setting: String,
|
||||
#[serde(rename = "Language")]
|
||||
pub language: String,
|
||||
#[serde(rename = "HasPassword")]
|
||||
pub has_password: bool,
|
||||
#[serde(rename = "HasMeteor")]
|
||||
pub has_meteor: bool,
|
||||
#[serde(rename = "DistributionStationItems")]
|
||||
pub distribution_station_items: String,
|
||||
#[serde(rename = "Playtimes")]
|
||||
pub playtimes: String,
|
||||
#[serde(rename = "DiscordAddress")]
|
||||
pub discord_address: String,
|
||||
#[serde(rename = "IsPaused")]
|
||||
pub is_paused: bool,
|
||||
#[serde(rename = "ActiveAndOnlinePlayers")]
|
||||
pub active_and_online_players: u32,
|
||||
#[serde(rename = "PeakActivePlayers")]
|
||||
pub peak_active_players: u32,
|
||||
#[serde(rename = "MaxActivePlayers")]
|
||||
pub max_active_players: u32,
|
||||
#[serde(rename = "ShelfLifeMultiplier")]
|
||||
pub shelf_life_multiplier: f64,
|
||||
#[serde(rename = "ExhaustionAfterHours")]
|
||||
pub exhaustion_after_hours: f64,
|
||||
#[serde(rename = "IsLimitingHours")]
|
||||
pub is_limiting_hours: bool,
|
||||
#[serde(rename = "ServerAchievementsDict")]
|
||||
pub server_achievements_dict: HashMap<String, String>,
|
||||
#[serde(rename = "RelayAddress")]
|
||||
pub relay_address: String,
|
||||
#[serde(rename = "Access")]
|
||||
pub access: String,
|
||||
#[serde(rename = "JoinUrl")]
|
||||
pub join_url: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Response {
|
||||
pub external: bool,
|
||||
pub port: u32,
|
||||
pub query_port: u32,
|
||||
pub is_lan: bool,
|
||||
pub description: String, // this and other fields require some text filtering
|
||||
pub description_detailed: String,
|
||||
pub description_economy: String,
|
||||
pub category: String,
|
||||
pub players_online: u32,
|
||||
pub players_maximum: u32,
|
||||
pub players: Vec<Player>,
|
||||
pub admin_online: bool,
|
||||
pub time_since_start: f64,
|
||||
pub time_left: f64,
|
||||
pub animals: u32,
|
||||
pub plants: u32,
|
||||
pub laws: u32,
|
||||
pub world_size: String,
|
||||
pub game_version: String,
|
||||
pub skill_specialization_setting: String,
|
||||
pub language: String,
|
||||
pub has_password: bool,
|
||||
pub has_meteor: bool,
|
||||
pub distribution_station_items: String,
|
||||
pub playtimes: String,
|
||||
pub discord_address: String,
|
||||
pub is_paused: bool,
|
||||
pub active_and_online_players: u32,
|
||||
pub peak_active_players: u32,
|
||||
pub max_active_players: u32,
|
||||
pub shelf_life_multiplier: f64,
|
||||
pub exhaustion_after_hours: f64,
|
||||
pub is_limiting_hours: bool,
|
||||
pub server_achievements_dict: HashMap<String, String>,
|
||||
pub relay_address: String,
|
||||
pub access: String,
|
||||
pub connect: String,
|
||||
}
|
||||
|
||||
impl From<Root> for Response {
|
||||
fn from(root: Root) -> Self {
|
||||
let value = root.info;
|
||||
Self {
|
||||
external: value.external,
|
||||
port: value.game_port,
|
||||
query_port: value.web_port,
|
||||
is_lan: value.is_lan,
|
||||
description: value.description,
|
||||
description_detailed: value.detailed_description,
|
||||
description_economy: value.economy_desc,
|
||||
category: value.category,
|
||||
players_online: value.online_players,
|
||||
players_maximum: value.total_players,
|
||||
players: value
|
||||
.online_players_names
|
||||
.iter()
|
||||
.map(|player| {
|
||||
Player {
|
||||
name: player.clone(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
admin_online: value.admin_online,
|
||||
time_since_start: value.time_since_start,
|
||||
time_left: value.time_left,
|
||||
animals: value.animals,
|
||||
plants: value.plants,
|
||||
laws: value.laws,
|
||||
world_size: value.world_size,
|
||||
game_version: value.version,
|
||||
skill_specialization_setting: value.skill_specialization_setting,
|
||||
language: value.language,
|
||||
has_password: value.has_password,
|
||||
has_meteor: value.has_meteor,
|
||||
distribution_station_items: value.distribution_station_items,
|
||||
playtimes: value.playtimes,
|
||||
discord_address: value.discord_address,
|
||||
is_paused: value.is_paused,
|
||||
active_and_online_players: value.active_and_online_players,
|
||||
peak_active_players: value.peak_active_players,
|
||||
max_active_players: value.max_active_players,
|
||||
shelf_life_multiplier: value.shelf_life_multiplier,
|
||||
exhaustion_after_hours: value.exhaustion_after_hours,
|
||||
is_limiting_hours: value.is_limiting_hours,
|
||||
server_achievements_dict: value.server_achievements_dict,
|
||||
relay_address: value.relay_address,
|
||||
access: value.access,
|
||||
connect: value.join_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ pub use valve::*;
|
|||
|
||||
/// Battalion 1944
|
||||
pub mod battalion1944;
|
||||
/// Eco
|
||||
pub mod eco;
|
||||
/// Frontlines: Fuel of War
|
||||
pub mod ffow;
|
||||
/// Just Cause 2: Multiplayer
|
||||
|
|
|
|||
37
crates/lib/src/http.rs
Normal file
37
crates/lib/src/http.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use crate::GDErrorKind::{PacketSend, ProtocolFormat, SocketConnect};
|
||||
use crate::{GDResult, TimeoutSettings};
|
||||
use reqwest::blocking::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub struct HttpClient {
|
||||
client: Client,
|
||||
address: String,
|
||||
}
|
||||
|
||||
impl HttpClient {
|
||||
pub fn new(address: &SocketAddr, timeout_settings: &Option<TimeoutSettings>) -> GDResult<Self>
|
||||
where Self: Sized {
|
||||
let client = Client::builder()
|
||||
.connect_timeout(TimeoutSettings::get_connect_or_default(timeout_settings))
|
||||
.timeout(TimeoutSettings::get_connect_or_default(timeout_settings))
|
||||
.build()
|
||||
.map_err(|e| SocketConnect.context(e))?;
|
||||
|
||||
Ok(Self {
|
||||
client,
|
||||
address: format!("http://{}:{}", address.ip(), address.port()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn concat_path(&self, path: &str) -> String { format!("{}{}", self.address, path) }
|
||||
|
||||
pub fn request<T: DeserializeOwned>(&mut self, path: &str) -> GDResult<T> {
|
||||
self.client
|
||||
.get(self.concat_path(path))
|
||||
.send()
|
||||
.map_err(|e| PacketSend.context(e))?
|
||||
.json::<T>()
|
||||
.map_err(|e| ProtocolFormat.context(e))
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@ pub mod protocols;
|
|||
pub mod services;
|
||||
|
||||
mod buffer;
|
||||
mod http;
|
||||
mod socket;
|
||||
mod utils;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue