You've already forked AstralRinth
forked from didirus/AstralRinth
223 lines
5.9 KiB
Rust
223 lines
5.9 KiB
Rust
use crate::launcher::meta::{
|
|
Asset, AssetIndex, AssetsIndex, DownloadType, Library, Os, RuleAction, VersionInfo,
|
|
};
|
|
use futures::future;
|
|
use std::fs::File;
|
|
use std::io::{BufReader, Write};
|
|
use std::path::Path;
|
|
|
|
pub async fn download_client(client_path: &Path, version_info: &VersionInfo) {
|
|
let client = download_file(
|
|
&version_info
|
|
.downloads
|
|
.get(&DownloadType::Client)
|
|
.unwrap()
|
|
.url,
|
|
)
|
|
.await;
|
|
|
|
save_file(
|
|
&*client_path
|
|
.join(&version_info.id)
|
|
.join(format!("{}.jar", &version_info.id)),
|
|
&client,
|
|
);
|
|
save_file(
|
|
&*client_path
|
|
.join(&version_info.id)
|
|
.join(format!("{}.json", &version_info.id)),
|
|
&bytes::Bytes::from(serde_json::to_string(version_info).unwrap()),
|
|
);
|
|
}
|
|
|
|
pub async fn download_assets(
|
|
assets_path: &Path,
|
|
legacy_path: Option<&Path>,
|
|
meta: AssetIndex,
|
|
index: &AssetsIndex,
|
|
) {
|
|
save_file(
|
|
&*assets_path
|
|
.join("indexes")
|
|
.join(format!("{}.json", meta.id)),
|
|
&bytes::Bytes::from(serde_json::to_string(index).unwrap()),
|
|
);
|
|
|
|
future::join_all(
|
|
index
|
|
.objects
|
|
.iter()
|
|
.map(|x| download_asset(assets_path, legacy_path, x.0, x.1)),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn download_asset(
|
|
assets_path: &Path,
|
|
legacy_path: Option<&Path>,
|
|
name: &String,
|
|
asset: &Asset,
|
|
) {
|
|
let sub_hash = &&asset.hash[..2];
|
|
|
|
let resource = download_file(&format!(
|
|
"https://resources.download.minecraft.net/{}/{}",
|
|
sub_hash, asset.hash
|
|
))
|
|
.await;
|
|
|
|
let resource_path = assets_path.join(sub_hash).join(&asset.hash);
|
|
save_file(resource_path.as_path(), &resource);
|
|
|
|
if let Some(legacy_path) = legacy_path {
|
|
let resource_path =
|
|
legacy_path.join(name.replace('/', &*std::path::MAIN_SEPARATOR.to_string()));
|
|
save_file(resource_path.as_path(), &resource);
|
|
}
|
|
}
|
|
|
|
pub async fn download_libraries(libraries_path: &Path, natives_path: &Path, libraries: &[Library]) {
|
|
future::join_all(
|
|
libraries
|
|
.iter()
|
|
.map(|x| download_library(libraries_path, natives_path, x)),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn download_library(libraries_path: &Path, natives_path: &Path, library: &Library) {
|
|
if let Some(rules) = &library.rules {
|
|
let mut allowed = true;
|
|
|
|
for rule in rules {
|
|
match rule.action {
|
|
RuleAction::Allow => {
|
|
if let Some(os) = &rule.os {
|
|
allowed = os.name == &get_os()
|
|
} else {
|
|
allowed = true
|
|
}
|
|
}
|
|
RuleAction::Disallow => {
|
|
if let Some(os) = &rule.os {
|
|
allowed = os.name != &get_os()
|
|
} else {
|
|
allowed = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !allowed {
|
|
return;
|
|
}
|
|
}
|
|
|
|
let name_items = library.name.split(':').collect::<Vec<&str>>();
|
|
|
|
let package = name_items.get(0).unwrap();
|
|
let name = name_items.get(1).unwrap();
|
|
let version = name_items.get(2).unwrap();
|
|
|
|
future::join(
|
|
download_library_jar(libraries_path, library, package, name, version),
|
|
download_native(
|
|
libraries_path,
|
|
natives_path,
|
|
library,
|
|
package,
|
|
name,
|
|
version,
|
|
),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn download_library_jar(
|
|
libraries_path: &Path,
|
|
library: &Library,
|
|
package: &str,
|
|
name: &str,
|
|
version: &str,
|
|
) {
|
|
if let Some(library) = &library.downloads.artifact {
|
|
let bytes = download_file(&library.url).await;
|
|
|
|
save_file(
|
|
&libraries_path
|
|
.join(package)
|
|
.join(name)
|
|
.join(version)
|
|
.join(format!("{}-{}.jar", name, version)),
|
|
&bytes,
|
|
);
|
|
}
|
|
}
|
|
|
|
async fn download_native(
|
|
libraries_path: &Path,
|
|
natives_path: &Path,
|
|
library: &Library,
|
|
package: &str,
|
|
name: &str,
|
|
version: &str,
|
|
) {
|
|
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(native) = classifiers.get(&*parsed_key) {
|
|
let path = &libraries_path
|
|
.join(package)
|
|
.join(name)
|
|
.join(version)
|
|
.join(format!("{}-{}-{}.jar", name, version, parsed_key));
|
|
|
|
let bytes = download_file(&native.url).await;
|
|
|
|
save_file(path, &bytes);
|
|
|
|
let file = File::open(path).unwrap();
|
|
let reader = BufReader::new(file);
|
|
|
|
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
|
archive.extract(natives_path).unwrap();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn save_file(path: &Path, bytes: &bytes::Bytes) {
|
|
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
|
let mut file = File::create(path).unwrap();
|
|
file.write_all(bytes).unwrap();
|
|
}
|
|
|
|
async fn download_file(url: &str) -> bytes::Bytes {
|
|
reqwest::Client::builder()
|
|
.tcp_keepalive(Some(std::time::Duration::from_secs(10)))
|
|
.build()
|
|
.unwrap()
|
|
.get(url)
|
|
.send()
|
|
.await
|
|
.unwrap()
|
|
.bytes()
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
fn get_os() -> Os {
|
|
match std::env::consts::OS {
|
|
"windows" => Os::Windows,
|
|
"macos" => Os::Osx,
|
|
"linux" => Os::Linux,
|
|
_ => Os::Unknown,
|
|
}
|
|
}
|