Process manager templating & game importing (#96)
* feat: add new template options, asahi support, and refactoring * feat: install dir scanning, validation fixes, progress fixes, download manager refactor This kind of ballooned out of scope, but I implemented some much needed fixes for the download manager. First off, I cleanup the Downloadable trait, there was some duplication of function. Second, I refactored the "validate" into the GameDownloadAgent, which calls a 'validate_chunk_logic' yada, same structure as downloading. Third, I fixed the progress and validation issues. Fourth, I added game scanning * feat: out of box support for Asahi Linux * fix: clippy * fix: don't break database
This commit is contained in:
@@ -7,7 +7,7 @@ use std::{
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
database::db::borrow_db_mut_checked, error::download_manager_error::DownloadManagerError,
|
||||
database::{db::borrow_db_mut_checked, scan::scan_install_dirs}, error::download_manager_error::DownloadManagerError,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -59,6 +59,8 @@ pub fn add_download_dir(new_dir: PathBuf) -> Result<(), DownloadManagerError<()>
|
||||
lock.applications.install_dirs.push(new_dir);
|
||||
drop(lock);
|
||||
|
||||
scan_install_dirs();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use chrono::Utc;
|
||||
use log::{debug, error, info, warn};
|
||||
use native_model::{Decode, Encode};
|
||||
use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use url::Url;
|
||||
|
||||
use crate::DB;
|
||||
@@ -67,17 +67,18 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
|
||||
let exists = fs::exists(db_path.clone()).unwrap();
|
||||
|
||||
if exists { match PathDatabase::load_from_path(db_path.clone()) {
|
||||
Ok(db) => db,
|
||||
Err(e) => handle_invalid_database(e, db_path, games_base_dir, cache_dir),
|
||||
} } else {
|
||||
if exists {
|
||||
match PathDatabase::load_from_path(db_path.clone()) {
|
||||
Ok(db) => db,
|
||||
Err(e) => handle_invalid_database(e, db_path, games_base_dir, cache_dir),
|
||||
}
|
||||
} else {
|
||||
let default = Database::new(games_base_dir, None, cache_dir);
|
||||
debug!(
|
||||
"Creating database at path {}",
|
||||
db_path.as_os_str().to_str().unwrap()
|
||||
);
|
||||
PathDatabase::create_at_path(db_path, default)
|
||||
.expect("Database could not be created")
|
||||
PathDatabase::create_at_path(db_path, default).expect("Database could not be created")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,24 +122,24 @@ fn handle_invalid_database(
|
||||
pub struct DBRead<'a>(RwLockReadGuard<'a, Database>);
|
||||
pub struct DBWrite<'a>(ManuallyDrop<RwLockWriteGuard<'a, Database>>);
|
||||
impl<'a> Deref for DBWrite<'a> {
|
||||
type Target = RwLockWriteGuard<'a, Database>;
|
||||
type Target = Database;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl<'a> DerefMut for DBWrite<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
impl<'a> Deref for DBRead<'a> {
|
||||
type Target = RwLockReadGuard<'a, Database>;
|
||||
type Target = Database;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for DBWrite<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
impl Drop for DBWrite<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
@@ -154,6 +155,7 @@ impl Drop for DBWrite<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow_db_checked<'a>() -> DBRead<'a> {
|
||||
match DB.borrow_data() {
|
||||
Ok(data) => DBRead(data),
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod commands;
|
||||
pub mod db;
|
||||
pub mod debug;
|
||||
pub mod models;
|
||||
pub mod scan;
|
||||
@@ -1,8 +1,14 @@
|
||||
use crate::database::models::data::Database;
|
||||
|
||||
/**
|
||||
* NEXT BREAKING CHANGE
|
||||
*
|
||||
* UPDATE DATABASE TO USE RPMSERDENAMED
|
||||
*
|
||||
* WE CAN'T DELETE ANY FIELDS
|
||||
*/
|
||||
pub mod data {
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
use native_model::native_model;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -18,16 +24,14 @@ pub mod data {
|
||||
pub type DatabaseApplications = v2::DatabaseApplications;
|
||||
pub type DatabaseCompatInfo = v2::DatabaseCompatInfo;
|
||||
|
||||
use std::{collections::HashMap, process::Command};
|
||||
|
||||
use crate::process::process_manager::UMU_LAUNCHER_EXECUTABLE;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub mod v1 {
|
||||
use crate::process::process_manager::Platform;
|
||||
use serde_with::serde_as;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use super::{Serialize, Deserialize, native_model};
|
||||
use super::{Deserialize, Serialize, native_model};
|
||||
|
||||
fn default_template() -> String {
|
||||
"{}".to_owned()
|
||||
@@ -115,6 +119,7 @@ pub mod data {
|
||||
Downloading { version_name: String },
|
||||
Uninstalling {},
|
||||
Updating { version_name: String },
|
||||
Validating { version_name: String },
|
||||
Running {},
|
||||
}
|
||||
|
||||
@@ -174,7 +179,10 @@ pub mod data {
|
||||
|
||||
use serde_with::serde_as;
|
||||
|
||||
use super::{Serialize, Deserialize, native_model, Settings, DatabaseAuth, v1, GameVersion, DownloadableMetadata, ApplicationTransientStatus};
|
||||
use super::{
|
||||
ApplicationTransientStatus, DatabaseAuth, Deserialize, DownloadableMetadata,
|
||||
GameVersion, Serialize, Settings, native_model, v1,
|
||||
};
|
||||
|
||||
#[native_model(id = 1, version = 2, with = native_model::rmp_serde_1_3::RmpSerde)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
@@ -206,7 +214,7 @@ pub mod data {
|
||||
applications: value.applications,
|
||||
prev_database: value.prev_database,
|
||||
cache_dir: value.cache_dir,
|
||||
compat_info: crate::database::models::Database::create_new_compat_info(),
|
||||
compat_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +291,10 @@ pub mod data {
|
||||
mod v3 {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{Serialize, Deserialize, native_model, Settings, DatabaseAuth, DatabaseApplications, DatabaseCompatInfo, v2};
|
||||
use super::{
|
||||
DatabaseApplications, DatabaseAuth, DatabaseCompatInfo, Deserialize, Serialize,
|
||||
Settings, native_model, v2,
|
||||
};
|
||||
#[native_model(id = 1, version = 3, with = native_model::rmp_serde_1_3::RmpSerde)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
pub struct Database {
|
||||
@@ -297,6 +308,7 @@ pub mod data {
|
||||
pub cache_dir: PathBuf,
|
||||
pub compat_info: Option<DatabaseCompatInfo>,
|
||||
}
|
||||
|
||||
impl From<v2::Database> for Database {
|
||||
fn from(value: v2::Database) -> Self {
|
||||
Self {
|
||||
@@ -306,22 +318,13 @@ pub mod data {
|
||||
applications: value.applications.into(),
|
||||
prev_database: value.prev_database,
|
||||
cache_dir: value.cache_dir,
|
||||
compat_info: Database::create_new_compat_info(),
|
||||
compat_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Database {
|
||||
fn create_new_compat_info() -> Option<DatabaseCompatInfo> {
|
||||
#[cfg(target_os = "windows")]
|
||||
return None;
|
||||
|
||||
let has_umu_installed = Command::new(UMU_LAUNCHER_EXECUTABLE).spawn().is_ok();
|
||||
Some(DatabaseCompatInfo {
|
||||
umu_installed: has_umu_installed,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new<T: Into<PathBuf>>(
|
||||
games_base_dir: T,
|
||||
prev_database: Option<PathBuf>,
|
||||
@@ -340,7 +343,7 @@ pub mod data {
|
||||
auth: None,
|
||||
settings: Settings::default(),
|
||||
cache_dir,
|
||||
compat_info: Database::create_new_compat_info(),
|
||||
compat_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
use std::fs;
|
||||
|
||||
use log::warn;
|
||||
|
||||
use crate::{
|
||||
database::{
|
||||
db::borrow_db_mut_checked,
|
||||
models::data::v1::{DownloadType, DownloadableMetadata},
|
||||
},
|
||||
games::{
|
||||
downloads::drop_data::{v1::DropData, DROP_DATA_PATH},
|
||||
library::set_partially_installed_db,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn scan_install_dirs() {
|
||||
let mut db_lock = borrow_db_mut_checked();
|
||||
for install_dir in db_lock.applications.install_dirs.clone() {
|
||||
let Ok(files) = fs::read_dir(install_dir) else {
|
||||
continue;
|
||||
};
|
||||
for game in files.into_iter().flatten() {
|
||||
let drop_data_file = game.path().join(DROP_DATA_PATH);
|
||||
if !drop_data_file.exists() {
|
||||
continue;
|
||||
}
|
||||
let game_id = game.file_name().into_string().unwrap();
|
||||
let Ok(drop_data) = DropData::read(&game.path()) else {
|
||||
warn!(
|
||||
".dropdata exists for {}, but couldn't read it. is it corrupted?",
|
||||
game.file_name().into_string().unwrap()
|
||||
);
|
||||
continue;
|
||||
};
|
||||
if db_lock.applications.game_statuses.contains_key(&game_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let metadata = DownloadableMetadata::new(
|
||||
drop_data.game_id,
|
||||
Some(drop_data.game_version),
|
||||
DownloadType::Game,
|
||||
);
|
||||
set_partially_installed_db(
|
||||
&mut db_lock,
|
||||
&metadata,
|
||||
drop_data.base_path.to_str().unwrap().to_string(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user