Support diffing against an older version

This commit is contained in:
Gabriel Tofvesson 2025-05-07 03:27:18 +02:00
parent e524aba94b
commit 9e09e71623

View File

@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::env::args;
use std::ffi::OsStr;
use std::io::Read;
use std::path::PathBuf;
use zip::ZipArchive;
@ -48,7 +49,8 @@ impl Dependency {
.json::<Vec<FabricInstallerVersion>>()
.await?
.first()
.ok_or("No fabric installer version found")?.version
.ok_or("No fabric installer version found")?
.version
),
Dependency::Minecraft => {
let version_map = reqwest::get("https://raw.githubusercontent.com/liebki/MinecraftServerForkDownloads/refs/heads/main/release_vanilla_downloads.json")
@ -65,7 +67,7 @@ impl Dependency {
let target = target.into();
if target.exists() {
println!("File {} already exists, skipping download", target.display());
eprintln!("File {} already exists, skipping download", target.display());
return Ok(());
}
let response = reqwest::get(url.clone()).await?;
@ -80,7 +82,7 @@ impl Dependency {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
struct ConfigFile {
path: String,
hashes: HashMap<String, String>,
@ -94,21 +96,24 @@ impl ConfigFile {
async fn download(&self, target: impl Into<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
let target = target.into();
let target_path = target.join(&self.path);
if target_path.exists() {
println!("File {} already exists, skipping download", target_path.display());
let mut disabled_path = target_path.extension().unwrap_or(OsStr::new("jar")).to_os_string();
disabled_path.push(".disabled");
if target_path.exists() || target_path.with_extension(disabled_path).exists() {
eprintln!("File {} already exists (or is disabled), skipping download", target_path.display());
return Ok(());
}
for link in &self.downloads {
let response = reqwest::get(link).await?;
if response.status().is_success() {
println!("Downloading file from {}", link);
let content = response.bytes().await?;
let mut file = File::create(&target_path).await?;
tokio::io::copy(&mut content.as_ref(), &mut file).await?;
return Ok(())
} else {
println!("Failed to download file from {}", link);
eprintln!("Failed to download file from {}", link);
}
}
@ -143,6 +148,33 @@ struct FabricInstallerVersion {
stable: bool,
}
#[derive(Debug, Clone)]
struct GameSetupParams {
dependency: Dependency,
version: String,
mc_version: String,
}
impl GameSetupParams {
fn new(config: &ModrinthConfig) -> Self {
let (server, server_version, mc_version) = if config.is_modded() {
let (dep, version) = config.dependencies.iter().find(|(k, _)| **k != Dependency::Minecraft).expect("Modded dependency version not found");
let mc_version = config.dependencies.iter().find(|(k, _)| **k == Dependency::Minecraft).map(|(_, v)| v.clone()).expect("Minecraft version not found");
(dep.clone(), version.clone(), mc_version)
} else {
let (dep, version) = config.dependencies.iter().find(|(k, _)| **k == Dependency::Minecraft).expect("Modded dependency version not found");
(dep.clone(), version.clone(), version.clone())
};
Self {
dependency: server,
version: server_version,
mc_version,
}
}
}
async fn get_config_from_archive(archive: &mut ZipArchive<std::fs::File>, out_dir: impl Into<PathBuf>) -> Option<ModrinthConfig> {
let overrides = "overrides/";
@ -155,7 +187,6 @@ async fn get_config_from_archive(archive: &mut ZipArchive<std::fs::File>, out_di
if file.name() == modrinth_index {
let mut contents = String::new();
if let Ok(_) = file.read_to_string(&mut contents) {
println!("Found modrinth index file");
return Some(serde_json::from_str::<ModrinthConfig>(contents.as_str()).expect("Unable to parse modrinth config"))
} else {
eprintln!("Can't read contents");
@ -178,17 +209,26 @@ async fn get_config_from_archive(archive: &mut ZipArchive<std::fs::File>, out_di
None
}
async fn get_config_from_file(file: &str) -> Option<ModrinthConfig> {
let file = std::fs::File::open(file).expect("Unable to open file");
let mut archive = ZipArchive::new(file).expect("Unable to read zip archive");
get_config_from_archive(&mut archive, "out/").await
}
#[tokio::main]
async fn main() {
let args: Vec<String> = args().collect();
if args.len() != 2 && args.len() != 3 {
eprintln!("Usage: {} <mrpack> [target dir]", args[0]);
if args.len() < 2 || args.len() > 4 {
eprintln!("Usage: {} <mrpack> [target dir] [previous version]", args[0]);
return;
}
let out_dir = if args.len() == 3 {
PathBuf::from(&args[2])
} else {
PathBuf::from("out/")
let out_dir = match args.len() {
3 | 4 => PathBuf::from(&args[2]),
_ => PathBuf::from("out/"),
};
let previous_version = match args.len() {
4 => Some(get_config_from_file(args[3].as_str()).await.unwrap()),
_ => None,
};
let file = std::fs::File::open(&args[1]).expect("Unable to open file");
let mut archive = ZipArchive::new(file).expect("Unable to read zip archive");
@ -196,22 +236,31 @@ async fn main() {
// Create directory "out" and subdirectory "mods"
std::fs::create_dir_all(&out_dir).expect("Unable to create directory");
println!("Extracting files from {}", args[1]);
let config = get_config_from_archive(&mut archive, &out_dir).await.unwrap();
for entry in &config.files {
entry.download(&out_dir).await.unwrap();
}
let (server, server_version, mc_version) = if config.is_modded() {
let (dep, version) = config.dependencies.iter().find(|(k, _)| **k != Dependency::Minecraft).expect("Modded dependency version not found");
let mc_version = config.dependencies.iter().find(|(k, _)| **k == Dependency::Minecraft).map(|(_, v)| v.clone()).expect("Minecraft version not found");
(dep.clone(), version.clone(), mc_version)
} else {
let (dep, version) = config.dependencies.iter().find(|(k, _)| **k == Dependency::Minecraft).expect("Modded dependency version not found");
(dep.clone(), version.clone(), version.clone())
};
if let Some(ref previous_version) = previous_version {
for removal in previous_version.files.iter().filter(|v| config.files.iter().filter(|v1| v1.path == v.path).count() == 0).map(|c| {
tokio::fs::remove_file(c.path.clone())
}) {
let _ = removal.await;
}
}
server.download(out_dir.join("server.jar"), server_version, mc_version).await.unwrap();
let server_jar = out_dir.join("server.jar");
let old_params = previous_version.as_ref().map(|prev| GameSetupParams::new(prev));
let params = GameSetupParams::new(&config);
if match old_params { None => true, Some(old_params) => old_params.dependency != params.dependency || !server_jar.exists() } {
let _ = tokio::fs::remove_file(&server_jar).await;
println!("Downloading server jar");
params.dependency.download(server_jar, params.version, params.mc_version).await.unwrap();
} else {
println!("Server jar already exists, skipping download");
}
let eula = "#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA).
#Thu Jan 01 00:00:00 GMT 1970