2023-10-08 09:25:14 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
|
|
|
|
|
|
|
|
use std::fs;
|
|
|
|
use std::io;
|
2023-11-02 05:19:21 +01:00
|
|
|
use std::net::{IpAddr, Ipv4Addr};
|
2023-10-08 09:25:14 +02:00
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
use log::LevelFilter;
|
|
|
|
use serde::{de::Error as _, Deserialize, Deserializer};
|
|
|
|
use thiserror::Error;
|
2023-10-17 18:15:14 +02:00
|
|
|
use xash3d_protocol::admin;
|
2023-10-17 09:21:52 +02:00
|
|
|
use xash3d_protocol::filter::Version;
|
2023-10-08 16:07:02 +02:00
|
|
|
|
2023-10-08 09:25:14 +02:00
|
|
|
pub const DEFAULT_MASTER_SERVER_IP: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
|
|
|
pub const DEFAULT_MASTER_SERVER_PORT: u16 = 27010;
|
2023-11-02 05:19:21 +01:00
|
|
|
pub const DEFAULT_CHALLENGE_TIMEOUT: u32 = 10;
|
2024-01-28 07:01:42 +01:00
|
|
|
pub const DEFAULT_SERVER_TIMEOUT: u32 = 300;
|
2023-10-18 07:41:14 +02:00
|
|
|
pub const DEFAULT_ADMIN_TIMEOUT: u32 = 10;
|
2023-10-08 09:25:14 +02:00
|
|
|
|
2024-01-28 08:28:04 +01:00
|
|
|
pub const DEFAULT_MAX_SERVERS_PER_IP: u16 = 14;
|
|
|
|
|
2024-01-28 07:01:42 +01:00
|
|
|
pub const DEFAULT_HASH_LEN: usize = admin::HASH_LEN;
|
|
|
|
|
|
|
|
macro_rules! impl_helpers {
|
|
|
|
($($f:ident: $t:ty),+$(,)?) => (
|
|
|
|
$(const fn $f<const N: $t>() -> $t { N })+
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_helpers! {
|
|
|
|
default_u16: u16,
|
|
|
|
default_u32: u32,
|
|
|
|
default_usize: usize,
|
|
|
|
}
|
|
|
|
|
2023-10-08 09:25:14 +02:00
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum Error {
|
|
|
|
#[error(transparent)]
|
|
|
|
Toml(#[from] toml::de::Error),
|
|
|
|
#[error(transparent)]
|
|
|
|
Io(#[from] io::Error),
|
|
|
|
}
|
|
|
|
|
2024-01-28 09:04:37 +01:00
|
|
|
#[derive(Default, Deserialize, Debug)]
|
2023-10-08 09:25:14 +02:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct Config {
|
|
|
|
#[serde(default)]
|
|
|
|
pub log: LogConfig,
|
|
|
|
#[serde(default)]
|
|
|
|
pub server: ServerConfig,
|
2023-10-08 16:07:02 +02:00
|
|
|
#[serde(default)]
|
|
|
|
pub client: ClientConfig,
|
2023-10-17 18:15:14 +02:00
|
|
|
#[serde(default)]
|
|
|
|
pub hash: HashConfig,
|
|
|
|
#[serde(rename = "admin")]
|
|
|
|
#[serde(default)]
|
|
|
|
pub admin_list: Box<[AdminConfig]>,
|
2024-04-07 09:52:38 +02:00
|
|
|
#[serde(default)]
|
|
|
|
pub stat: StatConfig,
|
2023-10-08 09:25:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct LogConfig {
|
|
|
|
#[serde(default = "default_log_level")]
|
|
|
|
#[serde(deserialize_with = "deserialize_log_level")]
|
|
|
|
pub level: LevelFilter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for LogConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
level: default_log_level(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
2024-01-28 08:28:04 +01:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
2023-10-08 09:25:14 +02:00
|
|
|
pub struct ServerConfig {
|
|
|
|
#[serde(default = "default_server_ip")]
|
|
|
|
pub ip: IpAddr,
|
2024-01-28 07:01:42 +01:00
|
|
|
#[serde(default = "default_u16::<DEFAULT_MASTER_SERVER_PORT>")]
|
2023-10-08 09:25:14 +02:00
|
|
|
pub port: u16,
|
2024-01-28 08:28:04 +01:00
|
|
|
#[serde(default = "default_u16::<DEFAULT_MAX_SERVERS_PER_IP>")]
|
|
|
|
pub max_servers_per_ip: u16,
|
2023-10-08 09:25:14 +02:00
|
|
|
#[serde(default)]
|
|
|
|
pub timeout: TimeoutConfig,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ServerConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
ip: default_server_ip(),
|
2024-01-28 07:01:42 +01:00
|
|
|
port: DEFAULT_MASTER_SERVER_PORT,
|
2024-01-28 08:28:04 +01:00
|
|
|
max_servers_per_ip: DEFAULT_MAX_SERVERS_PER_IP,
|
2023-10-08 09:25:14 +02:00
|
|
|
timeout: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct TimeoutConfig {
|
2024-01-28 07:01:42 +01:00
|
|
|
#[serde(default = "default_u32::<DEFAULT_CHALLENGE_TIMEOUT>")]
|
2023-10-08 09:25:14 +02:00
|
|
|
pub challenge: u32,
|
2024-01-28 07:01:42 +01:00
|
|
|
#[serde(default = "default_u32::<DEFAULT_SERVER_TIMEOUT>")]
|
2023-10-08 09:25:14 +02:00
|
|
|
pub server: u32,
|
2024-01-28 07:01:42 +01:00
|
|
|
#[serde(default = "default_u32::<DEFAULT_ADMIN_TIMEOUT>")]
|
2023-10-18 07:41:14 +02:00
|
|
|
pub admin: u32,
|
2023-10-08 09:25:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TimeoutConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2024-01-28 07:01:42 +01:00
|
|
|
challenge: DEFAULT_CHALLENGE_TIMEOUT,
|
|
|
|
server: DEFAULT_SERVER_TIMEOUT,
|
|
|
|
admin: DEFAULT_ADMIN_TIMEOUT,
|
2023-10-08 09:25:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 09:04:37 +01:00
|
|
|
#[derive(Deserialize, Debug)]
|
2023-10-08 16:07:02 +02:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct ClientConfig {
|
2024-04-15 08:50:02 +02:00
|
|
|
#[serde(default = "default_client_version")]
|
2023-10-08 16:07:02 +02:00
|
|
|
#[serde(deserialize_with = "deserialize_version")]
|
|
|
|
pub version: Version,
|
2024-04-15 08:50:02 +02:00
|
|
|
#[serde(default = "default_client_update_map")]
|
2023-10-08 16:07:02 +02:00
|
|
|
pub update_map: Box<str>,
|
2024-04-15 08:50:02 +02:00
|
|
|
#[serde(default = "default_client_update_title")]
|
2023-10-08 16:07:02 +02:00
|
|
|
pub update_title: Box<str>,
|
|
|
|
#[serde(default)]
|
2023-11-02 05:19:21 +01:00
|
|
|
pub update_addr: Option<Box<str>>,
|
2023-10-08 16:07:02 +02:00
|
|
|
}
|
|
|
|
|
2024-01-28 09:04:37 +01:00
|
|
|
impl Default for ClientConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2024-04-15 08:50:02 +02:00
|
|
|
version: default_client_version(),
|
|
|
|
update_map: default_client_update_map(),
|
|
|
|
update_title: default_client_update_title(),
|
2024-01-28 09:04:37 +01:00
|
|
|
update_addr: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
2023-10-17 18:15:14 +02:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct HashConfig {
|
2024-01-28 07:01:42 +01:00
|
|
|
#[serde(default = "default_usize::<DEFAULT_HASH_LEN>")]
|
2023-10-17 18:15:14 +02:00
|
|
|
pub len: usize,
|
|
|
|
#[serde(default = "default_hash_key")]
|
|
|
|
pub key: Box<str>,
|
|
|
|
#[serde(default = "default_hash_personal")]
|
|
|
|
pub personal: Box<str>,
|
|
|
|
}
|
|
|
|
|
2024-01-28 09:04:37 +01:00
|
|
|
impl Default for HashConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
len: DEFAULT_HASH_LEN,
|
|
|
|
key: default_hash_key(),
|
|
|
|
personal: default_hash_personal(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
2023-10-17 18:15:14 +02:00
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct AdminConfig {
|
|
|
|
pub name: Box<str>,
|
|
|
|
pub password: Box<str>,
|
|
|
|
}
|
|
|
|
|
2024-04-07 09:52:38 +02:00
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
|
|
#[serde(deny_unknown_fields)]
|
|
|
|
pub struct StatConfig {
|
|
|
|
pub interval: u32,
|
|
|
|
#[serde(default = "default_stats_format")]
|
|
|
|
pub format: Box<str>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for StatConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
interval: 0,
|
|
|
|
format: default_stats_format(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-08 09:25:14 +02:00
|
|
|
fn default_log_level() -> LevelFilter {
|
|
|
|
LevelFilter::Warn
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_server_ip() -> IpAddr {
|
|
|
|
DEFAULT_MASTER_SERVER_IP
|
|
|
|
}
|
|
|
|
|
2024-04-15 08:50:02 +02:00
|
|
|
fn default_client_version() -> Version {
|
|
|
|
Version::new(0, 19)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_client_update_map() -> Box<str> {
|
|
|
|
Box::from("Update please")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_client_update_title() -> Box<str> {
|
|
|
|
Box::from("https://github.com/FWGS/xash3d-fwgs")
|
|
|
|
}
|
|
|
|
|
2023-10-17 18:15:14 +02:00
|
|
|
fn default_hash_key() -> Box<str> {
|
|
|
|
Box::from(admin::HASH_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_hash_personal() -> Box<str> {
|
|
|
|
Box::from(admin::HASH_PERSONAL)
|
|
|
|
}
|
|
|
|
|
2024-04-07 09:52:38 +02:00
|
|
|
fn default_stats_format() -> Box<str> {
|
|
|
|
Box::from("stats: %s servers, %a add/s, %d del/s, %q query/s, %e error/s")
|
|
|
|
}
|
|
|
|
|
2023-10-08 09:25:14 +02:00
|
|
|
fn deserialize_log_level<'de, D>(de: D) -> Result<LevelFilter, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let s = <&str>::deserialize(de)?;
|
2023-10-08 16:07:02 +02:00
|
|
|
parse_log_level(s).ok_or_else(|| D::Error::custom(format!("Invalid log level: \"{}\"", s)))
|
2023-10-08 09:25:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_log_level(s: &str) -> Option<LevelFilter> {
|
|
|
|
use LevelFilter as E;
|
|
|
|
|
|
|
|
let level_filter = match s {
|
|
|
|
_ if "off".starts_with(s) => E::Off,
|
|
|
|
_ if "error".starts_with(s) => E::Error,
|
|
|
|
_ if "warn".starts_with(s) => E::Warn,
|
|
|
|
_ if "info".starts_with(s) => E::Info,
|
|
|
|
_ if "debug".starts_with(s) => E::Debug,
|
|
|
|
_ if "trace".starts_with(s) => E::Trace,
|
|
|
|
_ => match s.parse::<u8>() {
|
|
|
|
Ok(0) => E::Off,
|
|
|
|
Ok(1) => E::Error,
|
|
|
|
Ok(2) => E::Warn,
|
|
|
|
Ok(3) => E::Info,
|
|
|
|
Ok(4) => E::Debug,
|
|
|
|
Ok(5) => E::Trace,
|
|
|
|
_ => return None,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
Some(level_filter)
|
|
|
|
}
|
|
|
|
|
2023-10-08 16:07:02 +02:00
|
|
|
fn deserialize_version<'de, D>(de: D) -> Result<Version, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let s = <&str>::deserialize(de)?;
|
|
|
|
s.parse()
|
|
|
|
.map_err(|_| D::Error::custom(format!("Invalid version: \"{}\"", s)))
|
|
|
|
}
|
|
|
|
|
2023-10-08 09:25:14 +02:00
|
|
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config, Error> {
|
|
|
|
let data = fs::read(path)?;
|
|
|
|
Ok(toml::de::from_slice(&data)?)
|
|
|
|
}
|