Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cli Improvements #132

Merged
merged 9 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .etc/example-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ network_compression_threshold = 64
compression = "gzip"
# Path to the world database
db_path = "world"
# Path to world import folder (e.g., %APPDATA%/.minecraft/saves/world)
import_path = "import"
# Compression level (0-22) for the database. Higher values mean less disk space but will take longer to read/write.
compression_level = 5
# Map size
Expand Down
65 changes: 65 additions & 0 deletions src/bin/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use clap::{Parser, Subcommand, ValueEnum};
use tracing::Level;

#[derive(Parser)]
pub struct CLIArgs {
#[command(subcommand)]
pub command: Option<Command>,
#[clap(long)]
#[arg(value_enum, default_value_t = LogLevel(Level::TRACE))]
pub log: LogLevel,
}

#[derive(Subcommand, Clone)]
pub enum Command {
/// Sets up the config
Setup,
/// Import the world data
Import(ImportArgs),
/// Start the server
Run,
}

#[derive(Debug, Clone, Parser)]
pub struct ImportArgs {
/// Path to world import folder
///
/// This should point to the folder that contains directories such as `region`, `poi`, `playerdata`, etc. Usually found at %APPDATA%/.minecraft/saves.
#[clap(long, required = true)]
pub import_path: String,
}

// Wrapper struct for the Level enum
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LogLevel(Level);

// Implement `ValueEnum` for the wrapper
impl ValueEnum for LogLevel {
fn value_variants<'a>() -> &'a [Self] {
static VARIANTS: &[LogLevel] = &[
LogLevel(Level::TRACE),
LogLevel(Level::DEBUG),
LogLevel(Level::INFO),
LogLevel(Level::WARN),
LogLevel(Level::ERROR),
];
VARIANTS
}

fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
match self.0 {
Level::TRACE => Some(clap::builder::PossibleValue::new("trace")),
Level::DEBUG => Some(clap::builder::PossibleValue::new("debug")),
Level::INFO => Some(clap::builder::PossibleValue::new("info")),
Level::WARN => Some(clap::builder::PossibleValue::new("warn")),
Level::ERROR => Some(clap::builder::PossibleValue::new("error")),
}
}
}

// Add a conversion method to make using the wrapper easier
impl From<LogLevel> for Level {
fn from(log_level: LogLevel) -> Self {
log_level.0
}
}
115 changes: 35 additions & 80 deletions src/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
extern crate core;

use crate::errors::BinaryError;
use clap::{Parser, ValueEnum};
use clap::Parser;
use ferrumc_config::statics::get_global_config;
use ferrumc_ecs::Universe;
use ferrumc_general_purpose::paths::get_root_path;
Expand All @@ -12,54 +12,11 @@ use ferrumc_state::ServerState;
use ferrumc_world::World;
use std::sync::Arc;
use systems::definition;
use tracing::{error, info, Level};

#[derive(clap::Parser)]
struct CLIArgs {
#[clap(long)]
import: bool,
#[clap(long)]
#[arg(value_enum, default_value_t = LogLevel(Level::TRACE))]
log: LogLevel,
#[clap(long)]
setup: bool,
}

// Wrapper struct for the Level enum
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LogLevel(Level);

// Implement `ValueEnum` for the wrapper
impl ValueEnum for LogLevel {
fn value_variants<'a>() -> &'a [Self] {
static VARIANTS: &[LogLevel] = &[
LogLevel(Level::TRACE),
LogLevel(Level::DEBUG),
LogLevel(Level::INFO),
LogLevel(Level::WARN),
LogLevel(Level::ERROR),
];
VARIANTS
}

fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
match self.0 {
Level::TRACE => Some(clap::builder::PossibleValue::new("trace")),
Level::DEBUG => Some(clap::builder::PossibleValue::new("debug")),
Level::INFO => Some(clap::builder::PossibleValue::new("info")),
Level::WARN => Some(clap::builder::PossibleValue::new("warn")),
Level::ERROR => Some(clap::builder::PossibleValue::new("error")),
}
}
}
use tracing::{error, info};

// Add a conversion method to make using the wrapper easier
impl From<LogLevel> for Level {
fn from(log_level: LogLevel) -> Self {
log_level.0
}
}
pub(crate) mod errors;
use crate::cli::{CLIArgs, Command, ImportArgs};
mod cli;
mod packet_handlers;
mod systems;

