use std::{ fs::{self, File}, path::{Path, PathBuf}, }; use serde_json::Value; use walkdir::WalkDir; const META_SERVER: &str = "https://meta.fabricmc.net/"; const MAVEN_SERVER: &str = "https://maven.fabricmc.net/"; fn query_meta_json(path: &str) -> Value { ureq::get(&format!("{META_SERVER}{path}")).call().unwrap().into_json().unwrap() } fn download_maven(url: &str, path: &Path) { fs::create_dir_all(path.parent().unwrap()).unwrap(); let mut file = File::create(path).unwrap(); if url.starts_with(MAVEN_SERVER) { crate::download::download(&format!("{url}"), &mut file).unwrap(); } else { crate::download::download(&format!("{META_SERVER}{url}"), &mut file).unwrap(); } } fn library_from_json(json: &Value) -> Option<(String, String, String)> { let name = json.get("name")?.as_str()?; let url = json.get("url")?.as_str()?; // Split "group:artifact:version" let parts: Vec<&str> = name.splitn(3, ':').collect(); if parts.len() != 3 { return None; } let group = parts[0].replace('.', "/"); let artifact = parts[1]; let version = parts[2]; // Build remote URL (like Java's getURL) let jar_name = format!("{artifact}-{version}.jar"); let remote_path = format!("{}/{}/{}/{jar_name}", group, artifact, version); let full_url = format!("{}{}", url, remote_path); // Build local path (like Java's getPath) let group_fs = parts[0].replace('.', &std::path::MAIN_SEPARATOR.to_string()); let local_path = PathBuf::from(group_fs) .join(artifact) .join(version) .join(jar_name) .to_string_lossy() .replace(' ', "_"); Some((name.to_string(), full_url, local_path)) } pub fn install_fabric(minecraft_path: &Path, minecraft_version: &str, loader_version: &str) -> Result<(), crate::error::Error> { let profile_name = format!("{minecraft_version}-fabric"); let versions_dir = minecraft_path.join("versions"); let profile_dir = versions_dir.join(&profile_name); let profile_json = profile_dir.join(format!("{profile_name}.json")); let libs_dir = minecraft_path.join("libraries"); let mut libs = WalkDir::new(&libs_dir) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()); let mut json = query_meta_json(&format!("v2/versions/loader/{minecraft_version}/{loader_version}/profile/json")); *json.get_mut("id").unwrap() = Value::String(profile_name.clone()); fs::create_dir_all(&profile_dir).unwrap(); fs::write(&profile_json, json.to_string()).unwrap(); fs::copy( versions_dir.join(minecraft_version).join(format!("{minecraft_version}.jar")), profile_dir.join(profile_name + ".jar"), ) .unwrap(); for lib_json in json.get("libraries").unwrap().as_array().unwrap() { if let Some((name, url, path)) = library_from_json(lib_json) { let lib_file = libs_dir.join(path); let lib_id = lib_file.parent().unwrap().parent().unwrap(); // Removes the version/lib.jar if !libs.any(|e| e.path().starts_with(lib_id)) { println!("Installing library '{name}'"); download_maven(&url, &lib_file); } else { println!("library '{name}' Already exists, skipping"); } } else { eprintln!("Failed to get json from {lib_json}") } } Ok(()) }