From cd8bf1667494070c3a22ab5d63b559a9742b8a1a Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sat, 18 Jan 2025 23:47:48 -0600 Subject: more stuff --- src/launcher.rs | 144 ++++++++++++++++++++++++-------------------------------- 1 file changed, 61 insertions(+), 83 deletions(-) (limited to 'src/launcher.rs') diff --git a/src/launcher.rs b/src/launcher.rs index 8a68a65..7611011 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -1,92 +1,43 @@ mod constants; mod version; -mod profile; mod strsub; mod download; mod rules; mod assets; mod extract; +mod settings; use std::borrow::Cow; use std::cmp::min; -use std::collections::HashMap; use std::env::consts::{ARCH, OS}; use std::error::Error; use std::ffi::OsStr; use std::fmt::{Display, Formatter}; -use std::fs::FileType; use std::io::ErrorKind; use std::io::ErrorKind::AlreadyExists; use std::path::{Component, Path, PathBuf}; use std::{env, process}; use std::env::JoinPathsError; use std::time::{SystemTime, UNIX_EPOCH}; -use chrono::NaiveDateTime; use const_format::formatcp; -use futures::{stream, FutureExt, StreamExt, TryStreamExt}; +use futures::{StreamExt, TryStreamExt}; use log::{debug, info, warn}; use reqwest::Client; -use serde::{Deserialize, Serialize}; use sha1_smol::Sha1; use sysinfo::System; use tokio::{fs, io}; use tokio::fs::File; use tokio::io::AsyncWriteExt; use tokio_stream::wrappers::ReadDirStream; -use zip::ZipArchive; use download::{MultiDownloader, VerifiedDownload}; use rules::{CompatCheck, IncompatibleError}; use version::{VersionList, VersionResolveError, VersionResult}; use crate::version::{Logging, Library, OSRestriction, OperatingSystem, DownloadType, DownloadInfo, LibraryExtractRule}; -pub use profile::{Instance, Profile}; -use crate::launcher::assets::{AssetError, AssetRepository}; -use crate::util; -use crate::util::{FileVerifyError, IntegrityError}; +use assets::{AssetError, AssetRepository}; +use crate::util::{self, FileVerifyError, IntegrityError}; -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct Settings { - profiles: HashMap, - instances: HashMap -} - -#[derive(Debug)] -enum SettingsLoadError { - IO(io::Error), - Format(serde_json::Error) -} - -impl Display for SettingsLoadError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - SettingsLoadError::IO(err) => write!(f, "I/O error loading settings: {}", err), - SettingsLoadError::Format(err) => write!(f, "settings format error: {}", err), - } - } -} - -impl Error for SettingsLoadError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - SettingsLoadError::IO(err) => Some(err), - SettingsLoadError::Format(err) => Some(err), - } - } -} - -impl Settings { - async fn load(path: impl AsRef) -> Result { - let data = match fs::read_to_string(&path).await { - Ok(data) => data, - Err(e) => return match e.kind() { - ErrorKind::NotFound => Ok(Settings::default()), - _ => Err(SettingsLoadError::IO(e)) - } - }; - - serde_json::from_str(data.as_str()).map_err(SettingsLoadError::Format) - } -} +pub use settings::*; #[derive(Debug)] pub enum LogConfigError { @@ -144,9 +95,6 @@ pub struct Launcher { home: PathBuf, versions: VersionList, - settings_path: PathBuf, // maybe redundant but idc >:3 - settings: Settings, - system_info: SystemInfo, libraries: LibraryRepository, @@ -155,6 +103,8 @@ pub struct Launcher { #[derive(Debug)] pub enum LaunchError { + UnknownInstance(String), + // version resolution errors UnknownVersion(String), LoadVersion(Box), @@ -186,6 +136,7 @@ pub enum LaunchError { impl Display for LaunchError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self { + LaunchError::UnknownInstance(inst) => write!(f, "unknown instance: {inst}"), LaunchError::UnknownVersion(id) => write!(f, "unknown version id: {id}"), LaunchError::LoadVersion(e) => write!(f, "error loading remote version: {e}"), LaunchError::ResolveVersion(e) => write!(f, "error resolving remote version: {e}"), @@ -229,12 +180,8 @@ impl Error for LaunchError { impl Launcher { // FIXME: more descriptive error type por favor - pub async fn new(home: &Path, online: bool) -> Result> { - let home = home.to_owned(); - let versions_home = home.join("versions"); - let versions; - - match tokio::fs::create_dir_all(&home).await { + pub async fn new(home: impl AsRef, online: bool) -> Result> { + match tokio::fs::create_dir_all(home.as_ref()).await { Err(e) if e.kind() != AlreadyExists => { warn!("Failed to create launcher home directory: {}", e); return Err(e.into()); @@ -242,23 +189,22 @@ impl Launcher { _ => () } + let home = fs::canonicalize(home.as_ref()).await?; + let versions_home = home.join("versions"); + let versions; + debug!("Version list online?: {online}"); if online { versions = VersionList::online(versions_home.as_ref()).await?; } else { versions = VersionList::offline(versions_home.as_ref()).await?; } - - let settings_path = home.join("ozone.json"); - let settings = Settings::load(&settings_path).await?; let assets_path = home.join("assets"); Ok(Launcher { online, versions, - settings_path, - settings, system_info: SystemInfo::new(), libraries: LibraryRepository { home: home.join("libraries"), @@ -390,7 +336,7 @@ impl Launcher { })) } - pub async fn prepare_launch(&self, profile: &Profile) -> Result<(), LaunchError> { + pub async fn prepare_launch(&self, version_id: &ProfileVersion, instance: &Instance) -> Result<(), LaunchError> { /* tasks 2 l;aunch the gayme!!!! :3 * - java runtime * - normal process (good research, past figboot :3) @@ -403,26 +349,44 @@ impl Launcher { * - (done) logging * - (done) download the config if present and necessary * - (done) (explode if offline mode and we need to download stuff) - * - assets + * - (done) assets * - (done) get asset index (check if our local copy is good and redownload if not) * - (done) check what ones are good and what needs to be downloaded * - (done) download them * - (done) (if offline mode, explode) - * - if virtual or resource-mapped, copy (or perhaps hardlink? that would be cool) + * - (done) if virtual or resource-mapped, copy (or perhaps hardlink? that would be cool) * - (done) the actual client jar * - (done) check integriddy and download if needed * - (done) (explode if offline mode) * - launch the game * - build argument list and whatnot also */ - info!("Preparing launch for \"{}\"...", profile.version_id); + let Some(version_id) = self.versions.get_profile_version_id(version_id) else { + // idk how common this usecase actually is + warn!("Can't use latest release/snapshot profiles while offline!"); + return Err(LaunchError::UnknownVersion("".into())); + }; + + info!("Preparing launch for \"{}\"...", version_id); + + let inst_home = instance.get_path(&self.home).await.map_err(|e| LaunchError::IO { + what: "resolving instance directory", + error: e + })?; + + fs::create_dir_all(inst_home.as_path()).await.map_err(|e| LaunchError::IO { + what: "creating instance directory", + error: e + })?; + + info!("Launching the game in {}", inst_home.display()); - let ver_res = self.versions.get_version_lazy(&profile.version_id); + let ver_res = self.versions.get_version_lazy(version_id.as_ref()); let ver = match ver_res { VersionResult::Remote(mv) => Cow::Owned(self.versions.load_remote_version(mv).await.map_err(|e| LaunchError::LoadVersion(e))?), VersionResult::Complete(cv) => Cow::Borrowed(cv), VersionResult::None => { - return Err(LaunchError::UnknownVersion(profile.version_id.clone()).into()) + return Err(LaunchError::UnknownVersion(version_id.into_owned()).into()) } }; @@ -483,12 +447,18 @@ impl Launcher { // download assets - if let Some(idx_download) = ver.asset_index.as_ref() { - let asset_idx = self.assets.load_index(idx_download, ver.assets.as_ref().map(|s| s.as_ref())).await - .map_err(|e| LaunchError::Assets(e))?; + let (asset_idx_name, asset_idx) = + if let Some(idx_download) = ver.asset_index.as_ref() { + let asset_idx_name = idx_download.id.as_ref().or(ver.assets.as_ref()).map(String::as_str); + let asset_idx = self.assets.load_index(idx_download, asset_idx_name).await + .map_err(|e| LaunchError::Assets(e))?; - self.assets.ensure_assets(&asset_idx).await.map_err(|e| LaunchError::Assets(e))?; - } + self.assets.ensure_assets(&asset_idx).await.map_err(|e| LaunchError::Assets(e))?; + + (Some(asset_idx_name), Some(asset_idx)) + } else { + (None, None) + }; // download client jar @@ -513,10 +483,18 @@ impl Launcher { info!("Cleaned up {} old natives directories.", nnatives); // extract natives (execute this function unconditionally because we still need the natives dir to exist) - info!("Extracting natives."); + info!("Extracting natives from libraries"); let natives_dir = self.libraries.extract_natives(extract_jobs).await?; - info!("Building classpath."); + let assets_root = if let Some(asset_idx) = asset_idx { + info!("Reconstructing assets"); + self.assets.reconstruct_assets(&asset_idx, inst_home.as_path(), asset_idx_name.unwrap()).await + .map_err(|e| LaunchError::Assets(e))? + } else { + None + }; + + info!("Building classpath"); let classpath = env::join_paths(downloads.iter() .map(|job| job.get_path()) .chain(client_jar_path.iter().map(|p| p.as_path()))) @@ -650,10 +628,10 @@ impl LibraryRepository { let path = entry.path(); info!("Deleting old natives directory {}", path.display()); - /*fs::remove_dir_all(&path).await.map_err(|e| LaunchError::IO { + fs::remove_dir_all(&path).await.map_err(|e| LaunchError::IO { what: "reading natives entry", error: e - })?;*/ + })?; return Ok(true); } -- cgit v1.2.3-70-g09d2