everything tbh

This commit is contained in:
2024-08-29 22:02:19 +02:00
parent 2cdf7f38d1
commit 8d05683bd1
10 changed files with 168 additions and 141 deletions

1
Cargo.lock generated
View File

@@ -26,6 +26,7 @@ dependencies = [
"futures-util", "futures-util",
"indicatif", "indicatif",
"mslnk", "mslnk",
"once_cell",
"openssl", "openssl",
"rand", "rand",
"reqwest", "reqwest",

View File

@@ -17,6 +17,7 @@ serde_json = "1.0.125"
rand = "0.8.5" rand = "0.8.5"
semver = "1.0.23" semver = "1.0.23"
colored = "2.1.0" colored = "2.1.0"
once_cell = "1.19.0"
reqwest = { version = "0.12.5", features = ["stream"] } reqwest = { version = "0.12.5", features = ["stream"] }
futures-util = "0.3.30" futures-util = "0.3.30"
indicatif = "0.17.8" indicatif = "0.17.8"

View File

@@ -28,5 +28,5 @@ pub async fn latest_version(owner: &str, repo: &str) -> Version {
} }
pub fn latest_release_url(owner: &str, repo: &str) -> String { pub fn latest_release_url(owner: &str, repo: &str) -> String {
format!("https://github.com/{}/{}/releases/latest", owner, repo) format!("https://github.com/{owner}/{repo}/releases/latest")
} }

View File

@@ -1,5 +1,53 @@
pub const MASTER: &str = "cdn.alterware.ovh"; use crate::structs::PrintPrefix;
use colored::Colorize;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;
pub const GH_OWNER: &str = "mxve"; pub const GH_OWNER: &str = "mxve";
pub const GH_REPO: &str = "alterware-launcher"; pub const GH_REPO: &str = "alterware-launcher";
pub const GH_IW4X_OWNER: &str = "iw4x"; pub const GH_IW4X_OWNER: &str = "iw4x";
pub const GH_IW4X_REPO: &str = "iw4x-client"; pub const GH_IW4X_REPO: &str = "iw4x-client";
pub static MASTER: Lazy<Mutex<String>> =
Lazy::new(|| Mutex::new("https://cdn.alterware.ovh".to_owned()));
pub static PREFIXES: Lazy<HashMap<&'static str, PrintPrefix>> = Lazy::new(|| {
HashMap::from([
(
"info",
PrintPrefix {
text: "Info".bright_magenta(),
padding: 8,
},
),
(
"downloading",
PrintPrefix {
text: "Downloading".bright_yellow(),
padding: 1,
},
),
(
"checked",
PrintPrefix {
text: "Checked".bright_blue(),
padding: 5,
},
),
(
"removed",
PrintPrefix {
text: "Removed".bright_red(),
padding: 5,
},
),
(
"error",
PrintPrefix {
text: "Error".red(),
padding: 7,
},
),
])
});

View File

