Files
lazymc/src/config.rs
2021-11-17 20:31:22 +01:00

466 lines
11 KiB
Rust

use std::fs;
use std::io;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use clap::ArgMatches;
use serde::Deserialize;
use version_compare::Cmp;
use crate::proto;
use crate::util::error::{quit_error, quit_error_msg, ErrorHintsBuilder};
use crate::util::serde::to_socket_addrs;
/// Default configuration file location.
pub const CONFIG_FILE: &str = "lazymc.toml";
/// Configuration version user should be using, or warning will be shown.
const CONFIG_VERSION: &str = "0.2.1";
/// Load config from file, based on CLI arguments.
///
/// Quits with an error message on failure.
pub fn load(matches: &ArgMatches) -> Config {
// Get config path, attempt to canonicalize
let mut path = PathBuf::from(matches.value_of("config").unwrap());
if let Ok(p) = path.canonicalize() {
path = p;
}
// Ensure configuration file exists
if !path.is_file() {
quit_error_msg(
format!(
"Config file does not exist: {}",
path.to_str().unwrap_or("?")
),
ErrorHintsBuilder::default()
.config(true)
.config_generate(true)
.build()
.unwrap(),
);
}
// Load config
let config = match Config::load(path) {
Ok(config) => config,
Err(err) => {
quit_error(
anyhow!(err).context("Failed to load config"),
ErrorHintsBuilder::default()
.config(true)
.config_test(true)
.build()
.unwrap(),
);
}
};
config
}
/// Configuration.
#[derive(Debug, Deserialize)]
pub struct Config {
/// Public configuration.
#[serde(default)]
pub public: Public,
/// Server configuration.
pub server: Server,
/// Time configuration.
#[serde(default)]
pub time: Time,
/// MOTD configuration.
#[serde(default)]
pub motd: Motd,
/// Join configuration.
#[serde(default)]
pub join: Join,
/// Lockout feature.
#[serde(default)]
pub lockout: Lockout,
/// RCON configuration.
#[serde(default)]
pub rcon: Rcon,
/// Advanced configuration.
#[serde(default)]
pub advanced: Advanced,
/// Config configuration.
#[serde(default)]
pub config: ConfigConfig,
}
impl Config {
/// Load configuration from file.
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
let data = fs::read(path)?;
let config: Config = toml::from_slice(&data)?;
// Show warning if config version is problematic
match &config.config.version {
None => warn!(target: "lazymc::config", "Config version unknown, it may be outdated"),
Some(version) => match version_compare::compare_to(version, CONFIG_VERSION, Cmp::Ge) {
Ok(false) => {
warn!(target: "lazymc::config", "Config is for older lazymc version, you may need to update it")
}
Err(_) => {
warn!(target: "lazymc::config", "Config version is invalid, you may need to update it")
}
Ok(true) => {}
},
}
Ok(config)
}
}
/// Public configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Public {
/// Public address.
#[serde(deserialize_with = "to_socket_addrs")]
pub address: SocketAddr,
/// Minecraft protocol version name hint.
pub version: String,
/// Minecraft protocol version hint.
pub protocol: u32,
}
impl Default for Public {
fn default() -> Self {
Self {
address: "0.0.0.0:25565".parse().unwrap(),
version: proto::PROTO_DEFAULT_VERSION.to_string(),
protocol: proto::PROTO_DEFAULT_PROTOCOL,
}
}
}
/// Server configuration.
#[derive(Debug, Deserialize)]
pub struct Server {
/// Server directory.
#[serde(default = "option_pathbuf_dot")]
pub directory: Option<PathBuf>,
/// Start command.
pub command: String,
/// Server address.
#[serde(
deserialize_with = "to_socket_addrs",
default = "server_address_default"
)]
pub address: SocketAddr,
/// Immediately wake server when starting lazymc.
#[serde(default)]
pub wake_on_start: bool,
/// Immediately wake server after crash.
#[serde(default)]
pub wake_on_crash: bool,
/// Server starting timeout. Force kill server process if it takes longer.
#[serde(default = "u32_300")]
pub start_timeout: u32,
/// Server stopping timeout. Force kill server process if it takes longer.
#[serde(default = "u32_150")]
pub stop_timeout: u32,
/// Block banned IPs as listed in banned-ips.json in server directory.
#[serde(default = "bool_true")]
pub block_banned_ips: bool,
/// Drop connections from banned IPs.
#[serde(default)]
pub drop_banned_ips: bool,
}
/// Time configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Time {
/// Sleep after number of seconds.
pub sleep_after: u32,
/// Minimum time in seconds to stay online when server is started.
#[serde(default, alias = "minimum_online_time")]
pub min_online_time: u32,
}
impl Default for Time {
fn default() -> Self {
Self {
sleep_after: 60,
min_online_time: 60,
}
}
}
/// MOTD configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Motd {
/// MOTD when server is sleeping.
pub sleeping: String,
/// MOTD when server is starting.
pub starting: String,
/// MOTD when server is stopping.
pub stopping: String,
/// Use MOTD from Minecraft server once known.
pub from_server: bool,
}
impl Default for Motd {
fn default() -> Self {
Self {
sleeping: "☠ Server is sleeping\n§2☻ Join to start it up".into(),
starting: "§2☻ Server is starting...\n§7⌛ Please wait...".into(),
stopping: "☠ Server going to sleep...\n⌛ Please wait...".into(),
from_server: false,
}
}
}
/// Join method types.
#[derive(Debug, Deserialize, Copy, Clone, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Method {
/// Kick client with message.
Kick,
/// Hold client connection until server is ready.
Hold,
/// Forward connection to another host.
Forward,
/// Keep client in temporary fake lobby until server is ready.
Lobby,
}
/// Join configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Join {
/// Join methods.
pub methods: Vec<Method>,
/// Join kick configuration.
#[serde(default)]
pub kick: JoinKick,
/// Join hold configuration.
#[serde(default)]
pub hold: JoinHold,
/// Join forward configuration.
#[serde(default)]
pub forward: JoinForward,
/// Join lobby configuration.
#[serde(default)]
pub lobby: JoinLobby,
}
impl Default for Join {
fn default() -> Self {
Self {
methods: vec![Method::Hold, Method::Kick],
kick: Default::default(),
hold: Default::default(),
forward: Default::default(),
lobby: Default::default(),
}
}
}
/// Join kick configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct JoinKick {
/// Kick message when server is starting.
pub starting: String,
/// Kick message when server is stopping.
pub stopping: String,
}
impl Default for JoinKick {
fn default() -> Self {
Self {
starting: "Server is starting... §c♥§r\n\nThis may take some time.\n\nPlease try to reconnect in a minute.".into(),
stopping: "Server is going to sleep... §7☠§r\n\nPlease try to reconnect in a minute to wake it again.".into(),
}
}
}
/// Join hold configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct JoinHold {
/// Hold client for number of seconds on connect while server starts.
pub timeout: u32,
}
impl Default for JoinHold {
fn default() -> Self {
Self { timeout: 25 }
}
}
/// Join forward configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct JoinForward {
/// IP and port to forward to.
#[serde(deserialize_with = "to_socket_addrs")]
pub address: SocketAddr,
}
impl Default for JoinForward {
fn default() -> Self {
Self {
address: "127.0.0.1:25565".parse().unwrap(),
}
}
}
/// Join lobby configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct JoinLobby {
/// Hold client in lobby for number of seconds on connect while server starts.
pub timeout: u32,
/// Message banner in lobby shown to client.
pub message: String,
/// Sound effect to play when server is ready.
pub ready_sound: Option<String>,
}
impl Default for JoinLobby {
fn default() -> Self {
Self {
timeout: 10 * 60,
message: "§2Server is starting\n§7⌛ Please wait...".into(),
ready_sound: Some("block.note_block.chime".into()),
}
}
}
/// Lockout configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Lockout {
/// Enable to prevent everybody from connecting through lazymc. Instantly kicks player.
pub enabled: bool,
/// Kick players with following message.
pub message: String,
}
impl Default for Lockout {
fn default() -> Self {
Self {
enabled: false,
message: "Server is closed §7☠§r\n\nPlease come back another time.".into(),
}
}
}
/// RCON configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Rcon {
/// Enable sleeping server through RCON.
pub enabled: bool,
/// Server RCON port.
pub port: u16,
/// Server RCON password.
pub password: String,
/// Randomize server RCON password on each start.
pub randomize_password: bool,
}
impl Default for Rcon {
fn default() -> Self {
Self {
enabled: true,
port: 25575,
password: "".into(),
randomize_password: true,
}
}
}
/// Advanced configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct Advanced {
/// Rewrite server.properties.
pub rewrite_server_properties: bool,
}
impl Default for Advanced {
fn default() -> Self {
Self {
rewrite_server_properties: true,
}
}
}
/// Config configuration.
#[derive(Debug, Deserialize)]
#[serde(default)]
pub struct ConfigConfig {
/// Configuration for lazymc version.
pub version: Option<String>,
}
impl Default for ConfigConfig {
fn default() -> Self {
Self { version: None }
}
}
fn option_pathbuf_dot() -> Option<PathBuf> {
Some(".".into())
}
fn server_address_default() -> SocketAddr {
"127.0.0.1:25566".parse().unwrap()
}
fn u32_300() -> u32 {
300
}
fn u32_150() -> u32 {
300
}
fn bool_true() -> bool {
true
}