Move more things into Config
This commit is contained in:
parent
b4ed1572e9
commit
182bf76835
@ -1,5 +1,6 @@
|
|||||||
favicon = "server-icon.png"
|
log_level = "debug"
|
||||||
max_players = 20
|
max_players = 20
|
||||||
motd = "Hello world!"
|
motd = "Hello world!"
|
||||||
|
ping_game_version = "1.18.1"
|
||||||
port = 25565
|
port = 25565
|
||||||
log_level = "debug"
|
server_icon = "server-icon.png"
|
||||||
|
258
src/config.rs
258
src/config.rs
@ -1,12 +1,23 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use std::{fs::File, path::Path};
|
||||||
|
|
||||||
|
fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
||||||
|
let mut data = vec![];
|
||||||
|
let mut file = File::open(path)?;
|
||||||
|
file.read_to_end(&mut data)?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub max_players: usize,
|
pub max_players: usize,
|
||||||
pub motd: String,
|
pub motd: String,
|
||||||
pub favicon: String,
|
pub server_icon: String,
|
||||||
|
pub server_icon_bytes: Vec<u8>,
|
||||||
pub server_string: String,
|
pub server_string: String,
|
||||||
pub log_level: log::LevelFilter,
|
pub log_level: log::LevelFilter,
|
||||||
|
pub protocol_version: i32,
|
||||||
|
pub game_version: String,
|
||||||
pub server_version: String,
|
pub server_version: String,
|
||||||
}
|
}
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -20,33 +31,22 @@ impl Default for Config {
|
|||||||
port: 25565,
|
port: 25565,
|
||||||
max_players: 20,
|
max_players: 20,
|
||||||
motd: "Hello world!".to_owned(),
|
motd: "Hello world!".to_owned(),
|
||||||
favicon: "server-icon.png".to_owned(),
|
server_icon: "server-icon.png".to_owned(),
|
||||||
|
server_icon_bytes: include_bytes!("../server-icon.png").to_vec(),
|
||||||
server_string: server_version.clone(),
|
server_string: server_version.clone(),
|
||||||
log_level: if cfg!(debug_assertions) {
|
log_level: if cfg!(debug_assertions) {
|
||||||
log::LevelFilter::Debug
|
log::LevelFilter::Debug
|
||||||
} else {
|
} else {
|
||||||
log::LevelFilter::Info
|
log::LevelFilter::Info
|
||||||
},
|
},
|
||||||
|
protocol_version: 756,
|
||||||
|
game_version: "1.18.1".to_owned(),
|
||||||
server_version,
|
server_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn from_file(filename: &str) -> Config {
|
pub fn from_toml(cfg: toml::Value) -> 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 mut config = Config::default();
|
||||||
|
|
||||||
let get_string = |cfg: &toml::Value, field: &str, default: &str, error: &str| -> String {
|
let get_string = |cfg: &toml::Value, field: &str, default: &str, error: &str| -> String {
|
||||||
@ -62,77 +62,185 @@ impl Config {
|
|||||||
default.to_owned()
|
default.to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(cfg) = data.parse::<toml::Value>() {
|
if let Some(&toml::Value::Integer(port)) = cfg.get("port") {
|
||||||
if let Some(&toml::Value::Integer(port)) = cfg.get("port") {
|
if port < u16::MIN as i64 || port > u16::MAX as i64 {
|
||||||
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!(
|
warn!(
|
||||||
"Config port must be an integer in the range of {}-{}, using default port: {}",
|
"Config port must be an integer in the range of {}-{}, using default port: {}",
|
||||||
u16::MIN,
|
u16::MIN,
|
||||||
u16::MAX,
|
u16::MAX,
|
||||||
config.port
|
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 let Some(&toml::Value::Integer(max_players)) = cfg.get("max_players") {
|
||||||
if max_players < 0 {
|
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);
|
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.game_version = get_string(
|
||||||
|
&cfg,
|
||||||
|
"ping_game_version",
|
||||||
|
&config.game_version,
|
||||||
|
&format!(
|
||||||
|
"Config ping_game_version must be a string, using default ping_game_version: \"{}\"",
|
||||||
|
config.game_version
|
||||||
|
),
|
||||||
|
);
|
||||||
|
config.server_icon = get_string(
|
||||||
|
&cfg,
|
||||||
|
"server_icon",
|
||||||
|
&config.server_icon,
|
||||||
|
&format!(
|
||||||
|
"Config server_icon must be a string, using default server_icon: \"{}\"",
|
||||||
|
config.server_icon
|
||||||
|
),
|
||||||
|
);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
pub fn load() -> Config {
|
||||||
|
let mut config = Config::default();
|
||||||
|
|
||||||
|
// Load the config
|
||||||
|
let config_path = Path::new("composition.toml");
|
||||||
|
if config_path.exists() {
|
||||||
|
if let Ok(cfg) = read_file(config_path) {
|
||||||
|
let cfg = String::from_utf8_lossy(&cfg);
|
||||||
|
if let Ok(cfg) = cfg.parse::<toml::Value>() {
|
||||||
|
config = Config::from_toml(cfg);
|
||||||
} else {
|
} else {
|
||||||
config.max_players = max_players as usize;
|
error!("Could not parse configuration file");
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("Config max_players must be an integer in the range of {}-{}, using default max_players: {}", usize::MIN, usize::MAX, config.max_players);
|
warn!(
|
||||||
}
|
"Could not read configuration file, creating {}",
|
||||||
|
config_path.to_str().unwrap_or("")
|
||||||
config.motd = get_string(
|
);
|
||||||
&cfg,
|
if config.write(config_path).is_err() {
|
||||||
"motd",
|
error!("Could not write configuration file");
|
||||||
&config.motd,
|
std::process::exit(1);
|
||||||
&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 {
|
} else {
|
||||||
warn!("Could not parse configuration file, using default");
|
warn!(
|
||||||
config
|
"Configuration file does not exist, creating {}",
|
||||||
|
config_path.to_str().unwrap_or("")
|
||||||
|
);
|
||||||
|
if config.write(config_path).is_err() {
|
||||||
|
error!("Could not write configuration file");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the server icon
|
||||||
|
let server_icon_path = Path::new(&config.server_icon);
|
||||||
|
if server_icon_path.exists() {
|
||||||
|
if let Ok(server_icon_bytes) = read_file(server_icon_path) {
|
||||||
|
config.server_icon_bytes = server_icon_bytes;
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Could not read server icon file, creating {}",
|
||||||
|
server_icon_path.to_str().unwrap_or("")
|
||||||
|
);
|
||||||
|
if config.write_server_icon(server_icon_path).is_err() {
|
||||||
|
error!("Could not write server icon file");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Server icon file does not exist, creating {}",
|
||||||
|
server_icon_path.to_str().unwrap_or("")
|
||||||
|
);
|
||||||
|
if config.write_server_icon(server_icon_path).is_err() {
|
||||||
|
error!("Could not write server icon file");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
pub fn write(&self, path: &Path) -> std::io::Result<()> {
|
||||||
|
use toml::{map::Map, Value};
|
||||||
|
|
||||||
|
let config = Value::Table({
|
||||||
|
let mut m = Map::new();
|
||||||
|
m.insert(
|
||||||
|
"server_icon".to_owned(),
|
||||||
|
Value::String(self.server_icon.clone()),
|
||||||
|
);
|
||||||
|
m.insert(
|
||||||
|
"log_level".to_owned(),
|
||||||
|
Value::String(format!("{}", self.log_level).to_ascii_lowercase()),
|
||||||
|
);
|
||||||
|
m.insert("max_players".to_owned(), Value::Integer(20));
|
||||||
|
m.insert("motd".to_owned(), Value::String(self.motd.clone()));
|
||||||
|
m.insert(
|
||||||
|
"ping_game_version".to_owned(),
|
||||||
|
Value::String(self.game_version.clone()),
|
||||||
|
);
|
||||||
|
m.insert("port".to_owned(), Value::Integer(25565));
|
||||||
|
m
|
||||||
|
});
|
||||||
|
if path.exists() {
|
||||||
|
std::fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
file.write_all(&toml::ser::to_vec(&config).unwrap())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn write_server_icon(&self, path: &Path) -> std::io::Result<()> {
|
||||||
|
if path.exists() {
|
||||||
|
std::fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
file.write_all(&self.server_icon_bytes)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/lib.rs
16
src/lib.rs
@ -8,22 +8,13 @@ pub mod server;
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use std::sync::mpsc::{self, Receiver};
|
use std::sync::mpsc::{self, Receiver};
|
||||||
|
|
||||||
pub static PROTOCOL_VERSION: i32 = 757;
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CONFIG: Config = Config::from_file("composition.toml");
|
pub static ref CONFIG: Config = Config::load();
|
||||||
pub static ref FAVICON: std::io::Result<Vec<u8>> = {
|
|
||||||
use std::{fs::File, io::prelude::*};
|
|
||||||
let mut data = vec![];
|
|
||||||
let mut file = File::open(CONFIG.favicon.clone())?;
|
|
||||||
file.read_to_end(&mut data)?;
|
|
||||||
Ok(data)
|
|
||||||
};
|
|
||||||
pub static ref START_TIME: std::time::Instant = std::time::Instant::now();
|
pub static ref START_TIME: std::time::Instant = std::time::Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up logging, read the config file, etc.
|
/// Set up logging, read the config file, etc.
|
||||||
pub fn init() -> Receiver<()> {
|
pub fn init() -> Receiver<()> {
|
||||||
// Load the START_TIME static - lazy_static lazy loads the value when first needed.
|
|
||||||
let _ = START_TIME.elapsed();
|
let _ = START_TIME.elapsed();
|
||||||
// Set up fern logging.
|
// Set up fern logging.
|
||||||
fern::Dispatch::new()
|
fern::Dispatch::new()
|
||||||
@ -57,7 +48,7 @@ pub async fn start_server() -> server::Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{config::Config, CONFIG, FAVICON, PROTOCOL_VERSION, START_TIME};
|
pub use crate::{config::Config, CONFIG, START_TIME};
|
||||||
pub use log::*;
|
pub use log::*;
|
||||||
pub use serde::{Deserialize, Serialize};
|
pub use serde::{Deserialize, Serialize};
|
||||||
pub use serde_json::json;
|
pub use serde_json::json;
|
||||||
@ -65,8 +56,9 @@ pub mod prelude {
|
|||||||
pub type JSON = serde_json::Value;
|
pub type JSON = serde_json::Value;
|
||||||
pub type NBT = quartz_nbt::NbtCompound;
|
pub type NBT = quartz_nbt::NbtCompound;
|
||||||
pub use std::collections::VecDeque;
|
pub use std::collections::VecDeque;
|
||||||
|
pub use std::io::{Read, Write};
|
||||||
pub use substring::Substring;
|
pub use substring::Substring;
|
||||||
pub use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
pub use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
NotEnoughData,
|
NotEnoughData,
|
||||||
|
@ -222,8 +222,7 @@ impl Packet {
|
|||||||
"online": current_players,
|
"online": current_players,
|
||||||
},
|
},
|
||||||
"description": description,
|
"description": description,
|
||||||
// TODO: Add base64 favicon
|
"favicon": format!("data:image/png;base64,{}", radix64::STD_NO_PAD.encode(&CONFIG.server_icon_bytes)),
|
||||||
"favicon": format!("data:image/png;base64,{}", radix64::STD_NO_PAD.encode(FAVICON.as_ref().unwrap())),
|
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
CS01Pong { payload } => (0x01, serialize_long(*payload).to_vec()),
|
CS01Pong { payload } => (0x01, serialize_long(*payload).to_vec()),
|
||||||
|
@ -108,10 +108,12 @@ impl Server {
|
|||||||
server_port: _,
|
server_port: _,
|
||||||
next_state,
|
next_state,
|
||||||
} => {
|
} => {
|
||||||
if protocol_version != PROTOCOL_VERSION {
|
if protocol_version != CONFIG.protocol_version
|
||||||
|
&& next_state == NetworkClientState::Login
|
||||||
|
{
|
||||||
debug!(
|
debug!(
|
||||||
"Disconnecting client {} for mismatched protocols: {} (expected {})",
|
"Disconnecting client {} for mismatched protocols: {} (expected {})",
|
||||||
client.id, protocol_version, PROTOCOL_VERSION
|
client.id, protocol_version, CONFIG.protocol_version
|
||||||
);
|
);
|
||||||
client.disconnect(None).await;
|
client.disconnect(None).await;
|
||||||
return Err(());
|
return Err(());
|
||||||
@ -121,8 +123,8 @@ impl Server {
|
|||||||
SS00Request => {
|
SS00Request => {
|
||||||
let _ = client
|
let _ = client
|
||||||
.send_packet(CS00Response {
|
.send_packet(CS00Response {
|
||||||
version_name: "1.18.1".to_owned(),
|
version_name: CONFIG.game_version.clone(),
|
||||||
protocol_version: PROTOCOL_VERSION,
|
protocol_version: CONFIG.protocol_version,
|
||||||
max_players: CONFIG.max_players,
|
max_players: CONFIG.max_players,
|
||||||
current_players,
|
current_players,
|
||||||
description: json!({
|
description: json!({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user