Improve configuration file parsing, add log_level to config, log server version, and move mctypes into crate::net
This commit is contained in:
parent
4d928973a6
commit
4849a7903d
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -117,6 +117,7 @@ dependencies = [
|
||||
"radix64",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"substring",
|
||||
"tokio",
|
||||
"toml",
|
||||
"uuid",
|
||||
@ -539,6 +540,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "substring"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.91"
|
||||
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "composition"
|
||||
version = "0.1.0"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.48"
|
||||
@ -21,6 +22,7 @@ serde_json = "1.0.59"
|
||||
tokio = {version = "1", features = ["full"]}
|
||||
toml = "0.5"
|
||||
uuid = "0.8.2"
|
||||
substring = "1.4.5"
|
||||
|
||||
# colorful = "0.2.1"
|
||||
# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2)
|
||||
|
9
build.rs
Normal file
9
build.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use std::process::Command;
|
||||
fn main() {
|
||||
if let Ok(output) = Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||
let git_hash = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
|
||||
} else {
|
||||
println!("cargo:rustc-env=GIT_HASH=00000000");
|
||||
}
|
||||
}
|
@ -2,3 +2,4 @@ favicon = "server-icon.png"
|
||||
max_players = 20
|
||||
motd = "Hello world!"
|
||||
port = 25565
|
||||
log_level = "debug"
|
138
src/config.rs
Normal file
138
src/config.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct Config {
|
||||
pub port: u16,
|
||||
pub max_players: usize,
|
||||
pub motd: String,
|
||||
pub favicon: String,
|
||||
pub server_string: String,
|
||||
pub log_level: log::LevelFilter,
|
||||
pub server_version: String,
|
||||
}
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let server_version = format!(
|
||||
"composition/{} ({})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("GIT_HASH").substring(0, 8)
|
||||
);
|
||||
Config {
|
||||
port: 25565,
|
||||
max_players: 20,
|
||||
motd: "Hello world!".to_owned(),
|
||||
favicon: "server-icon.png".to_owned(),
|
||||
server_string: server_version.clone(),
|
||||
log_level: if cfg!(debug_assertions) {
|
||||
log::LevelFilter::Debug
|
||||
} else {
|
||||
log::LevelFilter::Info
|
||||
},
|
||||
server_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Config {
|
||||
pub fn from_file(filename: &str) -> Config {
|
||||
let read_file = |filename: &str| -> std::io::Result<String> {
|
||||
use std::{fs::File, io::prelude::*};
|
||||
let mut data = String::new();
|
||||
let mut file = File::open(filename)?;
|
||||
file.read_to_string(&mut data)?;
|
||||
Ok(data)
|
||||
};
|
||||
if let Ok(config) = read_file(filename) {
|
||||
Config::parse(&config)
|
||||
} else {
|
||||
Config::default()
|
||||
}
|
||||
}
|
||||
pub fn parse(data: &str) -> Config {
|
||||
let mut config = Config::default();
|
||||
|
||||
let get_string = |cfg: &toml::Value, field: &str, default: &str, error: &str| -> String {
|
||||
if let Some(s) = cfg.get(field) {
|
||||
if let Some(s) = s.as_str() {
|
||||
return s.to_owned();
|
||||
} else {
|
||||
warn!("{}", error);
|
||||
}
|
||||
} else {
|
||||
warn!("{}", error);
|
||||
}
|
||||
default.to_owned()
|
||||
};
|
||||
|
||||
if let Ok(cfg) = data.parse::<toml::Value>() {
|
||||
if let Some(&toml::Value::Integer(port)) = cfg.get("port") {
|
||||
if port < u16::MIN as i64 || port > u16::MAX as i64 {
|
||||
warn!("Config port must be an integer in the range of {}-{}, using default port: {}", u16::MIN, u16::MAX, config.port);
|
||||
} else {
|
||||
config.port = port as u16;
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Config port must be an integer in the range of {}-{}, using default port: {}",
|
||||
u16::MIN,
|
||||
u16::MAX,
|
||||
config.port
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(&toml::Value::Integer(max_players)) = cfg.get("max_players") {
|
||||
if max_players < 0 {
|
||||
warn!("Config max_players must be an integer in the range of {}-{}, using default max_players: {}", usize::MIN, usize::MAX, config.max_players);
|
||||
} else {
|
||||
config.max_players = max_players as usize;
|
||||
}
|
||||
} else {
|
||||
warn!("Config max_players must be an integer in the range of {}-{}, using default max_players: {}", usize::MIN, usize::MAX, config.max_players);
|
||||
}
|
||||
|
||||
config.motd = get_string(
|
||||
&cfg,
|
||||
"motd",
|
||||
&config.motd,
|
||||
&format!(
|
||||
"Config motd must be a string, using default motd: \"{}\"",
|
||||
config.motd
|
||||
),
|
||||
);
|
||||
config.favicon = get_string(
|
||||
&cfg,
|
||||
"favicon",
|
||||
&config.favicon,
|
||||
&format!(
|
||||
"Config favicon must be a string, using default favicon: \"{}\"",
|
||||
config.favicon
|
||||
),
|
||||
);
|
||||
let default_log_level = format!("{}", config.log_level).to_ascii_lowercase();
|
||||
config.log_level = match &get_string(
|
||||
&cfg,
|
||||
"log_level",
|
||||
&default_log_level,
|
||||
&format!(
|
||||
"Config log_level must be a string, using default log_level: {}",
|
||||
default_log_level
|
||||
),
|
||||
)[..]
|
||||
{
|
||||
"off" => log::LevelFilter::Off,
|
||||
"error" => log::LevelFilter::Error,
|
||||
"warn" => log::LevelFilter::Warn,
|
||||
"info" => log::LevelFilter::Info,
|
||||
"debug" => log::LevelFilter::Debug,
|
||||
"trace" => log::LevelFilter::Trace,
|
||||
_ => {
|
||||
warn!("Config log_level must be one of the predefined levels: off, error, warn, info, debug, trace");
|
||||
config.log_level
|
||||
}
|
||||
};
|
||||
|
||||
config
|
||||
} else {
|
||||
warn!("Could not parse configuration file, using default");
|
||||
config
|
||||
}
|
||||
}
|
||||
}
|
48
src/lib.rs
48
src/lib.rs
@ -1,50 +1,16 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub mod mctypes;
|
||||
pub mod config;
|
||||
pub mod net;
|
||||
pub mod server;
|
||||
|
||||
use crate::prelude::*;
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub port: u16,
|
||||
pub max_players: usize,
|
||||
pub motd: String,
|
||||
pub favicon: String,
|
||||
}
|
||||
|
||||
pub static PROTOCOL_VERSION: i32 = 757;
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: Config = {
|
||||
let config_from_file = || -> std::io::Result<Config> {
|
||||
use std::{fs::File, io::prelude::*};
|
||||
let mut data = String::new();
|
||||
let mut file = File::open("composition.toml")?;
|
||||
file.read_to_string(&mut data)?;
|
||||
if let Ok(c) = toml::from_str::<Config>(&data) {
|
||||
Ok(c)
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Could not parse toml",
|
||||
))
|
||||
}
|
||||
};
|
||||
if let Ok(c) = config_from_file() {
|
||||
c
|
||||
} else {
|
||||
warn!("Could not load config from file, using default");
|
||||
Config {
|
||||
port: 25565,
|
||||
max_players: 20,
|
||||
motd: "Hello world!".to_owned(),
|
||||
favicon: "server-icon.png".to_owned(),
|
||||
}
|
||||
}
|
||||
};
|
||||
pub static ref CONFIG: Config = Config::from_file("composition.toml");
|
||||
pub static ref FAVICON: std::io::Result<Vec<u8>> = {
|
||||
use std::{fs::File, io::prelude::*};
|
||||
let mut data = vec![];
|
||||
@ -70,15 +36,12 @@ pub fn init() -> Receiver<()> {
|
||||
message = message,
|
||||
))
|
||||
})
|
||||
.level(if cfg!(debug_assertions) {
|
||||
log::LevelFilter::Debug
|
||||
} else {
|
||||
log::LevelFilter::Info
|
||||
})
|
||||
.level(log::LevelFilter::Trace)
|
||||
.chain(std::io::stdout())
|
||||
.chain(fern::log_file("output.log").unwrap())
|
||||
.apply()
|
||||
.unwrap();
|
||||
log::set_max_level(CONFIG.log_level);
|
||||
// Set up the ctrl-c handler.
|
||||
let (ctrlc_tx, ctrlc_rx) = mpsc::channel();
|
||||
ctrlc::set_handler(move || {
|
||||
@ -94,7 +57,7 @@ pub async fn start_server() -> server::Server {
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{mctypes::*, CONFIG, FAVICON, PROTOCOL_VERSION, START_TIME};
|
||||
pub use crate::{config::Config, CONFIG, FAVICON, PROTOCOL_VERSION, START_TIME};
|
||||
pub use log::*;
|
||||
pub use serde::{Deserialize, Serialize};
|
||||
pub use serde_json::json;
|
||||
@ -102,6 +65,7 @@ pub mod prelude {
|
||||
pub type JSON = serde_json::Value;
|
||||
pub type NBT = fastnbt::Value;
|
||||
pub use std::collections::VecDeque;
|
||||
pub use substring::Substring;
|
||||
pub use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ParseError {
|
||||
|
@ -5,7 +5,7 @@ use std::time::Duration;
|
||||
#[tokio::main]
|
||||
pub async fn main() {
|
||||
let ctrlc_rx = composition::init();
|
||||
info!("Starting server...");
|
||||
info!("Starting {}", composition::CONFIG.server_version);
|
||||
let mut server = composition::start_server().await;
|
||||
info!("Done! Start took {:?}", composition::START_TIME.elapsed());
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
pub mod mctypes;
|
||||
pub mod packets;
|
||||
|
||||
use crate::prelude::*;
|
||||
use mctypes::*;
|
||||
pub use packets::Packet;
|
||||
use std::time::Instant;
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::NetworkClientState;
|
||||
use super::{mctypes::*, NetworkClientState};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
|
Loading…
x
Reference in New Issue
Block a user