@@ -3,7 +3,6 @@ use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use colored::*;
use futures_util::StreamExt; use futures_util::StreamExt;
use indicatif::ProgressBar; use indicatif::ProgressBar;
use reqwest::Client; use reqwest::Client;
@@ -29,18 +28,18 @@ pub async fn download_file_progress(
) )
.send() .send()
.await .await
.or(Err(format!("Failed to GET from '{}'", &url)))?; .or(Err(format!("Failed to GET from '{url}'")))?;
// Fix for CF shenanigans // Fix for CF shenanigans
let total_size = res.content_length().unwrap_or(size); let total_size = res.content_length().unwrap_or(size);
pb.set_length(total_size); pb.set_length(total_size);
let msg = format!( let msg = format!(
"[{}] {} ({})", "{}{} ({})",
"Downloading".bright_yellow(), misc::prefix("downloading"),
misc::cute_path(path), misc::cute_path(path),
misc::human_readable_bytes(total_size) misc::human_readable_bytes(total_size)
); );
pb.println(&msg); pb.println(&msg);
info!("{}", msg); info!("{msg}");
pb.set_message(path.file_name().unwrap().to_str().unwrap().to_string()); pb.set_message(path.file_name().unwrap().to_str().unwrap().to_string());
let mut file = let mut file =
@@ -109,7 +108,7 @@ pub async fn get_body(url: &str) -> Result<Vec<u8>, String> {
.await .await
{ {
Ok(res) => { Ok(res) => {
debug!("{} {}", res.status().to_string(), url); debug!("{} {url}", res.status().to_string());
let body = res.bytes().await.or(Err("Failed to get body"))?; let body = res.bytes().await.or(Err("Failed to get body"))?;
Ok(body.to_vec()) Ok(body.to_vec())
} }

View File

@@ -3,7 +3,6 @@ use crate::global::*;
use crate::http_async; use crate::http_async;
use crate::misc; use crate::misc;
use colored::*;
use std::{fs, path::Path}; use std::{fs, path::Path};
pub fn local_revision(dir: &Path) -> u16 { pub fn local_revision(dir: &Path) -> u16 {
@@ -23,20 +22,17 @@ pub async fn update(dir: &Path) {
let local = local_revision(dir); let local = local_revision(dir);
if remote <= local && dir.join("iw4x.dll").exists() { if remote <= local && dir.join("iw4x.dll").exists() {
crate::println_info!( crate::println_info!("No files to download for IW4x");
"[{}] No files to download for IW4x",
"Info".bright_magenta(),
);
return; return;
} }
crate::println_info!( crate::println_info!(
"[{}] Downloading outdated or missing files for IW4x", "{}Downloading outdated or missing files for IW4x",
"Info".bright_magenta() misc::prefix("info")
); );
crate::println_info!( crate::println_info!(
"[{}] {}", "{}{}",
"Downloading".bright_yellow(), misc::prefix("downloading"),
misc::cute_path(&dir.join("iw4x.dll")) misc::cute_path(&dir.join("iw4x.dll"))
); );
http_async::download_file( http_async::download_file(
@@ -48,5 +44,5 @@ pub async fn update(dir: &Path) {
) )
.await .await
.unwrap(); .unwrap();
fs::write(dir.join(".iw4xrevision"), format!("r{}", remote)).unwrap(); fs::write(dir.join(".iw4xrevision"), format!("r{remote}")).unwrap();
} }

View File

@@ -30,7 +30,7 @@ fn get_installed_games(games: &Vec<Game>) -> Vec<(u32, PathBuf)> {
let steamdir = match steamdir_result { let steamdir = match steamdir_result {
Ok(steamdir) => steamdir, Ok(steamdir) => steamdir,
Err(error) => { Err(error) => {
crate::println_error!("Error locating Steam: {}", error); crate::println_error!("Error locating Steam: {error}");
return installed_games; return installed_games;
} }
}; };
@@ -55,7 +55,7 @@ fn create_shortcut(path: &Path, target: &Path, icon: String, args: String) {
sl.set_arguments(Some(args)); sl.set_arguments(Some(args));
sl.set_icon_location(Some(icon)); sl.set_icon_location(Some(icon));
sl.create_lnk(path).unwrap_or_else(|error| { sl.create_lnk(path).unwrap_or_else(|error| {
crate::println_error!("Error creating shortcut.\n{:#?}", error); crate::println_error!("Error creating shortcut.\n{error}");
}); });
} else { } else {
crate::println_error!("Error creating shortcut."); crate::println_error!("Error creating shortcut.");
@@ -70,10 +70,10 @@ fn setup_client_links(game: &Game, game_dir: &Path) {
for c in game.client.iter() { for c in game.client.iter() {
create_shortcut( create_shortcut(
&game_dir.join(format!("launch-{}.lnk", c)), &game_dir.join(format!("launch-{c}.lnk")),
&game_dir.join("alterware-launcher.exe"), &game_dir.join("alterware-launcher.exe"),
game_dir game_dir
.join(format!("{}.exe", c)) .join(format!("{c}.exe"))
.to_string_lossy() .to_string_lossy()
.into_owned(), .into_owned(),
c.to_string(), c.to_string(),
@@ -86,16 +86,14 @@ fn setup_desktop_links(path: &Path, game: &Game) {
println!("Create Desktop shortcut? (Y/n)"); println!("Create Desktop shortcut? (Y/n)");
let input = misc::stdin().to_ascii_lowercase(); let input = misc::stdin().to_ascii_lowercase();
if input == "y" || input.is_empty() { if input != "n" {
let desktop = PathBuf::from(&format!("{}\\Desktop", env::var("USERPROFILE").unwrap())); let desktop = PathBuf::from(&format!("{}\\Desktop", env::var("USERPROFILE").unwrap()));
for c in game.client.iter() { for c in game.client.iter() {
create_shortcut( create_shortcut(
&desktop.join(format!("{}.lnk", c)), &desktop.join(format!("{c}.lnk")),
&path.join("alterware-launcher.exe"), &path.join("alterware-launcher.exe"),
path.join(format!("{}.exe", c)) path.join(format!("{c}.exe")).to_string_lossy().into_owned(),
.to_string_lossy()
.into_owned(),
c.to_string(), c.to_string(),
); );
} }
@@ -103,14 +101,14 @@ fn setup_desktop_links(path: &Path, game: &Game) {
} }
#[cfg(windows)] #[cfg(windows)]
async fn auto_install(path: &Path, game: &Game<'_>, master_url: &String) { async fn auto_install(path: &Path, game: &Game<'_>) {
setup_client_links(game, path); setup_client_links(game, path);
setup_desktop_links(path, game); setup_desktop_links(path, game);
update(game, path, false, false, None, master_url, None).await; update(game, path, false, false, None, None).await;
} }
#[cfg(windows)] #[cfg(windows)]
async fn windows_launcher_install(games: &Vec<Game<'_>>, master_url: &String) { async fn windows_launcher_install(games: &Vec<Game<'_>>) {
crate::println_info!( crate::println_info!(
"{}", "{}",
"No game specified/found. Checking for installed Steam games..".yellow() "No game specified/found. Checking for installed Steam games..".yellow()
@@ -124,7 +122,7 @@ async fn windows_launcher_install(games: &Vec<Game<'_>>, master_url: &String) {
crate::println_info!("Found game in current directory."); crate::println_info!("Found game in current directory.");
crate::println_info!("Installing AlterWare client for {}.", id); crate::println_info!("Installing AlterWare client for {}.", id);
let game = games.iter().find(|&g| g.app_id == *id).unwrap(); let game = games.iter().find(|&g| g.app_id == *id).unwrap();
auto_install(path, game, master_url).await; auto_install(path, game).await;
crate::println_info!("Installation complete. Please run the launcher again or use a shortcut to launch the game."); crate::println_info!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
std::io::stdin().read_line(&mut String::new()).unwrap(); std::io::stdin().read_line(&mut String::new()).unwrap();
std::process::exit(0); std::process::exit(0);
@@ -134,7 +132,7 @@ async fn windows_launcher_install(games: &Vec<Game<'_>>, master_url: &String) {
println!("Installed games:"); println!("Installed games:");
for (id, path) in installed_games.iter() { for (id, path) in installed_games.iter() {
println!("{}: {}", id, path.display()); println!("{id}: {}", path.display());
} }
println!("Enter the ID of the game you want to install the AlterWare client for:"); println!("Enter the ID of the game you want to install the AlterWare client for:");
@@ -148,11 +146,16 @@ async fn windows_launcher_install(games: &Vec<Game<'_>>, master_url: &String) {
let target_path = path.join("alterware-launcher.exe"); let target_path = path.join("alterware-launcher.exe");
if launcher_path != target_path { if launcher_path != target_path {
fs::copy(launcher_path, target_path).unwrap(); fs::copy(launcher_path, &target_path).unwrap();
crate::println_info!("Launcher copied to {}", path.display()); crate::println_info!("Launcher copied to {}", path.display());
} }
auto_install(path, game, master_url).await; auto_install(path, game).await;
crate::println_info!("Installation complete. Please run the launcher again or use a shortcut to launch the game."); crate::println_info!("Installation complete.");
crate::println_info!("Please use one of the shortcuts (on your Desktop or in the game folder) to play.");
crate::println_info!(
"Alternatively run the launcher again from the game folder {}",
target_path.display()
);
std::io::stdin().read_line(&mut String::new()).unwrap(); std::io::stdin().read_line(&mut String::new()).unwrap();
break; break;
} }
@@ -168,7 +171,7 @@ async fn windows_launcher_install(games: &Vec<Game<'_>>, master_url: &String) {
} }
fn total_download_size(cdn_info: &Vec<CdnFile>, remote_dir: &str) -> u64 { fn total_download_size(cdn_info: &Vec<CdnFile>, remote_dir: &str) -> u64 {
let remote_dir = format!("{}/", remote_dir); let remote_dir = format!("{remote_dir}/");
let mut size: u64 = 0; let mut size: u64 = 0;
for file in cdn_info { for file in cdn_info {
if !file.name.starts_with(&remote_dir) || file.name == "iw4/iw4x.dll" { if !file.name.starts_with(&remote_dir) || file.name == "iw4/iw4x.dll" {
@@ -186,11 +189,10 @@ async fn update_dir(
hashes: &mut HashMap<String, String>, hashes: &mut HashMap<String, String>,
pb: &ProgressBar, pb: &ProgressBar,
skip_iw4x_sp: bool, skip_iw4x_sp: bool,
master_url: &String,
) { ) {
misc::pb_style_download(pb, false); misc::pb_style_download(pb, false);
let remote_dir_pre = format!("{}/", remote_dir); let remote_dir_pre = format!("{remote_dir}/");
let mut files_to_download: Vec<CdnFile> = vec![]; let mut files_to_download: Vec<CdnFile> = vec![];
@@ -215,13 +217,9 @@ async fn update_dir(
if hash_local != hash_remote { if hash_local != hash_remote {
files_to_download.push(file.clone()); files_to_download.push(file.clone());
} else { } else {
let msg = format!( let msg = format!("{}{}", misc::prefix("checked"), misc::cute_path(&file_path));
"[{}] {}",
"Checked".bright_blue(),
misc::cute_path(&file_path)
);
pb.println(&msg); pb.println(&msg);
info!("{}", msg); info!("{msg}");
hashes.insert(file_name.to_owned(), file.blake3.to_lowercase()); hashes.insert(file_name.to_owned(), file.blake3.to_lowercase());
} }
} else { } else {
@@ -231,22 +229,21 @@ async fn update_dir(
if files_to_download.is_empty() { if files_to_download.is_empty() {
let msg = format!( let msg = format!(
"[{}] No files to download for {}", "{}No files to download for {}",
"Info".bright_magenta(), misc::prefix("info"),
remote_dir remote_dir
); );
pb.println(&msg); pb.println(&msg);
info!("{}", msg); info!("{msg}");
return; return;
} }
let msg = format!( let msg = format!(
"[{}] Downloading outdated or missing files for {}, {}", "{}Downloading outdated or missing files for {remote_dir}, {}",
"Info".bright_magenta(), misc::prefix("info"),
remote_dir,
misc::human_readable_bytes(total_download_size(&files_to_download, remote_dir)) misc::human_readable_bytes(total_download_size(&files_to_download, remote_dir))
); );
pb.println(&msg); pb.println(&msg);
info!("{}", msg); info!("{msg}");
misc::pb_style_download(pb, true); misc::pb_style_download(pb, true);
let client = reqwest::Client::new(); let client = reqwest::Client::new();
@@ -260,18 +257,21 @@ async fn update_dir(
} }
// Prompt user to retry downloads if they fail // Prompt user to retry downloads if they fail
let mut download_complete : bool = false; let mut download_complete: bool = false;
while !download_complete { while !download_complete {
if let Err(err) = http_async::download_file_progress( if let Err(err) = http_async::download_file_progress(
&client, &client,
pb, pb,
&format!("{}/{}", master_url, file.name), &format!("{}/{}", MASTER.lock().unwrap(), file.name),
&file_path, &file_path,
file.size as u64, file.size as u64,
) )
.await .await
{ {
println!("Failed to download file {}, retry? (Y/n)", file_path.clone().display()); println!(
"Failed to download file {}, retry? (Y/n)",
file_path.clone().display()
);
let input = misc::stdin().to_ascii_lowercase(); let input = misc::stdin().to_ascii_lowercase();
if input == "n" { if input == "n" {
panic!("{err}"); panic!("{err}");
@@ -286,7 +286,7 @@ async fn update_dir(
if file_name.ends_with(".exe") { if file_name.ends_with(".exe") {
let perms = std::os::unix::fs::PermissionsExt::from_mode(0o755); let perms = std::os::unix::fs::PermissionsExt::from_mode(0o755);
fs::set_permissions(&file_path, perms).unwrap_or_else(|error| { fs::set_permissions(&file_path, perms).unwrap_or_else(|error| {
crate::println_error!("Error setting permissions for {}: {:#?}", file_name, error); crate::println_error!("Error setting permissions for {file_name}: {error}");
}) })
} }
} }
@@ -299,15 +299,15 @@ async fn update(
bonus_content: bool, bonus_content: bool,
force: bool, force: bool,
skip_iw4x_sp: Option<bool>, skip_iw4x_sp: Option<bool>,
master_url: &String,
ignore_required_files: Option<bool>, ignore_required_files: Option<bool>,
) { ) {
let skip_iw4x_sp = skip_iw4x_sp.unwrap_or(false); let skip_iw4x_sp = skip_iw4x_sp.unwrap_or(false);
let ignore_required_files = ignore_required_files.unwrap_or(false); let ignore_required_files = ignore_required_files.unwrap_or(false);
let res = http_async::get_body_string(format!("{}/files.json", master_url).as_str()) let res =
.await http_async::get_body_string(format!("{}/files.json", MASTER.lock().unwrap()).as_str())
.unwrap(); .await
.unwrap();
let cdn_info: Vec<CdnFile> = serde_json::from_str(&res).unwrap(); let cdn_info: Vec<CdnFile> = serde_json::from_str(&res).unwrap();
if !ignore_required_files && !game.required_files_exist(dir) { if !ignore_required_files && !game.required_files_exist(dir) {
@@ -323,7 +323,7 @@ async fn update(
match fs::remove_file(dir.join(".sha-sums")) { match fs::remove_file(dir.join(".sha-sums")) {
Ok(_) => {} Ok(_) => {}
Err(error) => { Err(error) => {
crate::println_error!("Error removing .sha-sums: {:#?}", error); crate::println_error!("Error removing .sha-sums: {error}");
} }
} }
} }
@@ -381,15 +381,15 @@ async fn update(
} }
crate::println_info!( crate::println_info!(
"[{}] {}", "{}{}",
"Removed".bright_red(), misc::prefix("removed"),
misc::cute_path(&file_path) misc::cute_path(&file_path)
); );
if fs::remove_file(&file_path).is_err() { if fs::remove_file(&file_path).is_err() {
crate::println_error!( crate::println_error!(
"[{}] Couldn't delete {}", "{}Couldn't delete {}",
"Error".bright_red(), misc::prefix("error"),
misc::cute_path(&file_path) misc::cute_path(&file_path)
); );
} }
@@ -400,29 +400,11 @@ async fn update(
} }
let pb = ProgressBar::new(0); let pb = ProgressBar::new(0);
update_dir( update_dir(&cdn_info, game.engine, dir, &mut hashes, &pb, skip_iw4x_sp).await;
&cdn_info,
game.engine,
dir,
&mut hashes,
&pb,
skip_iw4x_sp,
master_url,
)
.await;
if bonus_content && !game.bonus.is_empty() { if bonus_content && !game.bonus.is_empty() {
for bonus in game.bonus.iter() { for bonus in game.bonus.iter() {
update_dir( update_dir(&cdn_info, bonus, dir, &mut hashes, &pb, skip_iw4x_sp).await;
&cdn_info,
bonus,
dir,
&mut hashes,
&pb,
skip_iw4x_sp,
master_url,
)
.await;
} }
} }
@@ -433,37 +415,29 @@ async fn update(
if file_path.is_file() { if file_path.is_file() {
if fs::remove_file(&file_path).is_err() { if fs::remove_file(&file_path).is_err() {
println!( println!(
"[{}] Couldn't delete {}", "{}Couldn't delete {}",
"Error".bright_red(), misc::prefix("error"),
misc::cute_path(&file_path) misc::cute_path(&file_path)
); );
} else { } else {
println!( println!("{}{}", misc::prefix("removed"), misc::cute_path(&file_path));
"[{}] {}",
"Removed".bright_red(),
misc::cute_path(&file_path)
);
} }
} else if file_path.is_dir() { } else if file_path.is_dir() {
if fs::remove_dir_all(&file_path).is_err() { if fs::remove_dir_all(&file_path).is_err() {
println!( println!(
"[{}] Couldn't delete {}", "{}Couldn't delete {}",
"Error".bright_red(), misc::prefix("error"),
misc::cute_path(&file_path) misc::cute_path(&file_path)
); );
} else { } else {
println!( println!("{}{}", misc::prefix("removed"), misc::cute_path(&file_path));
"[{}] {}",
"Removed".bright_red(),
misc::cute_path(&file_path)
);
} }
} }
} }
let mut hash_file_content = String::new(); let mut hash_file_content = String::new();
for (file, hash) in hashes.iter() { for (file, hash) in hashes.iter() {
hash_file_content.push_str(&format!("{} {}\n", hash, file)); hash_file_content.push_str(&format!("{hash} {file}\n"));
} }
fs::write(dir.join(".hashes"), hash_file_content).unwrap(); fs::write(dir.join(".hashes"), hash_file_content).unwrap();
} }
@@ -471,7 +445,7 @@ async fn update(
#[cfg(windows)] #[cfg(windows)]
fn launch(file_path: &PathBuf, args: &str) { fn launch(file_path: &PathBuf, args: &str) {
println!("\n\nJoin the AlterWare Discord server:\nhttps://discord.gg/2ETE8engZM\n\n"); println!("\n\nJoin the AlterWare Discord server:\nhttps://discord.gg/2ETE8engZM\n\n");
crate::println_info!("Launching {} {}", file_path.display(), args); crate::println_info!("Launching {} {args}", file_path.display());
let exit_status = std::process::Command::new(file_path) let exit_status = std::process::Command::new(file_path)
.args(args.trim().split(' ')) .args(args.trim().split(' '))
.current_dir(file_path.parent().unwrap()) .current_dir(file_path.parent().unwrap())
@@ -480,7 +454,7 @@ fn launch(file_path: &PathBuf, args: &str) {
.wait() .wait()
.expect("Failed to wait for the game process to finish"); .expect("Failed to wait for the game process to finish");
crate::println_error!("Game exited with {}", exit_status); crate::println_error!("Game exited with {exit_status}");
if !exit_status.success() { if !exit_status.success() {
misc::stdin(); misc::stdin();
} }
@@ -489,7 +463,7 @@ fn launch(file_path: &PathBuf, args: &str) {
#[cfg(unix)] #[cfg(unix)]
fn launch(file_path: &PathBuf, args: &str) { fn launch(file_path: &PathBuf, args: &str) {
println!("\n\nJoin the AlterWare Discord server:\nhttps://discord.gg/2ETE8engZM\n\n"); println!("\n\nJoin the AlterWare Discord server:\nhttps://discord.gg/2ETE8engZM\n\n");
crate::println_info!("Launching {} {}", file_path.display(), args); crate::println_info!("Launching {} {args}", file_path.display());
let exit_status = if misc::is_program_in_path("wine") { let exit_status = if misc::is_program_in_path("wine") {
println!("Found wine, launching game using wine.\nIf you run into issues or want to launch a different way, run {} manually.", file_path.display()); println!("Found wine, launching game using wine.\nIf you run into issues or want to launch a different way, run {} manually.", file_path.display());
std::process::Command::new("wine") std::process::Command::new("wine")
@@ -509,7 +483,7 @@ fn launch(file_path: &PathBuf, args: &str) {
.expect("Failed to wait for the game process to finish") .expect("Failed to wait for the game process to finish")
}; };
crate::println_error!("Game exited with {}", exit_status); crate::println_error!("Game exited with {exit_status}");
if !exit_status.success() { if !exit_status.success() {
misc::stdin(); misc::stdin();
} }
@@ -610,7 +584,7 @@ async fn main() {
"AlterWare Launcher".bright_green(), "AlterWare Launcher".bright_green(),
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
); );
println!("https://github.com/{}/{}", GH_OWNER, GH_REPO); println!("https://github.com/{GH_OWNER}/{GH_REPO}");
println!( println!(
"\n{}{}{}{}{}{}{}", "\n{}{}{}{}{}{}{}",
"For ".on_black(), "For ".on_black(),
@@ -637,10 +611,9 @@ async fn main() {
let mut cfg = config::load(install_path.join("alterware-launcher.json")); let mut cfg = config::load(install_path.join("alterware-launcher.json"));
let master_url = if cfg.use_https { if !cfg.use_https {
format!("https://{}", MASTER) let mut master_url = MASTER.lock().unwrap();
} else { *master_url = master_url.replace("https://", "http://");
format!("http://{}", MASTER)
}; };
if !arg_bool(&args, "--skip-launcher-update") && !cfg.skip_self_update { if !arg_bool(&args, "--skip-launcher-update") && !cfg.skip_self_update {
@@ -693,9 +666,10 @@ async fn main() {
std::process::exit(0); std::process::exit(0);
} }
let games_json = http_async::get_body_string(format!("{}/games.json", master_url).as_str()) let games_json =
.await http_async::get_body_string(format!("{}/games.json", MASTER.lock().unwrap()).as_str())
.unwrap(); .await
.unwrap();
let games: Vec<Game> = serde_json::from_str(&games_json).unwrap_or_else(|error| { let games: Vec<Game> = serde_json::from_str(&games_json).unwrap_or_else(|error| {
crate::println_error!("Error parsing games.json: {:#?}", error); crate::println_error!("Error parsing games.json: {:#?}", error);
misc::stdin(); misc::stdin();
@@ -722,7 +696,7 @@ async fn main() {
println!("Multiple clients installed, set the client as the first argument to launch a specific client."); println!("Multiple clients installed, set the client as the first argument to launch a specific client.");
println!("Select a client to launch:"); println!("Select a client to launch:");
for (i, c) in g.client.iter().enumerate() { for (i, c) in g.client.iter().enumerate() {
println!("{}: {}", i, c); println!("{i}: {c}");
} }
game = String::from(g.client[misc::stdin().parse::<usize>().unwrap()]); game = String::from(g.client[misc::stdin().parse::<usize>().unwrap()]);
break 'main; break 'main;
@@ -759,15 +733,6 @@ async fn main() {
} }
} }
if cfg.engine == "iw4" && !cfg.args.contains("+set logfile 1") {
cfg.args = format!("{} +set logfile 1", cfg.args);
config::save_value_s(
install_path.join("alterware-launcher.json"),
"args",
cfg.args.clone(),
);
}
if cfg.ask_bonus_content && !g.bonus.is_empty() { if cfg.ask_bonus_content && !g.bonus.is_empty() {
println!("Download bonus content? (Y/n)"); println!("Download bonus content? (Y/n)");
let input = misc::stdin().to_ascii_lowercase(); let input = misc::stdin().to_ascii_lowercase();
@@ -790,12 +755,11 @@ async fn main() {
cfg.download_bonus_content, cfg.download_bonus_content,
cfg.force_update, cfg.force_update,
Some(&game != "iw4x-sp"), Some(&game != "iw4x-sp"),
&master_url,
Some(ignore_required_files), Some(ignore_required_files),
) )
.await; .await;
if !cfg.update_only { if !cfg.update_only {
launch(&install_path.join(format!("{}.exe", c)), &cfg.args); launch(&install_path.join(format!("{c}.exe")), &cfg.args);
} }
return; return;
} }
@@ -803,9 +767,9 @@ async fn main() {
} }
#[cfg(windows)] #[cfg(windows)]
windows_launcher_install(&games, &master_url).await; windows_launcher_install(&games).await;
crate::println_error!("{}", "Game not found!".bright_red()); crate::println_error!("Game not found!");
println!("Place the launcher in the game folder, if that doesn't work specify the client on the command line (ex. alterware-launcher.exe iw4-sp)"); println!("Place the launcher in the game folder, if that doesn't work specify the client on the command line (ex. alterware-launcher.exe iw4-sp)");
println!("Press enter to exit..."); println!("Press enter to exit...");
std::io::stdin().read_line(&mut String::new()).unwrap(); std::io::stdin().read_line(&mut String::new()).unwrap();

View File

@@ -1,7 +1,7 @@
use indicatif::{ProgressBar, ProgressStyle};
use std::{fs, path::Path}; use std::{fs, path::Path};
use colored::Colorize; use crate::global;
use indicatif::{ProgressBar, ProgressStyle};
pub fn file_blake3(file: &std::path::Path) -> std::io::Result<String> { pub fn file_blake3(file: &std::path::Path) -> std::io::Result<String> {
let mut blake3 = blake3::Hasher::new(); let mut blake3 = blake3::Hasher::new();
@@ -31,7 +31,7 @@ pub fn rev_to_int(rev: &str) -> u16 {
} }
pub fn fatal_error(error: &str) { pub fn fatal_error(error: &str) {
crate::println_error!("{}: {}", "Error".bright_red(), error); crate::println_error!("{}: {error}", prefix("error"));
stdin(); stdin();
std::process::exit(1); std::process::exit(1);
} }
@@ -44,7 +44,7 @@ pub fn human_readable_bytes(bytes: u64) -> String {
bytes /= 1024.0; bytes /= 1024.0;
i += 1; i += 1;
} }
format!("{:.2}{}", bytes, units[i]) format!("{bytes:.2}{}", units[i])
} }
pub fn pb_style_download(pb: &ProgressBar, state: bool) { pub fn pb_style_download(pb: &ProgressBar, state: bool) {
@@ -66,7 +66,7 @@ pub fn cute_path(path: &Path) -> String {
pub fn is_program_in_path(program: &str) -> bool { pub fn is_program_in_path(program: &str) -> bool {
if let Ok(path) = std::env::var("PATH") { if let Ok(path) = std::env::var("PATH") {
for p in path.split(':') { for p in path.split(':') {
let p_str = format!("{}/{}", p, program); let p_str = format!("{p}/{program}");
if fs::metadata(p_str).is_ok() { if fs::metadata(p_str).is_ok() {
return true; return true;
} }
@@ -78,7 +78,7 @@ pub fn is_program_in_path(program: &str) -> bool {
#[macro_export] #[macro_export]
macro_rules! println_info { macro_rules! println_info {
($($arg:tt)*) => {{ ($($arg:tt)*) => {{
println!($($arg)*); println!("{}", format!("{}{}", $crate::misc::prefix("info"), format!($($arg)*)));
info!($($arg)*); info!($($arg)*);
}} }}
} }
@@ -86,7 +86,7 @@ macro_rules! println_info {
#[macro_export] #[macro_export]
macro_rules! println_error { macro_rules! println_error {
($($arg:tt)*) => {{ ($($arg:tt)*) => {{
eprintln!($($arg)*); eprintln!("{}", format!("{}{}", $crate::misc::prefix("error"), format!($($arg)*)));
error!($($arg)*); error!($($arg)*);
}} }}
} }
@@ -97,7 +97,7 @@ fn install_dependency(path: &Path, args: &[&str]) {
match runas::Command::new(path).args(args).status() { match runas::Command::new(path).args(args).status() {
Ok(status) => { Ok(status) => {
if !status.success() && !matches!(status.code(), Some(1638) | Some(3010)) { if !status.success() && !matches!(status.code(), Some(1638) | Some(3010)) {
println_error!("Error installing dependency {}, {}", path.display(), status); println_error!("Error installing dependency {}, {status}", path.display());
} else { } else {
info!("{} installed successfully", path.display()); info!("{} installed successfully", path.display());
} }
@@ -109,7 +109,7 @@ fn install_dependency(path: &Path, args: &[&str]) {
path.display() path.display()
); );
} else { } else {
println_error!("Error running file {}: {}", path.display(), e); println_error!("Error running file {}: {e}", path.display());
} }
} }
} }
@@ -121,19 +121,19 @@ fn install_dependency(path: &Path, args: &[&str]) {
#[cfg(windows)] #[cfg(windows)]
async fn download_and_install_dependency(url: &str, path: &Path, args: &[&str]) { async fn download_and_install_dependency(url: &str, path: &Path, args: &[&str]) {
if !path.exists() { if !path.exists() {
info!("Downloading {} from {}", path.display(), url); info!("Downloading {} from {url}", path.display());
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
match fs::create_dir_all(parent) { match fs::create_dir_all(parent) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
println_error!("Error creating directory {}: {}", parent.display(), e); println_error!("Error creating directory {}: {e}", parent.display());
return; return;
} }
} }
} }
match crate::http_async::download_file(url, &std::path::PathBuf::from(path)).await { match crate::http_async::download_file(url, &std::path::PathBuf::from(path)).await {
Ok(_) => info!("Downloaded {}", path.display()), Ok(_) => info!("Downloaded {}", path.display()),
Err(e) => println_error!("Error downloading {}: {}", path.display(), e), Err(e) => println_error!("Error downloading {}: {e}", path.display()),
} }
} }
install_dependency(path, args); install_dependency(path, args);
@@ -154,7 +154,13 @@ pub async fn install_dependencies(install_path: &Path) {
for (name, url, file, args) in redists.iter() { for (name, url, file, args) in redists.iter() {
let path = redist_dir.join(file); let path = redist_dir.join(file);
println_info!("Installing {}", name); println_info!("Installing {name}");
download_and_install_dependency(url, &path, args).await; download_and_install_dependency(url, &path, args).await;
} }
} }
pub fn prefix(tag_name: &str) -> String {
global::PREFIXES
.get(tag_name)
.map_or_else(|| tag_name.to_string(), |tag| tag.formatted())
}

View File

@@ -102,7 +102,7 @@ pub async fn run(update_only: bool) {
// restarting spawns a new console, automation should manually restart on exit code 201 // restarting spawns a new console, automation should manually restart on exit code 201
if !update_only { if !update_only {
let restart_error = restart().to_string(); let restart_error = restart().to_string();
crate::println_error!("Failed to restart launcher: {}", restart_error); crate::println_error!("Failed to restart launcher: {restart_error}");
println!("Please restart the launcher manually."); println!("Please restart the launcher manually.");
misc::stdin(); misc::stdin();
} }

View File

@@ -1,3 +1,4 @@
use colored::*;
use std::path::Path; use std::path::Path;
#[derive(serde::Deserialize, serde::Serialize, Clone)] #[derive(serde::Deserialize, serde::Serialize, Clone)]
@@ -62,3 +63,14 @@ impl Default for Config {
} }
} }
} }
pub struct PrintPrefix {
pub text: ColoredString,
pub padding: usize,
}
impl PrintPrefix {
pub fn formatted(&self) -> String {
format!("[{}]{:width$}", self.text, "", width = self.padding).to_string()
}
}