//! Main module for the DML Launcher. //! //! This module contains the entry point and orchestrates the full lifecycle //! of the launcher. It coordinates configuration loading, environment setup, //! version management, asset downloading, native extraction, and client launch. //! //! # Workflow //! //! 1. Load environment variables from .env. //! 2. Initialize logging with the warning level. //! 3. Parse command-line arguments using clap. //! 4. Load the persistent configuration file. //! 5. Apply command-line overrides to configuration values. //! 6. Ensure all necessary directories exist. //! 7. Load the version manifest for the selected Minecraft version. //! 8. Download required assets including libraries and client binaries. //! 9. Extract native libraries for the current platform. //! 10. Launch the Minecraft client with the configured JVM arguments and //! session information. //! //! # Error Handling //! //! All operations that can fail propagate errors through the //! McError type. This centralizes error management and //! simplifies the use of the ? operator throughout the launcher. //! //! # Asynchronous Operations //! //! HTTP requests and asset downloads are performed asynchronously using //! tokio to maximize efficiency and reduce blocking. Local file //! operations and the client launch remain synchronous to maintain //! consistency and avoid race conditions. mod config; mod constants; mod errors; mod minecraft; mod platform; mod util; use clap::Parser; use dotenvy::dotenv; use errors::McError; use log::LevelFilter::Warn; use reqwest::Client; use platform::paths::ensure_directories; use crate::{ config::ConfigLoader, minecraft::{ downloads::download_all_files, extraction::extract_natives, launcher::launch, manifests::load_version, }, }; use crate::config::RuntimeConfig; use crate::minecraft::manifests::Version; /// Command-line interface definition. /// /// Provides user-configurable options for the launcher, including /// specifying a Minecraft version, a username, and additional /// JVM arguments. Utilizes clap for argument parsing and validation. #[derive(Parser, Debug)] #[command(author, about, disable_version_flag = true)] struct Cli { /// Optional Minecraft version to launch. #[arg(long)] version: Option, /// Optional username for the Minecraft session. #[arg(long)] username: Option, /// Optional JVM arguments to pass to the Minecraft process. /// /// Supports multiple values and allows arguments that begin with hyphens. #[arg(long, num_args(0..), allow_hyphen_values = true)] jvm_args: Vec, } /// Launcher entry point. /// /// This asynchronous function orchestrates the entire launch process, /// handling configuration, directory setup, asset management, native /// extraction, and client execution. Returns a Result<(), McError> /// to capture and propagate all errors encountered during execution. /// /// # Steps /// /// 1. Load environment variables from .env. /// 2. Initialize logging with a warning level filter. /// 3. Parse CLI arguments and merge them into the configuration. /// 4. Ensure required directories exist for game data and assets. /// 5. Load the manifest for the specified Minecraft version. /// 6. Build an HTTP client with HTTP/2 support for asset downloads. /// 7. Download all required assets including libraries and client binaries. /// 8. Extract platform-specific native libraries. /// 9. Launch the Minecraft client using the configured JVM arguments and /// session. #[tokio::main] async fn main() -> Result<(), McError> { dotenv().ok(); env_logger::Builder::new() .filter_level(Warn) .init(); let cli: Cli = Cli::parse(); let mut config: RuntimeConfig = ConfigLoader::load(None)?; if let Some(v) = cli.version { config.version = v; } if let Some(u) = cli.username { config.username = u; } if !cli.jvm_args.is_empty() { config.jvm_args = cli.jvm_args; } ensure_directories(&config)?; let version: Version = load_version(&config).await?; let client: Client = Client::builder() .http2_prior_knowledge() .build()?; download_all_files(&client, &config, &version).await?; extract_natives(&config, &version)?; launch(&config, &version)?; Ok(()) }