You've already forked AstralRinth
forked from didirus/AstralRinth
Finish launching modded versions of Minecraft
This commit is contained in:
@@ -1,30 +1,36 @@
|
||||
use crate::launcher::meta::{
|
||||
use crate::launcher::LauncherError;
|
||||
use daedalus::get_path_from_artifact;
|
||||
use daedalus::minecraft::{
|
||||
fetch_assets_index, fetch_version_info, Asset, AssetsIndex, DownloadType, Library, Os, Version,
|
||||
VersionInfo,
|
||||
};
|
||||
use crate::launcher::LauncherError;
|
||||
use daedalus::modded::{fetch_partial_version, merge_partial_version, LoaderVersion};
|
||||
use futures::future;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub async fn download_version_info(
|
||||
client_path: &Path,
|
||||
version: &Version,
|
||||
loader_version: Option<&LoaderVersion>,
|
||||
) -> Result<VersionInfo, LauncherError> {
|
||||
let path = &*client_path
|
||||
.join(&version.id)
|
||||
.join(format!("{}.json", &version.id));
|
||||
let id = loader_version.map(|x| &x.id).unwrap_or(&version.id);
|
||||
|
||||
let path = &*client_path.join(id).join(format!("{}.json", id));
|
||||
|
||||
if path.exists() {
|
||||
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||
} else {
|
||||
let info = fetch_version_info(version)
|
||||
.await
|
||||
.map_err(|err| LauncherError::FetchError {
|
||||
inner: err,
|
||||
item: "version info".to_string(),
|
||||
})?;
|
||||
let mut info = fetch_version_info(version).await?;
|
||||
|
||||
if let Some(loader_version) = loader_version {
|
||||
let partial = fetch_partial_version(&*loader_version.url).await?;
|
||||
|
||||
info = merge_partial_version(partial, info);
|
||||
|
||||
info.id = loader_version.id.clone();
|
||||
}
|
||||
|
||||
save_file(path, &bytes::Bytes::from(serde_json::to_string(&info)?))?;
|
||||
|
||||
@@ -50,7 +56,7 @@ pub async fn download_client(
|
||||
.join(&version_info.id)
|
||||
.join(format!("{}.jar", &version_info.id));
|
||||
|
||||
save_and_download_file(path, &client_download.url, &client_download.sha1).await?;
|
||||
save_and_download_file(path, &client_download.url, Some(&client_download.sha1)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -66,12 +72,7 @@ pub async fn download_assets_index(
|
||||
if path.exists() {
|
||||
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||
} else {
|
||||
let index = fetch_assets_index(version)
|
||||
.await
|
||||
.map_err(|err| LauncherError::FetchError {
|
||||
inner: err,
|
||||
item: "assets index".to_string(),
|
||||
})?;
|
||||
let index = fetch_assets_index(version).await?;
|
||||
|
||||
save_file(path, &bytes::Bytes::from(serde_json::to_string(&index)?))?;
|
||||
|
||||
@@ -113,7 +114,7 @@ async fn download_asset(
|
||||
"https://resources.download.minecraft.net/{}/{}",
|
||||
sub_hash, asset.hash
|
||||
),
|
||||
&*asset.hash,
|
||||
Some(&*asset.hash),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -154,34 +155,9 @@ async fn download_library(
|
||||
}
|
||||
}
|
||||
|
||||
let name_items = library.name.split(':').collect::<Vec<&str>>();
|
||||
|
||||
let package = name_items.get(0).ok_or_else(|| {
|
||||
LauncherError::ParseError(format!(
|
||||
"Unable to find package for library {}",
|
||||
&library.name
|
||||
))
|
||||
})?;
|
||||
let name = name_items.get(1).ok_or_else(|| {
|
||||
LauncherError::ParseError(format!("Unable to find name for library {}", &library.name))
|
||||
})?;
|
||||
let version = name_items.get(2).ok_or_else(|| {
|
||||
LauncherError::ParseError(format!(
|
||||
"Unable to find version for library {}",
|
||||
&library.name
|
||||
))
|
||||
})?;
|
||||
|
||||
let (a, b) = future::join(
|
||||
download_library_jar(libraries_path, library, package, name, version),
|
||||
download_native(
|
||||
libraries_path,
|
||||
natives_path,
|
||||
library,
|
||||
package,
|
||||
name,
|
||||
version,
|
||||
),
|
||||
download_library_jar(libraries_path, library),
|
||||
download_native(natives_path, library),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -194,61 +170,51 @@ async fn download_library(
|
||||
async fn download_library_jar(
|
||||
libraries_path: &Path,
|
||||
library: &Library,
|
||||
package: &str,
|
||||
name: &str,
|
||||
version: &str,
|
||||
) -> Result<(), LauncherError> {
|
||||
if let Some(library) = &library.downloads.artifact {
|
||||
let mut path = libraries_path.to_path_buf();
|
||||
let mut path = libraries_path.to_path_buf();
|
||||
path.push(get_path_from_artifact(&*library.name)?);
|
||||
|
||||
for directory in package.split('.') {
|
||||
path.push(directory);
|
||||
if let Some(downloads) = &library.downloads {
|
||||
if let Some(library) = &downloads.artifact {
|
||||
save_and_download_file(&*path, &library.url, Some(&library.sha1)).await?;
|
||||
}
|
||||
|
||||
path.push(name);
|
||||
path.push(version);
|
||||
path.push(format!("{}-{}.jar", name, version));
|
||||
|
||||
save_and_download_file(&*path, &library.url, &library.sha1).await?;
|
||||
} else {
|
||||
save_and_download_file(
|
||||
&*path,
|
||||
&format!(
|
||||
"{}{}",
|
||||
library
|
||||
.url
|
||||
.as_deref()
|
||||
.unwrap_or("https://libraries.minecraft.net/"),
|
||||
get_path_from_artifact(&*library.name)?
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn download_native(
|
||||
libraries_path: &Path,
|
||||
natives_path: &Path,
|
||||
library: &Library,
|
||||
package: &str,
|
||||
name: &str,
|
||||
version: &str,
|
||||
) -> Result<(), LauncherError> {
|
||||
async fn download_native(natives_path: &Path, library: &Library) -> Result<(), LauncherError> {
|
||||
if let Some(natives) = &library.natives {
|
||||
if let Some(os_key) = natives.get(&get_os()) {
|
||||
if let Some(classifiers) = &library.downloads.classifiers {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let parsed_key = os_key.replace("${arch}", "64");
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let parsed_key = os_key.replace("${arch}", "32");
|
||||
if let Some(downloads) = &library.downloads {
|
||||
if let Some(classifiers) = &downloads.classifiers {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let parsed_key = os_key.replace("${arch}", "64");
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let parsed_key = os_key.replace("${arch}", "32");
|
||||
|
||||
if let Some(native) = classifiers.get(&*parsed_key) {
|
||||
let mut path = libraries_path.to_path_buf();
|
||||
if let Some(native) = classifiers.get(&*parsed_key) {
|
||||
let file = download_file(&native.url, Some(&native.sha1)).await?;
|
||||
|
||||
for directory in package.split('.') {
|
||||
path.push(directory);
|
||||
let reader = std::io::Cursor::new(&*file);
|
||||
|
||||
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
||||
archive.extract(natives_path).unwrap();
|
||||
}
|
||||
|
||||
path.push(name);
|
||||
path.push(version);
|
||||
path.push(format!("{}-{}-{}.jar", name, version, parsed_key));
|
||||
|
||||
save_and_download_file(&*path, &native.url, &native.sha1).await?;
|
||||
|
||||
let file = File::open(&path).unwrap();
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
||||
archive.extract(natives_path).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,14 +226,14 @@ async fn download_native(
|
||||
async fn save_and_download_file(
|
||||
path: &Path,
|
||||
url: &str,
|
||||
sha1: &str,
|
||||
sha1: Option<&str>,
|
||||
) -> Result<bytes::Bytes, LauncherError> {
|
||||
let read = std::fs::read(path).ok().map(bytes::Bytes::from);
|
||||
|
||||
if let Some(bytes) = read {
|
||||
Ok(bytes)
|
||||
} else {
|
||||
let file = download_file(url, Some(sha1)).await?;
|
||||
let file = download_file(url, sha1).await?;
|
||||
|
||||
save_file(path, &file)?;
|
||||
|
||||
@@ -286,7 +252,16 @@ fn save_file(path: &Path, bytes: &bytes::Bytes) -> Result<(), std::io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn download_file(url: &str, sha1: Option<&str>) -> Result<bytes::Bytes, LauncherError> {
|
||||
pub fn get_os() -> Os {
|
||||
match std::env::consts::OS {
|
||||
"windows" => Os::Windows,
|
||||
"macos" => Os::Osx,
|
||||
"linux" => Os::Linux,
|
||||
_ => Os::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_file(url: &str, sha1: Option<&str>) -> Result<bytes::Bytes, LauncherError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.tcp_keepalive(Some(std::time::Duration::from_secs(10)))
|
||||
.build()
|
||||
@@ -295,7 +270,7 @@ async fn download_file(url: &str, sha1: Option<&str>) -> Result<bytes::Bytes, La
|
||||
item: url.to_string(),
|
||||
})?;
|
||||
|
||||
for attempt in 1..4 {
|
||||
for attempt in 1..=4 {
|
||||
let result = client.get(url).send().await;
|
||||
|
||||
match result {
|
||||
@@ -340,17 +315,9 @@ async fn download_file(url: &str, sha1: Option<&str>) -> Result<bytes::Bytes, La
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
async fn get_hash(bytes: bytes::Bytes) -> Result<String, LauncherError> {
|
||||
/// Computes a checksum of the input bytes
|
||||
pub async fn get_hash(bytes: bytes::Bytes) -> Result<String, LauncherError> {
|
||||
let hash = tokio::task::spawn_blocking(|| sha1::Sha1::from(bytes).hexdigest()).await?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
pub fn get_os() -> Os {
|
||||
match std::env::consts::OS {
|
||||
"windows" => Os::Windows,
|
||||
"macos" => Os::Osx,
|
||||
"linux" => Os::Linux,
|
||||
_ => Os::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user