Move server config to subsection of config file
This commit is contained in:
parent
e72f19a814
commit
514e48c6ad
160
src/config.rs
160
src/config.rs
@ -4,6 +4,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::{fs::File, path::Path, path::PathBuf};
|
use std::{fs::File, path::Path, path::PathBuf};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use crate::server::config::{ServerConfig, ServerArgs, DEFAULT_SERVER_ARGS};
|
||||||
|
|
||||||
/// The globally-accessible static instance of Config.
|
/// The globally-accessible static instance of Config.
|
||||||
/// On program startup, Config::load() should be called to initialize it.
|
/// On program startup, Config::load() should be called to initialize it.
|
||||||
@ -13,12 +16,11 @@ pub static CONFIG: OnceCell<Config> = OnceCell::new();
|
|||||||
/// On program startup, Args::load() should be called to initialize it.
|
/// On program startup, Args::load() should be called to initialize it.
|
||||||
pub static ARGS: OnceCell<Args> = OnceCell::new();
|
pub static ARGS: OnceCell<Args> = OnceCell::new();
|
||||||
static DEFAULT_ARGS: Lazy<Args> = Lazy::new(Args::default);
|
static DEFAULT_ARGS: Lazy<Args> = Lazy::new(Args::default);
|
||||||
static DEFAULT_SERVER_ARGS: Lazy<ServerArgs> = Lazy::new(ServerArgs::default);
|
|
||||||
|
|
||||||
/// Helper function to read a file from a `Path`
|
/// Helper function to read a file from a `Path`
|
||||||
/// and return its bytes as a `Vec<u8>`.
|
/// and return its bytes as a `Vec<u8>`.
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
pub fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
||||||
trace!("{:?}", path);
|
trace!("{:?}", path);
|
||||||
let mut data = vec![];
|
let mut data = vec![];
|
||||||
let mut file = File::open(path)?;
|
let mut file = File::open(path)?;
|
||||||
@ -26,45 +28,14 @@ fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
|||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main server configuration struct.
|
/// The global configuration.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub port: u16,
|
#[serde(rename = "composition")]
|
||||||
pub max_players: usize,
|
pub global: GlobalConfig,
|
||||||
pub motd: String,
|
pub server: ServerConfig,
|
||||||
pub server_icon: PathBuf,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub server_icon_bytes: Vec<u8>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub protocol_version: i32,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub game_version: String,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub server_version: String,
|
|
||||||
pub server_threads: Option<usize>,
|
|
||||||
}
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
let server_version = format!(
|
|
||||||
"composition {} ({} {})",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
&env!("GIT_HASH")[0..9],
|
|
||||||
&env!("GIT_DATE")[0..10]
|
|
||||||
);
|
|
||||||
Config {
|
|
||||||
port: 25565,
|
|
||||||
max_players: 20,
|
|
||||||
motd: "Hello world!".to_owned(),
|
|
||||||
server_icon: PathBuf::from("server-icon.png"),
|
|
||||||
server_icon_bytes: include_bytes!("./server-icon.png").to_vec(),
|
|
||||||
protocol_version: 762,
|
|
||||||
game_version: "1.19.4".to_owned(),
|
|
||||||
server_version,
|
|
||||||
server_threads: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn instance() -> &'static Self {
|
pub fn instance() -> &'static Self {
|
||||||
@ -73,9 +44,8 @@ impl Config {
|
|||||||
None => Self::load(),
|
None => Self::load(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tracing::instrument]
|
fn load() -> &'static Self {
|
||||||
pub fn load() -> &'static Self {
|
trace!("GlobalConfig::load()");
|
||||||
trace!("Config::load()");
|
|
||||||
let args = Args::instance();
|
let args = Args::instance();
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
let config_path = Path::new(&args.config_file);
|
let config_path = Path::new(&args.config_file);
|
||||||
@ -100,26 +70,12 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the server icon
|
// Load the server icon
|
||||||
config.server_icon = args
|
config.server.server_icon = args
|
||||||
.server
|
.server
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| s.server_icon.clone())
|
.map(|s| s.server_icon.clone())
|
||||||
.unwrap_or(DEFAULT_SERVER_ARGS.server_icon.clone());
|
.unwrap_or(DEFAULT_SERVER_ARGS.server_icon.clone());
|
||||||
let server_icon_path = Path::new(&config.server_icon);
|
config.server.load_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, using default");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!(
|
|
||||||
"Server icon file does not exist, creating {}",
|
|
||||||
server_icon_path.to_str().unwrap_or("")
|
|
||||||
);
|
|
||||||
config.write_server_icon(server_icon_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
CONFIG.set(config).expect("could not set CONFIG");
|
CONFIG.set(config).expect("could not set CONFIG");
|
||||||
Self::instance()
|
Self::instance()
|
||||||
@ -143,21 +99,39 @@ impl Config {
|
|||||||
error!("Could not write configuration file");
|
error!("Could not write configuration file");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
#[tracing::instrument]
|
}
|
||||||
fn write_server_icon(&self, path: &Path) {
|
|
||||||
trace!("Config.write_server_icon()");
|
/// The global configuration.
|
||||||
if let Ok(mut file) = File::options()
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
.write(true)
|
#[serde(rename_all = "kebab-case")]
|
||||||
.create(true)
|
#[serde(default)]
|
||||||
.truncate(true)
|
pub struct GlobalConfig {
|
||||||
.open(path)
|
#[serde(skip)]
|
||||||
{
|
pub version: String,
|
||||||
if file.write_all(&self.server_icon_bytes).is_ok() {
|
#[serde(skip)]
|
||||||
return;
|
pub protocol_version: i32,
|
||||||
}
|
#[serde(skip)]
|
||||||
|
pub game_version: String,
|
||||||
|
pub threads: Option<usize>,
|
||||||
|
}
|
||||||
|
impl Default for GlobalConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
GlobalConfig {
|
||||||
|
version: format!(
|
||||||
|
"composition {} ({} {})",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
&env!("GIT_HASH")[0..9],
|
||||||
|
&env!("GIT_DATE")[0..10]
|
||||||
|
),
|
||||||
|
protocol_version: 762,
|
||||||
|
game_version: "1.19.4".to_owned(),
|
||||||
|
threads: None,
|
||||||
}
|
}
|
||||||
error!("Could not write server icon file");
|
}
|
||||||
std::process::exit(1);
|
}
|
||||||
|
impl GlobalConfig {
|
||||||
|
pub fn instance() -> &'static Self {
|
||||||
|
&Config::instance().global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +150,7 @@ pub struct Args {
|
|||||||
pub log_level: Option<tracing::Level>,
|
pub log_level: Option<tracing::Level>,
|
||||||
pub log_dir: PathBuf,
|
pub log_dir: PathBuf,
|
||||||
pub subcommand: Subcommand,
|
pub subcommand: Subcommand,
|
||||||
server: Option<ServerArgs>,
|
pub server: Option<ServerArgs>,
|
||||||
}
|
}
|
||||||
impl Default for Args {
|
impl Default for Args {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -201,7 +175,6 @@ impl Args {
|
|||||||
Self::instance()
|
Self::instance()
|
||||||
}
|
}
|
||||||
fn command() -> clap::Command {
|
fn command() -> clap::Command {
|
||||||
use std::ffi::OsStr;
|
|
||||||
clap::Command::new("composition")
|
clap::Command::new("composition")
|
||||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||||
.disable_version_flag(true)
|
.disable_version_flag(true)
|
||||||
@ -249,17 +222,7 @@ impl Args {
|
|||||||
.value_hint(clap::ValueHint::DirPath)
|
.value_hint(clap::ValueHint::DirPath)
|
||||||
.default_value(OsStr::new(&DEFAULT_ARGS.log_dir)),
|
.default_value(OsStr::new(&DEFAULT_ARGS.log_dir)),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(ServerArgs::command())
|
||||||
clap::Command::new("server")
|
|
||||||
.about("Run composition in server mode")
|
|
||||||
.arg(
|
|
||||||
Arg::new("server-icon")
|
|
||||||
.long("server-icon")
|
|
||||||
.help("Server icon file path")
|
|
||||||
.value_hint(clap::ValueHint::FilePath)
|
|
||||||
.default_value(OsStr::new(&DEFAULT_SERVER_ARGS.server_icon)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
fn parse() -> Self {
|
fn parse() -> Self {
|
||||||
let mut args = Self::default();
|
let mut args = Self::default();
|
||||||
@ -281,7 +244,7 @@ impl Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.get_flag("version") {
|
if m.get_flag("version") {
|
||||||
println!("{}", Config::default().server_version);
|
println!("{}", GlobalConfig::default().version);
|
||||||
if m.get_flag("verbose") {
|
if m.get_flag("verbose") {
|
||||||
println!("release: {}", env!("CARGO_PKG_VERSION"));
|
println!("release: {}", env!("CARGO_PKG_VERSION"));
|
||||||
println!("commit-hash: {}", env!("GIT_HASH"));
|
println!("commit-hash: {}", env!("GIT_HASH"));
|
||||||
@ -296,11 +259,7 @@ impl Args {
|
|||||||
match m.subcommand() {
|
match m.subcommand() {
|
||||||
Some(("server", m)) => {
|
Some(("server", m)) => {
|
||||||
args.subcommand = Subcommand::Server;
|
args.subcommand = Subcommand::Server;
|
||||||
let mut server_args = ServerArgs::default();
|
args.server = Some(ServerArgs::parse(m.clone()))
|
||||||
server_args.server_icon = m
|
|
||||||
.get_one::<String>("server-icon")
|
|
||||||
.map_or(server_args.server_icon, PathBuf::from);
|
|
||||||
args.server = Some(server_args);
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let _ = Self::command().print_help();
|
let _ = Self::command().print_help();
|
||||||
@ -312,24 +271,3 @@ impl Args {
|
|||||||
args
|
args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ServerArgs {
|
|
||||||
server_icon: PathBuf,
|
|
||||||
}
|
|
||||||
impl Default for ServerArgs {
|
|
||||||
fn default() -> Self {
|
|
||||||
let config = Config::default();
|
|
||||||
ServerArgs {
|
|
||||||
server_icon: config.server_icon,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ServerArgs {
|
|
||||||
pub fn instance() -> Option<&'static Self> {
|
|
||||||
Args::instance().server.as_ref()
|
|
||||||
}
|
|
||||||
pub fn load() -> Option<&'static Self> {
|
|
||||||
Args::load().server.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -38,7 +38,7 @@ pub fn main() {
|
|||||||
// Load the config.
|
// Load the config.
|
||||||
let config = composition::config::Config::instance();
|
let config = composition::config::Config::instance();
|
||||||
|
|
||||||
match config.server_threads {
|
match config.global.threads {
|
||||||
Some(1) => {
|
Some(1) => {
|
||||||
warn!("Running on only one thread");
|
warn!("Running on only one thread");
|
||||||
tokio::runtime::Builder::new_current_thread()
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
107
src/server/config.rs
Normal file
107
src/server/config.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use clap::Arg;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::{fs::File, path::Path, path::PathBuf};
|
||||||
|
use tracing::{error, trace, warn};
|
||||||
|
use crate::config::{Config, Args, read_file};
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
pub static DEFAULT_SERVER_ARGS: Lazy<ServerArgs> = Lazy::new(ServerArgs::default);
|
||||||
|
|
||||||
|
/// The main server configuration struct.
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct ServerConfig {
|
||||||
|
pub port: u16,
|
||||||
|
pub max_players: usize,
|
||||||
|
pub motd: String,
|
||||||
|
pub server_icon: PathBuf,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub server_icon_bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
impl Default for ServerConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
ServerConfig {
|
||||||
|
port: 25565,
|
||||||
|
max_players: 20,
|
||||||
|
motd: "Hello world!".to_owned(),
|
||||||
|
server_icon: PathBuf::from("server-icon.png"),
|
||||||
|
server_icon_bytes: include_bytes!("../server-icon.png").to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ServerConfig {
|
||||||
|
pub fn instance() -> &'static Self {
|
||||||
|
&Config::instance().server
|
||||||
|
}
|
||||||
|
pub fn load_icon(&mut self) {
|
||||||
|
let server_icon_path = Path::new(&self.server_icon);
|
||||||
|
|
||||||
|
if server_icon_path.exists() {
|
||||||
|
if let Ok(server_icon_bytes) = read_file(server_icon_path) {
|
||||||
|
self.server_icon_bytes = server_icon_bytes;
|
||||||
|
} else {
|
||||||
|
warn!("Could not read server icon file, using default");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(
|
||||||
|
"Server icon file does not exist, creating {}",
|
||||||
|
server_icon_path.to_str().unwrap_or("")
|
||||||
|
);
|
||||||
|
self.write_server_icon(server_icon_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn write_server_icon(&self, path: &Path) {
|
||||||
|
trace!("ServerConfig.write_server_icon()");
|
||||||
|
if let Ok(mut file) = File::options()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
|
if file.write_all(&self.server_icon_bytes).is_ok() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error!("Could not write server icon file");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServerArgs {
|
||||||
|
pub server_icon: PathBuf,
|
||||||
|
}
|
||||||
|
impl Default for ServerArgs {
|
||||||
|
fn default() -> Self {
|
||||||
|
let config = Config::default();
|
||||||
|
ServerArgs {
|
||||||
|
server_icon: config.server.server_icon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ServerArgs {
|
||||||
|
pub fn instance() -> Option<&'static Self> {
|
||||||
|
Args::instance().server.as_ref()
|
||||||
|
}
|
||||||
|
pub fn command() -> clap::Command {
|
||||||
|
clap::Command::new("server")
|
||||||
|
.about("Run composition in server mode")
|
||||||
|
.arg(
|
||||||
|
Arg::new("server-icon")
|
||||||
|
.long("server-icon")
|
||||||
|
.help("Server icon file path")
|
||||||
|
.value_hint(clap::ValueHint::FilePath)
|
||||||
|
.default_value(OsStr::new(&DEFAULT_SERVER_ARGS.server_icon)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn parse(m: clap::ArgMatches) -> Self {
|
||||||
|
let mut server_args = ServerArgs::default();
|
||||||
|
server_args.server_icon = m
|
||||||
|
.get_one::<String>("server-icon")
|
||||||
|
.map_or(server_args.server_icon, PathBuf::from);
|
||||||
|
server_args
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
/// Network operations.
|
/// Network operations.
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
/// Server-specific configuration.
|
||||||
|
pub mod config;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::protocol::ClientState;
|
use crate::protocol::ClientState;
|
||||||
@ -23,9 +25,9 @@ impl Server {
|
|||||||
/// Start the server.
|
/// Start the server.
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn run() {
|
pub async fn run() {
|
||||||
let config = crate::config::Config::instance();
|
let config = Config::instance();
|
||||||
info!("Starting {} on port {}", config.server_version, config.port);
|
info!("Starting {} on port {}", config.global.version, config.server.port);
|
||||||
let (mut server, running) = Self::new(format!("0.0.0.0:{}", Config::instance().port)).await;
|
let (mut server, running) = Self::new(format!("0.0.0.0:{}", config.server.port)).await;
|
||||||
info!(
|
info!(
|
||||||
"Done! Start took {:?}",
|
"Done! Start took {:?}",
|
||||||
crate::START_TIME.get().unwrap().elapsed()
|
crate::START_TIME.get().unwrap().elapsed()
|
||||||
@ -242,18 +244,18 @@ impl Server {
|
|||||||
client.queue_packet(CS00StatusResponse {
|
client.queue_packet(CS00StatusResponse {
|
||||||
response: serde_json::json!({
|
response: serde_json::json!({
|
||||||
"version": {
|
"version": {
|
||||||
"name": config.game_version,
|
"name": config.global.game_version,
|
||||||
"protocol": config.protocol_version
|
"protocol": config.global.protocol_version
|
||||||
},
|
},
|
||||||
"players": {
|
"players": {
|
||||||
"max": config.max_players,
|
"max": config.server.max_players,
|
||||||
"online": online_players,
|
"online": online_players,
|
||||||
"sample": []
|
"sample": []
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"text": config.motd
|
"text": config.server.motd
|
||||||
},
|
},
|
||||||
"favicon": format!("data:image/png;base64,{}", base64::engine::general_purpose::STANDARD_NO_PAD.encode(&config.server_icon_bytes)),
|
"favicon": format!("data:image/png;base64,{}", base64::engine::general_purpose::STANDARD_NO_PAD.encode(&config.server.server_icon_bytes)),
|
||||||
"enforcesSecureChat": true
|
"enforcesSecureChat": true
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user