From bdb738648046f4abdde8c975fe638e598e934716 Mon Sep 17 00:00:00 2001 From: Toasterkitten Date: Wed, 29 Jul 2026 15:29:21 -0400 Subject: [PATCH] modrinth mod dl and update version num (oops) --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/download.rs | 4 +++- src/main.rs | 6 +++-- src/modrinth.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ src/mrpack.rs | 11 ++++----- src/overlay.rs | 0 src/util.rs | 8 +++++++ 8 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 src/modrinth.rs create mode 100644 src/overlay.rs diff --git a/Cargo.lock b/Cargo.lock index 2a8f6fe..02e4bfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1166,7 +1166,7 @@ checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "untitled-minecraft-launcher" -version = "0.1.0" +version = "0.4.0" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index 66b0b71..2db2c72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "untitled-minecraft-launcher" -version = "0.1.0" +version = "0.4.0" edition = "2024" [dependencies] diff --git a/src/download.rs b/src/download.rs index c309c54..7c216c0 100644 --- a/src/download.rs +++ b/src/download.rs @@ -10,7 +10,9 @@ pub fn download(url: &str, path: &Path) -> anyhow::Result<()> { if let Some(parent) = path.parent() { create_dir_all(parent)?; } - let mut res = get(url).call()?; + let mut res = get(url) + .header("User-Agent", crate::util::USER_AGENT) + .call()?; let mut file = File::create(path)?; copy(&mut res.body_mut().as_reader(), &mut file)?; Ok(()) diff --git a/src/main.rs b/src/main.rs index 2b2470a..054b71a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,15 +8,17 @@ mod fabric; mod instance; mod launch; mod meta; +mod modrinth; mod mrpack; +mod overlay; mod prism; mod util; use clap::{Parser, Subcommand}; use launch::run; use meta::fetch_version; +use modrinth::{download_mod, get_version}; use std::path::{Path, PathBuf}; - -use crate::util::{is_valid_name, open_path}; +use util::{is_valid_name, open_path}; /// Untitled Minecraft Launcher #[derive(Parser)] diff --git a/src/modrinth.rs b/src/modrinth.rs new file mode 100644 index 0000000..a05c672 --- /dev/null +++ b/src/modrinth.rs @@ -0,0 +1,62 @@ +use std::path::Path; + +use crate::{ + download::{self, verify_sha512}, + util::Hashes, +}; +use serde::Deserialize; + +const USER_AGENT: &str = "owenthepuppy/uml/0.3.0"; + +#[derive(Deserialize)] +pub struct Version { + pub files: Vec, +} +#[derive(Deserialize, Clone)] +pub struct VersionFile { + pub url: String, + pub filename: String, + pub hashes: Hashes, + pub primary: bool, +} +pub fn get_versions(slug: &str, loader: &str, game_version: &str) -> anyhow::Result> { + let versions = match ureq::get(format!( + "https://api.modrinth.com/v2/project/{slug}/version" + )) + .header("User-Agent", USER_AGENT) + .query("loaders", format!("[\"{loader}\"]")) + .query("game_versions", format!("[\"{game_version}\"]")) + .call() + { + Ok(mut resp) => resp.body_mut().read_json()?, + Err(ureq::Error::StatusCode(404)) => { + anyhow::bail!("mod not found: {slug}"); + } + Err(e) => return Err(e.into()), + }; + Ok(versions) +} +pub fn get_version(slug: &str, loader: &str, game_version: &str) -> anyhow::Result { + let mod_versions = get_versions(slug, loader, game_version)?; + let latest_version = mod_versions + .first() + .ok_or_else(|| anyhow::anyhow!("no compatible version for {slug}"))?; + let file = latest_version + .files + .iter() + .find(|f| f.primary) + .or_else(|| latest_version.files.first()) + .ok_or_else(|| anyhow::anyhow!("version has no files"))?; + Ok(file.clone()) +} +pub fn download_mod(file: &VersionFile, mods_dir: &Path) -> anyhow::Result<()> { + let dest = mods_dir.join(&file.filename); + download::download(&file.url, &dest)?; + if !verify_sha512(&dest, &file.hashes.sha512)? { + anyhow::bail!( + "hash mismatch after download: {:?}. This file is NOT DELETED AND WILL LOAD ON NEXT LAUNCH. DO NOT LAUNCH MINECRAFT, until you are SURE this is safe.", + dest + ); + } + Ok(()) +} diff --git a/src/mrpack.rs b/src/mrpack.rs index 37293a7..e0b45aa 100644 --- a/src/mrpack.rs +++ b/src/mrpack.rs @@ -1,7 +1,7 @@ use crate::{ download::{download, verify_sha512}, instance::{Loader, create, load, save}, - util::is_safe_relative_path, + util::{Hashes, is_safe_relative_path}, }; use indicatif::{ProgressBar, ProgressStyle}; use serde::Deserialize; @@ -26,10 +26,6 @@ struct PackFile { #[serde(default)] env: Option, } -#[derive(Deserialize)] -struct Hashes { - sha512: String, -} #[derive(Deserialize)] struct Env { @@ -157,7 +153,10 @@ pub fn import( .ok_or_else(|| anyhow::anyhow!("no download url for {}", f.path))?; download(url, &dest)?; if !verify_sha512(&dest, &f.hashes.sha512)? { - anyhow::bail!("hash mismatch after download: {}", f.path); + anyhow::bail!( + "hash mismatch after download: {}. This file is NOT DELETED AND WILL LOAD ON NEXT LAUNCH. DO NOT LAUNCH MINECRAFT, until you are SURE this is safe.", + f.path + ); } } pb.inc(1); diff --git a/src/overlay.rs b/src/overlay.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/util.rs b/src/util.rs index 6fc1b2e..6738394 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,9 @@ use std::path::Path; +use serde::Deserialize; + +pub const USER_AGENT: &str = concat!("owenthepuppy/uml/", env!("CARGO_PKG_VERSION")); + pub fn is_valid_name(name: &str) -> bool { let mut components = Path::new(name).components(); matches!( @@ -17,3 +21,7 @@ pub fn open_path(path: &Path) -> anyhow::Result<()> { std::process::Command::new("xdg-open").arg(path).spawn()?; Ok(()) } +#[derive(Deserialize, Clone)] +pub struct Hashes { + pub sha512: String, +}