Compare commits
2 Commits
0147956b5f
...
62a2561539
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a2561539 | |||
| 59f040bc8b |
2
.gitignore
vendored
@ -30,3 +30,5 @@ src-tauri/perf*
|
|||||||
|
|
||||||
/*.AppImage
|
/*.AppImage
|
||||||
/squashfs-root
|
/squashfs-root
|
||||||
|
|
||||||
|
/target/
|
||||||
|
|||||||
2184
src-tauri/src-tauri/Cargo.lock → Cargo.lock
generated
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"client",
|
||||||
|
"database",
|
||||||
|
"src-tauri",
|
||||||
|
"process",
|
||||||
|
"remote",
|
||||||
|
"utils",
|
||||||
|
"cloud_saves",
|
||||||
|
"download_manager",
|
||||||
|
"games",
|
||||||
|
]
|
||||||
|
|
||||||
|
resolver = "3"
|
||||||
47
client/src/compat.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use std::{ffi::OsStr, path::PathBuf, process::{Command, Stdio}, sync::LazyLock};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
pub static COMPAT_INFO: LazyLock<Option<CompatInfo>> = LazyLock::new(create_new_compat_info);
|
||||||
|
|
||||||
|
pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
|
||||||
|
let x = get_umu_executable();
|
||||||
|
info!("{:?}", &x);
|
||||||
|
x
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CompatInfo {
|
||||||
|
pub umu_installed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_new_compat_info() -> Option<CompatInfo> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
return None;
|
||||||
|
|
||||||
|
let has_umu_installed = UMU_LAUNCHER_EXECUTABLE.is_some();
|
||||||
|
Some(CompatInfo {
|
||||||
|
umu_installed: has_umu_installed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
|
||||||
|
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
|
||||||
|
|
||||||
|
fn get_umu_executable() -> Option<PathBuf> {
|
||||||
|
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
|
||||||
|
return Some(PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in UMU_INSTALL_DIRS {
|
||||||
|
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
|
||||||
|
if check_executable_exists(&p) {
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
|
||||||
|
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).output();
|
||||||
|
has_umu_installed.is_ok()
|
||||||
|
}
|
||||||
4
client/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod autostart;
|
||||||
|
pub mod user;
|
||||||
|
pub mod app_status;
|
||||||
|
pub mod compat;
|
||||||
@ -11,7 +11,3 @@ pub struct User {
|
|||||||
profile_picture_object_id: String,
|
profile_picture_object_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct CompatInfo {
|
|
||||||
umu_installed: bool,
|
|
||||||
}
|
|
||||||
@ -11,6 +11,7 @@ regex = "1.11.3"
|
|||||||
rustix = "1.1.2"
|
rustix = "1.1.2"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.145"
|
||||||
|
serde_with = "3.15.0"
|
||||||
tar = "0.4.44"
|
tar = "0.4.44"
|
||||||
tempfile = "3.23.0"
|
tempfile = "3.23.0"
|
||||||
uuid = "1.18.1"
|
uuid = "1.18.1"
|
||||||
27
cloud_saves/src/error.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde_with::SerializeDisplay;
|
||||||
|
|
||||||
|
#[derive(Debug, SerializeDisplay, Clone, Copy)]
|
||||||
|
|
||||||
|
pub enum BackupError {
|
||||||
|
InvalidSystem,
|
||||||
|
|
||||||
|
NotFound,
|
||||||
|
|
||||||
|
ParseError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BackupError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
BackupError::InvalidSystem => "Attempted to generate path for invalid system",
|
||||||
|
|
||||||
|
BackupError::NotFound => "Could not generate or find path",
|
||||||
|
|
||||||
|
BackupError::ParseError => "Failed to parse path",
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ use database::{borrow_db_checked, borrow_db_mut_checked, ApplicationTransientSta
|
|||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use remote::{auth::generate_authorization_header, error::RemoteAccessError, requests::generate_url, utils::DROP_CLIENT_SYNC};
|
use remote::{auth::generate_authorization_header, error::RemoteAccessError, requests::generate_url, utils::DROP_CLIENT_SYNC};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::AppHandle;
|
use tauri::{AppHandle, Emitter};
|
||||||
use utils::app_emit;
|
use utils::app_emit;
|
||||||
|
|
||||||
use crate::{downloads::error::LibraryError, state::{GameStatusManager, GameStatusWithTransient}};
|
use crate::{downloads::error::LibraryError, state::{GameStatusManager, GameStatusWithTransient}};
|
||||||
@ -8,10 +8,11 @@ chrono = "0.4.42"
|
|||||||
client = { version = "0.1.0", path = "../client" }
|
client = { version = "0.1.0", path = "../client" }
|
||||||
database = { version = "0.1.0", path = "../database" }
|
database = { version = "0.1.0", path = "../database" }
|
||||||
dynfmt = "0.1.5"
|
dynfmt = "0.1.5"
|
||||||
|
games = { version = "0.1.0", path = "../games" }
|
||||||
log = "0.4.28"
|
log = "0.4.28"
|
||||||
|
page_size = "0.6.0"
|
||||||
serde = "1.0.228"
|
serde = "1.0.228"
|
||||||
serde_with = "3.15.0"
|
serde_with = "3.15.0"
|
||||||
shared_child = "1.1.1"
|
shared_child = "1.1.1"
|
||||||
tauri = "2.8.5"
|
|
||||||
tauri-plugin-opener = "2.5.0"
|
tauri-plugin-opener = "2.5.0"
|
||||||
utils = { version = "0.1.0", path = "../utils" }
|
utils = { version = "0.1.0", path = "../utils" }
|
||||||
@ -12,7 +12,8 @@ pub enum ProcessError {
|
|||||||
FormatError(String), // String errors supremacy
|
FormatError(String), // String errors supremacy
|
||||||
InvalidPlatform,
|
InvalidPlatform,
|
||||||
OpenerError(tauri_plugin_opener::Error),
|
OpenerError(tauri_plugin_opener::Error),
|
||||||
InvalidArguments(String)
|
InvalidArguments(String),
|
||||||
|
FailedLaunch(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ProcessError {
|
impl Display for ProcessError {
|
||||||
@ -26,8 +27,13 @@ impl Display for ProcessError {
|
|||||||
ProcessError::InvalidPlatform => "This game cannot be played on the current platform",
|
ProcessError::InvalidPlatform => "This game cannot be played on the current platform",
|
||||||
ProcessError::FormatError(error) => &format!("Could not format template: {error:?}"),
|
ProcessError::FormatError(error) => &format!("Could not format template: {error:?}"),
|
||||||
ProcessError::OpenerError(error) => &format!("Could not open directory: {error:?}"),
|
ProcessError::OpenerError(error) => &format!("Could not open directory: {error:?}"),
|
||||||
ProcessError::InvalidArguments(arguments) => &format!("Invalid arguments in command {arguments}"),
|
ProcessError::InvalidArguments(arguments) => {
|
||||||
};
|
&format!("Invalid arguments in command {arguments}")
|
||||||
|
}
|
||||||
|
ProcessError::FailedLaunch(game_id) => {
|
||||||
|
&format!("Drop detected that the game {game_id} may have failed to launch properly")
|
||||||
|
}
|
||||||
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
14
process/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#![feature(nonpoison_mutex)]
|
||||||
|
#![feature(sync_nonpoison)]
|
||||||
|
|
||||||
|
use std::sync::{LazyLock, nonpoison::Mutex};
|
||||||
|
|
||||||
|
use crate::process_manager::ProcessManager;
|
||||||
|
|
||||||
|
pub static PROCESS_MANAGER: LazyLock<Mutex<ProcessManager>> =
|
||||||
|
LazyLock::new(|| Mutex::new(ProcessManager::new()));
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod format;
|
||||||
|
pub mod process_handlers;
|
||||||
|
pub mod process_manager;
|
||||||
@ -1,12 +1,6 @@
|
|||||||
use std::{
|
use client::compat::{COMPAT_INFO, UMU_LAUNCHER_EXECUTABLE};
|
||||||
ffi::OsStr,
|
|
||||||
path::PathBuf,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
sync::LazyLock,
|
|
||||||
};
|
|
||||||
|
|
||||||
use database::{platform::Platform, Database, DownloadableMetadata, GameVersion};
|
use database::{platform::Platform, Database, DownloadableMetadata, GameVersion};
|
||||||
use log::{debug, info};
|
use log::debug;
|
||||||
|
|
||||||
|
|
||||||
use crate::{error::ProcessError, process_manager::ProcessHandler};
|
use crate::{error::ProcessError, process_manager::ProcessHandler};
|
||||||
@ -24,36 +18,11 @@ impl ProcessHandler for NativeGameLauncher {
|
|||||||
Ok(format!("\"{}\" {}", launch_command, args.join(" ")))
|
Ok(format!("\"{}\" {}", launch_command, args.join(" ")))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_for_platform(&self, _db: &Database, _state: &AppState, _target: &Platform) -> bool {
|
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static UMU_LAUNCHER_EXECUTABLE: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
|
|
||||||
let x = get_umu_executable();
|
|
||||||
info!("{:?}", &x);
|
|
||||||
x
|
|
||||||
});
|
|
||||||
const UMU_BASE_LAUNCHER_EXECUTABLE: &str = "umu-run";
|
|
||||||
const UMU_INSTALL_DIRS: [&str; 4] = ["/app/share", "/use/local/share", "/usr/share", "/opt"];
|
|
||||||
|
|
||||||
fn get_umu_executable() -> Option<PathBuf> {
|
|
||||||
if check_executable_exists(UMU_BASE_LAUNCHER_EXECUTABLE) {
|
|
||||||
return Some(PathBuf::from(UMU_BASE_LAUNCHER_EXECUTABLE));
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in UMU_INSTALL_DIRS {
|
|
||||||
let p = PathBuf::from(dir).join(UMU_BASE_LAUNCHER_EXECUTABLE);
|
|
||||||
if check_executable_exists(&p) {
|
|
||||||
return Some(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn check_executable_exists<P: AsRef<OsStr>>(exec: P) -> bool {
|
|
||||||
let has_umu_installed = Command::new(exec).stdout(Stdio::null()).output();
|
|
||||||
has_umu_installed.is_ok()
|
|
||||||
}
|
|
||||||
pub struct UMULauncher;
|
pub struct UMULauncher;
|
||||||
impl ProcessHandler for UMULauncher {
|
impl ProcessHandler for UMULauncher {
|
||||||
fn create_launch_process(
|
fn create_launch_process(
|
||||||
@ -83,8 +52,8 @@ impl ProcessHandler for UMULauncher {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_for_platform(&self, _db: &Database, state: &AppState, _target: &Platform) -> bool {
|
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
|
||||||
let Some(ref compat_info) = state.compat_info else {
|
let Some(compat_info) = &*COMPAT_INFO else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
compat_info.umu_installed
|
compat_info.umu_installed
|
||||||
@ -124,7 +93,7 @@ impl ProcessHandler for AsahiMuvmLauncher {
|
|||||||
|
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn valid_for_platform(&self, _db: &Database, state: &AppState, _target: &Platform) -> bool {
|
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -136,7 +105,7 @@ impl ProcessHandler for AsahiMuvmLauncher {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(ref compat_info) = state.compat_info else {
|
let Some(compat_info) = &*COMPAT_INFO else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1,27 +1,31 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::{OpenOptions, create_dir_all},
|
fs::{OpenOptions, create_dir_all},
|
||||||
io::{self},
|
io,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Command, ExitStatus},
|
process::{Command, ExitStatus},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{Arc, Mutex},
|
sync::Arc,
|
||||||
thread::spawn,
|
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use database::{borrow_db_checked, borrow_db_mut_checked, db::DATA_ROOT_DIR, platform::Platform, ApplicationTransientStatus, Database, DownloadType, DownloadableMetadata, GameDownloadStatus, GameVersion};
|
use database::{
|
||||||
|
ApplicationTransientStatus, Database, DownloadType, DownloadableMetadata, GameDownloadStatus,
|
||||||
|
GameVersion, borrow_db_checked, borrow_db_mut_checked, db::DATA_ROOT_DIR, platform::Platform,
|
||||||
|
};
|
||||||
use dynfmt::Format;
|
use dynfmt::Format;
|
||||||
use dynfmt::SimpleCurlyFormat;
|
use dynfmt::SimpleCurlyFormat;
|
||||||
|
use games::state::GameStatusManager;
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use shared_child::SharedChild;
|
use shared_child::SharedChild;
|
||||||
use tauri::{AppHandle, Emitter, Manager};
|
|
||||||
use tauri_plugin_opener::OpenerExt;
|
|
||||||
use utils::lock;
|
|
||||||
|
|
||||||
use crate::{error::ProcessError, format::DropFormatArgs, process_handlers::{AsahiMuvmLauncher, NativeGameLauncher, UMULauncher}};
|
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
PROCESS_MANAGER,
|
||||||
|
error::ProcessError,
|
||||||
|
format::DropFormatArgs,
|
||||||
|
process_handlers::{AsahiMuvmLauncher, NativeGameLauncher, UMULauncher},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct RunningProcess {
|
pub struct RunningProcess {
|
||||||
handle: Arc<SharedChild>,
|
handle: Arc<SharedChild>,
|
||||||
@ -33,7 +37,6 @@ pub struct ProcessManager<'a> {
|
|||||||
current_platform: Platform,
|
current_platform: Platform,
|
||||||
log_output_dir: PathBuf,
|
log_output_dir: PathBuf,
|
||||||
processes: HashMap<String, RunningProcess>,
|
processes: HashMap<String, RunningProcess>,
|
||||||
app_handle: AppHandle,
|
|
||||||
game_launchers: Vec<(
|
game_launchers: Vec<(
|
||||||
(Platform, Platform),
|
(Platform, Platform),
|
||||||
&'a (dyn ProcessHandler + Sync + Send + 'static),
|
&'a (dyn ProcessHandler + Sync + Send + 'static),
|
||||||
@ -41,7 +44,7 @@ pub struct ProcessManager<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ProcessManager<'_> {
|
impl ProcessManager<'_> {
|
||||||
pub fn new(app_handle: AppHandle) -> Self {
|
pub fn new() -> Self {
|
||||||
let log_output_dir = DATA_ROOT_DIR.join("logs");
|
let log_output_dir = DATA_ROOT_DIR.join("logs");
|
||||||
|
|
||||||
ProcessManager {
|
ProcessManager {
|
||||||
@ -54,7 +57,6 @@ impl ProcessManager<'_> {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
current_platform: Platform::Linux,
|
current_platform: Platform::Linux,
|
||||||
|
|
||||||
app_handle,
|
|
||||||
processes: HashMap::new(),
|
processes: HashMap::new(),
|
||||||
log_output_dir,
|
log_output_dir,
|
||||||
game_launchers: vec![
|
game_launchers: vec![
|
||||||
@ -102,21 +104,16 @@ impl ProcessManager<'_> {
|
|||||||
self.log_output_dir.join(game_id)
|
self.log_output_dir.join(game_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_process_logs(&mut self, game_id: String) -> Result<(), ProcessError> {
|
fn on_process_finish(
|
||||||
let dir = self.get_log_dir(game_id);
|
&mut self,
|
||||||
self.app_handle
|
game_id: String,
|
||||||
.opener()
|
result: Result<ExitStatus, std::io::Error>,
|
||||||
.open_path(dir.display().to_string(), None::<&str>)
|
) -> Result<(), ProcessError> {
|
||||||
.map_err(ProcessError::OpenerError)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_process_finish(&mut self, game_id: String, result: Result<ExitStatus, std::io::Error>) {
|
|
||||||
if !self.processes.contains_key(&game_id) {
|
if !self.processes.contains_key(&game_id) {
|
||||||
warn!(
|
warn!(
|
||||||
"process on_finish was called, but game_id is no longer valid. finished with result: {result:?}"
|
"process on_finish was called, but game_id is no longer valid. finished with result: {result:?}"
|
||||||
);
|
);
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("process for {:?} exited with {:?}", &game_id, result);
|
debug!("process for {:?} exited with {:?}", &game_id, result);
|
||||||
@ -125,7 +122,7 @@ impl ProcessManager<'_> {
|
|||||||
Some(process) => process,
|
Some(process) => process,
|
||||||
None => {
|
None => {
|
||||||
info!("Attempted to stop process {game_id} which didn't exist");
|
info!("Attempted to stop process {game_id} which didn't exist");
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,7 +160,8 @@ impl ProcessManager<'_> {
|
|||||||
&& (elapsed.as_secs() <= 2 || result.map_or(true, |r| !r.success()))
|
&& (elapsed.as_secs() <= 2 || result.map_or(true, |r| !r.success()))
|
||||||
{
|
{
|
||||||
warn!("drop detected that the game {game_id} may have failed to launch properly");
|
warn!("drop detected that the game {game_id} may have failed to launch properly");
|
||||||
let _ = self.app_handle.emit("launch_external_error", &game_id);
|
return Err(ProcessError::FailedLaunch(game_id));
|
||||||
|
// let _ = self.app_handle.emit("launch_external_error", &game_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let version_data = match db_handle.applications.game_versions.get(&game_id) {
|
let version_data = match db_handle.applications.game_versions.get(&game_id) {
|
||||||
@ -174,18 +172,19 @@ impl ProcessManager<'_> {
|
|||||||
|
|
||||||
let status = GameStatusManager::fetch_state(&game_id, &db_handle);
|
let status = GameStatusManager::fetch_state(&game_id, &db_handle);
|
||||||
|
|
||||||
push_game_update(
|
// TODO
|
||||||
&self.app_handle,
|
// push_game_update(
|
||||||
&game_id,
|
// &self.app_handle,
|
||||||
Some(version_data.clone()),
|
// &game_id,
|
||||||
status,
|
// Some(version_data.clone()),
|
||||||
);
|
// status,
|
||||||
|
// );
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_process_handler(
|
fn fetch_process_handler(
|
||||||
&self,
|
&self,
|
||||||
db_lock: &Database,
|
db_lock: &Database,
|
||||||
state: &AppState,
|
|
||||||
target_platform: &Platform,
|
target_platform: &Platform,
|
||||||
) -> Result<&(dyn ProcessHandler + Send + Sync), ProcessError> {
|
) -> Result<&(dyn ProcessHandler + Send + Sync), ProcessError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
@ -195,23 +194,20 @@ impl ProcessManager<'_> {
|
|||||||
let (e_current, e_target) = e.0;
|
let (e_current, e_target) = e.0;
|
||||||
e_current == self.current_platform
|
e_current == self.current_platform
|
||||||
&& e_target == *target_platform
|
&& e_target == *target_platform
|
||||||
&& e.1.valid_for_platform(db_lock, state, target_platform)
|
&& e.1.valid_for_platform(db_lock, target_platform)
|
||||||
})
|
})
|
||||||
.ok_or(ProcessError::InvalidPlatform)?
|
.ok_or(ProcessError::InvalidPlatform)?
|
||||||
.1)
|
.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn valid_platform(&self, platform: &Platform, state: &AppState) -> bool {
|
pub fn valid_platform(&self, platform: &Platform) -> bool {
|
||||||
let db_lock = borrow_db_checked();
|
let db_lock = borrow_db_checked();
|
||||||
let process_handler = self.fetch_process_handler(&db_lock, state, platform);
|
let process_handler = self.fetch_process_handler(&db_lock, platform);
|
||||||
process_handler.is_ok()
|
process_handler.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn launch_process(
|
/// Must be called through spawn as it is currently blocking
|
||||||
&mut self,
|
pub fn launch_process(&mut self, game_id: String) -> Result<(), ProcessError> {
|
||||||
game_id: String,
|
|
||||||
state: &AppState,
|
|
||||||
) -> Result<(), ProcessError> {
|
|
||||||
if self.processes.contains_key(&game_id) {
|
if self.processes.contains_key(&game_id) {
|
||||||
return Err(ProcessError::AlreadyRunning);
|
return Err(ProcessError::AlreadyRunning);
|
||||||
}
|
}
|
||||||
@ -293,7 +289,7 @@ impl ProcessManager<'_> {
|
|||||||
|
|
||||||
let target_platform = game_version.platform;
|
let target_platform = game_version.platform;
|
||||||
|
|
||||||
let process_handler = self.fetch_process_handler(&db_lock, state, &target_platform)?;
|
let process_handler = self.fetch_process_handler(&db_lock, &target_platform)?;
|
||||||
|
|
||||||
let (launch, args) = match game_status {
|
let (launch, args) = match game_status {
|
||||||
GameDownloadStatus::Installed {
|
GameDownloadStatus::Installed {
|
||||||
@ -367,35 +363,17 @@ impl ProcessManager<'_> {
|
|||||||
.transient_statuses
|
.transient_statuses
|
||||||
.insert(meta.clone(), ApplicationTransientStatus::Running {});
|
.insert(meta.clone(), ApplicationTransientStatus::Running {});
|
||||||
|
|
||||||
push_game_update(
|
// TODO
|
||||||
&self.app_handle,
|
// push_game_update(
|
||||||
&meta.id,
|
// &self.app_handle,
|
||||||
None,
|
// &meta.id,
|
||||||
(None, Some(ApplicationTransientStatus::Running {})),
|
// None,
|
||||||
);
|
// (None, Some(ApplicationTransientStatus::Running {})),
|
||||||
|
// );
|
||||||
|
|
||||||
let wait_thread_handle = launch_process_handle.clone();
|
let wait_thread_handle = launch_process_handle.clone();
|
||||||
let wait_thread_apphandle = self.app_handle.clone();
|
|
||||||
let wait_thread_game_id = meta.clone();
|
let wait_thread_game_id = meta.clone();
|
||||||
|
|
||||||
spawn(move || {
|
|
||||||
let result: Result<ExitStatus, std::io::Error> = launch_process_handle.wait();
|
|
||||||
|
|
||||||
let app_state = wait_thread_apphandle.state::<Mutex<AppState>>();
|
|
||||||
let app_state_handle = lock!(app_state);
|
|
||||||
|
|
||||||
let mut process_manager_handle = app_state_handle
|
|
||||||
.process_manager
|
|
||||||
.lock()
|
|
||||||
.expect("Failed to lock onto process manager");
|
|
||||||
process_manager_handle.on_process_finish(wait_thread_game_id.id, result);
|
|
||||||
|
|
||||||
// As everything goes out of scope, they should get dropped
|
|
||||||
// But just to explicit about it
|
|
||||||
drop(process_manager_handle);
|
|
||||||
drop(app_state_handle);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.processes.insert(
|
self.processes.insert(
|
||||||
meta.id,
|
meta.id,
|
||||||
RunningProcess {
|
RunningProcess {
|
||||||
@ -404,7 +382,12 @@ impl ProcessManager<'_> {
|
|||||||
manually_killed: false,
|
manually_killed: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(())
|
|
||||||
|
let result: Result<ExitStatus, std::io::Error> = launch_process_handle.wait();
|
||||||
|
|
||||||
|
PROCESS_MANAGER
|
||||||
|
.lock()
|
||||||
|
.on_process_finish(wait_thread_game_id.id, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,5 +401,5 @@ pub trait ProcessHandler: Send + 'static {
|
|||||||
current_dir: &str,
|
current_dir: &str,
|
||||||
) -> Result<String, ProcessError>;
|
) -> Result<String, ProcessError>;
|
||||||
|
|
||||||
fn valid_for_platform(&self, db: &Database, state: &AppState, target: &Platform) -> bool;
|
fn valid_for_platform(&self, db: &Database, target: &Platform) -> bool;
|
||||||
}
|
}
|
||||||
2136
src-tauri/Cargo.lock
generated
@ -1,14 +1,137 @@
|
|||||||
[workspace]
|
[package]
|
||||||
members = [
|
name = "drop-app"
|
||||||
"client",
|
version = "0.3.3"
|
||||||
"database",
|
description = "The client application for the open-source, self-hosted game distribution platform Drop"
|
||||||
"src-tauri",
|
authors = ["Drop OSS"]
|
||||||
"process",
|
edition = "2024"
|
||||||
"remote",
|
|
||||||
"utils",
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
"cloud_saves",
|
|
||||||
"download_manager",
|
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\"))".dependencies]
|
||||||
"games",
|
tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
# The `_lib` suffix may seem redundant but it is necessary
|
||||||
|
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||||
|
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||||
|
name = "drop_app_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
rustflags = ["-C", "target-feature=+aes,+sse2"]
|
||||||
|
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2.0.0", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri-plugin-shell = "2.2.1"
|
||||||
|
serde_json = "1"
|
||||||
|
rayon = "1.10.0"
|
||||||
|
webbrowser = "1.0.2"
|
||||||
|
url = "2.5.2"
|
||||||
|
tauri-plugin-deep-link = "2"
|
||||||
|
log = "0.4.22"
|
||||||
|
hex = "0.4.3"
|
||||||
|
tauri-plugin-dialog = "2"
|
||||||
|
http = "1.1.0"
|
||||||
|
urlencoding = "2.1.3"
|
||||||
|
md5 = "0.7.0"
|
||||||
|
chrono = "0.4.38"
|
||||||
|
tauri-plugin-os = "2"
|
||||||
|
boxcar = "0.2.7"
|
||||||
|
umu-wrapper-lib = "0.1.0"
|
||||||
|
tauri-plugin-autostart = "2.0.0"
|
||||||
|
shared_child = "1.0.1"
|
||||||
|
serde_with = "3.12.0"
|
||||||
|
slice-deque = "0.3.0"
|
||||||
|
throttle_my_fn = "0.2.6"
|
||||||
|
parking_lot = "0.12.3"
|
||||||
|
atomic-instant-full = "0.1.0"
|
||||||
|
cacache = "13.1.0"
|
||||||
|
http-serde = "2.1.1"
|
||||||
|
reqwest-middleware = "0.4.0"
|
||||||
|
reqwest-middleware-cache = "0.1.1"
|
||||||
|
deranged = "=0.4.0"
|
||||||
|
droplet-rs = "0.7.3"
|
||||||
|
gethostname = "1.0.1"
|
||||||
|
zstd = "0.13.3"
|
||||||
|
tar = "0.4.44"
|
||||||
|
rand = "0.9.1"
|
||||||
|
regex = "1.11.1"
|
||||||
|
tempfile = "3.19.1"
|
||||||
|
schemars = "0.8.22"
|
||||||
|
sha1 = "0.10.6"
|
||||||
|
dirs = "6.0.0"
|
||||||
|
whoami = "1.6.0"
|
||||||
|
filetime = "0.2.25"
|
||||||
|
walkdir = "2.5.0"
|
||||||
|
known-folders = "1.2.0"
|
||||||
|
native_model = { version = "0.6.4", features = ["rmp_serde_1_3"], git = "https://github.com/Drop-OSS/native_model.git"}
|
||||||
|
tauri-plugin-opener = "2.4.0"
|
||||||
|
bitcode = "0.6.6"
|
||||||
|
reqwest-websocket = "0.5.0"
|
||||||
|
futures-lite = "2.6.0"
|
||||||
|
page_size = "0.6.0"
|
||||||
|
sysinfo = "0.36.1"
|
||||||
|
humansize = "2.1.3"
|
||||||
|
tokio-util = { version = "0.7.16", features = ["io"] }
|
||||||
|
futures-core = "0.3.31"
|
||||||
|
bytes = "1.10.1"
|
||||||
|
# tailscale = { path = "./tailscale" }
|
||||||
|
|
||||||
|
|
||||||
|
# Workspaces
|
||||||
|
client = { path = "../client" }
|
||||||
|
database = { path = "../database" }
|
||||||
|
process = { path = "../process" }
|
||||||
|
remote = { path = "../remote" }
|
||||||
|
utils = { path = "../utils" }
|
||||||
|
|
||||||
|
[dependencies.dynfmt]
|
||||||
|
version = "0.1.5"
|
||||||
|
features = ["curly"]
|
||||||
|
|
||||||
|
[dependencies.tauri]
|
||||||
|
version = "2.7.0"
|
||||||
|
features = ["protocol-asset", "tray-icon"]
|
||||||
|
|
||||||
|
[dependencies.tokio]
|
||||||
|
version = "1.40.0"
|
||||||
|
features = ["rt", "tokio-macros", "signal"]
|
||||||
|
|
||||||
|
[dependencies.log4rs]
|
||||||
|
version = "1.3.0"
|
||||||
|
features = ["console_appender", "file_appender"]
|
||||||
|
|
||||||
|
[dependencies.rustix]
|
||||||
|
version = "0.38.37"
|
||||||
|
features = ["fs"]
|
||||||
|
|
||||||
|
[dependencies.uuid]
|
||||||
|
version = "1.10.0"
|
||||||
|
features = ["v4", "fast-rng", "macro-diagnostics"]
|
||||||
|
|
||||||
|
[dependencies.rustbreak]
|
||||||
|
version = "2"
|
||||||
|
features = ["other_errors"] # You can also use "yaml_enc" or "bin_enc"
|
||||||
|
|
||||||
|
[dependencies.reqwest]
|
||||||
|
version = "0.12.22"
|
||||||
|
default-features = false
|
||||||
|
features = [
|
||||||
|
"json",
|
||||||
|
"http2",
|
||||||
|
"blocking",
|
||||||
|
"rustls-tls",
|
||||||
|
"native-tls-alpn",
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
"stream",
|
||||||
]
|
]
|
||||||
|
|
||||||
resolver = "3"
|
[dependencies.serde]
|
||||||
|
version = "1"
|
||||||
|
features = ["derive", "rc"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = 'abort'
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
pub mod autostart;
|
|
||||||
pub mod user;
|
|
||||||
pub mod app_status;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
pub enum BackupError {
|
|
||||||
InvalidSystem,
|
|
||||||
ParseError,
|
|
||||||
NotFound
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 911 B After Width: | Height: | Size: 911 B |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 803 B |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |