From a393e0a2f2c3678a3ea869dc1417fa269f2b1040 Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Sat, 24 Jan 2026 08:29:14 +0100 Subject: Resolve audio not loading bug Ensure all assets are downloading for each version Temporarily disable minecraft versions older than 1.8 because of the asset/manifest loading issues Implement basic documentation of modules Implement basic async/multithreading for downloading assets --- src/minecraft/downloads.rs | 172 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 150 insertions(+), 22 deletions(-) (limited to 'src/minecraft/downloads.rs') diff --git a/src/minecraft/downloads.rs b/src/minecraft/downloads.rs index 5be5a05..a994146 100644 --- a/src/minecraft/downloads.rs +++ b/src/minecraft/downloads.rs @@ -1,5 +1,10 @@ use log::{debug, info}; -use tokio::{fs, io::AsyncWriteExt}; +use reqwest::get; +use serde::Deserialize; +use tokio::{ + fs::{self, File, create_dir_all}, + io::AsyncWriteExt, +}; use crate::{ config::Config, @@ -8,58 +13,181 @@ use crate::{ platform::paths, }; -/// Download everything required to launch: +#[derive(Debug, Deserialize)] +struct AssetObject { + hash: String, + size: u64, +} + +#[derive(Debug, Deserialize)] +struct AssetIndexManifest { + objects: std::collections::HashMap, +} + +/// Pobiera wszystko potrzebne do uruchomienia Minecraft: /// - client jar -/// - libraries -pub async fn download_all(config: &Config, version: &Version) -> Result<(), McError> { +/// - biblioteki (artifact + natives) +/// - assets (w tym textures, sounds) +pub async fn download_all( + config: &Config, + version: &Version, +) -> Result<(), McError> { download_client(config, version).await?; download_libraries(config, &version.libraries).await?; + download_assets(config, version).await?; Ok(()) } -async fn download_client(config: &Config, version: &Version) -> Result<(), McError> { +async fn download_client( + config: &Config, + version: &Version, +) -> Result<(), McError> { let jar_path = paths::client_jar(config, &version.id)?; if jar_path.exists() { - debug!("Client jar already exists"); + debug!("Client jar already exists: {}", jar_path.display()); return Ok(()); } info!("Downloading client {}", version.id); - download_file(&version.downloads.client.url, &jar_path).await } -async fn download_libraries(config: &Config, libraries: &[Library]) -> Result<(), McError> { - for lib in libraries { - let Some(artifact) = &lib.downloads.artifact else { - continue; - }; +async fn download_libraries( + config: &Config, + libraries: &[Library], +) -> Result<(), McError> { + for library in libraries { + // ===== CLASSPATH LIBRARIES ===== + if let Some(artifact) = &library.downloads.artifact { + let library_path = paths::library_file(config, &artifact.path)?; + + if !library_path.exists() { + info!("Downloading library {}", artifact.path); + download_file(&artifact.url, &library_path).await?; + } + } + + // ===== NATIVES ===== + if let Some(classifiers) = &library.downloads.classifiers { + for (_, native) in classifiers { + let native_path = paths::library_file(config, &native.path)?; + + if native_path.exists() { + continue; + } + + info!("Downloading native library {}", native.path); + download_file(&native.url, &native_path).await?; + } + } + } + + Ok(()) +} - let lib_path = paths::library_file(config, &artifact.path)?; +async fn download_asset_index( + config: &Config, + version: &Version, +) -> Result { + let assets_dir = paths::assets_dir(config); + create_dir_all(assets_dir.join("indexes")).await?; + + let asset_index = version.asset_index.as_ref().ok_or_else(|| { + McError::Config("Missing asset_index in version.json".into()) + })?; + + // Nie pozwalamy na legacy dla nowoczesnych wersji + if asset_index.id == "legacy" { + return Err(McError::Config( + "Legacy assetIndex detected – pobierz właściwy version.json".into(), + )); + } + + let index_path = assets_dir + .join("indexes") + .join(format!("{}.json", asset_index.id)); + + // Jeśli indeks istnieje lokalnie + if index_path.exists() { + let index_data = fs::read_to_string(&index_path).await?; + let manifest: AssetIndexManifest = serde_json::from_str(&index_data)?; + return Ok(manifest); + } + + // Pobierz indeks z sieci + info!("Downloading asset index {}", asset_index.id); + let response = get(&asset_index.url).await?; + let manifest_text = response.text().await?; + + fs::write(&index_path, &manifest_text).await?; + + let manifest: AssetIndexManifest = serde_json::from_str(&manifest_text)?; + Ok(manifest) +} - if lib_path.exists() { +async fn download_assets( + config: &Config, + version: &Version, +) -> Result<(), McError> { + let assets_dir = paths::assets_dir(config); + + // Katalogi MUSZĄ istnieć + create_dir_all(assets_dir.join("objects")).await?; + create_dir_all(assets_dir.join("indexes")).await?; + + let manifest = download_asset_index(config, version).await?; + + // Pobieramy wszystkie obiekty + for (logical_path, asset) in &manifest.objects { + let subdir = &asset.hash[0..2]; + let file_path = assets_dir + .join("objects") + .join(subdir) + .join(&asset.hash); + + if file_path.exists() { continue; } - info!("Downloading library {}", artifact.path); - download_file(&artifact.url, &lib_path).await?; + let url = format!( + "https://resources.download.minecraft.net/{}/{}", + subdir, asset.hash + ); + info!("Downloading asset {} -> {}", logical_path, file_path.display()); + download_file(&url, &file_path).await?; + } + + // Pobierz sounds.json jeśli istnieje + if let Some(asset) = manifest.objects.get("sounds.json") { + let file_path = assets_dir.join("indexes").join("sounds.json"); + if !file_path.exists() { + let subdir = &asset.hash[0..2]; + let url = format!( + "https://resources.download.minecraft.net/{}/{}", + subdir, asset.hash + ); + info!("Downloading sounds.json"); + download_file(&url, &file_path).await?; + } } Ok(()) } -/* ---------------- helper ---------------- */ - -async fn download_file(url: &str, path: &std::path::Path) -> Result<(), McError> { +/// Helper do pobierania plików +async fn download_file( + url: &str, + path: &std::path::Path, +) -> Result<(), McError> { if let Some(parent) = path.parent() { - fs::create_dir_all(parent).await?; + create_dir_all(parent).await?; } - let response = reqwest::get(url).await?; + let response = get(url).await?; let bytes = response.bytes().await?; - let mut file = fs::File::create(path).await?; + let mut file = File::create(path).await?; file.write_all(&bytes).await?; Ok(()) -- cgit v1.2.3