From 803f26c7a805e20cefb4b5d84c6fb7a654fd1628 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Tue, 14 Jan 2025 20:09:03 -0600 Subject: add logger config stuffs --- Cargo.lock | 1 - ozone-cli/Cargo.toml | 1 - src/launcher.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/version.rs | 2 +- 4 files changed, 169 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6d2df19..8849ddd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2736,7 +2736,6 @@ dependencies = [ "simple_logger", "sysinfo", "tokio", - "tokio-macros", ] [[package]] diff --git a/ozone-cli/Cargo.toml b/ozone-cli/Cargo.toml index 730a37f..e60a539 100644 --- a/ozone-cli/Cargo.toml +++ b/ozone-cli/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" sysinfo = { version = "0.33.1", features = ["system", "multithread"] } o3launcher = { path = ".." } tokio = { version = "1.43.0", features = ["rt", "rt-multi-thread", "macros"] } -tokio-macros = "2.5.0" simple_logger = { version = "5.0.0", features = ["colors"] } 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), + 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, 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(()) } diff --git a/src/version.rs b/src/version.rs index af61481..ab78bef 100644 --- a/src/version.rs +++ b/src/version.rs @@ -264,7 +264,7 @@ pub struct ClientLogging { pub argument: String, #[serde(rename = "type")] - pub logger_type: String, + pub log_type: String, pub file: DownloadInfo } -- cgit v1.2.3-70-g09d2