mirror of
https://github.com/alterware/alterware-launcher.git
synced 2025-12-04 15:27:48 +00:00
offline mode, connectivity check, backup cdn
probably the last real update for this codebase
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -19,7 +19,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "alterware-launcher"
|
||||
version = "0.9.3"
|
||||
version = "0.10.2"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"colored",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "alterware-launcher"
|
||||
version = "0.9.3"
|
||||
version = "0.10.2"
|
||||
edition = "2021"
|
||||
build = "res/build.rs"
|
||||
|
||||
|
||||
@@ -150,6 +150,9 @@
|
||||
- Install or reinstall redistributables
|
||||
- ```--prerelease```
|
||||
- Update to prerelease version of clients (currently only available for IW4x) and launcher
|
||||
- ```--cdn-url```
|
||||
- ```--offline```
|
||||
- ```--skip-connectivity-check```
|
||||
|
||||
##### Example:
|
||||
```shell
|
||||
@@ -171,6 +174,9 @@ alterware-launcher.exe iw4x --bonus -u --path "C:\Games\IW4x" --pass "-console"
|
||||
- `use_https`: Use HTTPS for downloads. Default: `true`.
|
||||
- `skip_redist`: Skip redistributable installations. Default: `false`.
|
||||
- `prerelease`: Update to prerelease version of clients and launcher. Default: `false`.
|
||||
- `cdn_url`
|
||||
- `offline`
|
||||
- `skip-connectivity-check`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,14 +3,21 @@ use colored::Colorize;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
use crate::http_async;
|
||||
use serde_json::Value;
|
||||
|
||||
pub const GH_OWNER: &str = "mxve";
|
||||
pub const GH_REPO: &str = "alterware-launcher";
|
||||
pub const GH_IW4X_OWNER: &str = "iw4x";
|
||||
pub const GH_IW4X_REPO: &str = "iw4x-client";
|
||||
pub const DEFAULT_MASTER: &str = "https://cdn.alterware.ovh";
|
||||
pub const BACKUP_MASTER: &str = "https://cdn.iw4x.getserve.rs";
|
||||
|
||||
pub static MASTER: Lazy<Mutex<String>> =
|
||||
Lazy::new(|| Mutex::new("https://cdn.alterware.ovh".to_owned()));
|
||||
pub static MASTER_URL: Lazy<Mutex<String>> = Lazy::new(|| {
|
||||
Mutex::new(String::from(DEFAULT_MASTER))
|
||||
});
|
||||
|
||||
pub static IS_OFFLINE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||
|
||||
@@ -54,14 +61,52 @@ pub static PREFIXES: Lazy<HashMap<&'static str, PrintPrefix>> = Lazy::new(|| {
|
||||
])
|
||||
});
|
||||
|
||||
pub async fn check_connectivity() -> bool {
|
||||
let master_url = MASTER.lock().unwrap().clone();
|
||||
pub fn check_connectivity(master_url: Option<String>) -> Pin<Box<dyn Future<Output = bool> + Send>> {
|
||||
Box::pin(async move {
|
||||
let retry = master_url.is_some();
|
||||
if !retry {
|
||||
crate::println_info!("Running connectivity check on {}", DEFAULT_MASTER);
|
||||
} else {
|
||||
let master = master_url.unwrap();
|
||||
*MASTER_URL.lock().unwrap() = master.clone();
|
||||
crate::println_info!("Running connectivity check on {}", master);
|
||||
}
|
||||
|
||||
match crate::http_async::get_body_string(&master_url).await {
|
||||
let master_url = MASTER_URL.lock().unwrap().clone();
|
||||
|
||||
// Check ASN number using the new get_json function
|
||||
let asn_response: Result<Value, String> = http_async::get_json("https://ip2asn.getserve.rs/v1/as/ip/self").await;
|
||||
|
||||
let mut switched_to_backup = false;
|
||||
|
||||
if let Ok(asn_data) = asn_response {
|
||||
if let Some(as_number) = asn_data.get("as_number").and_then(|v| v.as_i64()) {
|
||||
if as_number == 3320 && master_url == DEFAULT_MASTER {
|
||||
*MASTER_URL.lock().unwrap() = String::from(BACKUP_MASTER);
|
||||
crate::println_info!("Detected DTAG as ISP, switched to backup master URL: {}", BACKUP_MASTER);
|
||||
switched_to_backup = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run connectivity check regardless of ASN switch
|
||||
let result = match crate::http_async::get_body_string(&master_url).await {
|
||||
Ok(_) => true,
|
||||
Err(_) => {
|
||||
*IS_OFFLINE.lock().unwrap() = true;
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !result {
|
||||
crate::println_error!("Failed to connect to CDN {}", master_url);
|
||||
}
|
||||
|
||||
// If we switched to backup, do not retry
|
||||
if !retry && !result && !switched_to_backup {
|
||||
check_connectivity(Some(String::from(BACKUP_MASTER))).await
|
||||
} else {
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -103,3 +103,34 @@ pub async fn get_body_string(url: &str) -> Result<String, String> {
|
||||
let body = get_body(url).await?;
|
||||
Ok(String::from_utf8(body).unwrap())
|
||||
}
|
||||
|
||||
pub async fn get_json<T: serde::de::DeserializeOwned>(url: &str) -> Result<T, String> {
|
||||
let client = Client::new();
|
||||
let res = client
|
||||
.get(url)
|
||||
.header(
|
||||
"User-Agent",
|
||||
format!(
|
||||
"AlterWare Launcher | github.com/{}/{}",
|
||||
crate::global::GH_OWNER,
|
||||
crate::global::GH_REPO
|
||||
),
|
||||
)
|
||||
.header("Accept", "application/json")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to send request: {}", e))?;
|
||||
|
||||
debug!("{} {}", res.status(), url);
|
||||
|
||||
if !res.status().is_success() {
|
||||
return Err(format!("Request failed with status: {}", res.status()));
|
||||
}
|
||||
|
||||
let body = res.bytes()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read response body: {}", e))?;
|
||||
|
||||
serde_json::from_slice::<T>(&body)
|
||||
.map_err(|e| format!("Failed to parse JSON: {}", e))
|
||||
}
|
||||
|
||||
79
src/main.rs
79
src/main.rs
@@ -272,7 +272,7 @@ async fn update_dir(
|
||||
let mut bust_cache = false;
|
||||
let mut local_hash = String::default();
|
||||
while !download_complete {
|
||||
let url = format!("{}/{}", MASTER.lock().unwrap(), file.name);
|
||||
let url = format!("{}/{}", MASTER_URL.lock().unwrap(), file.name);
|
||||
let url = if bust_cache {
|
||||
bust_cache = false;
|
||||
format!("{}?{}", url, misc::random_string(6))
|
||||
@@ -347,7 +347,7 @@ async fn update(
|
||||
let ignore_required_files = ignore_required_files.unwrap_or(false);
|
||||
|
||||
let res =
|
||||
http_async::get_body_string(format!("{}/files.json", MASTER.lock().unwrap()).as_str())
|
||||
http_async::get_body_string(format!("{}/files.json", MASTER_URL.lock().unwrap()).as_str())
|
||||
.await
|
||||
.unwrap();
|
||||
debug!("Retrieved files.json from server");
|
||||
@@ -653,6 +653,8 @@ async fn main() {
|
||||
println!(" --skip-redist: Skip redistributable installation");
|
||||
println!(" --redist: (Re-)Install redistributables");
|
||||
println!(" --prerelease: Update to prerelease version of clients and launcher");
|
||||
println!(" --offline: Run in offline mode");
|
||||
println!(" --skip-connectivity-check: Don't check connectivity");
|
||||
println!(
|
||||
"\nExample:\n alterware-launcher.exe iw4x --bonus --pass \"-console -nointro\""
|
||||
);
|
||||
@@ -679,8 +681,46 @@ async fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let offline_mode = !global::check_connectivity().await;
|
||||
if offline_mode {
|
||||
let install_path: PathBuf;
|
||||
if let Some(path) = arg_value(&args, "--path") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "--path");
|
||||
} else if let Some(path) = arg_value(&args, "-p") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "-p");
|
||||
} else {
|
||||
install_path = env::current_dir().unwrap();
|
||||
}
|
||||
|
||||
let mut cfg = config::load(install_path.join("alterware-launcher.json"));
|
||||
|
||||
if let Some(cdn_url) = arg_value(&args, "--cdn-url") {
|
||||
cfg.cdn_url = cdn_url;
|
||||
arg_remove_value(&mut args, "--cdn-url");
|
||||
}
|
||||
|
||||
if arg_bool(&args, "--offline") {
|
||||
cfg.offline = true;
|
||||
arg_remove(&mut args, "--offline");
|
||||
}
|
||||
|
||||
if arg_bool(&args, "--skip-connectivity-check") {
|
||||
cfg.skip_connectivity_check = true;
|
||||
arg_remove(&mut args, "--skip-connectivity-check");
|
||||
}
|
||||
|
||||
let initial_cdn = if !cfg.cdn_url.is_empty() {
|
||||
info!("Using custom CDN URL: {}", cfg.cdn_url);
|
||||
Some(cfg.cdn_url.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if !cfg.offline && !cfg.skip_connectivity_check {
|
||||
cfg.offline = !global::check_connectivity(initial_cdn).await;
|
||||
}
|
||||
|
||||
if cfg.offline {
|
||||
// Check if this is a first-time run (no stored data)
|
||||
let stored_data = cache::get_stored_data();
|
||||
if stored_data.is_none() {
|
||||
@@ -701,20 +741,6 @@ async fn main() {
|
||||
);
|
||||
warn!("No internet connection or MASTER server is unreachable. Running in offline mode...");
|
||||
|
||||
// Handle path the same way as online mode
|
||||
let install_path: PathBuf;
|
||||
if let Some(path) = arg_value(&args, "--path") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "--path");
|
||||
} else if let Some(path) = arg_value(&args, "-p") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "-p");
|
||||
} else {
|
||||
install_path = env::current_dir().unwrap();
|
||||
}
|
||||
|
||||
let cfg = config::load(install_path.join("alterware-launcher.json"));
|
||||
|
||||
// Try to get stored game data
|
||||
let stored_data = cache::get_stored_data();
|
||||
if let Some(ref data) = stored_data {
|
||||
@@ -763,21 +789,8 @@ async fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let install_path: PathBuf;
|
||||
if let Some(path) = arg_value(&args, "--path") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "--path");
|
||||
} else if let Some(path) = arg_value(&args, "-p") {
|
||||
install_path = PathBuf::from(path);
|
||||
arg_remove_value(&mut args, "-p");
|
||||
} else {
|
||||
install_path = env::current_dir().unwrap();
|
||||
}
|
||||
|
||||
let mut cfg = config::load(install_path.join("alterware-launcher.json"));
|
||||
|
||||
if !cfg.use_https {
|
||||
let mut master_url = MASTER.lock().unwrap();
|
||||
let mut master_url = MASTER_URL.lock().unwrap();
|
||||
*master_url = master_url.replace("https://", "http://");
|
||||
};
|
||||
|
||||
@@ -841,7 +854,7 @@ async fn main() {
|
||||
}
|
||||
|
||||
let games_json =
|
||||
http_async::get_body_string(format!("{}/games.json", MASTER.lock().unwrap()).as_str())
|
||||
http_async::get_body_string(format!("{}/games.json", MASTER_URL.lock().unwrap()).as_str())
|
||||
.await
|
||||
.unwrap_or_else(|error| {
|
||||
crate::println_error!("Failed to get games.json: {:#?}", error);
|
||||
|
||||
@@ -48,6 +48,12 @@ pub struct Config {
|
||||
pub skip_redist: bool,
|
||||
#[serde(default)]
|
||||
pub prerelease: bool,
|
||||
#[serde(default)]
|
||||
pub cdn_url: String,
|
||||
#[serde(default)]
|
||||
pub offline: bool,
|
||||
#[serde(default)]
|
||||
pub skip_connectivity_check: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -63,6 +69,9 @@ impl Default for Config {
|
||||
use_https: true,
|
||||
skip_redist: false,
|
||||
prerelease: false,
|
||||
cdn_url: String::default(),
|
||||
offline: false,
|
||||
skip_connectivity_check: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user