summaryrefslogtreecommitdiffstats
path: root/src/launcher.rs
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-01-18 23:47:48 -0600
committerLibravatar bigfoot547 <[email protected]>2025-01-18 23:47:48 -0600
commitcd8bf1667494070c3a22ab5d63b559a9742b8a1a (patch)
tree6f93f0c0fbdccfa18733499845a8bc7c298c402f /src/launcher.rs
parentbuilding classpath (diff)
more stuff
Diffstat (limited to 'src/launcher.rs')
-rw-r--r--src/launcher.rs144
1 files changed, 61 insertions, 83 deletions
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<String, Profile>,
- instances: HashMap<String, Instance>
-}
-
-#[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<Path>) -> Result<Settings, SettingsLoadError> {
- 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<dyn Error>),
@@ -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<Launcher, Box<dyn Error>> {
- 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<Path>, online: bool) -> Result<Launcher, Box<dyn Error>> {
+ 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("<latest>".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);
}