summaryrefslogtreecommitdiffstats
path: root/src/launcher.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-14 20:09:03 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-14 20:09:03 -0600
commit803f26c7a805e20cefb4b5d84c6fb7a654fd1628 (patch)
tree136d7b2609be66bed75d33c4e3c732be9e682345 /src/launcher.rs
parentverify libraries when offline (diff)
add logger config stuffs
Diffstat (limited to 'src/launcher.rs')
-rw-r--r--src/launcher.rs175
1 files changed, 168 insertions, 7 deletions
diff --git a/src/launcher.rs b/src/launcher.rs
index 1f28257..03b289d 100644
--- a/src/launcher.rs
+++ b/src/launcher.rs
@@ -11,22 +11,24 @@ use std::env::consts::{ARCH, OS};
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::io::ErrorKind;
-use std::path::{Path, PathBuf};
+use std::path::{Component, Path, PathBuf};
use const_format::formatcp;
use futures::{stream, StreamExt, TryStreamExt};
use log::{debug, info, warn};
use serde::{Deserialize, Serialize};
+use sha1_smol::Sha1;
use sysinfo::System;
use tokio::{fs, io};
-use version::VersionList;
+use tokio::fs::File;
+use tokio::io::AsyncWriteExt;
use download::{MultiDownloader, VerifiedDownload};
use rules::{CompatCheck, IncompatibleError};
-use version::{VersionResolveError, VersionResult};
-use crate::version::{Library, OSRestriction, OperatingSystem};
+use version::{VersionList, VersionResolveError, VersionResult};
+use crate::version::{Logging, Library, OSRestriction, OperatingSystem};
pub use profile::{Instance, Profile};
use crate::util;
-use crate::util::FileVerifyError;
+use crate::util::{FileVerifyError, IntegrityError};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Settings {
@@ -72,6 +74,46 @@ impl Settings {
}
}
+#[derive(Debug)]
+pub enum LogConfigError {
+ UnknownType(String),
+ InvalidId(Option<String>),
+ MissingURL,
+ IO{ what: &'static str, error: io::Error },
+ Offline,
+ Download{ url: String, error: reqwest::Error },
+
+ Integrity(IntegrityError)
+}
+
+impl Display for LogConfigError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ LogConfigError::UnknownType(log_type) => write!(f, "unknown log type {}", log_type),
+ LogConfigError::InvalidId(oid) => match oid {
+ Some(id) => write!(f, "invalid log config id: {}", id),
+ None => f.write_str("missing log config id")
+ },
+ LogConfigError::MissingURL => f.write_str("missing log config download URL"),
+ LogConfigError::IO { what, error} => write!(f, "i/o error ({}): {}", what, error),
+ LogConfigError::Offline => f.write_str("launcher in offline mode"),
+ LogConfigError::Download { url, error } => write!(f, "failed to download log config ({}): {}", url, error),
+ LogConfigError::Integrity(e) => write!(f, "log config verify error: {}", e)
+ }
+ }
+}
+
+impl Error for LogConfigError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ LogConfigError::IO { error, .. } => Some(error),
+ LogConfigError::Download {error, ..} => Some(error),
+ LogConfigError::Integrity(error) => Some(error),
+ _ => None
+ }
+ }
+}
+
struct SystemInfo {
os: OperatingSystem,
os_version: String,
@@ -86,7 +128,7 @@ pub struct Launcher {
online: bool,
home: PathBuf,
versions: VersionList,
-
+
settings_path: PathBuf, // maybe redundant but idc >:3
settings: Settings,
@@ -106,7 +148,10 @@ pub enum LaunchError {
// library errors
LibraryDirError(PathBuf, io::Error),
LibraryVerifyError(FileVerifyError),
- LibraryDownloadError
+ LibraryDownloadError,
+
+ // log errors
+ LogConfig(LogConfigError)
}
impl Display for LaunchError {
@@ -119,6 +164,7 @@ impl Display for LaunchError {
LaunchError::LibraryDirError(path, e) => write!(f, "failed to create library directory {}: {}", path.display(), e),
LaunchError::LibraryVerifyError(e) => write!(f, "failed to verify library: {}", e),
LaunchError::LibraryDownloadError => f.write_str("library download failed (see above logs for details)"), // TODO: booo this sucks
+ LaunchError::LogConfig(e) => write!(f, "failed to configure logger: {}", e)
}
}
}
@@ -131,6 +177,7 @@ impl Error for LaunchError {
LaunchError::IncompatibleVersion(e) => Some(e),
LaunchError::LibraryDirError(_, e) => Some(e),
LaunchError::LibraryVerifyError(e) => Some(e),
+ LaunchError::LogConfig(e) => Some(e),
_ => None
}
}
@@ -177,6 +224,111 @@ impl Launcher {
})
}
+ async fn log_config_ensure(&self, config: &Logging) -> Result<Option<String>, LogConfigError> {
+ if config.client.log_type != "log4j2-xml" {
+ return Err(LogConfigError::UnknownType(config.client.log_type.clone()));
+ }
+
+ let dlinfo = &config.client.file;
+ let Some(id) = dlinfo.id.as_ref() else {
+ return Err(LogConfigError::InvalidId(None));
+ };
+
+ let mut path = self.home.join("logging");
+ fs::create_dir_all(path.as_path()).await
+ .map_err(|e| LogConfigError::IO{ what: "creating log directory", error: e })?;
+
+ let Some(Component::Normal(filename)) = Path::new(id).components().last() else {
+ return Err(LogConfigError::InvalidId(Some(id.clone())));
+ };
+
+ path.push(filename);
+
+ // creates the JVM argument for this logger config
+ fn get_arg(arg: &str, path: &Path) -> String {
+ strsub::replace_str(arg, |key| match key {
+ "path" => Some(path.to_string_lossy().clone()),
+ _ => None
+ })
+ }
+
+ // verify the file
+ match util::verify_file(path.as_path(), dlinfo.size, dlinfo.sha1).await {
+ // integrity passed. return
+ Ok(_) => return Ok(Some(get_arg(config.client.argument.as_str(), path.as_path()))),
+
+ // ruh roh
+ Err(e) => match e {
+ FileVerifyError::Open(_, ioe) => match ioe.kind() {
+ ErrorKind::NotFound => (),
+ _ => return Err(LogConfigError::IO{ what: "verify file (open)", error: ioe })
+ },
+ FileVerifyError::Read(_, ioe) => return Err(LogConfigError::IO{ what: "verify file (read)", error: ioe }),
+ FileVerifyError::Integrity(_, ie) => info!("log config failed integrity check: {}", ie)
+ }
+ }
+
+ if !self.online {
+ warn!("Cannot download log config! We are offline. Rerun the launcher in online mode to launch this version.");
+ return Err(LogConfigError::Offline);
+ }
+
+ // download it
+ let Some(url) = dlinfo.url.as_ref() else {
+ return Err(LogConfigError::MissingURL);
+ };
+
+ let mut file = File::create(path.as_path()).await.map_err(|e| LogConfigError::IO {
+ what: "save log config (open)",
+ error: e
+ })?;
+
+ let mut response = reqwest::get(url).await.map_err(|e| LogConfigError::Download{ url: url.to_owned(), error: e })?;
+ let mut tally = 0usize;
+ let mut sha1 = Sha1::new();
+
+ while let Some(chunk) = response.chunk().await.map_err(|e| LogConfigError::Download{ url: url.to_owned(), error: e })? {
+ let slice = chunk.as_ref();
+
+ file.write_all(slice).await.map_err(|e| LogConfigError::IO {
+ what: "save log config (write)",
+ error: e
+ })?;
+
+ tally += slice.len();
+ sha1.update(slice);
+ }
+
+ drop(file); // manually close file
+
+ let del_file_silent = || async {
+ let _ = fs::remove_file(path.as_path()).await.map_err(|e| warn!("failed to delete invalid log config: {}", e));
+ ()
+ };
+
+ if dlinfo.size.is_some_and(|s| s != tally) {
+ del_file_silent().await;
+
+ return Err(LogConfigError::Integrity(IntegrityError::SizeMismatch {
+ expect: dlinfo.size.unwrap(),
+ actual: tally
+ }));
+ }
+
+ let digest = sha1.digest();
+
+ if dlinfo.sha1.is_some_and(|exp_dig| exp_dig != digest) {
+ del_file_silent().await;
+
+ return Err(LogConfigError::Integrity(IntegrityError::Sha1Mismatch {
+ expect: dlinfo.sha1.unwrap(),
+ actual: digest
+ }));
+ }
+
+ Ok(Some(get_arg(config.client.argument.as_str(), path.as_path())))
+ }
+
pub async fn prepare_launch(&self, profile: &Profile) -> Result<(), LaunchError> {
/* tasks 2 l;aunch the gayme!!!! :3
* - java runtime
@@ -254,6 +406,15 @@ impl Launcher {
})?;
}
+ let log_arg;
+ if let Some(logging) = ver.logging.as_ref() {
+ info!("Ensuring log configuration exists and is valid.");
+ log_arg = self.log_config_ensure(logging).await
+ .map_err(|e| LaunchError::LogConfig(e))?;
+ } else {
+ log_arg = None;
+ }
+
//todo!()
Ok(())
}