Add ability to download a modpack from git or from any local path

This commit is contained in:
Warren Hood 2024-08-20 02:20:17 +02:00
parent 08eff7ea2a
commit 753c9d6417
6 changed files with 147 additions and 24 deletions

68
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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<String>,
/// Download mods from a local modpack
#[arg(long)]
path: Option<PathBuf>,
},
/// Update all mods to the latest possible version
Update {
@ -288,8 +294,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
};
}
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");
}

View file

@ -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<String, ModMeta>,
@ -60,8 +60,8 @@ impl ModpackMeta {
self.mods.values().into_iter()
}
pub fn load_from_directory(directory: &PathBuf) -> Result<Self, Box<dyn Error>> {
let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME));
pub fn load_from_directory(directory: &Path) -> Result<Self, Box<dyn Error>> {
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<dyn Error>> {
let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME));
pub fn init_project(&self, directory: &Path) -> Result<(), Box<dyn Error>> {
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 {}",

View file

@ -65,7 +65,7 @@ impl Modrinth {
}
}
pub async fn get_project(&self, project_id: &str) -> Result<ModrinthProject, Box<dyn Error>> {
async fn get_project(&self, project_id: &str) -> Result<ModrinthProject, Box<dyn Error>> {
let project: ModrinthProject = self
.client
.get(format!("https://api.modrinth.com/v2/project/{project_id}"))

View file

@ -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<OsString>) -> bool {
pub fn file_is_pinned(
&self,
file_name: &OsStr,
mod_side: DownloadSide,
cache: &mut HashSet<OsString>,
) -> 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<Self, Box<dyn Error>> {
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, Box<dyn Error>> {
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<Self, Box<dyn Error>> {
// 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)
}
}