From 753c9d641769e83e8253566908f017ab67260a99 Mon Sep 17 00:00:00 2001 From: Warren Hood Date: Tue, 20 Aug 2024 02:20:17 +0200 Subject: [PATCH] Add ability to download a modpack from git or from any local path --- Cargo.lock | 68 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 22 +++++++++++-- src/modpack.rs | 12 +++---- src/providers/modrinth.rs | 2 +- src/resolver.rs | 65 ++++++++++++++++++++++++++++--------- 6 files changed, 147 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c7a85e..203b173 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,8 @@ version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -365,6 +367,21 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "git2" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" +dependencies = [ + "bitflags 2.6.0", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "h2" version = "0.4.5" @@ -553,6 +570,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.70" @@ -574,6 +600,46 @@ version = "0.2.156" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" +[[package]] +name = "libgit2-sys" +version = "0.17.0+1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -601,11 +667,13 @@ name = "mcmpmgr" version = "0.1.0" dependencies = [ "clap", + "git2", "lhash", "reqwest", "semver", "serde", "sha2", + "tempfile", "tokio", "toml", ] diff --git a/Cargo.toml b/Cargo.toml index b0d08a8..2340efe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,10 +5,12 @@ edition = "2021" [dependencies] clap = { version = "4.5.15", features = ["derive"] } +git2 = "0.19.0" lhash = { version = "1.1.0", features = ["sha1", "sha512"] } reqwest = { version = "0.12.5", features = ["json"] } semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.207", features = ["derive"] } sha2 = "0.10.8" +tempfile = "3.12.0" tokio = { version = "1.39.2", features = ["full"] } toml = "0.8.19" diff --git a/src/main.rs b/src/main.rs index d2233da..3b256b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,6 +90,12 @@ enum Commands { /// Side to download for #[arg(long, short, default_value_t = DownloadSide::Both)] side: DownloadSide, + /// Download mods from a remote modpack in a git repo + #[arg(long)] + git: Option, + /// Download mods from a local modpack + #[arg(long)] + path: Option, }, /// Update all mods to the latest possible version Update { @@ -288,8 +294,20 @@ async fn main() -> Result<(), Box> { } }; } - Commands::Download { mods_dir, side } => { - let pack_lock = resolver::PinnedPackMeta::load_from_current_directory(true).await?; + Commands::Download { + mods_dir, + side, + git, + path, + } => { + let pack_lock = if let Some(git_url) = git { + resolver::PinnedPackMeta::load_from_git_repo(&git_url, true).await? + } else if let Some(local_path) = path { + resolver::PinnedPackMeta::load_from_directory(&local_path, true).await? + } else { + resolver::PinnedPackMeta::load_from_current_directory(true).await? + }; + pack_lock.download_mods(&mods_dir, side).await?; println!("Mods updated"); } diff --git a/src/modpack.rs b/src/modpack.rs index 223ae8f..34792fd 100644 --- a/src/modpack.rs +++ b/src/modpack.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, error::Error, - path::PathBuf, + path::{Path, PathBuf}, }; const MODPACK_FILENAME: &str = "modpack.toml"; @@ -38,7 +38,7 @@ impl std::str::FromStr for ModLoader { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ModpackMeta { - pack_name: String, + pub pack_name: String, pub mc_version: String, pub modloader: ModLoader, pub mods: HashMap, @@ -60,8 +60,8 @@ impl ModpackMeta { self.mods.values().into_iter() } - pub fn load_from_directory(directory: &PathBuf) -> Result> { - let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME)); + pub fn load_from_directory(directory: &Path) -> Result> { + let modpack_meta_file_path = directory.join(PathBuf::from(MODPACK_FILENAME)); if !modpack_meta_file_path.exists() { return Err(format!( "Directory '{}' does not seem to be a valid modpack project directory.", @@ -104,8 +104,8 @@ impl ModpackMeta { self } - pub fn init_project(&self, directory: &PathBuf) -> Result<(), Box> { - let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME)); + pub fn init_project(&self, directory: &Path) -> Result<(), Box> { + let modpack_meta_file_path = directory.join(PathBuf::from(MODPACK_FILENAME)); if modpack_meta_file_path.exists() { return Err(format!( "{MODPACK_FILENAME} already exists at {}", diff --git a/src/providers/modrinth.rs b/src/providers/modrinth.rs index 89b2830..0d6538b 100644 --- a/src/providers/modrinth.rs +++ b/src/providers/modrinth.rs @@ -65,7 +65,7 @@ impl Modrinth { } } - pub async fn get_project(&self, project_id: &str) -> Result> { + async fn get_project(&self, project_id: &str) -> Result> { let project: ModrinthProject = self .client .get(format!("https://api.modrinth.com/v2/project/{project_id}")) diff --git a/src/resolver.rs b/src/resolver.rs index d9a56d9..919c1f8 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -4,7 +4,7 @@ use std::{ collections::{HashMap, HashSet}, error::Error, ffi::{OsStr, OsString}, - path::PathBuf, + path::{Path, PathBuf}, }; use crate::{ @@ -61,7 +61,7 @@ impl PinnedPackMeta { match filesource { crate::providers::FileSource::Download { url, - sha1, + sha1: _, sha512, filename, } => { @@ -90,10 +90,10 @@ impl PinnedPackMeta { tokio::fs::write(mods_dir.join(filename), file_contents).await?; } crate::providers::FileSource::Local { - path, - sha1, - sha512, - filename, + path: _, + sha1: _, + sha512: _, + filename: _, } => unimplemented!(), } } @@ -102,7 +102,12 @@ impl PinnedPackMeta { Ok(()) } - pub fn file_is_pinned(&self, file_name: &OsStr, mod_side: DownloadSide, cache: &mut HashSet) -> bool { + pub fn file_is_pinned( + &self, + file_name: &OsStr, + mod_side: DownloadSide, + cache: &mut HashSet, + ) -> bool { if cache.contains(file_name) { return true; } @@ -114,9 +119,9 @@ impl PinnedPackMeta { for filesource in pinned_mod.source.iter() { match filesource { crate::providers::FileSource::Download { - url, - sha1, - sha512, + url: _, + sha1: _, + sha512: _, filename, } => { let pinned_filename = OsStr::new(filename); @@ -126,9 +131,9 @@ impl PinnedPackMeta { } } crate::providers::FileSource::Local { - path, - sha1, - sha512, + path: _, + sha1: _, + sha512: _, filename, } => { let pinned_filename = OsStr::new(filename); @@ -353,10 +358,10 @@ impl PinnedPackMeta { } pub async fn load_from_directory( - directory: &PathBuf, + directory: &Path, ignore_transitive_versions: bool, ) -> Result> { - let modpack_lock_file_path = directory.clone().join(PathBuf::from(MODPACK_LOCK_FILENAME)); + let modpack_lock_file_path = directory.join(PathBuf::from(MODPACK_LOCK_FILENAME)); if !modpack_lock_file_path.exists() { let mut new_modpack_lock = Self::new(); new_modpack_lock @@ -376,4 +381,34 @@ impl PinnedPackMeta { ) -> Result> { Self::load_from_directory(&std::env::current_dir()?, ignore_transitive_versions).await } + + pub async fn load_from_git_repo( + git_url: &str, + ignore_transitive_versions: bool, + ) -> Result> { + // TODO: Refactor the way this works since temp dirs will be deleted before we get to access local mods + // That is a problem for the future + + let pack_dir = tempfile::tempdir()?; + println!( + "Cloning modpack from git repo {} to {:#?}...", + git_url, + pack_dir.path() + ); + let _repo = git2::Repository::clone(git_url, pack_dir.path())?; + + let modpack_meta = ModpackMeta::load_from_directory(pack_dir.path())?; + let pinned_pack_meta = + PinnedPackMeta::load_from_directory(pack_dir.path(), ignore_transitive_versions) + .await?; + + println!( + "Loaded modpack '{}' (MC {} - {}) from git", + modpack_meta.pack_name, + modpack_meta.mc_version, + modpack_meta.modloader.to_string() + ); + + Ok(pinned_pack_meta) + } }