creeper 1.0.0-alpha.1

Minecraft Package Manager
use std::path::PathBuf;

use anyhow::{anyhow, ensure};
use tokio::{
    fs::{create_dir_all, read_to_string, write},
    process::Command,
};

use crate::{Creeper, Install, vanilla::AssetIndex};

impl Creeper {
    pub async fn launch(&self) -> anyhow::Result<Command> {
        let game_dir = self.game_dir().await?;

        let path = game_dir.join(".creeper").join("install.json");
        let json = read_to_string(path).await?;

        let install = serde_json::from_str::<Install>(&json)?;

        let mut cmd = Command::new("java");

        cmd.current_dir(game_dir);

        for flag in install.java_flag {
            cmd.arg(flag);
        }

        let lib_path = game_dir.join(".creeper").join("lib");
        create_dir_all(&lib_path).await?;

        let mut cp = vec![];

        for (path, art) in install.java_lib_class {
            let path = lib_path.join(path);
            self.retrieve_artifact_to(&art, &path).await?;
            cp.push(path.display().to_string());
        }

        if let Some(mc_jar) = install.mc_jar
            && !install.disable_mc_jar
        {
            let art = self.retrieve_artifact(&mc_jar).await?;
            cp.push(art.display().to_string());
        }

        let cp = cp.join(":");
        if !cp.is_empty() {
            cmd.arg("--class-path").arg(cp);
        }

        let mut p = vec![];

        for (path, art) in install.java_lib_mod {
            let path = lib_path.join(path);
            self.retrieve_artifact_to(&art, &path).await?;
            p.push(path.display().to_string());
        }

        let p = p.join(":");
        if !p.is_empty() {
            cmd.arg("--module-path").arg(p);
        }

        for (path, art) in install.java_lib_file {
            let path = lib_path.join(path);
            self.retrieve_artifact_to(&art, &path).await?;
        }

        if let Some(java_main_class) = install.java_main_class {
            cmd.arg(java_main_class);
        }

        let asset_path = game_dir.join(".creeper").join("asset");
        create_dir_all(&asset_path).await?;

        fn sha1_indexed_path(sha1: &str) -> anyhow::Result<PathBuf> {
            ensure!(sha1.len() == 40, "invalid sha1 length");
            let first2 = &sha1[0..2];
            let path = PathBuf::from(".").join(first2).join(sha1);
            Ok(path)
        }

        for (_path, art) in &install.mc_asset {
            let sha1 = art.sha1.as_ref().ok_or(anyhow!("missing SHA-1 checksum"))?;
            let path = asset_path.join("objects").join(sha1_indexed_path(&sha1)?);

            self.retrieve_artifact_to(&art, &path).await?;
        }

        let asset_index = AssetIndex::from_map(install.mc_asset)?;

        let json = serde_json::to_string(&asset_index)?;
        let path = asset_path.join("indexes").join("index.json");
        create_dir_all(path.parent().unwrap()).await?;
        write(path, json).await?;

        cmd.arg("--assetsDir").arg(asset_path);
        cmd.arg("--assetIndex").arg("index");

        for flag in install.mc_flag {
            cmd.arg(flag);
        }

        cmd.arg("--gameDir").arg(game_dir);

        Ok(cmd)
    }
}