Game updates (#187)
* refactor: split umu launcher * feat: latest version picker + fixes * feat: frontend latest changes * feat: game update detection w/ setting * feat: fixes and refactor for game update * fix: windows ui * fix: deps * feat: update modifications * feat: missing ui and lock update * fix: create install dir on init * fix: clippy * fix: clippy x2 * feat: add configuration option to toggle updates * feat: uninstall dropdown on partiallyinstalled
This commit is contained in:
@@ -25,8 +25,53 @@ impl ProcessHandler for NativeGameLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UMULauncher;
|
||||
impl ProcessHandler for UMULauncher {
|
||||
pub struct UMUNativeLauncher;
|
||||
impl ProcessHandler for UMUNativeLauncher {
|
||||
fn create_launch_process(
|
||||
&self,
|
||||
meta: &DownloadableMetadata,
|
||||
launch_command: String,
|
||||
game_version: &GameVersion,
|
||||
_current_dir: &str,
|
||||
_database: &Database,
|
||||
) -> Result<String, ProcessError> {
|
||||
let umu_id_override = game_version
|
||||
.launches
|
||||
.iter()
|
||||
.find(|v| v.platform == meta.target_platform)
|
||||
.and_then(|v| v.umu_id_override.as_ref())
|
||||
.map_or("", |v| v);
|
||||
|
||||
let game_id = if umu_id_override.is_empty() {
|
||||
&game_version.version_id
|
||||
} else {
|
||||
umu_id_override
|
||||
};
|
||||
|
||||
let pfx_dir = DATA_ROOT_DIR.join("pfx");
|
||||
let pfx_dir = pfx_dir.join(meta.id.clone());
|
||||
create_dir_all(&pfx_dir)?;
|
||||
|
||||
Ok(format!(
|
||||
"GAMEID={game_id} UMU_NO_PROTON=1 WINEPREFIX={} {umu:?} {launch}",
|
||||
pfx_dir.to_string_lossy(),
|
||||
umu = UMU_LAUNCHER_EXECUTABLE
|
||||
.as_ref()
|
||||
.expect("Failed to get UMU_LAUNCHER_EXECUTABLE as ref"),
|
||||
launch = launch_command,
|
||||
))
|
||||
}
|
||||
|
||||
fn valid_for_platform(&self, _db: &Database, _target: &Platform) -> bool {
|
||||
let Some(compat_info) = &*COMPAT_INFO else {
|
||||
return false;
|
||||
};
|
||||
compat_info.umu_installed
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UMUCompatLauncher;
|
||||
impl ProcessHandler for UMUCompatLauncher {
|
||||
fn create_launch_process(
|
||||
&self,
|
||||
meta: &DownloadableMetadata,
|
||||
@@ -52,39 +97,29 @@ impl ProcessHandler for UMULauncher {
|
||||
let pfx_dir = pfx_dir.join(meta.id.clone());
|
||||
create_dir_all(&pfx_dir)?;
|
||||
|
||||
let no_proton = match meta.target_platform {
|
||||
Platform::Linux => Some("UMU_NO_PROTON=1"),
|
||||
_ => None,
|
||||
};
|
||||
let proton_path = game_version
|
||||
.user_configuration
|
||||
.override_proton_path
|
||||
.as_ref()
|
||||
.or(database.applications.default_proton_path.as_ref())
|
||||
.ok_or(ProcessError::NoCompat)?;
|
||||
|
||||
let proton_env = if no_proton.is_none() {
|
||||
let proton_path = game_version
|
||||
.user_configuration
|
||||
.override_proton_path
|
||||
.as_ref()
|
||||
.or(database.applications.default_proton_path.as_ref())
|
||||
.ok_or(ProcessError::NoCompat)?;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let proton_valid = crate::compat::read_proton_path(PathBuf::from(proton_path))
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some();
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let proton_valid = false;
|
||||
if !proton_valid {
|
||||
return Err(ProcessError::NoCompat);
|
||||
}
|
||||
Some(format!("PROTONPATH={}", proton_path))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
let proton_valid = crate::compat::read_proton_path(PathBuf::from(proton_path))
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some();
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let proton_valid = false;
|
||||
if !proton_valid {
|
||||
return Err(ProcessError::NoCompat);
|
||||
}
|
||||
let proton_env = format!("PROTONPATH={}", proton_path);
|
||||
|
||||
Ok(format!(
|
||||
"GAMEID={game_id} {} WINEPREFIX={} {} {umu:?} {launch}",
|
||||
proton_env.unwrap_or(String::new()),
|
||||
"GAMEID={game_id} {} WINEPREFIX={} {umu:?} {launch}",
|
||||
proton_env,
|
||||
pfx_dir.to_string_lossy(),
|
||||
no_proton.unwrap_or(""),
|
||||
umu = UMU_LAUNCHER_EXECUTABLE
|
||||
.as_ref()
|
||||
.expect("Failed to get UMU_LAUNCHER_EXECUTABLE as ref"),
|
||||
@@ -110,7 +145,7 @@ impl ProcessHandler for AsahiMuvmLauncher {
|
||||
current_dir: &str,
|
||||
database: &Database,
|
||||
) -> Result<String, ProcessError> {
|
||||
let umu_launcher = UMULauncher {};
|
||||
let umu_launcher = UMUCompatLauncher {};
|
||||
let umu_string = umu_launcher.create_launch_process(
|
||||
meta,
|
||||
launch_command,
|
||||
|
||||
@@ -11,7 +11,8 @@ use std::{
|
||||
|
||||
use database::{
|
||||
ApplicationTransientStatus, Database, DownloadableMetadata, GameDownloadStatus, GameVersion,
|
||||
borrow_db_checked, borrow_db_mut_checked, db::DATA_ROOT_DIR, platform::Platform,
|
||||
borrow_db_checked, borrow_db_mut_checked, db::DATA_ROOT_DIR, models::data::InstalledGameType,
|
||||
platform::Platform,
|
||||
};
|
||||
use dynfmt::Format;
|
||||
use dynfmt::SimpleCurlyFormat;
|
||||
@@ -26,7 +27,9 @@ use crate::{
|
||||
error::ProcessError,
|
||||
format::DropFormatArgs,
|
||||
parser::{LaunchParameters, ParsedCommand},
|
||||
process_handlers::{AsahiMuvmLauncher, NativeGameLauncher, UMULauncher},
|
||||
process_handlers::{
|
||||
AsahiMuvmLauncher, NativeGameLauncher, UMUCompatLauncher, UMUNativeLauncher,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct RunningProcess {
|
||||
@@ -75,7 +78,7 @@ impl ProcessManager<'_> {
|
||||
),
|
||||
(
|
||||
(Platform::Linux, Platform::Linux),
|
||||
&UMULauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
|
||||
&UMUNativeLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
|
||||
),
|
||||
(
|
||||
(Platform::macOS, Platform::macOS),
|
||||
@@ -87,7 +90,7 @@ impl ProcessManager<'_> {
|
||||
),
|
||||
(
|
||||
(Platform::Linux, Platform::Windows),
|
||||
&UMULauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
|
||||
&UMUCompatLauncher {} as &(dyn ProcessHandler + Sync + Send + 'static),
|
||||
),
|
||||
],
|
||||
app_handle,
|
||||
@@ -145,21 +148,15 @@ impl ProcessManager<'_> {
|
||||
.unwrap_or_else(|| panic!("Could not get installed version of {}", &game_id));
|
||||
db_handle.applications.transient_statuses.remove(&meta);
|
||||
|
||||
let current_state = db_handle.applications.game_statuses.get(&game_id).cloned();
|
||||
if let Some(GameDownloadStatus::SetupRequired {
|
||||
version_name,
|
||||
install_dir,
|
||||
let current_state = db_handle.applications.game_statuses.get_mut(&game_id);
|
||||
if let Some(GameDownloadStatus::Installed {
|
||||
install_type,
|
||||
..
|
||||
}) = current_state
|
||||
&& let Ok(exit_code) = result
|
||||
&& exit_code.success()
|
||||
{
|
||||
db_handle.applications.game_statuses.insert(
|
||||
game_id.clone(),
|
||||
GameDownloadStatus::Installed {
|
||||
version_name: version_name.to_string(),
|
||||
install_dir: install_dir.to_string(),
|
||||
},
|
||||
);
|
||||
*install_type = InstalledGameType::Installed;
|
||||
}
|
||||
|
||||
let elapsed = process.start.elapsed().unwrap_or(Duration::ZERO);
|
||||
@@ -268,12 +265,10 @@ impl ProcessManager<'_> {
|
||||
|
||||
let (version_name, install_dir) = match game_status {
|
||||
GameDownloadStatus::Installed {
|
||||
version_name,
|
||||
install_dir,
|
||||
} => (version_name, install_dir),
|
||||
GameDownloadStatus::SetupRequired {
|
||||
version_name,
|
||||
version_id: version_name,
|
||||
install_dir,
|
||||
install_type: InstalledGameType::Installed | InstalledGameType::SetupRequired,
|
||||
..
|
||||
} => (version_name, install_dir),
|
||||
_ => return Err(ProcessError::NotInstalled),
|
||||
};
|
||||
@@ -323,8 +318,8 @@ impl ProcessManager<'_> {
|
||||
|
||||
let (target_command, emulator) = match game_status {
|
||||
GameDownloadStatus::Installed {
|
||||
version_name: _,
|
||||
install_dir: _,
|
||||
install_type: InstalledGameType::Installed,
|
||||
..
|
||||
} => {
|
||||
let (_, launch_config) = game_version
|
||||
.launches
|
||||
@@ -338,9 +333,9 @@ impl ProcessManager<'_> {
|
||||
launch_config.emulator.as_ref(),
|
||||
)
|
||||
}
|
||||
GameDownloadStatus::SetupRequired {
|
||||
version_name: _,
|
||||
install_dir: _,
|
||||
GameDownloadStatus::Installed {
|
||||
install_type: InstalledGameType::SetupRequired,
|
||||
..
|
||||
} => {
|
||||
let setup_config = game_version
|
||||
.setups
|
||||
@@ -375,12 +370,12 @@ impl ProcessManager<'_> {
|
||||
|
||||
let emulator_install_dir = match emulator_game_status {
|
||||
GameDownloadStatus::Installed {
|
||||
version_name: _,
|
||||
install_dir,
|
||||
install_type: InstalledGameType::Installed,
|
||||
..
|
||||
} => Ok(install_dir),
|
||||
GameDownloadStatus::SetupRequired {
|
||||
version_name: _,
|
||||
install_dir: _,
|
||||
GameDownloadStatus::Installed {
|
||||
install_type: InstalledGameType::SetupRequired,
|
||||
..
|
||||
} => todo!(),
|
||||
_ => Err(err.clone()),
|
||||
}?;
|
||||
@@ -407,8 +402,6 @@ impl ProcessManager<'_> {
|
||||
*v = v.replace("{rom}", &target_command.command);
|
||||
});
|
||||
|
||||
|
||||
|
||||
process_handler.create_launch_process(
|
||||
emulator_metadata,
|
||||
exe_command.reconstruct(),
|
||||
@@ -417,8 +410,6 @@ impl ProcessManager<'_> {
|
||||
&db_lock,
|
||||
)?
|
||||
} else {
|
||||
|
||||
|
||||
process_handler.create_launch_process(
|
||||
&meta,
|
||||
target_command.reconstruct(),
|
||||
@@ -474,9 +465,10 @@ impl ProcessManager<'_> {
|
||||
.map(|e| e.split("=").map(|v| v.to_string()).collect::<Vec<String>>())
|
||||
{
|
||||
if let Some(key) = parts.first()
|
||||
&& let Some(value) = parts.get(1) {
|
||||
command.env(key, value);
|
||||
}
|
||||
&& let Some(value) = parts.get(1)
|
||||
{
|
||||
command.env(key, value);
|
||||
}
|
||||
}
|
||||
command
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user