-
+
@@ -49,68 +35,47 @@
{{ element.status }}
-
-
+
-
{{
- formatKilobytes(element.current / 1000)
- }}
- /
- {{ formatKilobytes(element.max / 1000) }}
cancelGame(element.meta)" class="group">
-
+
Loading...
-
diff --git a/plugins/global-error-handler.ts b/plugins/global-error-handler.ts
index b7bcd45..f9d7249 100644
--- a/plugins/global-error-handler.ts
+++ b/plugins/global-error-handler.ts
@@ -1,7 +1,7 @@
export default defineNuxtPlugin((nuxtApp) => {
// Also possible
nuxtApp.hook("vue:error", (error, instance, info) => {
- console.error(error, info);
+ console.log(error);
const router = useRouter();
router.replace(`/error`);
});
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 22b5365..0c8123b 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -255,12 +255,6 @@ dependencies = [
"system-deps",
]
-[[package]]
-name = "atomic-instant-full"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6541700e074cda41b1c6f98c2cae6cde819967bf142078f069cad85387cdbe"
-
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -852,6 +846,37 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "derive_builder"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
+dependencies = [
+ "derive_builder_macro",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.91",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
+dependencies = [
+ "derive_builder_core",
+ "syn 2.0.91",
+]
+
[[package]]
name = "derive_more"
version = "0.99.18"
@@ -1006,17 +1031,18 @@ dependencies = [
[[package]]
name = "drop-app"
-version = "0.2.0-beta-prerelease-1"
+version = "0.1.0"
dependencies = [
- "atomic-instant-full",
"boxcar",
"chrono",
+ "derive_builder",
"directories",
"hex",
"http",
"log",
"log4rs",
"md5",
+ "merge-struct",
"openssl",
"parking_lot 0.12.3",
"rayon",
@@ -1026,6 +1052,7 @@ dependencies = [
"serde",
"serde-binary",
"serde_json",
+ "serde_merge",
"serde_with",
"shared_child",
"slice-deque",
@@ -2428,6 +2455,16 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "merge-struct"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d82012d21e24135b839b6b9bebd622b7ff0cb40071498bc2d066d3a6d04dd4a"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "mime"
version = "0.3.17"
@@ -3876,6 +3913,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_merge"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "606e91878516232ac3b16c12e063d4468d762f16d77e7aef14a1f2326c5f409b"
+dependencies = [
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "serde_repr"
version = "0.1.19"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index f9f24c3..c48f490 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "drop-app"
-version = "0.2.0-beta-prerelease-1"
+version = "0.1.0"
description = "The client application for the open-source, self-hosted game distribution platform Drop"
authors = ["Drop OSS"]
edition = "2021"
@@ -47,9 +47,11 @@ tauri-plugin-autostart = "2.0.0"
shared_child = "1.0.1"
serde_with = "3.12.0"
slice-deque = "0.3.0"
+derive_builder = "0.20.2"
throttle_my_fn = "0.2.6"
parking_lot = "0.12.3"
-atomic-instant-full = "0.1.0"
+merge-struct = "0.1.0"
+serde_merge = "0.1.3"
[dependencies.tauri]
version = "2.1.1"
diff --git a/src-tauri/src/autostart.rs b/src-tauri/src/autostart.rs
index dec9148..da0c1aa 100644
--- a/src-tauri/src/autostart.rs
+++ b/src-tauri/src/autostart.rs
@@ -1,4 +1,4 @@
-use crate::database::db::{borrow_db_checked, borrow_db_mut_checked, save_db};
+use crate::DB;
use log::debug;
use tauri::AppHandle;
use tauri_plugin_autostart::ManagerExt;
@@ -7,24 +7,24 @@ pub fn toggle_autostart_logic(app: AppHandle, enabled: bool) -> Result<(), Strin
let manager = app.autolaunch();
if enabled {
manager.enable().map_err(|e| e.to_string())?;
- debug!("enabled autostart");
+ debug!("Enabled autostart");
} else {
manager.disable().map_err(|e| e.to_string())?;
- debug!("eisabled autostart");
+ debug!("Disabled autostart");
}
// Store the state in DB
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().map_err(|e| e.to_string())?;
db_handle.settings.autostart = enabled;
drop(db_handle);
- save_db();
+ DB.save().map_err(|e| e.to_string())?;
Ok(())
}
pub fn get_autostart_enabled_logic(app: AppHandle) -> Result
{
// First check DB state
- let db_handle = borrow_db_checked();
+ let db_handle = DB.borrow_data().unwrap();
let db_state = db_handle.settings.autostart;
drop(db_handle);
@@ -46,7 +46,7 @@ pub fn get_autostart_enabled_logic(app: AppHandle) -> Result Result<(), String> {
- let db_handle = borrow_db_checked();
+ let db_handle = DB.borrow_data().map_err(|e| e.to_string())?;
let should_be_enabled = db_handle.settings.autostart;
drop(db_handle);
@@ -56,10 +56,10 @@ pub fn sync_autostart_on_startup(app: &AppHandle) -> Result<(), String> {
if current_state != should_be_enabled {
if should_be_enabled {
manager.enable().map_err(|e| e.to_string())?;
- debug!("synced autostart: enabled");
+ debug!("Synced autostart: enabled");
} else {
manager.disable().map_err(|e| e.to_string())?;
- debug!("synced autostart: disabled");
+ debug!("Synced autostart: disabled");
}
}
diff --git a/src-tauri/src/cleanup.rs b/src-tauri/src/cleanup.rs
index de0e4e6..d8817ae 100644
--- a/src-tauri/src/cleanup.rs
+++ b/src-tauri/src/cleanup.rs
@@ -1,23 +1,13 @@
-use log::{debug, error};
+use log::{debug, info};
use tauri::AppHandle;
-use crate::AppState;
-
#[tauri::command]
-pub fn quit(app: tauri::AppHandle, state: tauri::State<'_, std::sync::Mutex>>) {
- cleanup_and_exit(&app, &state);
+pub fn quit(app: tauri::AppHandle) {
+ cleanup_and_exit(&app);
}
-pub fn cleanup_and_exit(app: &AppHandle, state: &tauri::State<'_, std::sync::Mutex>>) {
- debug!("cleaning up and exiting application");
- let download_manager = state.lock().unwrap().download_manager.clone();
- match download_manager.ensure_terminated() {
- Ok(res) => match res {
- Ok(_) => debug!("download manager terminated correctly"),
- Err(_) => error!("download manager failed to terminate correctly"),
- },
- Err(e) => panic!("{:?}", e),
- }
+pub fn cleanup_and_exit(app: &AppHandle) {
+ debug!("Cleaning up and exiting application");
app.exit(0);
}
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index b47a348..a0b646e 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -1,4 +1,9 @@
-use crate::AppState;
+use tauri::AppHandle;
+
+use crate::{
+ autostart::{get_autostart_enabled_logic, toggle_autostart_logic},
+ AppState,
+};
#[tauri::command]
pub fn fetch_state(
diff --git a/src-tauri/src/database/commands.rs b/src-tauri/src/database/commands.rs
index 18befb8..b254e00 100644
--- a/src-tauri/src/database/commands.rs
+++ b/src-tauri/src/database/commands.rs
@@ -6,83 +6,74 @@ use std::{
use serde_json::Value;
-use crate::{
- database::{db::borrow_db_mut_checked, settings::Settings},
- download_manager::internal_error::InternalError,
-};
+use crate::{database::settings::Settings, error::user_error::UserValue, DB};
-use super::{
- db::{borrow_db_checked, save_db, DATA_ROOT_DIR},
- debug::SystemData,
-};
+use super::{db::DATA_ROOT_DIR, debug::SystemData};
// Will, in future, return disk/remaining size
// Just returns the directories that have been set up
#[tauri::command]
pub fn fetch_download_dir_stats() -> Vec {
- let lock = borrow_db_checked();
+ let lock = DB.borrow_data().unwrap();
lock.applications.install_dirs.clone()
}
#[tauri::command]
pub fn delete_download_dir(index: usize) {
- let mut lock = borrow_db_mut_checked();
+ let mut lock = DB.borrow_data_mut().unwrap();
lock.applications.install_dirs.remove(index);
drop(lock);
- save_db();
+ DB.save().unwrap();
}
#[tauri::command]
-pub fn add_download_dir(new_dir: PathBuf) -> Result<(), InternalError<()>> {
+pub fn add_download_dir(new_dir: PathBuf) -> UserValue<(), Error> {
// Check the new directory is all good
let new_dir_path = Path::new(&new_dir);
if new_dir_path.exists() {
let dir_contents = new_dir_path.read_dir()?;
if dir_contents.count() != 0 {
- return Err(Error::new(
+ return UserValue::Err(Error::new(
ErrorKind::DirectoryNotEmpty,
"Selected directory cannot contain any existing files",
- )
- .into());
+ ));
}
} else {
create_dir_all(new_dir_path)?;
}
// Add it to the dictionary
- let mut lock = borrow_db_mut_checked();
+ let mut lock = DB.borrow_data_mut().unwrap();
if lock.applications.install_dirs.contains(&new_dir) {
- return Err(Error::new(
+ return UserValue::Err(Error::new(
ErrorKind::AlreadyExists,
"Selected directory already exists in database",
- )
- .into());
+ ));
}
lock.applications.install_dirs.push(new_dir);
drop(lock);
- save_db();
+ DB.save().unwrap();
- Ok(())
+ UserValue::Ok(())
}
#[tauri::command]
pub fn update_settings(new_settings: Value) {
- let mut db_lock = borrow_db_mut_checked();
+ println!("{}", new_settings);
+ let mut db_lock = DB.borrow_data_mut().unwrap();
let mut current_settings = serde_json::to_value(db_lock.settings.clone()).unwrap();
for (key, value) in new_settings.as_object().unwrap() {
current_settings[key] = value.clone();
}
+ println!("New settings unset: {}", ¤t_settings);
let new_settings: Settings = serde_json::from_value(current_settings).unwrap();
db_lock.settings = new_settings;
- println!("new Settings: {:?}", db_lock.settings);
-}
-#[tauri::command]
-pub fn fetch_settings() -> Settings {
- borrow_db_checked().settings.clone()
+ println!("New Settings: {:?}", db_lock.settings);
}
+
#[tauri::command]
pub fn fetch_system_data() -> SystemData {
- let db_handle = borrow_db_checked();
+ let db_handle = DB.borrow_data().unwrap();
SystemData::new(
db_handle.auth.as_ref().unwrap().client_id.clone(),
db_handle.base_url.clone(),
diff --git a/src-tauri/src/database/db.rs b/src-tauri/src/database/db.rs
index b92de3d..376bcb0 100644
--- a/src-tauri/src/database/db.rs
+++ b/src-tauri/src/database/db.rs
@@ -1,14 +1,13 @@
use std::{
collections::HashMap,
fs::{self, create_dir_all},
- hash::Hash,
path::{Path, PathBuf},
- sync::{LazyLock, Mutex, RwLockReadGuard, RwLockWriteGuard},
+ sync::{LazyLock, Mutex, RwLockWriteGuard},
};
use chrono::Utc;
use directories::BaseDirs;
-use log::{debug, error, info};
+use log::{debug, info};
use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_with::serde_as;
@@ -57,23 +56,11 @@ pub enum ApplicationTransientStatus {
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct GameVersion {
- pub game_id: String,
- pub version_name: String,
-
- pub platform: Platform,
-
- pub launch_command: String,
- pub launch_args: Vec,
-
- pub setup_command: String,
- pub setup_args: Vec,
-
- pub only_setup: bool,
-
pub version_index: usize,
- pub delta: bool,
-
- pub umu_id_override: Option,
+ pub version_name: String,
+ pub launch_command: String,
+ pub setup_command: String,
+ pub platform: Platform,
}
#[serde_as]
@@ -104,18 +91,10 @@ impl Database {
Self {
applications: DatabaseApplications {
install_dirs: vec![games_base_dir.into()],
- game_statuses: HashMap::new(),
- game_versions: HashMap::new(),
- installed_game_version: HashMap::new(),
- transient_statuses: HashMap::new(),
+ ..Default::default()
},
prev_database,
- base_url: "".to_owned(),
- auth: None,
- settings: Settings {
- autostart: false,
- max_download_threads: 4,
- },
+ ..Default::default()
}
}
}
@@ -151,7 +130,7 @@ impl DatabaseImpls for DatabaseInterface {
let games_base_dir = data_root_dir.join("games");
let logs_root_dir = data_root_dir.join("logs");
- debug!("creating data directory at {:?}", data_root_dir);
+ debug!("Creating data directory at {:?}", data_root_dir);
create_dir_all(data_root_dir.clone()).unwrap();
create_dir_all(games_base_dir.clone()).unwrap();
create_dir_all(logs_root_dir.clone()).unwrap();
@@ -190,10 +169,10 @@ pub fn set_game_status, &Downloada
meta: DownloadableMetadata,
setter: F,
) {
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
setter(&mut db_handle, &meta);
drop(db_handle);
- save_db();
+ DB.save().unwrap();
let status = GameStatusManager::fetch_state(&meta.id);
@@ -208,49 +187,16 @@ fn handle_invalid_database(
let new_path = {
let time = Utc::now().timestamp();
let mut base = db_path.clone();
- base.set_file_name(format!("drop.db.backup-{}", time));
+ base.set_file_name(format!("drop.db.backup-{}", time.to_string()));
base
};
- info!(
- "old database stored at: {}",
- new_path.to_string_lossy().to_string()
- );
+ info!("{:?}", new_path);
fs::rename(&db_path, &new_path).unwrap();
let db = Database::new(
games_base_dir.into_os_string().into_string().unwrap(),
- Some(new_path),
+ Some(new_path.into()),
);
PathDatabase::create_at_path(db_path, db).expect("Database could not be created")
}
-
-pub fn borrow_db_checked<'a>() -> RwLockReadGuard<'a, Database> {
- match DB.borrow_data() {
- Ok(data) => data,
- Err(e) => {
- error!("database borrow failed with error {}", e);
- panic!("database borrow failed with error {}", e);
- }
- }
-}
-
-pub fn borrow_db_mut_checked<'a>() -> RwLockWriteGuard<'a, Database> {
- match DB.borrow_data_mut() {
- Ok(data) => data,
- Err(e) => {
- error!("database borrow mut failed with error {}", e);
- panic!("database borrow mut failed with error {}", e);
- }
- }
-}
-
-pub fn save_db() {
- match DB.save() {
- Ok(_) => {}
- Err(e) => {
- error!("database failed to save with error {}", e);
- panic!("database failed to save with error {}", e)
- }
- }
-}
diff --git a/src-tauri/src/database/debug.rs b/src-tauri/src/database/debug.rs
index 45d2034..8547b73 100644
--- a/src-tauri/src/database/debug.rs
+++ b/src-tauri/src/database/debug.rs
@@ -1,3 +1,4 @@
+use crate::{DATA_ROOT_DIR, DB};
use serde::Serialize;
#[derive(Serialize)]
diff --git a/src-tauri/src/download_manager/commands.rs b/src-tauri/src/download_manager/commands.rs
index 0a65c0d..aa69aae 100644
--- a/src-tauri/src/download_manager/commands.rs
+++ b/src-tauri/src/download_manager/commands.rs
@@ -1,4 +1,4 @@
-use std::sync::Mutex;
+use std::sync::{mpsc::SendError, Arc, Mutex};
use crate::{download_manager::downloadable_metadata::DownloadableMetadata, AppState};
diff --git a/src-tauri/src/download_manager/download_manager.rs b/src-tauri/src/download_manager/download_manager.rs
index b3dee68..8e5474a 100644
--- a/src-tauri/src/download_manager/download_manager.rs
+++ b/src-tauri/src/download_manager/download_manager.rs
@@ -4,12 +4,12 @@ use std::{
fmt::Debug,
sync::{
mpsc::{SendError, Sender},
- Mutex, MutexGuard,
+ MutexGuard,
},
thread::JoinHandle,
};
-use log::{debug, info};
+use log::info;
use serde::Serialize;
use crate::error::application_download_error::ApplicationDownloadError;
@@ -84,7 +84,7 @@ pub enum DownloadStatus {
/// which provides raw access to the underlying queue.
/// THIS EDITING IS BLOCKING!!!
pub struct DownloadManager {
- terminator: Mutex>>>,
+ terminator: JoinHandle>,
download_queue: Queue,
progress: CurrentProgressObject,
command_sender: Sender,
@@ -99,7 +99,7 @@ impl DownloadManager {
command_sender: Sender,
) -> Self {
Self {
- terminator: Mutex::new(Some(terminator)),
+ terminator,
download_queue,
progress,
command_sender,
@@ -110,7 +110,7 @@ impl DownloadManager {
&self,
download: DownloadAgent,
) -> Result<(), SendError> {
- info!("creating download with meta {:?}", download.metadata());
+ info!("Adding download id {:?}", download.metadata());
self.command_sender
.send(DownloadManagerSignal::Queue(download))?;
self.command_sender.send(DownloadManagerSignal::Go)
@@ -151,14 +151,12 @@ impl DownloadManager {
.unwrap();
}
- debug!(
- "moving download at index {} to index {}",
- current_index, new_index
- );
+ info!("moving {} to {}", current_index, new_index);
let mut queue = self.edit();
let to_move = queue.remove(current_index).unwrap();
queue.insert(new_index, to_move);
+ info!("new queue: {:?}", queue);
drop(queue);
if needs_pause {
@@ -167,10 +165,6 @@ impl DownloadManager {
self.command_sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
- self.command_sender
- .send(DownloadManagerSignal::Go)
- .unwrap();
-
}
pub fn pause_downloads(&self) {
self.command_sender
@@ -180,12 +174,11 @@ impl DownloadManager {
pub fn resume_downloads(&self) {
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
}
- pub fn ensure_terminated(&self) -> Result, Box> {
+ pub fn ensure_terminated(self) -> Result, Box> {
self.command_sender
.send(DownloadManagerSignal::Finish)
.unwrap();
- let terminator = self.terminator.lock().unwrap().take();
- terminator.unwrap().join()
+ self.terminator.join()
}
pub fn uninstall_application(&self, meta: DownloadableMetadata) {
self.command_sender
diff --git a/src-tauri/src/download_manager/download_manager_builder.rs b/src-tauri/src/download_manager/download_manager_builder.rs
index dd43dc2..4738730 100644
--- a/src-tauri/src/download_manager/download_manager_builder.rs
+++ b/src-tauri/src/download_manager/download_manager_builder.rs
@@ -7,7 +7,7 @@ use std::{
thread::{spawn, JoinHandle},
};
-use log::{debug, error, info, warn};
+use log::{debug, error, info};
use tauri::{AppHandle, Emitter};
use crate::{
@@ -179,13 +179,13 @@ impl DownloadManagerBuilder {
}
}
fn manage_queue_signal(&mut self, download_agent: DownloadAgent) {
- debug!("got signal Queue");
+ info!("Got signal Queue");
let meta = download_agent.metadata();
- debug!("queue metadata: {:?}", meta);
+ info!("Meta: {:?}", meta);
if self.download_queue.exists(meta.clone()) {
- warn!("download with same ID already exists");
+ info!("Download with same ID already exists");
return;
}
@@ -199,9 +199,9 @@ impl DownloadManagerBuilder {
}
fn manage_go_signal(&mut self) {
- debug!("got signal Go");
+ info!("Got signal Go");
if self.download_agent_registry.is_empty() {
- debug!(
+ info!(
"Download agent registry: {:?}",
self.download_agent_registry.len()
);
@@ -209,21 +209,19 @@ impl DownloadManagerBuilder {
}
if self.current_download_agent.is_some() {
- if self.download_queue.read().front().unwrap() == &self.current_download_agent.as_ref().unwrap().metadata() {
- debug!(
- "Current download agent: {:?}",
- self.current_download_agent.as_ref().unwrap().metadata()
- );
- return;
- }
+ info!(
+ "Current download agent: {:?}",
+ self.current_download_agent.as_ref().unwrap().metadata()
+ );
+ return;
}
- debug!("current download queue: {:?}", self.download_queue.read());
+ info!("Current download queue: {:?}", self.download_queue.read());
// Should always be Some if the above two statements keep going
let agent_data = self.download_queue.read().front().unwrap().clone();
- info!("starting download for {:?}", agent_data);
+ info!("Starting download for {:?}", agent_data);
let download_agent = self
.download_agent_registry
@@ -267,7 +265,7 @@ impl DownloadManagerBuilder {
active_control_flag.set(DownloadThreadControlFlag::Go);
}
fn manage_stop_signal(&mut self) {
- debug!("got signal Stop");
+ info!("Got signal Stop");
if let Some(active_control_flag) = self.active_control_flag.clone() {
self.set_status(DownloadManagerStatus::Paused);
@@ -275,9 +273,10 @@ impl DownloadManagerBuilder {
}
}
fn manage_completed_signal(&mut self, meta: DownloadableMetadata) {
- debug!("got signal Completed");
+ info!("Got signal Completed");
if let Some(interface) = &self.current_download_agent {
if interface.metadata() == meta {
+ info!("Popping consumed data");
self.remove_and_cleanup_front_download(&meta);
}
}
@@ -285,17 +284,18 @@ impl DownloadManagerBuilder {
self.sender.send(DownloadManagerSignal::Go).unwrap();
}
fn manage_error_signal(&mut self, error: ApplicationDownloadError) {
- debug!("got signal Error");
- if let Some(current_agent) = self.current_download_agent.clone() {
- current_agent.on_error(&self.app_handle, error.clone());
+ info!("Got signal Error");
+ let current_agent = self.current_download_agent.clone().unwrap();
+
+ current_agent.on_error(&self.app_handle, error.clone());
+
+ self.stop_and_wait_current_download();
+ self.remove_and_cleanup_front_download(¤t_agent.metadata());
- self.stop_and_wait_current_download();
- self.remove_and_cleanup_front_download(¤t_agent.metadata());
- }
self.set_status(DownloadManagerStatus::Error(error));
}
fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) {
- debug!("got signal Cancel");
+ info!("Got signal Cancel");
if let Some(current_download) = &self.current_download_agent {
if ¤t_download.metadata() == meta {
@@ -306,30 +306,32 @@ impl DownloadManagerBuilder {
self.download_queue.pop_front();
self.cleanup_current_download();
- debug!("current download queue: {:?}", self.download_queue.read());
+ info!("Current donwload queue: {:?}", self.download_queue.read());
}
// TODO: Collapse these two into a single if statement somehow
else if let Some(download_agent) = self.download_agent_registry.get(meta) {
+ info!("Object exists in registry");
let index = self.download_queue.get_by_meta(meta);
if let Some(index) = index {
download_agent.on_cancelled(&self.app_handle);
let _ = self.download_queue.edit().remove(index).unwrap();
let removed = self.download_agent_registry.remove(meta);
- debug!(
- "removed {:?} from queue {:?}",
+ info!(
+ "Removed {:?} from queue {:?}",
removed.map(|x| x.metadata()),
self.download_queue.read()
);
}
}
} else if let Some(download_agent) = self.download_agent_registry.get(meta) {
+ info!("Object exists in registry");
let index = self.download_queue.get_by_meta(meta);
if let Some(index) = index {
download_agent.on_cancelled(&self.app_handle);
let _ = self.download_queue.edit().remove(index).unwrap();
let removed = self.download_agent_registry.remove(meta);
- debug!(
- "removed {:?} from queue {:?}",
+ info!(
+ "Removed {:?} from queue {:?}",
removed.map(|x| x.metadata()),
self.download_queue.read()
);
@@ -352,8 +354,6 @@ impl DownloadManagerBuilder {
meta: DownloadableMetadata::clone(key),
status: val.status(),
progress: val.progress().get_progress(),
- current: val.progress().sum(),
- max: val.progress().get_max(),
}
})
.collect();
diff --git a/src-tauri/src/download_manager/internal_error.rs b/src-tauri/src/download_manager/internal_error.rs
deleted file mode 100644
index 4864599..0000000
--- a/src-tauri/src/download_manager/internal_error.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use std::{fmt::Display, io, sync::mpsc::SendError};
-
-use serde_with::SerializeDisplay;
-
-#[derive(SerializeDisplay)]
-pub enum InternalError {
- IOError(io::Error),
- SignalError(SendError),
-}
-impl Display for InternalError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- InternalError::IOError(error) => write!(f, "{}", error),
- InternalError::SignalError(send_error) => write!(f, "{}", send_error),
- }
- }
-}
-impl From> for InternalError {
- fn from(value: SendError) -> Self {
- InternalError::SignalError(value)
- }
-}
-impl From for InternalError {
- fn from(value: io::Error) -> Self {
- InternalError::IOError(value)
- }
-}
diff --git a/src-tauri/src/download_manager/mod.rs b/src-tauri/src/download_manager/mod.rs
index 0bac198..0d664dd 100644
--- a/src-tauri/src/download_manager/mod.rs
+++ b/src-tauri/src/download_manager/mod.rs
@@ -4,7 +4,5 @@ pub mod download_manager_builder;
pub mod download_thread_control_flag;
pub mod downloadable;
pub mod downloadable_metadata;
-pub mod internal_error;
pub mod progress_object;
pub mod queue;
-pub mod rolling_progress_updates;
diff --git a/src-tauri/src/download_manager/progress_object.rs b/src-tauri/src/download_manager/progress_object.rs
index fc0907b..5d65207 100644
--- a/src-tauri/src/download_manager/progress_object.rs
+++ b/src-tauri/src/download_manager/progress_object.rs
@@ -2,17 +2,15 @@ use std::{
sync::{
atomic::{AtomicUsize, Ordering},
mpsc::Sender,
- Arc, Mutex,
+ Arc, Mutex, RwLock,
},
time::{Duration, Instant},
};
-use atomic_instant_full::AtomicInstant;
+use log::info;
use throttle_my_fn::throttle;
-use super::{
- download_manager::DownloadManagerSignal, rolling_progress_updates::RollingProgressWindow,
-};
+use super::download_manager::DownloadManagerSignal;
#[derive(Clone)]
pub struct ProgressObject {
@@ -20,10 +18,10 @@ pub struct ProgressObject {
progress_instances: Arc>>>,
start: Arc>,
sender: Sender,
- //last_update: Arc>,
- last_update_time: Arc,
- bytes_last_update: Arc,
- rolling: RollingProgressWindow<250>,
+ points_towards_update: Arc,
+ points_to_push_update: Arc,
+ last_update: Arc>,
+ amount_last_update: Arc,
}
pub struct ProgressHandle {
@@ -44,32 +42,67 @@ impl ProgressHandle {
pub fn add(&self, amount: usize) {
self.progress
.fetch_add(amount, std::sync::atomic::Ordering::Relaxed);
- calculate_update(&self.progress_object);
- }
- pub fn skip(&self, amount: usize) {
- self.progress
- .fetch_add(amount, std::sync::atomic::Ordering::Relaxed);
- // Offset the bytes at last offset by this amount
- self.progress_object
- .bytes_last_update
- .fetch_add(amount, Ordering::Relaxed);
- // Dont' fire update
+ self.progress_object.check_push_update(amount);
}
}
+static PROGRESS_UPDATES: usize = 100;
+
impl ProgressObject {
pub fn new(max: usize, length: usize, sender: Sender) -> Self {
let arr = Mutex::new((0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect());
// TODO: consolidate this calculation with the set_max function below
+ let points_to_push_update = max / PROGRESS_UPDATES;
Self {
max: Arc::new(Mutex::new(max)),
progress_instances: Arc::new(arr),
start: Arc::new(Mutex::new(Instant::now())),
sender,
- last_update_time: Arc::new(AtomicInstant::now()),
- bytes_last_update: Arc::new(AtomicUsize::new(0)),
- rolling: RollingProgressWindow::new(),
+ points_towards_update: Arc::new(AtomicUsize::new(0)),
+ points_to_push_update: Arc::new(AtomicUsize::new(points_to_push_update)),
+ last_update: Arc::new(RwLock::new(Instant::now())),
+ amount_last_update: Arc::new(AtomicUsize::new(0)),
+ }
+ }
+
+ pub fn check_push_update(&self, amount_added: usize) {
+ let current_amount = self
+ .points_towards_update
+ .fetch_add(amount_added, Ordering::Relaxed);
+
+ let to_update = self.points_to_push_update.fetch_add(0, Ordering::Relaxed);
+
+ if current_amount >= to_update {
+ self.points_towards_update
+ .fetch_sub(to_update, Ordering::Relaxed);
+ update_queue(&self);
+ }
+
+ let last_update = self.last_update.read().unwrap();
+ let last_update_difference = Instant::now().duration_since(*last_update).as_millis();
+ if last_update_difference > 1000 {
+ // push update
+ drop(last_update);
+ let mut last_update = self.last_update.write().unwrap();
+ *last_update = Instant::now();
+ drop(last_update);
+
+ let current_amount = self.sum();
+ let max = self.get_max();
+ let amount_at_last_update = self.amount_last_update.fetch_add(0, Ordering::Relaxed);
+ self.amount_last_update
+ .store(current_amount, Ordering::Relaxed);
+
+ let amount_since_last_update = current_amount - amount_at_last_update;
+
+ let kilobytes_per_second =
+ amount_since_last_update / (last_update_difference as usize).max(1);
+
+ let remaining = max - current_amount; // bytes
+ let time_remaining = (remaining / 1000) / kilobytes_per_second.max(1);
+
+ update_ui(&self, kilobytes_per_second, time_remaining);
}
}
@@ -89,6 +122,9 @@ impl ProgressObject {
}
pub fn set_max(&self, new_max: usize) {
*self.max.lock().unwrap() = new_max;
+ self.points_to_push_update
+ .store(new_max / PROGRESS_UPDATES, Ordering::Relaxed);
+ info!("points to push update: {}", new_max / PROGRESS_UPDATES);
}
pub fn set_size(&self, length: usize) {
*self.progress_instances.lock().unwrap() =
@@ -100,43 +136,9 @@ impl ProgressObject {
pub fn get(&self, index: usize) -> Arc {
self.progress_instances.lock().unwrap()[index].clone()
}
- fn update_window(&self, kilobytes_per_second: usize) {
- self.rolling.update(kilobytes_per_second);
- }
-}
-
-#[throttle(1, Duration::from_millis(20))]
-pub fn calculate_update(progress: &ProgressObject) {
- let last_update_time = progress
- .last_update_time
- .swap(Instant::now(), Ordering::SeqCst);
- let time_since_last_update = Instant::now().duration_since(last_update_time).as_millis();
-
- let current_bytes_downloaded = progress.sum();
- let max = progress.get_max();
- let bytes_at_last_update = progress
- .bytes_last_update
- .swap(current_bytes_downloaded, Ordering::Relaxed);
-
- let bytes_since_last_update = current_bytes_downloaded - bytes_at_last_update;
-
- let kilobytes_per_second = bytes_since_last_update / (time_since_last_update as usize).max(1);
-
- let bytes_remaining = max - current_bytes_downloaded; // bytes
-
- progress.update_window(kilobytes_per_second);
- push_update(progress, bytes_remaining);
-}
-
-#[throttle(1, Duration::from_millis(500))]
-pub fn push_update(progress: &ProgressObject, bytes_remaining: usize) {
- let average_speed = progress.rolling.get_average();
- let time_remaining = (bytes_remaining / 1000) / average_speed.max(1);
-
- update_ui(progress, average_speed, time_remaining);
- update_queue(progress);
}
+#[throttle(50, Duration::from_secs(1))]
fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time_remaining: usize) {
progress_object
.sender
@@ -147,9 +149,11 @@ fn update_ui(progress_object: &ProgressObject, kilobytes_per_second: usize, time
.unwrap();
}
+#[throttle(50, Duration::from_secs(1))]
fn update_queue(progress: &ProgressObject) {
progress
.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
+
diff --git a/src-tauri/src/download_manager/queue.rs b/src-tauri/src/download_manager/queue.rs
index f3e9493..8025c91 100644
--- a/src-tauri/src/download_manager/queue.rs
+++ b/src-tauri/src/download_manager/queue.rs
@@ -32,7 +32,7 @@ impl Queue {
pub fn pop_front(&self) -> Option {
self.edit().pop_front()
}
- pub fn is_empty(&self) -> bool {
+ pub fn empty(&self) -> bool {
self.inner.lock().unwrap().len() == 0
}
pub fn exists(&self, meta: DownloadableMetadata) -> bool {
@@ -52,7 +52,10 @@ impl Queue {
}
pub fn pop_front_if_equal(&self, meta: &DownloadableMetadata) -> Option {
let mut queue = self.edit();
- let front = queue.front()?;
+ let front = match queue.front() {
+ Some(front) => front,
+ None => return None,
+ };
if front == meta {
return queue.pop_front();
}
diff --git a/src-tauri/src/download_manager/rolling_progress_updates.rs b/src-tauri/src/download_manager/rolling_progress_updates.rs
deleted file mode 100644
index 2239b9a..0000000
--- a/src-tauri/src/download_manager/rolling_progress_updates.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use std::sync::{
- atomic::{AtomicUsize, Ordering},
- Arc,
-};
-
-#[derive(Clone)]
-pub struct RollingProgressWindow {
- window: Arc<[AtomicUsize; S]>,
- current: Arc,
-}
-impl RollingProgressWindow {
- pub fn new() -> Self {
- Self {
- window: Arc::new([(); S].map(|_| AtomicUsize::new(0))),
- current: Arc::new(AtomicUsize::new(0)),
- }
- }
- pub fn update(&self, kilobytes_per_second: usize) {
- let index = self.current.fetch_add(1, Ordering::SeqCst);
- let current = &self.window[index % S];
- current.store(kilobytes_per_second, Ordering::SeqCst);
- }
- pub fn get_average(&self) -> usize {
- let current = self.current.load(Ordering::SeqCst);
- self.window
- .iter()
- .enumerate()
- .filter(|(i, _)| i < ¤t)
- .map(|(_, x)| x.load(Ordering::Relaxed))
- .sum::()
- / S
- }
-}
diff --git a/src-tauri/src/error/application_download_error.rs b/src-tauri/src/error/application_download_error.rs
index d68bd71..fb6035a 100644
--- a/src-tauri/src/error/application_download_error.rs
+++ b/src-tauri/src/error/application_download_error.rs
@@ -3,12 +3,10 @@ use std::{
io,
};
-use serde_with::SerializeDisplay;
-
use super::{remote_access_error::RemoteAccessError, setup_error::SetupError};
// TODO: Rename / separate from downloads
-#[derive(Debug, Clone, SerializeDisplay)]
+#[derive(Debug, Clone)]
pub enum ApplicationDownloadError {
Communication(RemoteAccessError),
Checksum,
@@ -22,11 +20,11 @@ impl Display for ApplicationDownloadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ApplicationDownloadError::Communication(error) => write!(f, "{}", error),
- ApplicationDownloadError::Setup(error) => write!(f, "an error occurred while setting up the download: {}", error),
- ApplicationDownloadError::Lock => write!(f, "failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
- ApplicationDownloadError::Checksum => write!(f, "checksum failed to validate for download"),
+ ApplicationDownloadError::Setup(error) => write!(f, "An error occurred while setting up the download: {}", error),
+ ApplicationDownloadError::Lock => write!(f, "Failed to acquire lock. Something has gone very wrong internally. Please restart the application"),
+ ApplicationDownloadError::Checksum => write!(f, "Checksum failed to validate for download"),
ApplicationDownloadError::IoError(error) => write!(f, "{}", error),
- ApplicationDownloadError::DownloadError => write!(f, "download failed. See Download Manager status for specific error"),
+ ApplicationDownloadError::DownloadError => write!(f, "Download failed. See Download Manager status for specific error"),
}
}
}
diff --git a/src-tauri/src/error/library_error.rs b/src-tauri/src/error/library_error.rs
index c13dd23..4957ae9 100644
--- a/src-tauri/src/error/library_error.rs
+++ b/src-tauri/src/error/library_error.rs
@@ -1,8 +1,5 @@
use std::fmt::Display;
-use serde_with::SerializeDisplay;
-
-#[derive(SerializeDisplay)]
pub enum LibraryError {
MetaNotFound(String),
}
diff --git a/src-tauri/src/error/mod.rs b/src-tauri/src/error/mod.rs
index 89b74ae..a38a696 100644
--- a/src-tauri/src/error/mod.rs
+++ b/src-tauri/src/error/mod.rs
@@ -4,3 +4,4 @@ pub mod library_error;
pub mod process_error;
pub mod remote_access_error;
pub mod setup_error;
+pub mod user_error;
diff --git a/src-tauri/src/error/process_error.rs b/src-tauri/src/error/process_error.rs
index 8afc9dc..8a105a7 100644
--- a/src-tauri/src/error/process_error.rs
+++ b/src-tauri/src/error/process_error.rs
@@ -1,8 +1,5 @@
use std::{fmt::Display, io::Error};
-use serde_with::SerializeDisplay;
-
-#[derive(SerializeDisplay)]
pub enum ProcessError {
SetupRequired,
NotInstalled,
diff --git a/src-tauri/src/error/remote_access_error.rs b/src-tauri/src/error/remote_access_error.rs
index 32572b2..40740b2 100644
--- a/src-tauri/src/error/remote_access_error.rs
+++ b/src-tauri/src/error/remote_access_error.rs
@@ -5,12 +5,11 @@ use std::{
};
use http::StatusCode;
-use serde_with::SerializeDisplay;
use url::ParseError;
use super::drop_server_error::DropServerError;
-#[derive(Debug, Clone, SerializeDisplay)]
+#[derive(Debug, Clone)]
pub enum RemoteAccessError {
FetchError(Arc),
ParsingError(ParseError),
@@ -40,17 +39,17 @@ impl Display for RemoteAccessError {
RemoteAccessError::ParsingError(parse_error) => {
write!(f, "{}", parse_error)
}
- RemoteAccessError::InvalidEndpoint => write!(f, "invalid drop endpoint"),
- RemoteAccessError::HandshakeFailed(message) => write!(f, "failed to complete handshake: {}", message),
- RemoteAccessError::GameNotFound => write!(f, "could not find game on server"),
- RemoteAccessError::InvalidResponse(error) => write!(f, "server returned an invalid response: {} {}", error.status_code, error.status_message),
- RemoteAccessError::InvalidRedirect => write!(f, "server redirect was invalid"),
+ RemoteAccessError::InvalidEndpoint => write!(f, "Invalid drop endpoint"),
+ RemoteAccessError::HandshakeFailed(message) => write!(f, "Failed to complete handshake: {}", message),
+ RemoteAccessError::GameNotFound => write!(f, "Could not find game on server"),
+ RemoteAccessError::InvalidResponse(error) => write!(f, "Server returned an invalid response: {} {}", error.status_code, error.status_message),
+ RemoteAccessError::InvalidRedirect => write!(f, "Server redirect was invalid"),
RemoteAccessError::ManifestDownloadFailed(status, response) => write!(
f,
- "failed to download game manifest: {} {}",
+ "Failed to download game manifest: {} {}",
status, response
),
- RemoteAccessError::OutOfSync => write!(f, "server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other"),
+ RemoteAccessError::OutOfSync => write!(f, "Server's and client's time are out of sync. Please ensure they are within at least 30 seconds of each other."),
RemoteAccessError::Generic(message) => write!(f, "{}", message),
}
}
diff --git a/src-tauri/src/error/setup_error.rs b/src-tauri/src/error/setup_error.rs
index bd76ce5..bae7fea 100644
--- a/src-tauri/src/error/setup_error.rs
+++ b/src-tauri/src/error/setup_error.rs
@@ -8,7 +8,7 @@ pub enum SetupError {
impl Display for SetupError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
- SetupError::Context => write!(f, "failed to generate contexts for download"),
+ SetupError::Context => write!(f, "Failed to generate contexts for download"),
}
}
}
diff --git a/src-tauri/src/error/user_error.rs b/src-tauri/src/error/user_error.rs
new file mode 100644
index 0000000..bb019fc
--- /dev/null
+++ b/src-tauri/src/error/user_error.rs
@@ -0,0 +1,66 @@
+use std::{
+ fmt::Display,
+ ops::{FromResidual, Try},
+};
+
+use serde::Serialize;
+
+pub enum UserValue
+where
+ T: Serialize,
+ D: Display,
+{
+ Ok(T),
+ Err(D),
+}
+impl Serialize for UserValue {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: serde::Serializer,
+ {
+ match self {
+ UserValue::Ok(data) => data.serialize(serializer),
+ UserValue::Err(err) => serializer.serialize_str(err.to_string().as_ref()),
+ }
+ }
+}
+
+impl From> for UserValue {
+ fn from(value: Result) -> Self {
+ match value {
+ Ok(data) => UserValue::Ok(data),
+ Err(data) => UserValue::Err(data),
+ }
+ }
+}
+
+impl Try for UserValue {
+ type Output = T;
+
+ type Residual = D;
+
+ fn from_output(output: Self::Output) -> Self {
+ Self::Ok(output)
+ }
+
+ fn branch(self) -> std::ops::ControlFlow {
+ match self {
+ UserValue::Ok(data) => std::ops::ControlFlow::Continue(data),
+ UserValue::Err(e) => std::ops::ControlFlow::Break(e),
+ }
+ }
+}
+impl FromResidual for UserValue {
+ fn from_residual(residual: ::Residual) -> Self {
+ UserValue::Err(residual)
+ }
+}
+impl FromResidual> for UserValue {
+ fn from_residual(residual: Result) -> Self {
+ match residual {
+ Ok(_) => unreachable!(),
+ Err(e) => UserValue::Err(e),
+ }
+ }
+}
+
diff --git a/src-tauri/src/games/commands.rs b/src-tauri/src/games/commands.rs
index 8f1d652..beec0bc 100644
--- a/src-tauri/src/games/commands.rs
+++ b/src-tauri/src/games/commands.rs
@@ -3,28 +3,32 @@ use std::sync::Mutex;
use tauri::AppHandle;
use crate::{
- database::db::GameVersion, error::{library_error::LibraryError, remote_access_error::RemoteAccessError}, games::library::{get_current_meta, uninstall_game_logic}, AppState
+ error::{
+ library_error::LibraryError, remote_access_error::RemoteAccessError, user_error::UserValue,
+ },
+ games::library::{get_current_meta, uninstall_game_logic},
+ AppState,
};
use super::{
library::{
fetch_game_logic, fetch_game_verion_options_logic, fetch_library_logic, FetchGameStruct,
- Game,
+ Game, GameVersionOption,
},
state::{GameStatusManager, GameStatusWithTransient},
};
#[tauri::command]
-pub fn fetch_library(app: AppHandle) -> Result, RemoteAccessError> {
- fetch_library_logic(app)
+pub fn fetch_library(app: AppHandle) -> UserValue, RemoteAccessError> {
+ fetch_library_logic(app).into()
}
#[tauri::command]
pub fn fetch_game(
game_id: String,
app: tauri::AppHandle,
-) -> Result {
- fetch_game_logic(game_id, app)
+) -> UserValue {
+ fetch_game_logic(game_id, app).into()
}
#[tauri::command]
@@ -33,21 +37,21 @@ pub fn fetch_game_status(id: String) -> GameStatusWithTransient {
}
#[tauri::command]
-pub fn uninstall_game(game_id: String, app_handle: AppHandle) -> Result<(), LibraryError> {
+pub fn uninstall_game(game_id: String, app_handle: AppHandle) -> UserValue<(), LibraryError> {
let meta = match get_current_meta(&game_id) {
Some(data) => data,
- None => return Err(LibraryError::MetaNotFound(game_id)),
+ None => return UserValue::Err(LibraryError::MetaNotFound(game_id)),
};
println!("{:?}", meta);
uninstall_game_logic(meta, &app_handle);
- Ok(())
+ UserValue::Ok(())
}
#[tauri::command]
pub fn fetch_game_verion_options(
game_id: String,
state: tauri::State<'_, Mutex>,
-) -> Result, RemoteAccessError> {
- fetch_game_verion_options_logic(game_id, state)
+) -> UserValue, RemoteAccessError> {
+ fetch_game_verion_options_logic(game_id, state).into()
}
diff --git a/src-tauri/src/games/downloads/commands.rs b/src-tauri/src/games/downloads/commands.rs
index 67b1359..d3c604a 100644
--- a/src-tauri/src/games/downloads/commands.rs
+++ b/src-tauri/src/games/downloads/commands.rs
@@ -1,10 +1,8 @@
-use std::sync::{Arc, Mutex};
+use std::sync::{mpsc::SendError, Arc, Mutex};
use crate::{
- download_manager::{
- download_manager::DownloadManagerSignal, downloadable::Downloadable,
- internal_error::InternalError,
- },
+ download_manager::{download_manager::DownloadManagerSignal, downloadable::Downloadable},
+ error::user_error::UserValue,
AppState,
};
@@ -16,7 +14,7 @@ pub fn download_game(
game_version: String,
install_dir: usize,
state: tauri::State<'_, Mutex>,
-) -> Result<(), InternalError> {
+) -> UserValue<(), SendError> {
let sender = state.lock().unwrap().download_manager.get_sender();
let game_download_agent = Arc::new(Box::new(GameDownloadAgent::new(
game_id,
@@ -24,9 +22,10 @@ pub fn download_game(
install_dir,
sender,
)) as Box);
- Ok(state
+ state
.lock()
.unwrap()
.download_manager
- .queue_download(game_download_agent)?)
+ .queue_download(game_download_agent)
+ .into()
}
diff --git a/src-tauri/src/games/downloads/download_agent.rs b/src-tauri/src/games/downloads/download_agent.rs
index d4fb1cd..a9da4c3 100644
--- a/src-tauri/src/games/downloads/download_agent.rs
+++ b/src-tauri/src/games/downloads/download_agent.rs
@@ -1,7 +1,6 @@
use crate::auth::generate_authorization_header;
use crate::database::db::{
- borrow_db_checked, set_game_status, ApplicationTransientStatus, DatabaseImpls,
- GameDownloadStatus,
+ set_game_status, ApplicationTransientStatus, DatabaseImpls, GameDownloadStatus,
};
use crate::download_manager::download_manager::{DownloadManagerSignal, DownloadStatus};
use crate::download_manager::download_thread_control_flag::{
@@ -14,11 +13,11 @@ use crate::error::application_download_error::ApplicationDownloadError;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::downloads::manifest::{DropDownloadContext, DropManifest};
use crate::games::library::{on_game_complete, push_game_update, GameUpdateEvent};
-use crate::remote::requests::make_request;
use crate::DB;
use log::{debug, error, info};
use rayon::ThreadPoolBuilder;
use slice_deque::SliceDeque;
+use std::collections::HashMap;
use std::fs::{create_dir_all, File};
use std::path::Path;
use std::sync::mpsc::Sender;
@@ -56,7 +55,7 @@ impl GameDownloadAgent {
// Don't run by default
let control_flag = DownloadThreadControl::new(DownloadThreadControlFlag::Stop);
- let db_lock = borrow_db_checked();
+ let db_lock = DB.borrow_data().unwrap();
let base_dir = db_lock.applications.install_dirs[target_download_dir].clone();
drop(db_lock);
@@ -83,8 +82,10 @@ impl GameDownloadAgent {
// Blocking
pub fn setup_download(&self) -> Result<(), ApplicationDownloadError> {
self.ensure_manifest_exists()?;
+ info!("Ensured manifest exists");
self.ensure_contexts()?;
+ info!("Ensured contexts exists");
self.control_flag.set(DownloadThreadControlFlag::Go);
@@ -93,8 +94,11 @@ impl GameDownloadAgent {
// Blocking
pub fn download(&self, app_handle: &AppHandle) -> Result {
+ info!("Setting up download");
self.setup_download()?;
+ info!("Setting progress object params");
self.set_progress_object_params();
+ info!("Running");
let timer = Instant::now();
push_game_update(
app_handle,
@@ -110,7 +114,7 @@ impl GameDownloadAgent {
.run()
.map_err(|_| ApplicationDownloadError::DownloadError);
- debug!(
+ info!(
"{} took {}ms to download",
self.id,
timer.elapsed().as_millis()
@@ -127,17 +131,25 @@ impl GameDownloadAgent {
}
fn download_manifest(&self) -> Result<(), ApplicationDownloadError> {
+ let base_url = DB.fetch_base_url();
+ let manifest_url = base_url
+ .join(
+ format!(
+ "/api/v1/client/game/manifest?id={}&version={}",
+ self.id,
+ encode(&self.version)
+ )
+ .as_str(),
+ )
+ .unwrap();
+
let header = generate_authorization_header();
let client = reqwest::blocking::Client::new();
- let response = make_request(
- &client,
- &["/api/v1/client/game/manifest"],
- &[("id", &self.id), ("version", &self.version)],
- |f| f.header("Authorization", header),
- )
- .map_err(|e| ApplicationDownloadError::Communication(e))?
- .send()
- .map_err(|e| ApplicationDownloadError::Communication(e.into()))?;
+ let response = client
+ .get(manifest_url.to_string())
+ .header("Authorization", header)
+ .send()
+ .unwrap();
if response.status() != 200 {
return Err(ApplicationDownloadError::Communication(
@@ -170,8 +182,11 @@ impl GameDownloadAgent {
let chunk_count = contexts.iter().map(|chunk| chunk.length).sum();
+ debug!("Setting ProgressObject max to {}", chunk_count);
self.progress.set_max(chunk_count);
+ debug!("Setting ProgressObject size to {}", length);
self.progress.set_size(length);
+ debug!("Setting ProgressObject time to now");
self.progress.set_time_now();
}
@@ -235,9 +250,9 @@ impl GameDownloadAgent {
// TODO: Change return value on Err
pub fn run(&self) -> Result {
- let max_download_threads = borrow_db_checked().settings.max_download_threads;
+ let max_download_threads = DB.borrow_data().unwrap().settings.max_download_threads;
- debug!(
+ info!(
"downloading game: {} with {} threads",
self.id, max_download_threads
);
@@ -249,6 +264,8 @@ impl GameDownloadAgent {
let completed_indexes = Arc::new(boxcar::Vec::new());
let completed_indexes_loop_arc = completed_indexes.clone();
+ let base_url = DB.fetch_base_url();
+
let contexts = self.contexts.lock().unwrap();
pool.scope(|scope| {
let client = &reqwest::blocking::Client::new();
@@ -258,32 +275,15 @@ impl GameDownloadAgent {
let progress = self.progress.get(index);
let progress_handle = ProgressHandle::new(progress, self.progress.clone());
-
// If we've done this one already, skip it
if self.completed_contexts.lock().unwrap().contains(&index) {
- progress_handle.skip(context.length);
+ progress_handle.add(context.length);
continue;
}
let sender = self.sender.clone();
- let request = match make_request(
- &client,
- &["/api/v1/client/chunk"],
- &[
- ("id", &context.game_id),
- ("version", &context.version),
- ("name", &context.file_name),
- ("chunk", &context.index.to_string()),
- ],
- |r| r.header("Authorization", generate_authorization_header()),
- ) {
- Ok(request) => request,
- Err(e) => {
- sender.send(DownloadManagerSignal::Error(ApplicationDownloadError::Communication(e))).unwrap();
- continue;
- },
- };
+ let request = generate_request(&base_url, client, context);
scope.spawn(move |_| {
match download_game_chunk(context, &self.control_flag, progress_handle, request)
@@ -298,6 +298,7 @@ impl GameDownloadAgent {
sender.send(DownloadManagerSignal::Error(e)).unwrap();
}
}
+ info!("Completed context id {}", index);
});
}
});
@@ -306,27 +307,28 @@ impl GameDownloadAgent {
let completed_lock_len = {
let mut completed_contexts_lock = self.completed_contexts.lock().unwrap();
- for (_, item) in newly_completed.iter() {
- completed_contexts_lock.push_front(*item);
+ for (item, _) in newly_completed.iter() {
+ completed_contexts_lock.push_front(item);
}
completed_contexts_lock.len()
};
+ info!("Got newly completed");
+
// If we're not out of contexts, we're not done, so we don't fire completed
if completed_lock_len != contexts.len() {
- info!(
- "download agent for {} exited without completing ({}/{})",
- self.id.clone(),
- completed_lock_len,
- contexts.len(),
- );
+ info!("da for {} exited without completing", self.id.clone());
self.stored_manifest
.set_completed_contexts(self.completed_contexts.lock().unwrap().as_slice());
+ info!("Setting completed contexts");
self.stored_manifest.write();
+ info!("Wrote completed contexts");
return Ok(false);
}
+ info!("Sending completed signal");
+
// We've completed
self.sender
.send(DownloadManagerSignal::Completed(self.metadata()))
@@ -336,6 +338,27 @@ impl GameDownloadAgent {
}
}
+fn generate_request(
+ base_url: &url::Url,
+ client: reqwest::blocking::Client,
+ context: &DropDownloadContext,
+) -> reqwest::blocking::RequestBuilder {
+ let chunk_url = base_url
+ .join(&format!(
+ "/api/v1/client/chunk?id={}&version={}&name={}&chunk={}",
+ // Encode the parts we don't trust
+ context.game_id,
+ encode(&context.version),
+ encode(&context.file_name),
+ context.index
+ ))
+ .unwrap();
+
+ let header = generate_authorization_header();
+
+ client.get(chunk_url).header("Authorization", header)
+}
+
impl Downloadable for GameDownloadAgent {
fn download(&self, app_handle: &AppHandle) -> Result {
*self.status.lock().unwrap() = DownloadStatus::Downloading;
@@ -384,7 +407,6 @@ impl Downloadable for GameDownloadAgent {
.unwrap();
}
- // TODO: fix this function. It doesn't restart the download properly, nor does it reset the state properly
fn on_incomplete(&self, app_handle: &tauri::AppHandle) {
let meta = self.metadata();
*self.status.lock().unwrap() = DownloadStatus::Queued;
diff --git a/src-tauri/src/games/downloads/download_logic.rs b/src-tauri/src/games/downloads/download_logic.rs
index 70e5b7e..87f057f 100644
--- a/src-tauri/src/games/downloads/download_logic.rs
+++ b/src-tauri/src/games/downloads/download_logic.rs
@@ -5,12 +5,12 @@ use crate::download_manager::progress_object::ProgressHandle;
use crate::error::application_download_error::ApplicationDownloadError;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::downloads::manifest::DropDownloadContext;
-use log::warn;
+use log::{error, warn};
use md5::{Context, Digest};
use reqwest::blocking::{RequestBuilder, Response};
use std::fs::{set_permissions, Permissions};
-use std::io::{ErrorKind, Read};
+use std::io::Read;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::{
@@ -39,17 +39,19 @@ impl DropWriter {
// Write automatically pushes to file and hasher
impl Write for DropWriter {
fn write(&mut self, buf: &[u8]) -> io::Result {
+ /*
self.hasher.write_all(buf).map_err(|e| {
io::Error::new(
ErrorKind::Other,
format!("Unable to write to hasher: {}", e),
)
})?;
+ */
self.destination.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
- self.hasher.flush()?;
+ // self.hasher.flush()?;
self.destination.flush()
}
}
@@ -133,6 +135,7 @@ pub fn download_game_chunk(
if response.status() != 200 {
let err = response.json().unwrap();
+ warn!("{:?}", err);
return Err(ApplicationDownloadError::Communication(
RemoteAccessError::InvalidResponse(err),
));
@@ -148,7 +151,7 @@ pub fn download_game_chunk(
let content_length = response.content_length();
if content_length.is_none() {
- warn!("recieved 0 length content from server");
+ warn!("Recieved 0 length content from server");
return Err(ApplicationDownloadError::Communication(
RemoteAccessError::InvalidResponse(response.json().unwrap()),
));
@@ -176,14 +179,16 @@ pub fn download_game_chunk(
set_permissions(ctx.path.clone(), permissions).unwrap();
}
+ /*
let checksum = pipeline
.finish()
- .map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
+ .map_err(|e| GameDownloadError::IoError(e))?;
let res = hex::encode(checksum.0);
if res != ctx.checksum {
- return Err(ApplicationDownloadError::Checksum);
+ return Err(GameDownloadError::Checksum);
}
+ */
Ok(true)
}
diff --git a/src-tauri/src/games/library.rs b/src-tauri/src/games/library.rs
index b2f2fe2..ab4cc9e 100644
--- a/src-tauri/src/games/library.rs
+++ b/src-tauri/src/games/library.rs
@@ -2,20 +2,21 @@ use std::fs::remove_dir_all;
use std::sync::Mutex;
use std::thread::spawn;
-use log::{debug, error, warn};
+use log::{error, info, warn};
use serde::{Deserialize, Serialize};
use tauri::Emitter;
use tauri::{AppHandle, Manager};
+use urlencoding::encode;
-use crate::database::db::{borrow_db_checked, borrow_db_mut_checked, save_db, GameVersion};
-use crate::database::db::{ApplicationTransientStatus, GameDownloadStatus};
+use crate::database::db::GameVersion;
+use crate::database::db::{ApplicationTransientStatus, DatabaseImpls, GameDownloadStatus};
use crate::download_manager::download_manager::DownloadStatus;
use crate::download_manager::downloadable_metadata::DownloadableMetadata;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::state::{GameStatusManager, GameStatusWithTransient};
+use crate::process::process_manager::Platform;
use crate::remote::auth::generate_authorization_header;
-use crate::remote::requests::make_request;
-use crate::AppState;
+use crate::{AppState, DB};
#[derive(serde::Serialize)]
pub struct FetchGameStruct {
@@ -51,8 +52,6 @@ pub struct QueueUpdateEventQueueData {
pub meta: DownloadableMetadata,
pub status: DownloadStatus,
pub progress: f64,
- pub current: usize,
- pub max: usize,
}
#[derive(serde::Serialize, Clone)]
@@ -66,14 +65,31 @@ pub struct StatsUpdateEvent {
pub time: usize,
}
+// Game version with some fields missing and size information
+#[derive(serde::Deserialize, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GameVersionOption {
+ version_index: usize,
+ version_name: String,
+ platform: Platform,
+ setup_command: String,
+ launch_command: String,
+ delta: bool,
+ umu_id_override: Option,
+ // total_size: usize,
+}
+
pub fn fetch_library_logic(app: AppHandle) -> Result, RemoteAccessError> {
+ let base_url = DB.fetch_base_url();
+ let library_url = base_url.join("/api/v1/client/user/library")?;
+
let header = generate_authorization_header();
let client = reqwest::blocking::Client::new();
- let response = make_request(&client, &["/api/v1/client/user/library"], &[], |f| {
- f.header("Authorization", header)
- })?
- .send()?;
+ let response = client
+ .get(library_url.to_string())
+ .header("Authorization", header)
+ .send()?;
if response.status() != 200 {
let err = response.json().unwrap();
@@ -86,7 +102,7 @@ pub fn fetch_library_logic(app: AppHandle) -> Result, RemoteAccessErro
let state = app.state::>();
let mut handle = state.lock().unwrap();
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
for game in games.iter() {
handle.games.insert(game.id.clone(), game.clone());
@@ -121,11 +137,17 @@ pub fn fetch_game_logic(
return Ok(data);
}
+
+ let base_url = DB.fetch_base_url();
+
+ let endpoint = base_url.join(&format!("/api/v1/game/{}", id))?;
+ let header = generate_authorization_header();
+
let client = reqwest::blocking::Client::new();
- let response = make_request(&client, &["/api/v1/game/", &id], &[], |r| {
- r.header("Authorization", generate_authorization_header())
- })?
- .send()?;
+ let response = client
+ .get(endpoint.to_string())
+ .header("Authorization", header)
+ .send()?;
if response.status() == 404 {
return Err(RemoteAccessError::GameNotFound);
@@ -139,7 +161,7 @@ pub fn fetch_game_logic(
let game: Game = response.json()?;
state_handle.games.insert(id.clone(), game.clone());
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
db_handle
.applications
@@ -161,16 +183,18 @@ pub fn fetch_game_logic(
pub fn fetch_game_verion_options_logic(
game_id: String,
state: tauri::State<'_, Mutex>,
-) -> Result, RemoteAccessError> {
- let client = reqwest::blocking::Client::new();
+) -> Result, RemoteAccessError> {
+ let base_url = DB.fetch_base_url();
- let response = make_request(
- &client,
- &["/api/v1/client/game/versions"],
- &[("id", &game_id)],
- |r| r.header("Authorization", generate_authorization_header()),
- )?
- .send()?;
+ let endpoint =
+ base_url.join(format!("/api/v1/client/game/versions?id={}", game_id).as_str())?;
+ let header = generate_authorization_header();
+
+ let client = reqwest::blocking::Client::new();
+ let response = client
+ .get(endpoint.to_string())
+ .header("Authorization", header)
+ .send()?;
if response.status() != 200 {
let err = response.json().unwrap();
@@ -178,11 +202,11 @@ pub fn fetch_game_verion_options_logic(
return Err(RemoteAccessError::InvalidResponse(err));
}
- let data: Vec = response.json()?;
+ let data: Vec = response.json()?;
let state_lock = state.lock().unwrap();
let process_manager_lock = state_lock.process_manager.lock().unwrap();
- let data: Vec = data
+ let data: Vec = data
.into_iter()
.filter(|v| process_manager_lock.valid_platform(&v.platform).unwrap())
.collect();
@@ -193,8 +217,8 @@ pub fn fetch_game_verion_options_logic(
}
pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle) {
- println!("triggered uninstall for agent");
- let mut db_handle = borrow_db_mut_checked();
+ println!("Triggered uninstall for agent");
+ let mut db_handle = DB.borrow_data_mut().unwrap();
db_handle
.applications
.transient_statuses
@@ -209,7 +233,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
let previous_state = db_handle.applications.game_statuses.get(&meta.id).cloned();
if previous_state.is_none() {
- warn!("uninstall job doesn't have previous state, failing silently");
+ info!("uninstall job doesn't have previous state, failing silently");
return;
}
let previous_state = previous_state.unwrap();
@@ -237,7 +261,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
error!("{}", e);
}
Ok(_) => {
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
db_handle.applications.transient_statuses.remove(&meta);
db_handle
.applications
@@ -245,9 +269,9 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
.entry(meta.id.clone())
.and_modify(|e| *e = GameDownloadStatus::Remote {});
drop(db_handle);
- save_db();
+ DB.save().unwrap();
- debug!("uninstalled game id {}", &meta.id);
+ info!("uninstalled game id {}", &meta.id);
push_game_update(
&app_handle,
@@ -260,7 +284,8 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
}
pub fn get_current_meta(game_id: &String) -> Option {
- borrow_db_checked()
+ DB.borrow_data()
+ .unwrap()
.applications
.installed_game_version
.get(game_id)
@@ -273,27 +298,30 @@ pub fn on_game_complete(
app_handle: &AppHandle,
) -> Result<(), RemoteAccessError> {
// Fetch game version information from remote
+ let base_url = DB.fetch_base_url();
if meta.version.is_none() {
return Err(RemoteAccessError::GameNotFound);
}
+ let endpoint = base_url.join(
+ format!(
+ "/api/v1/client/game/version?id={}&version={}",
+ meta.id,
+ encode(meta.version.as_ref().unwrap())
+ )
+ .as_str(),
+ )?;
let header = generate_authorization_header();
let client = reqwest::blocking::Client::new();
- let response = make_request(
- &client,
- &["/api/v1/client/metadata/version"],
- &[
- ("id", &meta.id),
- ("version", meta.version.as_ref().unwrap()),
- ],
- |f| f.header("Authorization", header),
- )?
- .send()?;
+ let response = client
+ .get(endpoint.to_string())
+ .header("Authorization", header)
+ .send()?;
let data: GameVersion = response.json()?;
- let mut handle = borrow_db_mut_checked();
+ let mut handle = DB.borrow_data_mut().unwrap();
handle
.applications
.game_versions
@@ -306,7 +334,7 @@ pub fn on_game_complete(
.insert(meta.id.clone(), meta.clone());
drop(handle);
- save_db();
+ DB.save().unwrap();
let status = if data.setup_command.is_empty() {
GameDownloadStatus::Installed {
@@ -320,13 +348,13 @@ pub fn on_game_complete(
}
};
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
db_handle
.applications
.game_statuses
.insert(meta.id.clone(), status.clone());
drop(db_handle);
- save_db();
+ DB.save().unwrap();
app_handle
.emit(
&format!("update_game/{}", meta.id),
diff --git a/src-tauri/src/games/state.rs b/src-tauri/src/games/state.rs
index 19b1769..29287db 100644
--- a/src-tauri/src/games/state.rs
+++ b/src-tauri/src/games/state.rs
@@ -1,4 +1,7 @@
-use crate::database::db::{borrow_db_checked, ApplicationTransientStatus, GameDownloadStatus};
+use crate::{
+ database::db::{ApplicationTransientStatus, GameDownloadStatus},
+ DB,
+};
pub type GameStatusWithTransient = (
Option,
@@ -8,7 +11,7 @@ pub struct GameStatusManager {}
impl GameStatusManager {
pub fn fetch_state(game_id: &String) -> GameStatusWithTransient {
- let db_lock = borrow_db_checked();
+ let db_lock = DB.borrow_data().unwrap();
let online_state = match db_lock.applications.installed_game_version.get(game_id) {
Some(meta) => db_lock.applications.transient_statuses.get(meta).cloned(),
None => None,
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 1e374eb..1a3903e 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,3 +1,5 @@
+#![feature(try_trait_v2)]
+
mod database;
mod games;
@@ -14,12 +16,10 @@ use autostart::{get_autostart_enabled, toggle_autostart};
use cleanup::{cleanup_and_exit, quit};
use commands::fetch_state;
use database::commands::{
- add_download_dir, delete_download_dir, fetch_download_dir_stats, fetch_settings,
- fetch_system_data, update_settings,
-};
-use database::db::{
- borrow_db_checked, borrow_db_mut_checked, DatabaseInterface, GameDownloadStatus, DATA_ROOT_DIR,
+ add_download_dir, delete_download_dir, fetch_download_dir_stats, fetch_system_data,
+ update_settings,
};
+use database::db::{DatabaseInterface, GameDownloadStatus, DATA_ROOT_DIR};
use download_manager::commands::{
cancel_game, move_download_in_queue, pause_downloads, resume_downloads,
};
@@ -44,11 +44,8 @@ use remote::auth::{self, generate_authorization_header, recieve_handshake};
use remote::commands::{
auth_initiate, gen_drop_url, manual_recieve_handshake, retry_connect, sign_out, use_remote,
};
-use remote::requests::make_request;
use serde::{Deserialize, Serialize};
-use std::env;
use std::path::Path;
-use std::str::FromStr;
use std::sync::Arc;
use std::{
collections::HashMap,
@@ -95,21 +92,15 @@ pub struct AppState<'a> {
fn setup(handle: AppHandle) -> AppState<'static> {
let logfile = FileAppender::builder()
- .encoder(Box::new(PatternEncoder::new(
- "{d} | {l} | {f}:{L} - {m}{n}",
- )))
+ .encoder(Box::new(PatternEncoder::new("{d} | {l} | {f} - {m}{n}")))
.append(false)
.build(DATA_ROOT_DIR.lock().unwrap().join("./drop.log"))
.unwrap();
let console = ConsoleAppender::builder()
- .encoder(Box::new(PatternEncoder::new(
- "{d} | {l} | {f}:{L} - {m}{n}",
- )))
+ .encoder(Box::new(PatternEncoder::new("{d} | {l} | {f} - {m}{n}")))
.build();
- let log_level = env::var("RUST_LOG").unwrap_or(String::from("Info"));
-
let config = Config::builder()
.appenders(vec![
Appender::builder().build("logfile", Box::new(logfile)),
@@ -118,7 +109,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
.build(
Root::builder()
.appenders(vec!["logfile", "console"])
- .build(LevelFilter::from_str(&log_level).expect("Invalid log level")),
+ .build(LevelFilter::Info),
)
.unwrap();
@@ -128,7 +119,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
let download_manager = Arc::new(DownloadManagerBuilder::build(handle.clone()));
let process_manager = Arc::new(Mutex::new(ProcessManager::new(handle.clone())));
- debug!("checking if database is set up");
+ debug!("Checking if database is set up");
let is_set_up = DB.database_is_set_up();
if !is_set_up {
return AppState {
@@ -140,12 +131,12 @@ fn setup(handle: AppHandle) -> AppState<'static> {
};
}
- debug!("database is set up");
+ debug!("Database is set up");
// TODO: Account for possible failure
- let (app_status, user) = auth::setup();
+ let (app_status, user) = auth::setup().unwrap();
- let db_handle = borrow_db_checked();
+ let db_handle = DB.borrow_data().unwrap();
let mut missing_games = Vec::new();
let statuses = db_handle.applications.game_statuses.clone();
drop(db_handle);
@@ -175,7 +166,7 @@ fn setup(handle: AppHandle) -> AppState<'static> {
info!("detected games missing: {:?}", missing_games);
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
for game_id in missing_games {
db_handle
.applications
@@ -186,11 +177,11 @@ fn setup(handle: AppHandle) -> AppState<'static> {
drop(db_handle);
- debug!("finished setup!");
+ info!("finished setup!");
// Sync autostart state
if let Err(e) = autostart::sync_autostart_on_startup(&handle) {
- warn!("failed to sync autostart state: {}", e);
+ warn!("Failed to sync autostart state: {}", e);
}
AppState {
@@ -227,7 +218,6 @@ pub fn run() {
fetch_system_data,
// User utils
update_settings,
- fetch_settings,
// Auth
auth_initiate,
retry_connect,
@@ -266,14 +256,14 @@ pub fn run() {
.setup(|app| {
let handle = app.handle().clone();
let state = setup(handle);
- debug!("initialized drop client");
+ info!("initialized drop client");
app.manage(Mutex::new(state));
#[cfg(any(target_os = "linux", all(debug_assertions, windows)))]
{
use tauri_plugin_deep_link::DeepLinkExt;
app.deep_link().register_all()?;
- debug!("registered all pre-defined deep links");
+ info!("registered all pre-defined deep links");
}
let handle = app.handle().clone();
@@ -293,7 +283,7 @@ pub fn run() {
.unwrap();
app.deep_link().on_open_url(move |event| {
- debug!("handling drop:// url");
+ info!("handling drop:// url");
let binding = event.urls();
let url = binding.first().unwrap();
if url.host_str().unwrap() == "handshake" {
@@ -323,18 +313,18 @@ pub fn run() {
app.webview_windows().get("main").unwrap().show().unwrap();
}
"quit" => {
- cleanup_and_exit(app, &app.state());
+ cleanup_and_exit(app);
}
_ => {
- println!("menu event not handled: {:?}", event.id);
+ println!("Menu event not handled: {:?}", event.id);
}
})
.build(app)
.expect("error while setting up tray menu");
{
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
if let Some(original) = db_handle.prev_database.take() {
warn!(
"Database corrupted. Original file at {}",
@@ -357,16 +347,23 @@ pub fn run() {
Ok(())
})
.register_asynchronous_uri_scheme_protocol("object", move |_ctx, request, responder| {
+ let base_url = DB.fetch_base_url();
+
// Drop leading /
let object_id = &request.uri().path()[1..];
+ let object_url = base_url
+ .join("/api/v1/client/object/")
+ .unwrap()
+ .join(object_id)
+ .unwrap();
+
let header = generate_authorization_header();
let client: reqwest::blocking::Client = reqwest::blocking::Client::new();
- let response = make_request(&client, &["/api/v1/client/object/", object_id], &[], |f| {
- f.header("Authorization", header)
- })
- .unwrap()
- .send();
+ let response = client
+ .get(object_url.to_string())
+ .header("Authorization", header)
+ .send();
if response.is_err() {
warn!(
"failed to fetch object with error: {}",
diff --git a/src-tauri/src/process/commands.rs b/src-tauri/src/process/commands.rs
index 8a213a4..0d7492e 100644
--- a/src-tauri/src/process/commands.rs
+++ b/src-tauri/src/process/commands.rs
@@ -1,12 +1,15 @@
use std::sync::Mutex;
-use crate::{error::process_error::ProcessError, AppState};
+use crate::{
+ error::{process_error::ProcessError, user_error::UserValue},
+ AppState, DB,
+};
#[tauri::command]
pub fn launch_game(
id: String,
state: tauri::State<'_, Mutex>,
-) -> Result<(), ProcessError> {
+) -> UserValue<(), ProcessError> {
let state_lock = state.lock().unwrap();
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
@@ -18,23 +21,24 @@ pub fn launch_game(
match process_manager_lock.launch_process(id) {
Ok(_) => {}
- Err(e) => return Err(e),
+ Err(e) => return UserValue::Err(e),
};
drop(process_manager_lock);
drop(state_lock);
- Ok(())
+ UserValue::Ok(())
}
#[tauri::command]
pub fn kill_game(
game_id: String,
state: tauri::State<'_, Mutex>,
-) -> Result<(), ProcessError> {
+) -> UserValue<(), ProcessError> {
let state_lock = state.lock().unwrap();
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
process_manager_lock
.kill_game(game_id)
.map_err(ProcessError::IOError)
+ .into()
}
diff --git a/src-tauri/src/process/process_manager.rs b/src-tauri/src/process/process_manager.rs
index 4d8ae12..f53ea20 100644
--- a/src-tauri/src/process/process_manager.rs
+++ b/src-tauri/src/process/process_manager.rs
@@ -15,9 +15,7 @@ use tauri::{AppHandle, Manager};
use umu_wrapper_lib::command_builder::UmuCommandBuilder;
use crate::{
- database::db::{
- borrow_db_mut_checked, ApplicationTransientStatus, GameDownloadStatus, GameVersion, DATA_ROOT_DIR
- },
+ database::db::{ApplicationTransientStatus, GameDownloadStatus, DATA_ROOT_DIR},
download_manager::downloadable_metadata::{DownloadType, DownloadableMetadata},
error::process_error::ProcessError,
games::{library::push_game_update, state::GameStatusManager},
@@ -66,8 +64,12 @@ impl ProcessManager<'_> {
}
}
- fn process_command(&self, install_dir: &String, command: Vec) -> (PathBuf, Vec) {
- let root = &command[0];
+ // There's no easy way to distinguish between an executable name with
+ // spaces and it's arguments.
+ // I think if we just join the install_dir to whatever the user provides us, we'll be alright
+ // In future, we should have a separate field for executable name and it's arguments
+ fn process_command(&self, install_dir: &String, raw_command: String) -> (PathBuf, Vec) {
+ let root = raw_command;
let install_dir = Path::new(install_dir);
let absolute_exe = install_dir.join(root);
@@ -81,7 +83,7 @@ impl ProcessManager<'_> {
(absolute_exe, Vec::new())
}
pub fn kill_game(&mut self, game_id: String) -> Result<(), io::Error> {
- match self.processes.get(&game_id) {
+ return match self.processes.get(&game_id) {
Some(child) => {
child.kill()?;
child.wait()?;
@@ -91,7 +93,7 @@ impl ProcessManager<'_> {
io::ErrorKind::NotFound,
"Game ID not running",
)),
- }
+ };
}
fn on_process_finish(&mut self, game_id: String, result: Result) {
@@ -104,7 +106,7 @@ impl ProcessManager<'_> {
self.processes.remove(&game_id);
- let mut db_handle = borrow_db_mut_checked();
+ let mut db_handle = DB.borrow_data_mut().unwrap();
let meta = db_handle
.applications
.installed_game_version
@@ -164,9 +166,9 @@ impl ProcessManager<'_> {
{
Some(GameDownloadStatus::Installed { version_name, .. }) => version_name,
Some(GameDownloadStatus::SetupRequired { .. }) => {
- return Err(ProcessError::SetupRequired)
+ return Err(ProcessError::SetupRequired).into()
}
- _ => return Err(ProcessError::NotInstalled),
+ _ => return Err(ProcessError::NotInstalled).into(),
};
let meta = DownloadableMetadata {
id: game_id.clone(),
@@ -174,7 +176,7 @@ impl ProcessManager<'_> {
download_type: DownloadType::Game,
};
- let mut db_lock = borrow_db_mut_checked();
+ let mut db_lock = DB.borrow_data_mut().unwrap();
debug!(
"Launching process {:?} with games {:?}",
&game_id, db_lock.applications.game_versions
@@ -186,18 +188,23 @@ impl ProcessManager<'_> {
.get(&game_id)
.ok_or(ProcessError::NotInstalled)?;
- let (version_name, install_dir) = match game_status {
+ let status_metadata: Option<(&String, &String)> = match game_status {
GameDownloadStatus::Installed {
version_name,
install_dir,
- } => (version_name, install_dir),
+ } => Some((&version_name, &install_dir)),
GameDownloadStatus::SetupRequired {
version_name,
install_dir,
- } => (version_name, install_dir),
- _ => return Err(ProcessError::NotDownloaded),
+ } => Some((version_name, install_dir)),
+ _ => None,
};
+ if status_metadata.is_none() {
+ return Err(ProcessError::NotDownloaded);
+ }
+
+ let (version_name, install_dir) = status_metadata.unwrap();
let game_version = db_lock
.applications
@@ -207,28 +214,19 @@ impl ProcessManager<'_> {
.get(version_name)
.ok_or(ProcessError::InvalidVersion)?;
- let mut command: Vec = Vec::new();
-
- match game_status {
+ let raw_command: String = match game_status {
GameDownloadStatus::Installed {
version_name: _,
install_dir: _,
- } => {
- command.extend([game_version.launch_command.clone()]);
- command.extend(game_version.launch_args.clone());
- },
+ } => game_version.launch_command.clone(),
GameDownloadStatus::SetupRequired {
version_name: _,
install_dir: _,
- } => {
- command.extend([game_version.setup_command.clone()]);
- command.extend(game_version.setup_args.clone());
- },
+ } => game_version.setup_command.clone(),
_ => panic!("unreachable code"),
};
- info!("Command: {:?}", &command);
- let (command, args) = self.process_command(install_dir, command);
+ let (command, args) = self.process_command(install_dir, raw_command);
let target_current_dir = command.parent().unwrap().to_str().unwrap();
@@ -276,8 +274,8 @@ impl ProcessManager<'_> {
let launch_process = game_launcher
.launch_process(
&meta,
- command.to_string_lossy().to_string(),
- game_version,
+ command.to_str().unwrap().to_owned(),
+ args,
target_current_dir,
log_file,
error_file,
@@ -318,6 +316,9 @@ impl ProcessManager<'_> {
});
self.processes.insert(meta.id, wait_thread_handle);
+
+ info!("finished spawning process");
+
Ok(())
}
}
@@ -332,8 +333,8 @@ pub trait ProcessHandler: Send + 'static {
fn launch_process(
&self,
meta: &DownloadableMetadata,
- launch_command: String,
- game_version: &GameVersion,
+ command: String,
+ args: Vec,
current_dir: &str,
log_file: File,
error_file: File,
@@ -345,17 +346,17 @@ impl ProcessHandler for NativeGameLauncher {
fn launch_process(
&self,
_meta: &DownloadableMetadata,
- launch_command: String,
- game_version: &GameVersion,
+ command: String,
+ args: Vec,
current_dir: &str,
log_file: File,
error_file: File,
) -> Result {
- Command::new(PathBuf::from(launch_command))
+ Command::new(command)
.current_dir(current_dir)
.stdout(log_file)
.stderr(error_file)
- .args(game_version.launch_args.clone())
+ .args(args)
.spawn()
}
}
@@ -366,21 +367,15 @@ impl ProcessHandler for UMULauncher {
fn launch_process(
&self,
_meta: &DownloadableMetadata,
- launch_command: String,
- game_version: &GameVersion,
+ command: String,
+ args: Vec,
_current_dir: &str,
_log_file: File,
_error_file: File,
) -> Result {
- println!("Game override: .{:?}.", &game_version.umu_id_override);
- let game_id = match &game_version.umu_id_override {
- Some(game_override) => game_override.is_empty().then_some(game_version.game_id.clone()).unwrap_or(game_override.clone()) ,
- None => game_version.game_id.clone()
- };
- info!("Game ID: {}", game_id);
- UmuCommandBuilder::new(UMU_LAUNCHER_EXECUTABLE, launch_command)
- .game_id(game_id)
- .launch_args(game_version.launch_args.clone())
+ UmuCommandBuilder::new(UMU_LAUNCHER_EXECUTABLE, command)
+ .game_id(String::from("0"))
+ .launch_args(args)
.build()
.spawn()
}
diff --git a/src-tauri/src/remote/auth.rs b/src-tauri/src/remote/auth.rs
index d2594de..afd0c9a 100644
--- a/src-tauri/src/remote/auth.rs
+++ b/src-tauri/src/remote/auth.rs
@@ -1,22 +1,18 @@
use std::{env, sync::Mutex};
use chrono::Utc;
-use log::{debug, error, warn};
+use log::{debug, error, info, warn};
use openssl::{ec::EcKey, hash::MessageDigest, pkey::PKey, sign::Signer};
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Emitter, Manager};
use url::Url;
use crate::{
- database::db::{
- borrow_db_checked, borrow_db_mut_checked, save_db, DatabaseAuth, DatabaseImpls,
- },
+ database::db::{DatabaseAuth, DatabaseImpls},
error::{drop_server_error::DropServerError, remote_access_error::RemoteAccessError},
AppState, AppStatus, User, DB,
};
-use super::requests::make_request;
-
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct InitiateRequestBody {
@@ -55,7 +51,7 @@ pub fn sign_nonce(private_key: String, nonce: String) -> Result {
pub fn generate_authorization_header() -> String {
let certs = {
- let db = borrow_db_checked();
+ let db = DB.borrow_data().unwrap();
db.auth.clone().unwrap()
};
@@ -69,15 +65,16 @@ pub fn generate_authorization_header() -> String {
pub fn fetch_user() -> Result {
let base_url = DB.fetch_base_url();
+ let endpoint = base_url.join("/api/v1/client/user")?;
let header = generate_authorization_header();
let client = reqwest::blocking::Client::new();
- let response = make_request(&client, &["/api/v1/client/user"], &[], |f| {
- f.header("Authorization", header)
- })?
- .send()?;
+ let response = client
+ .get(endpoint.to_string())
+ .header("Authorization", header)
+ .send()?;
if response.status() != 200 {
- let err: DropServerError = response.json()?;
+ let err: DropServerError = response.json().unwrap();
warn!("{:?}", err);
if err.status_message == "Nonce expired" {
@@ -100,7 +97,7 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc
}
let base_url = {
- let handle = borrow_db_checked();
+ let handle = DB.borrow_data().unwrap();
Url::parse(handle.base_url.as_str())?
};
@@ -114,18 +111,18 @@ fn recieve_handshake_logic(app: &AppHandle, path: String) -> Result<(), RemoteAc
let endpoint = base_url.join("/api/v1/client/auth/handshake")?;
let client = reqwest::blocking::Client::new();
let response = client.post(endpoint).json(&body).send()?;
- debug!("handshake responsded with {}", response.status().as_u16());
+ debug!("Handshake responsded with {}", response.status().as_u16());
let response_struct: HandshakeResponse = response.json()?;
{
- let mut handle = borrow_db_mut_checked();
+ let mut handle = DB.borrow_data_mut().unwrap();
handle.auth = Some(DatabaseAuth {
private: response_struct.private,
cert: response_struct.certificate,
client_id: response_struct.id,
});
drop(handle);
- save_db();
+ DB.save().unwrap();
}
{
@@ -154,7 +151,7 @@ pub fn recieve_handshake(app: AppHandle, path: String) {
pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> {
let base_url = {
- let db_lock = borrow_db_checked();
+ let db_lock = DB.borrow_data().unwrap();
Url::parse(&db_lock.base_url.clone())?
};
@@ -169,7 +166,7 @@ pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> {
if response.status() != 200 {
let data: DropServerError = response.json()?;
- error!("could not start handshake: {}", data.status_message);
+ error!("Could not start handshake: {}", data.status_message);
return Err(RemoteAccessError::HandshakeFailed(data.status_message));
}
@@ -183,19 +180,15 @@ pub fn auth_initiate_logic() -> Result<(), RemoteAccessError> {
Ok(())
}
-pub fn setup() -> (AppStatus, Option) {
- let data = borrow_db_checked();
+pub fn setup() -> Result<(AppStatus, Option), RemoteAccessError> {
+ let data = DB.borrow_data().unwrap();
let auth = data.auth.clone();
drop(data);
if auth.is_some() {
- let user_result = match fetch_user() {
- Ok(data) => data,
- Err(RemoteAccessError::FetchError(_)) => return (AppStatus::ServerUnavailable, None),
- Err(_) => return (AppStatus::SignedInNeedsReauth, None),
- };
- return (AppStatus::SignedIn, Some(user_result));
+ let user_result = fetch_user()?;
+ return Ok((AppStatus::SignedIn, Some(user_result)));
}
- (AppStatus::SignedOut, None)
+ Ok((AppStatus::SignedOut, None))
}
diff --git a/src-tauri/src/remote/commands.rs b/src-tauri/src/remote/commands.rs
index 82781c8..9dbfcb4 100644
--- a/src-tauri/src/remote/commands.rs
+++ b/src-tauri/src/remote/commands.rs
@@ -4,9 +4,8 @@ use tauri::{AppHandle, Emitter, Manager};
use url::Url;
use crate::{
- database::db::{borrow_db_checked, borrow_db_mut_checked, save_db},
- error::remote_access_error::RemoteAccessError,
- AppState, AppStatus,
+ error::{remote_access_error::RemoteAccessError, user_error::UserValue},
+ AppState, AppStatus, DB,
};
use super::{
@@ -18,31 +17,31 @@ use super::{
pub fn use_remote(
url: String,
state: tauri::State<'_, Mutex>>,
-) -> Result<(), RemoteAccessError> {
- use_remote_logic(url, state)
+) -> UserValue<(), RemoteAccessError> {
+ UserValue::Ok(use_remote_logic(url, state)?)
}
#[tauri::command]
-pub fn gen_drop_url(path: String) -> Result {
+pub fn gen_drop_url(path: String) -> UserValue {
let base_url = {
- let handle = borrow_db_checked();
+ let handle = DB.borrow_data().unwrap();
Url::parse(&handle.base_url).map_err(RemoteAccessError::ParsingError)?
};
let url = base_url.join(&path).unwrap();
- Ok(url.to_string())
+ UserValue::Ok(url.to_string())
}
#[tauri::command]
pub fn sign_out(app: AppHandle) {
// Clear auth from database
{
- let mut handle = borrow_db_mut_checked();
+ let mut handle = DB.borrow_data_mut().unwrap();
handle.auth = None;
drop(handle);
- save_db();
+ DB.save().unwrap();
}
// Update app state
@@ -58,18 +57,19 @@ pub fn sign_out(app: AppHandle) {
}
#[tauri::command]
-pub fn retry_connect(state: tauri::State<'_, Mutex>) {
- let (app_status, user) = setup();
+pub fn retry_connect(state: tauri::State<'_, Mutex>) -> UserValue<(), RemoteAccessError> {
+ let (app_status, user) = setup()?;
let mut guard = state.lock().unwrap();
guard.status = app_status;
guard.user = user;
drop(guard);
+ UserValue::Ok(())
}
#[tauri::command]
-pub fn auth_initiate() -> Result<(), RemoteAccessError> {
- auth_initiate_logic()
+pub fn auth_initiate() -> UserValue<(), RemoteAccessError> {
+ auth_initiate_logic().into()
}
#[tauri::command]
diff --git a/src-tauri/src/remote/mod.rs b/src-tauri/src/remote/mod.rs
index a2eb666..29d8c02 100644
--- a/src-tauri/src/remote/mod.rs
+++ b/src-tauri/src/remote/mod.rs
@@ -1,4 +1,3 @@
pub mod auth;
pub mod commands;
pub mod remote;
-pub mod requests;
diff --git a/src-tauri/src/remote/remote.rs b/src-tauri/src/remote/remote.rs
index 01f2c2b..7790982 100644
--- a/src-tauri/src/remote/remote.rs
+++ b/src-tauri/src/remote/remote.rs
@@ -1,15 +1,16 @@
-use std::sync::Mutex;
-
-use log::{debug, warn};
-use serde::Deserialize;
-use url::Url;
-
-use crate::{
- database::db::{borrow_db_mut_checked, save_db},
- error::remote_access_error::RemoteAccessError,
- AppState, AppStatus,
+use std::{
+ error::Error,
+ fmt::{Display, Formatter},
+ sync::{Arc, Mutex},
};
+use http::StatusCode;
+use log::{info, warn};
+use serde::Deserialize;
+use url::{ParseError, Url};
+
+use crate::{error::remote_access_error::RemoteAccessError, AppState, AppStatus, DB};
+
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct DropHealthcheck {
@@ -20,7 +21,7 @@ pub fn use_remote_logic(
url: String,
state: tauri::State<'_, Mutex>>,
) -> Result<(), RemoteAccessError> {
- debug!("connecting to url {}", url);
+ info!("connecting to url {}", url);
let base_url = Url::parse(&url)?;
// Test Drop url
@@ -38,11 +39,11 @@ pub fn use_remote_logic(
app_state.status = AppStatus::SignedOut;
drop(app_state);
- let mut db_state = borrow_db_mut_checked();
+ let mut db_state = DB.borrow_data_mut().unwrap();
db_state.base_url = base_url.to_string();
drop(db_state);
- save_db();
+ DB.save().unwrap();
Ok(())
}
diff --git a/src-tauri/src/remote/requests.rs b/src-tauri/src/remote/requests.rs
deleted file mode 100644
index 44cdc83..0000000
--- a/src-tauri/src/remote/requests.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use reqwest::blocking::{Client, RequestBuilder};
-
-use crate::{database::db::DatabaseImpls, error::remote_access_error::RemoteAccessError, DB};
-
-pub fn make_request, F: FnOnce(RequestBuilder) -> RequestBuilder>(
- client: &Client,
- path_components: &[T],
- query: &[(T, T)],
- f: F,
-) -> Result {
- let mut base_url = DB.fetch_base_url();
- for endpoint in path_components {
- base_url = base_url.join(endpoint.as_ref())?;
- }
- {
- let mut queries = base_url.query_pairs_mut();
- for (param, val) in query {
- queries.append_pair(param.as_ref(), val.as_ref());
- }
- }
- let response = client.get(base_url);
- Ok(f(response))
-}
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 1dead92..0efb8e0 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2.0.0",
"productName": "Drop Desktop Client",
- "version": "0.2.0-beta",
+ "version": "0.1.0",
"identifier": "dev.drop.app",
"build": {
"beforeDevCommand": "yarn dev --port 1432",
@@ -23,7 +23,7 @@
},
"bundle": {
"active": true,
- "targets": ["nsis", "deb", "rpm", "dmg", "appimage"],
+ "targets": ["nsis", "deb", "rpm", "dmg"],
"windows": {
"nsis": {
"installMode": "both"
diff --git a/types.ts b/types.ts
index 60a5b23..720ec28 100644
--- a/types.ts
+++ b/types.ts
@@ -71,9 +71,4 @@ export type DownloadableMetadata = {
id: string,
version: string,
downloadType: DownloadableType
-}
-
-export type Settings = {
- autostart: boolean,
- maxDownloadThreads: number,
}
\ No newline at end of file