cobble-core 1.2.0

Library for managing, installing and launching Minecraft instances and more.
Documentation
use cobble_core::minecraft::models::{AssetIndex, VersionData, VersionManifest};
use cobble_core::minecraft::{
    install_assets, install_client, install_libraries, install_log_config, AssetInstallationUpdate,
    ClientInstallationUpdate, InstallOptionsBuilder, InstallationUpdate, LibraryInstallationUpdate,
    LogConfigInstallationUpdate,
};
use cobble_core::utils::DownloadProgress;
use futures::join;
use std::env::temp_dir;
use tokio::sync::mpsc::{Receiver, Sender};
use tracing_subscriber::FmtSubscriber;

#[tokio::main]
async fn main() {
    FmtSubscriber::builder()
        .with_env_filter("info,cobble_core=debug")
        .init();

    // Fetch version manifest
    let version_manifest = VersionManifest::fetch().await.unwrap();

    // Get version summary
    let version_summary = version_manifest
        .versions
        .get(&version_manifest.latest.release)
        .unwrap();

    // Fetch version data
    let version_data = VersionData::fetch(&version_summary.url).await.unwrap();

    // Fetch asset_index
    let asset_index = version_data.asset_index.fetch_index().await.unwrap();

    let (tx, rx) = InstallationUpdate::channel(500);

    join!(install(version_data, asset_index, tx), process_updates(rx));
}

async fn install(
    version_data: VersionData,
    asset_index: AssetIndex,
    tx: Sender<InstallationUpdate>,
) {
    let mut libraries_path = temp_dir();
    libraries_path.push("cobble-core/libraries");
    let mut natives_path = temp_dir();
    natives_path.push("cobble-core/natives");
    let mut assets_path = temp_dir();
    assets_path.push("cobble-core/assets");
    let mut log_configs_path = temp_dir();
    log_configs_path.push("cobble-core/assets/log_configs");
    let mut minecraft_path = temp_dir();
    minecraft_path.push("cobble-core/.minecraft");

    let options = InstallOptionsBuilder::default()
        .version_data(version_data)
        .asset_index(asset_index)
        .libraries_path(libraries_path)
        .natives_path(natives_path)
        .assets_path(assets_path)
        .log_configs_path(log_configs_path)
        .minecraft_path(minecraft_path)
        .parallel_downloads(5)
        .download_retries(1)
        .verify_downloads(true)
        .build()
        .unwrap();

    install_libraries(&options, tx.clone()).await.unwrap();
    install_assets(&options, tx.clone()).await.unwrap();
    install_log_config(&options, tx.clone()).await.unwrap();
    install_client(&options, tx.clone()).await.unwrap();
}

async fn process_updates(mut rx: Receiver<InstallationUpdate>) {
    while let Some(update) = rx.recv().await {
        match update {
            InstallationUpdate::Library((n, p)) => match p {
                LibraryInstallationUpdate::Downloading(p) => print_progress(p),
                LibraryInstallationUpdate::Extracting => {
                    println!("{} extracting...", n);
                }
            },
            InstallationUpdate::Asset((n, p)) => match p {
                AssetInstallationUpdate::Downloading(p) => print_progress(p),
                AssetInstallationUpdate::Symlink => {
                    println!("{} symlink...", n);
                }
            },
            InstallationUpdate::LogConfig(p) => match p {
                LogConfigInstallationUpdate::Downloading(p) => print_progress(p),
            },
            InstallationUpdate::Client(p) => match p {
                ClientInstallationUpdate::Downloading(p) => print_progress(p),
            },
        }
    }
}

fn print_progress(p: DownloadProgress) {
    let name = p.file.file_name().map(|s| s.to_string_lossy()).unwrap();
    let percent = p.downloaded_bytes as f64 / p.total_bytes as f64;

    tracing::info!(
        "{} ({}/{}): {}% done",
        name,
        p.current_file + 1,
        p.total_files,
        (percent * 100.0) as u64
    );
}