1
0
Files
AstralRinth/theseus/src/launcher/download.rs
2021-06-29 22:32:52 -07:00

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,
}
}