diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8c266b8..55ff650 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,7 +26,6 @@ tauri-build = { version = "2.0.0", features = [] } [dependencies] tauri-plugin-shell = "2.0.0" -serde = { version = "1", features = ["derive", "rc"] } serde_json = "1" serde-binary = "0.5.0" rayon = "1.10.0" @@ -84,6 +83,10 @@ features = [] # You can also use "yaml_enc" or "bin_enc" version = "0.12" features = ["json", "blocking"] +[dependencies.serde] +version = "1" +features = ["derive", "rc"] + [profile.release] lto = true codegen-units = 1 diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index b3834d4..e55c908 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, fs::{self, create_dir_all}, path::{Path, PathBuf}, - sync::{LazyLock, Mutex, RwLockWriteGuard}, + sync::{Arc, LazyLock, Mutex, RwLockWriteGuard}, }; use directories::BaseDirs; @@ -12,7 +12,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tauri::AppHandle; use url::Url; -use crate::{library::push_application_update, process::process_manager::Platform, state::DownloadStatusManager, DB}; +use crate::{download_manager::downloadable_metadata::DownloadableMetadata, library::push_game_update, process::process_manager::Platform, state::DownloadStatusManager, DB}; #[derive(serde::Serialize, Clone, Deserialize)] pub struct DatabaseAuth { @@ -60,11 +60,11 @@ pub struct ApplicationVersion { pub struct DatabaseApplications { pub install_dirs: Vec, // Guaranteed to exist if the game also exists in the app state map - pub statuses: HashMap, - pub versions: HashMap>, + pub statuses: HashMap, + pub versions: HashMap>, #[serde(skip)] - pub transient_statuses: HashMap, + pub transient_statuses: HashMap, } #[derive(Serialize, Deserialize, Clone)] @@ -220,9 +220,9 @@ pub fn fetch_download_dir_stats() -> Result, String> { Ok(directories) } -pub fn set_application_status, &String)>( +pub fn set_game_status, &Arc)>( app_handle: &AppHandle, - id: String, + id: Arc, setter: F, ) { let mut db_handle = DB.borrow_data_mut().unwrap(); @@ -232,5 +232,5 @@ pub fn set_application_status, &St let status = DownloadStatusManager::fetch_state(&id); - push_application_update(app_handle, id, status); + push_game_update(app_handle, id, status); } diff --git a/src-tauri/src/download_manager/download_manager.rs b/src-tauri/src/download_manager/download_manager.rs index ef0b7d7..f8c91aa 100644 --- a/src-tauri/src/download_manager/download_manager.rs +++ b/src-tauri/src/download_manager/download_manager.rs @@ -12,16 +12,9 @@ use std::{ use log::info; use serde::Serialize; -use crate::downloads::download_agent::GameDownloadAgent; -use super::{application_download_error::ApplicationDownloadError, download_manager_builder::{CurrentProgressObject, DownloadAgent, DownloadableQueueStandin}, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, queue::Queue}; +use super::{application_download_error::ApplicationDownloadError, download_manager_builder::{CurrentProgressObject, DownloadAgent}, downloadable_metadata::DownloadableMetadata, queue::Queue}; -pub enum DownloadType { - Game, - Tool, - DLC, - Mod -} pub enum DownloadManagerSignal { /// Resumes (or starts) the DownloadManager Go, @@ -91,22 +84,6 @@ pub struct DownloadManager { progress: CurrentProgressObject, command_sender: Sender, } -impl From> for DownloadableQueueStandin { - fn from(value: Arc) -> Self { - Self { - id: value.id(), - status: Mutex::from(DownloadStatus::Queued), - progress: value.progress().clone(), - } - } -} -impl Debug for DownloadableQueueStandin { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DownloadableQueueStandin") - .field("id", &self.id) - .finish() - } -} #[allow(dead_code)] impl DownloadManager { @@ -126,40 +103,34 @@ impl DownloadManager { pub fn queue_download( &self, - id: String, - version: String, - target_download_dir: usize, + download: DownloadAgent ) -> Result<(), SendError> { - info!("Adding download id {}", id); - self.command_sender.send(DownloadManagerSignal::Queue( - id, - version, - target_download_dir, - ))?; + info!("Adding download id {:?}", download.metadata()); + self.command_sender.send(DownloadManagerSignal::Queue(download))?; self.command_sender.send(DownloadManagerSignal::Go) } - pub fn edit(&self) -> MutexGuard<'_, VecDeque>> { + pub fn edit(&self) -> MutexGuard<'_, VecDeque>> { self.download_queue.edit() } - pub fn read_queue(&self) -> VecDeque> { + pub fn read_queue(&self) -> VecDeque> { self.download_queue.read() } pub fn get_current_download_progress(&self) -> Option { let progress_object = (*self.progress.lock().unwrap()).clone()?; Some(progress_object.get_progress()) } - pub fn rearrange_string(&self, id: String, new_index: usize) { + pub fn rearrange_string(&self, meta: &Arc, new_index: usize) { let mut queue = self.edit(); - let current_index = get_index_from_id(&mut queue, id).unwrap(); + let current_index = get_index_from_id(&mut queue, meta).unwrap(); let to_move = queue.remove(current_index).unwrap(); queue.insert(new_index, to_move); self.command_sender .send(DownloadManagerSignal::UpdateUIQueue) .unwrap(); } - pub fn cancel(&self, id: String) { + pub fn cancel(&self, meta: Arc) { self.command_sender - .send(DownloadManagerSignal::Remove(id)) + .send(DownloadManagerSignal::Remove(meta)) .unwrap(); } pub fn rearrange(&self, current_index: usize, new_index: usize) { @@ -170,7 +141,7 @@ impl DownloadManager { let needs_pause = current_index == 0 || new_index == 0; if needs_pause { self.command_sender - .send(DownloadManagerSignal::Cancel) + .send(DownloadManagerSignal::Stop) .unwrap(); } @@ -203,20 +174,23 @@ impl DownloadManager { .unwrap(); self.terminator.join() } - pub fn uninstall_application(&self, id: String) { + pub fn uninstall_application(&self, meta: Arc) { self.command_sender - .send(DownloadManagerSignal::Uninstall(id)) + .send(DownloadManagerSignal::Uninstall(meta)) .unwrap(); } + pub fn get_sender(&self) -> Sender { + self.command_sender.clone() + } } /// Takes in the locked value from .edit() and attempts to /// get the index of whatever id is passed in fn get_index_from_id( - queue: &mut MutexGuard<'_, VecDeque>>, - id: String, + queue: &mut MutexGuard<'_, VecDeque>>, + meta: &Arc, ) -> Option { queue .iter() - .position(|download_agent| download_agent.id == id) + .position(|download_agent| download_agent == meta) } diff --git a/src-tauri/src/download_manager/download_manager_builder.rs b/src-tauri/src/download_manager/download_manager_builder.rs index ef1dc83..8be4e1d 100644 --- a/src-tauri/src/download_manager/download_manager_builder.rs +++ b/src-tauri/src/download_manager/download_manager_builder.rs @@ -12,19 +12,11 @@ use log::{error, info}; use tauri::{AppHandle, Emitter}; use crate::{ - db::{set_application_status, ApplicationStatus, ApplicationTransientStatus, Database}, download_manager::{download_manager::{DownloadStatus, DownloadType}, generate_downloadable::generate_downloadable}, downloads::download_agent::{self, GameDownloadAgent}, library::{ - on_game_complete, push_application_update, QueueUpdateEvent, - QueueUpdateEventQueueData, StatsUpdateEvent, - }, state::GameStatusManager, DB -}; + download_manager::{download_manager::DownloadStatus, generate_downloadable::generate_downloadable}, library::{QueueUpdateEvent, QueueUpdateEventQueueData, StatsUpdateEvent}} +; use super::{application_download_error::ApplicationDownloadError, download_manager::{DownloadManager, DownloadManagerSignal, DownloadManagerStatus}, download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject, queue::Queue}; -pub struct DownloadableQueueStandin { - pub id: String, - pub status: Mutex, - pub progress: Arc, -} pub type DownloadAgent = Arc>; pub type CurrentProgressObject = Arc>>>; @@ -163,12 +155,12 @@ impl DownloadManagerBuilder { DownloadManagerSignal::Error(e) => { self.manage_error_signal(e); } - //DownloadManagerSignal::UpdateUIQueue => { - // self.push_ui_queue_update(); - //} - //DownloadManagerSignal::UpdateUIStats(kbs, time) => { - // self.push_ui_stats_update(kbs, time); - //} + DownloadManagerSignal::UpdateUIQueue => { + self.push_ui_queue_update(); + } + DownloadManagerSignal::UpdateUIStats(kbs, time) => { + self.push_ui_stats_update(kbs, time); + } DownloadManagerSignal::Finish => { self.stop_and_wait_current_download(); return Ok(()); @@ -239,8 +231,7 @@ impl DownloadManagerBuilder { download_agent.on_incomplete(&app_handle); }, Err(e) => { - download_agent.on_error(&app_handle); - error!("error while managing download: {}", e); + download_agent.on_error(&app_handle, e.clone()); sender.send(DownloadManagerSignal::Error(e)).unwrap(); }, } @@ -272,7 +263,7 @@ impl DownloadManagerBuilder { info!("Got signal Error"); let current_agent = self.current_download_agent.clone().unwrap(); - current_agent.on_error(&self.app_handle); + current_agent.on_error(&self.app_handle, error.clone()); self.stop_and_wait_current_download(); self.remove_and_cleanup_front_download(¤t_agent.metadata()); @@ -310,6 +301,28 @@ impl DownloadManagerBuilder { self.manage_cancel_signal(meta); download_agent.on_uninstall(&self.app_handle); } + fn push_ui_stats_update(&self, kbs: usize, time: usize) { + let event_data = StatsUpdateEvent { speed: kbs, time }; + + self.app_handle.emit("update_stats", event_data); + } + fn push_ui_queue_update(&self) { + let registry = &self.download_agent_registry; + let queue_objs = registry + .iter() + .map(|(key, val)| QueueUpdateEventQueueData { + meta: DownloadableMetadata::clone(&key), + status: val.status(), + progress: val.progress().get_progress() + }) + .collect(); + + let event_data = QueueUpdateEvent { + queue: queue_objs, + status: self.status.lock().unwrap().clone(), + }; + self.app_handle.emit("update_queue", event_data); + } } /* // Refactored to consolidate this type. It's a monster. @@ -317,6 +330,33 @@ pub type DownloadAgent = Arc>>; impl DownloadManagerBuilder { + fn push_ui_stats_update(&self, kbs: usize, time: usize) { + let event_data = StatsUpdateEvent { speed: kbs, time }; + + self.app_handle.emit("update_stats", event_data).unwrap(); + } + + fn push_ui_queue_update(&self) { + let queue = self.download_queue.read(); + let queue_objs: Vec = queue + .iter() + .map(|interface| QueueUpdateEventQueueData { + id: interface.id.clone(), + status: interface.status.lock().unwrap().clone(), + progress: interface.progress.get_progress(), + }) + .collect(); + + let status_handle = self.status.lock().unwrap(); + let status = status_handle.clone(); + drop(status_handle); + + let event_data = QueueUpdateEvent { + queue: queue_objs, + status, + }; + self.app_handle.emit("update_queue", event_data).unwrap(); + } fn uninstall_application(&mut self, id: String) { // Removes the download if it's in the queue self.manage_remove_download_from_queue(id.clone()); @@ -606,34 +646,6 @@ impl DownloadManagerBuilder { .unwrap(); } - fn push_ui_stats_update(&self, kbs: usize, time: usize) { - let event_data = StatsUpdateEvent { speed: kbs, time }; - - self.app_handle.emit("update_stats", event_data).unwrap(); - } - - fn push_ui_queue_update(&self) { - let queue = self.download_queue.read(); - let queue_objs: Vec = queue - .iter() - .map(|interface| QueueUpdateEventQueueData { - id: interface.id.clone(), - status: interface.status.lock().unwrap().clone(), - progress: interface.progress.get_progress(), - }) - .collect(); - - let status_handle = self.status.lock().unwrap(); - let status = status_handle.clone(); - drop(status_handle); - - let event_data = QueueUpdateEvent { - queue: queue_objs, - status, - }; - self.app_handle.emit("update_queue", event_data).unwrap(); - } - fn stop_and_wait_current_download(&self) { self.set_status(DownloadManagerStatus::Paused); if let Some(current_flag) = &self.active_control_flag { @@ -816,4 +828,4 @@ impl DownloadManagerBuilder { .unwrap(); } } -*/ \ No newline at end of file +*/ diff --git a/src-tauri/src/download_manager/downloadable.rs b/src-tauri/src/download_manager/downloadable.rs index e3abbd7..8eb4956 100644 --- a/src-tauri/src/download_manager/downloadable.rs +++ b/src-tauri/src/download_manager/downloadable.rs @@ -1,18 +1,19 @@ -use std::sync::Arc; +use std::sync::{mpsc::Sender, Arc}; use tauri::AppHandle; use super::{ - application_download_error::ApplicationDownloadError, download_thread_control_flag::DownloadThreadControl, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject + application_download_error::ApplicationDownloadError, download_manager::{DownloadManagerSignal, DownloadStatus}, download_thread_control_flag::DownloadThreadControl, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject }; pub trait Downloadable: Send + Sync { fn download(&self) -> Result; fn progress(&self) -> Arc; fn control_flag(&self) -> DownloadThreadControl; + fn status(&self) -> DownloadStatus; fn metadata(&self) -> Arc; fn on_initialised(&self, app_handle: &AppHandle); - fn on_error(&self, app_handle: &AppHandle); + fn on_error(&self, app_handle: &AppHandle, error: ApplicationDownloadError); fn on_complete(&self, app_handle: &AppHandle); fn on_incomplete(&self, app_handle: &AppHandle); fn on_cancelled(&self, app_handle: &AppHandle); diff --git a/src-tauri/src/download_manager/downloadable_metadata.rs b/src-tauri/src/download_manager/downloadable_metadata.rs index 7036c51..dd2ba8c 100644 --- a/src-tauri/src/download_manager/downloadable_metadata.rs +++ b/src-tauri/src/download_manager/downloadable_metadata.rs @@ -1,4 +1,25 @@ -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DownloadableMetadata { - id: String, +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)] +pub enum DownloadType { + Game, + Tool, + DLC, + Mod } + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Clone)] +pub struct DownloadableMetadata { + pub id: String, + pub version: String, + pub download_type: DownloadType +} +impl DownloadableMetadata { + pub fn new(id: String, version: String, download_type: DownloadType) -> Self { + Self { + id, + version, + download_type + } + } +} \ No newline at end of file diff --git a/src-tauri/src/download_manager/queue.rs b/src-tauri/src/download_manager/queue.rs index 64a147e..2e8cddf 100644 --- a/src-tauri/src/download_manager/queue.rs +++ b/src-tauri/src/download_manager/queue.rs @@ -34,11 +34,11 @@ impl Queue { } /// Either inserts `interface` at the specified index, or appends to /// the back of the deque if index is greater than the length of the deque - pub fn insert(&self, interface: DownloadableMetadata, index: usize) { + pub fn insert(&self, interface: Arc, index: usize) { if self.read().len() > index { self.append(interface); } else { - self.edit().insert(index, Arc::new(interface)); + self.edit().insert(index, interface); } } pub fn append(&self, interface: Arc) { diff --git a/src-tauri/src/downloads/download_agent.rs b/src-tauri/src/downloads/download_agent.rs index e0503ac..1c9af86 100644 --- a/src-tauri/src/downloads/download_agent.rs +++ b/src-tauri/src/downloads/download_agent.rs @@ -1,15 +1,17 @@ use crate::auth::generate_authorization_header; -use crate::db::DatabaseImpls; +use crate::db::{set_game_status, DatabaseImpls}; use crate::download_manager::application_download_error::ApplicationDownloadError; use crate::download_manager::download_manager::DownloadManagerSignal; use crate::download_manager::download_thread_control_flag::{DownloadThreadControl, DownloadThreadControlFlag}; use crate::download_manager::downloadable::Downloadable; +use crate::download_manager::downloadable_metadata::DownloadableMetadata; use crate::download_manager::progress_object::{ProgressHandle, ProgressObject}; use crate::downloads::manifest::{DropDownloadContext, DropManifest}; use crate::remote::RemoteAccessError; use crate::DB; use log::{debug, error, info}; use rayon::ThreadPoolBuilder; +use tauri::Emitter; use std::collections::VecDeque; use std::fs::{create_dir_all, File}; use std::path::Path; @@ -85,18 +87,18 @@ impl GameDownloadAgent { } // Blocking - pub fn download(&self) -> Result<(), ApplicationDownloadError> { + pub fn download(&self) -> Result { self.setup_download()?; self.set_progress_object_params(); let timer = Instant::now(); - self.run().map_err(|_| ApplicationDownloadError::DownloadError)?; + let res = self.run().map_err(|_| ApplicationDownloadError::DownloadError); info!( "{} took {}ms to download", self.id, timer.elapsed().as_millis() ); - Ok(()) + res } pub fn ensure_manifest_exists(&self) -> Result<(), ApplicationDownloadError> { @@ -226,7 +228,7 @@ impl GameDownloadAgent { Ok(()) } - pub fn run(&self) -> Result<(), ()> { + pub fn run(&self) -> Result { info!("downloading game: {}", self.id); const DOWNLOAD_MAX_THREADS: usize = 1; @@ -290,45 +292,69 @@ impl GameDownloadAgent { info!("Setting completed contexts"); self.stored_manifest.write(); info!("Wrote completed contexts"); - return Ok(()); + return Ok(false); } // We've completed self.sender - .send(DownloadManagerSignal::Completed(self.id.clone())) + .send(DownloadManagerSignal::Completed(self.metadata())) .unwrap(); - Ok(()) + Ok(true) } } impl Downloadable for GameDownloadAgent { - - fn download(&self) -> Result<(), ApplicationDownloadError> { + fn download(&self) -> Result { self.download() } - + fn progress(&self) -> Arc { self.progress.clone() } - + fn control_flag(&self) -> DownloadThreadControl { self.control_flag.clone() } - - fn metadata(&self) -> crate::download_manager::downloadable_metadata::DownloadableMetadata { + + fn metadata(&self) -> Arc { + todo!() + } + + fn on_initialised(&self, _app_handle: &tauri::AppHandle) { + return; + } + + fn on_error(&self, app_handle: &tauri::AppHandle, error: ApplicationDownloadError) { + app_handle + .emit("download_error", error.to_string()) + .unwrap(); + + error!("error while managing download: {}", error); + + set_game_status(app_handle, self.metadata(), |db_handle, meta| { + db_handle.applications.transient_statuses.remove(meta); + }); + + } + + fn on_complete(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_incomplete(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_cancelled(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_uninstall(&self, app_handle: &tauri::AppHandle) { todo!() } - fn on_error(&self) { - todo!() - } - - fn on_complete(&self, app_handle: &AppHandle>) { - on_game_complete(id, version, install_dir, app_handle) - } - - fn on_initialised(&self, app_handle: &tauri::AppHandle) { + fn status(&self) -> crate::download_manager::download_manager::DownloadStatus { todo!() } } \ No newline at end of file diff --git a/src-tauri/src/downloads/download_commands.rs b/src-tauri/src/downloads/download_commands.rs index 4ac3554..f387d9d 100644 --- a/src-tauri/src/downloads/download_commands.rs +++ b/src-tauri/src/downloads/download_commands.rs @@ -1,6 +1,8 @@ -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; -use crate::AppState; +use crate::{download_manager::{downloadable::Downloadable, downloadable_metadata::DownloadableMetadata}, AppState}; + +use super::download_agent::GameDownloadAgent; #[tauri::command] pub fn download_game( @@ -9,11 +11,15 @@ pub fn download_game( install_dir: usize, state: tauri::State<'_, Mutex>, ) -> Result<(), String> { + let sender = state.lock().unwrap().download_manager.get_sender(); + let game_download_agent = Arc::new( + Box::new(GameDownloadAgent::new(game_id, game_version, install_dir, sender)) as Box + ); state .lock() .unwrap() .download_manager - .queue_download(game_id, game_version, install_dir) + .queue_download(game_download_agent) .map_err(|_| "An error occurred while communicating with the download manager.".to_string()) } @@ -41,8 +47,8 @@ pub fn move_game_in_queue( } #[tauri::command] -pub fn cancel_game(state: tauri::State<'_, Mutex>, game_id: String) { - state.lock().unwrap().download_manager.cancel(game_id) +pub fn cancel_game(state: tauri::State<'_, Mutex>, game_id: DownloadableMetadata) { + state.lock().unwrap().download_manager.cancel(Arc::new(game_id)) } /* diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 612e8ed..a0028e6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -27,6 +27,7 @@ use db::{ }; use download_manager::download_manager::DownloadManager; use download_manager::download_manager_builder::DownloadManagerBuilder; +use download_manager::downloadable_metadata::DownloadableMetadata; use debug::fetch_system_data; use downloads::download_commands::*; use http::Response; @@ -81,7 +82,7 @@ pub struct User { pub struct AppState<'a> { status: AppStatus, user: Option, - games: HashMap, + games: HashMap, #[serde(skip_serializing)] download_manager: Arc, @@ -181,7 +182,7 @@ fn setup(handle: AppHandle) -> AppState<'static> { db_handle .applications .statuses - .entry(game_id.to_string()) + .entry(game_id) .and_modify(|v| *v = ApplicationStatus::Remote {}); } drop(db_handle); diff --git a/src-tauri/src/library.rs b/src-tauri/src/library.rs index 62bd62b..969945b 100644 --- a/src-tauri/src/library.rs +++ b/src-tauri/src/library.rs @@ -1,4 +1,4 @@ -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use log::info; use serde::{Deserialize, Serialize}; @@ -10,6 +10,7 @@ use crate::db::DatabaseImpls; use crate::db::ApplicationVersion; use crate::db::ApplicationStatus; use crate::download_manager::download_manager::{DownloadManagerStatus, DownloadStatus}; +use crate::download_manager::downloadable_metadata::DownloadableMetadata; use crate::process::process_manager::Platform; use crate::remote::{DropServerError, RemoteAccessError}; use crate::state::{GameStatusManager, GameStatusWithTransient}; @@ -24,7 +25,7 @@ pub struct FetchGameStruct { #[derive(Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Game { - id: String, + meta: DownloadableMetadata, m_name: String, m_short_description: String, m_description: String, @@ -43,7 +44,7 @@ pub struct GameUpdateEvent { #[derive(Serialize, Clone)] pub struct QueueUpdateEventQueueData { - pub id: String, + pub meta: DownloadableMetadata, pub status: DownloadStatus, pub progress: f64, } @@ -98,12 +99,12 @@ fn fetch_library_logic(app: AppHandle) -> Result, RemoteAccessError> { let mut db_handle = DB.borrow_data_mut().unwrap(); for game in games.iter() { - handle.games.insert(game.id.clone(), game.clone()); - if !db_handle.applications.statuses.contains_key(&game.id) { + handle.games.insert(game.meta.clone(), game.clone()); + if !db_handle.applications.statuses.contains_key(&game.meta) { db_handle .applications .statuses - .insert(game.id.clone(), ApplicationStatus::Remote {}); + .insert(game.meta.clone(), ApplicationStatus::Remote {}); } } @@ -118,15 +119,15 @@ pub fn fetch_library(app: AppHandle) -> Result, String> { } fn fetch_game_logic( - id: String, + meta: DownloadableMetadata, app: tauri::AppHandle, ) -> Result { let state = app.state::>(); let mut state_handle = state.lock().unwrap(); - let game = state_handle.games.get(&id); + let game = state_handle.games.get(&meta); if let Some(game) = game { - let status = GameStatusManager::fetch_state(&id); + let status = DownloadStatusManager::fetch_state(&meta); let data = FetchGameStruct { game: game.clone(), @@ -138,7 +139,7 @@ fn fetch_game_logic( let base_url = DB.fetch_base_url(); - let endpoint = base_url.join(&format!("/api/v1/game/{}", id))?; + let endpoint = base_url.join(&format!("/api/v1/game/{}", meta.id))?; let header = generate_authorization_header(); let client = reqwest::blocking::Client::new(); @@ -157,18 +158,18 @@ fn fetch_game_logic( } let game = response.json::()?; - state_handle.games.insert(id.clone(), game.clone()); + state_handle.games.insert(meta.clone(), game.clone()); let mut db_handle = DB.borrow_data_mut().unwrap(); db_handle .applications .statuses - .entry(id.clone()) + .entry(meta.clone()) .or_insert(ApplicationStatus::Remote {}); drop(db_handle); - let status = GameStatusManager::fetch_state(&id); + let status = DownloadStatusManager::fetch_state(&meta); let data = FetchGameStruct { game: game.clone(), @@ -179,7 +180,7 @@ fn fetch_game_logic( } #[tauri::command] -pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { +pub fn fetch_game(id: DownloadableMetadata, app: tauri::AppHandle) -> Result { let result = fetch_game_logic(id, app); if result.is_err() { @@ -190,20 +191,20 @@ pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result Result { - let status = GameStatusManager::fetch_state(&id); +pub fn fetch_game_status(meta: Arc) -> Result { + let status = DownloadStatusManager::fetch_state(&meta); Ok(status) } fn fetch_game_verion_options_logic<'a>( - game_id: String, + meta: DownloadableMetadata, state: tauri::State<'_, Mutex>, ) -> Result, RemoteAccessError> { let base_url = DB.fetch_base_url(); let endpoint = - base_url.join(format!("/api/v1/client/metadata/versions?id={}", game_id).as_str())?; + base_url.join(format!("/api/v1/client/metadata/versions?id={}", meta.id).as_str())?; let header = generate_authorization_header(); let client = reqwest::blocking::Client::new(); @@ -234,7 +235,7 @@ fn fetch_game_verion_options_logic<'a>( #[tauri::command] pub fn fetch_game_verion_options<'a>( - game_id: String, + game_id: DownloadableMetadata, state: tauri::State<'_, Mutex>, ) -> Result, String> { fetch_game_verion_options_logic(game_id, state).map_err(|e| e.to_string()) @@ -242,7 +243,7 @@ pub fn fetch_game_verion_options<'a>( #[tauri::command] pub fn uninstall_game( - game_id: String, + game_id: Arc, state: tauri::State<'_, Mutex>, ) -> Result<(), String> { let state_lock = state.lock().unwrap(); @@ -252,12 +253,12 @@ pub fn uninstall_game( Ok(()) } -pub fn push_application_update(app_handle: &AppHandle, id: String, status: GameStatusWithTransient) { +pub fn push_game_update(app_handle: &AppHandle, meta: DownloadableMetadata, status: ApplicationStatusWithTransient) { app_handle .emit( - &format!("update_game/{}", id), + &format!("update_game/{}", meta.id), GameUpdateEvent { - game_id: id, + game_id: meta.id, status, }, ) @@ -265,8 +266,7 @@ pub fn push_application_update(app_handle: &AppHandle, id: String, status: GameS } pub fn on_game_complete( - game_id: String, - version_name: String, + meta: Arc, install_dir: String, app_handle: &AppHandle, ) -> Result<(), RemoteAccessError> { @@ -276,8 +276,8 @@ pub fn on_game_complete( let endpoint = base_url.join( format!( "/api/v1/client/metadata/version?id={}&version={}", - game_id, - encode(&version_name) + meta.id, + encode(&meta.version) ) .as_str(), )?; @@ -303,20 +303,20 @@ pub fn on_game_complete( handle .applications .versions - .entry(game_id.clone()) + .entry(*meta.clone()) .or_default() - .insert(version_name.clone(), data.clone()); + .insert(meta.version.clone(), data.clone()); drop(handle); DB.save().unwrap(); let status = if data.setup_command.is_empty() { ApplicationStatus::Installed { - version_name, + version_name: (*meta.version.clone()).to_string(), install_dir, } } else { ApplicationStatus::SetupRequired { - version_name, + version_name: (*meta.version.clone()).to_string(), install_dir, } }; @@ -325,14 +325,14 @@ pub fn on_game_complete( db_handle .applications .statuses - .insert(game_id.clone(), status.clone()); + .insert(*meta.clone(), status.clone()); drop(db_handle); DB.save().unwrap(); app_handle .emit( - &format!("update_game/{}", game_id), + &format!("update_game/{}", meta.id), GameUpdateEvent { - game_id, + game_id: (*meta.id.clone()).to_string(), status: (Some(status), None), }, ) diff --git a/src-tauri/src/process/process_commands.rs b/src-tauri/src/process/process_commands.rs index 74d7dfd..c80d89e 100644 --- a/src-tauri/src/process/process_commands.rs +++ b/src-tauri/src/process/process_commands.rs @@ -1,10 +1,10 @@ use std::sync::Mutex; -use crate::AppState; +use crate::{download_manager::downloadable_metadata::DownloadableMetadata, AppState}; #[tauri::command] pub fn launch_game( - game_id: String, + game_id: DownloadableMetadata, state: tauri::State<'_, Mutex>, ) -> Result<(), String> { let state_lock = state.lock().unwrap(); @@ -20,7 +20,7 @@ pub fn launch_game( #[tauri::command] pub fn kill_game( - game_id: String, + game_id: DownloadableMetadata, state: tauri::State<'_, Mutex>, ) -> Result<(), String> { let state_lock = state.lock().unwrap(); diff --git a/src-tauri/src/process/process_manager.rs b/src-tauri/src/process/process_manager.rs index f965449..e75bd06 100644 --- a/src-tauri/src/process/process_manager.rs +++ b/src-tauri/src/process/process_manager.rs @@ -15,16 +15,13 @@ use tauri::{AppHandle, Manager}; use umu_wrapper_lib::command_builder::UmuCommandBuilder; use crate::{ - db::{ApplicationStatus, ApplicationTransientStatus, DATA_ROOT_DIR}, - library::push_application_update, - state::GameStatusManager, - AppState, DB, + db::{ApplicationStatus, ApplicationTransientStatus, DATA_ROOT_DIR}, download_manager::{downloadable::Downloadable, downloadable_metadata::DownloadableMetadata}, library::push_game_update, state::DownloadStatusManager, AppState, DB }; pub struct ProcessManager<'a> { current_platform: Platform, log_output_dir: PathBuf, - processes: HashMap>, + processes: HashMap>, app_handle: AppHandle, game_launchers: HashMap<(Platform, Platform), &'a (dyn ProcessHandler + Sync + Send + 'static)>, } @@ -82,8 +79,8 @@ impl ProcessManager<'_> { */ (absolute_exe, Vec::new()) } - pub fn kill_game(&mut self, game_id: String) -> Result<(), io::Error> { - return match self.processes.get(&game_id) { + pub fn kill_game(&mut self, meta: DownloadableMetadata) -> Result<(), io::Error> { + return match self.processes.get(&meta) { Some(child) => { child.kill()?; child.wait()?; @@ -96,20 +93,20 @@ impl ProcessManager<'_> { }; } - fn on_process_finish(&mut self, game_id: String, result: Result) { - if !self.processes.contains_key(&game_id) { + fn on_process_finish(&mut self, meta: DownloadableMetadata, result: Result) { + if !self.processes.contains_key(&meta) { warn!("process on_finish was called, but game_id is no longer valid. finished with result: {:?}", result); return; } - info!("process for {} exited with {:?}", game_id, result); + info!("process for {:?} exited with {:?}", meta, result); - self.processes.remove(&game_id); + self.processes.remove(&meta); let mut db_handle = DB.borrow_data_mut().unwrap(); - db_handle.applications.transient_statuses.remove(&game_id); + db_handle.applications.transient_statuses.remove(&meta); - let current_state = db_handle.applications.statuses.get(&game_id).cloned(); + let current_state = db_handle.applications.statuses.get(&meta).cloned(); if let Some(saved_state) = current_state { if let ApplicationStatus::SetupRequired { version_name, @@ -119,7 +116,7 @@ impl ProcessManager<'_> { if let Ok(exit_code) = result { if exit_code.success() { db_handle.applications.statuses.insert( - game_id.clone(), + meta.clone(), ApplicationStatus::Installed { version_name: version_name.to_string(), install_dir: install_dir.to_string(), @@ -131,9 +128,9 @@ impl ProcessManager<'_> { } drop(db_handle); - let status = GameStatusManager::fetch_state(&game_id); + let status = DownloadStatusManager::fetch_state(&meta); - push_application_update(&self.app_handle, game_id.clone(), status); + push_game_update(&self.app_handle, meta.clone(), status); // TODO better management } @@ -145,8 +142,8 @@ impl ProcessManager<'_> { .contains_key(&(current.clone(), platform.clone()))) } - pub fn launch_process(&mut self, game_id: String) -> Result<(), String> { - if self.processes.contains_key(&game_id) { + pub fn launch_process(&mut self, meta: DownloadableMetadata) -> Result<(), String> { + if self.processes.contains_key(&meta) { return Err("Game or setup is already running.".to_owned()); } @@ -154,7 +151,7 @@ impl ProcessManager<'_> { let game_status = db_lock .applications .statuses - .get(&game_id) + .get(&meta) .ok_or("Game not installed")?; let status_metadata: Option<(&String, &String)> = match game_status { @@ -178,7 +175,7 @@ impl ProcessManager<'_> { let game_version = db_lock .applications .versions - .get(&game_id) + .get(&meta) .ok_or("Invalid game ID".to_owned())? .get(version_name) .ok_or("Invalid version name".to_owned())?; @@ -213,7 +210,7 @@ impl ProcessManager<'_> { .create(true) .open( self.log_output_dir - .join(format!("{}-{}.log", game_id, current_time.timestamp())), + .join(format!("{}-{}-{}.log", meta.id, meta.version, current_time.timestamp())), ) .map_err(|v| v.to_string())?; @@ -223,8 +220,9 @@ impl ProcessManager<'_> { .read(true) .create(true) .open(self.log_output_dir.join(format!( - "{}-{}-error.log", - game_id, + "{}-{}-{}-error.log", + meta.id, + meta.version, current_time.timestamp() ))) .map_err(|v| v.to_string())?; @@ -239,8 +237,7 @@ impl ProcessManager<'_> { .map_err(|e| e.to_string())?; let launch_process = game_launcher.launch_process( - &game_id, - version_name, + &meta, command.to_str().unwrap().to_owned(), args, &target_current_dir.to_string(), @@ -254,17 +251,17 @@ impl ProcessManager<'_> { db_lock .applications .transient_statuses - .insert(game_id.clone(), ApplicationTransientStatus::Running {}); + .insert(meta.clone(), ApplicationTransientStatus::Running {}); - push_application_update( + push_game_update( &self.app_handle, - game_id.clone(), + meta.clone(), (None, Some(ApplicationTransientStatus::Running {})), ); let wait_thread_handle = launch_process_handle.clone(); let wait_thread_apphandle = self.app_handle.clone(); - let wait_thread_game_id = game_id.clone(); + let wait_thread_game_id = meta.clone(); spawn(move || { let result: Result = launch_process_handle.wait(); @@ -281,7 +278,7 @@ impl ProcessManager<'_> { drop(app_state_handle); }); - self.processes.insert(game_id, wait_thread_handle); + self.processes.insert(meta, wait_thread_handle); info!("finished spawning process"); @@ -298,8 +295,7 @@ pub enum Platform { pub trait ProcessHandler: Send + 'static { fn launch_process( &self, - game_id: &String, - version_name: &String, + meta: &DownloadableMetadata, command: String, args: Vec, current_dir: &String, @@ -312,8 +308,7 @@ struct NativeGameLauncher; impl ProcessHandler for NativeGameLauncher { fn launch_process( &self, - game_id: &String, - version_name: &String, + meta: &DownloadableMetadata, command: String, args: Vec, current_dir: &String, @@ -335,8 +330,7 @@ struct UMULauncher; impl ProcessHandler for UMULauncher { fn launch_process( &self, - game_id: &String, - version_name: &String, + meta: &DownloadableMetadata, command: String, args: Vec, current_dir: &String, @@ -344,7 +338,7 @@ impl ProcessHandler for UMULauncher { error_file: File, ) -> Result { UmuCommandBuilder::new(UMU_LAUNCHER_EXECUTABLE, command) - .game_id(game_id.into()) + .game_id(String::from("0")) .launch_args(args) .build() .spawn() diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index df0585f..3c3458c 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -1,13 +1,15 @@ + +use std::sync::Arc; + use crate::{ - db::{ApplicationStatus, ApplicationTransientStatus, Database}, - DB, + db::{ApplicationStatus, ApplicationTransientStatus}, download_manager::downloadable_metadata::DownloadableMetadata, DB }; pub type GameStatusWithTransient = (Option, Option); pub struct GameStatusManager {} -impl GameStatusManager { - pub fn fetch_state(game_id: &String) -> GameStatusWithTransient { +impl DownloadStatusManager { + pub fn fetch_state(id: &DownloadableMetadata) -> ApplicationStatusWithTransient { let db_lock = DB.borrow_data().unwrap(); GameStatusManager::fetch_state_with_db(game_id, &db_lock) } diff --git a/src-tauri/src/tools/tool.rs b/src-tauri/src/tools/tool.rs index f3a0de0..0974037 100644 --- a/src-tauri/src/tools/tool.rs +++ b/src-tauri/src/tools/tool.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::download_manager::{download_thread_control_flag::DownloadThreadControl, downloadable::Downloadable, progress_object::ProgressObject}; +use crate::download_manager::{application_download_error::ApplicationDownloadError, download_thread_control_flag::DownloadThreadControl, downloadable::Downloadable, downloadable_metadata::DownloadableMetadata, progress_object::ProgressObject}; pub struct ToolDownloadAgent { id: String, @@ -10,7 +10,7 @@ pub struct ToolDownloadAgent { progress: Arc, } impl Downloadable for ToolDownloadAgent { - fn download(&mut self) -> Result<(), crate::download_manager::application_download_error::ApplicationDownloadError> { + fn download(&self) -> Result { todo!() } @@ -22,15 +22,35 @@ impl Downloadable for ToolDownloadAgent { todo!() } - fn metadata(&self) -> crate::download_manager::downloadable_metadata::DownloadableMetadata { + fn status(&self) -> crate::download_manager::download_manager::DownloadStatus { todo!() } - fn on_error(&self) { + fn metadata(&self) -> Arc { todo!() } - fn on_complete(&self) { + fn on_initialised(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_error(&self, app_handle: &tauri::AppHandle, error: crate::download_manager::application_download_error::ApplicationDownloadError) { + todo!() + } + + fn on_complete(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_incomplete(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_cancelled(&self, app_handle: &tauri::AppHandle) { + todo!() + } + + fn on_uninstall(&self, app_handle: &tauri::AppHandle) { todo!() } } \ No newline at end of file