Expand All @@ -70,32 +27,36 @@ async fn main() {
let cli_args = CLIArgs::parse();
ferrumc_logging::init_logging(cli_args.log.into());

info!("Starting server...");
match cli_args.command {
Some(Command::Setup) => {
info!("Starting setup...");
if let Err(e) = ferrumc_config::setup::setup() {
error!("Could not set up the server: {}", e.to_string());
} else {
info!("Server setup complete.");
}
}

if let Err(e) = entry(cli_args).await {
error!("Server exited with the following error: {}", e.to_string());
} else {
info!("Server exited successfully.");
Some(Command::Import(import_args)) => {
info!("Starting import...");
if let Err(e) = handle_import(import_args).await {
error!("Import failed with the following error: {}", e.to_string());
} else {
info!("Import completed successfully.");
}
}
Some(Command::Run) | None => {
info!("Starting server...");
if let Err(e) = entry().await {
error!("Server exited with the following error: {}", e.to_string());
} else {
info!("Server exited successfully.");
}
}
}
}

async fn entry(cli_args: CLIArgs) -> Result<()> {
if handle_import(cli_args.import).await? {
return Ok(());
}

if cli_args.setup {
return if let Err(e) = ferrumc_config::setup::setup() {
error!("Could not set up the server: {}", e.to_string());
Err(BinaryError::Custom(
"Could not set up the server.".to_string(),
))
} else {
info!("Server setup complete.");
Ok(())
};
}

async fn entry() -> Result<()> {
let state = create_state().await?;
let global_state = Arc::new(state);

Expand All @@ -110,23 +71,17 @@ async fn entry(cli_args: CLIArgs) -> Result<()> {
Ok(())
}

async fn handle_import(import: bool) -> Result<bool> {
//! Handles the import of the world if the `--import` flag is set.
//! Returns `true` if program should exit after this function, `false` otherwise.
if !import {
return Ok(false);
}

info!("`--import` flag detected. Importing world...");
async fn handle_import(import_args: ImportArgs) -> Result<()> {
//! Handles the import of the world.
info!("Importing world...");

// Import the world
let config = get_global_config();
let mut world = World::new().await;

let root_path = get_root_path();
let database_opts = &config.database;

let mut import_path = root_path.join(database_opts.import_path.clone());
let mut import_path = root_path.join(import_args.import_path);
if import_path.is_relative() {
import_path = root_path.join(import_path);
}
Expand All @@ -140,7 +95,7 @@ async fn handle_import(import: bool) -> Result<bool> {
return Err(BinaryError::Custom("Could not import world.".to_string()));
}

Ok(true)
Ok(())
}

async fn create_state() -> Result<ServerState> {
Expand Down
3 changes: 0 additions & 3 deletions src/lib/utils/config/src/server_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ pub struct ServerConfig {
/// - `compression` - Which compression algorithm to use. Options are `brotli`, `deflate`, `gzip`, `zlib`
/// and `zstd`
/// - `world_path`: The path to the world database.
/// - `import_path`: The path to the world to import. This should point to the folder that contains
/// directories such as `region`, `poi`, `playerdata`, etc. Usually found at %APPDATA%/.minecraft/saves.
/// - `compression_level`: The compression level to use. This is a number from 0-22. Not all compressors
/// support levels, so this will be a no-op for some compressors.
/// - `map_size`: The max size of the database's memory map. Basically you need this to be big enough
Expand All @@ -48,7 +46,6 @@ pub struct ServerConfig {
pub struct DatabaseConfig {
pub compression: String,
pub db_path: String,
pub import_path: String,
pub compression_level: i32,
pub map_size: u64,
pub cache_ttl: u64,
Expand Down
6 changes: 0 additions & 6 deletions src/lib/world/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ async fn check_config_validity() -> Result<(), WorldError> {
config.database.compression.clone(),
));
}
if config.database.import_path.is_empty() {
error!("No import path specified. Please set the import path in the configuration file.");
return Err(WorldError::InvalidImportPath(
config.database.import_path.clone(),
));
}

// Check if doing map_size * 1024^3 would overflow usize. You probably don't need a database
// that's 18 exabytes anyway.
Expand Down
Loading