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"
|
bzip2-rs = "0.1"
|
||||||
crc32fast = "1.3"
|
crc32fast = "1.3"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
|
|
||||||
serde = { version = "1.0", optional = true }
|
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
|
/// Battalion 1944
|
||||||
pub mod battalion1944;
|
pub mod battalion1944;
|
||||||
|
/// Eco
|
||||||
|
pub mod eco;
|
||||||
/// Frontlines: Fuel of War
|
/// Frontlines: Fuel of War
|
||||||
pub mod ffow;
|
pub mod ffow;
|
||||||
/// Just Cause 2: Multiplayer
|
/// 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;
|
pub mod services;
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
mod http;
|
||||||
mod socket;
|
mod socket;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue