Skip to content

Commit

Permalink
feat: initial http and eco support
Browse files Browse the repository at this point in the history
  • Loading branch information
CosminPerRam committed Jan 1, 2024
1 parent bd3727d commit 285bd7f
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down
10 changes: 10 additions & 0 deletions crates/lib/examples/test_eco.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions crates/lib/src/games/eco/mod.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions crates/lib/src/games/eco/protocol.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 190 additions & 0 deletions crates/lib/src/games/eco/types.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
}
2 changes: 2 additions & 0 deletions crates/lib/src/games/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions crates/lib/src/http.rs
Original file line number Diff line number Diff line change
@@ -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))
}
}
1 change: 1 addition & 0 deletions crates/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub mod protocols;
pub mod services;

mod buffer;
mod http;
mod socket;
mod utils;

Expand Down

0 comments on commit 285bd7f

Please sign in to comment.