mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-25 17:24:48 +10:00
feat(process): shared child with stop command
This commit is contained in:
@@ -57,6 +57,7 @@ const emit = defineEmits<{
|
|||||||
(e: "launch"): void;
|
(e: "launch"): void;
|
||||||
(e: "queue"): void;
|
(e: "queue"): void;
|
||||||
(e: "uninstall"): void;
|
(e: "uninstall"): void;
|
||||||
|
(e: "kill"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const showDropdown = computed(() => props.status.type === GameStatusEnum.Installed || props.status.type === GameStatusEnum.SetupRequired);
|
const showDropdown = computed(() => props.status.type === GameStatusEnum.Installed || props.status.type === GameStatusEnum.SetupRequired);
|
||||||
@@ -69,7 +70,7 @@ const styles: { [key in GameStatusEnum]: string } = {
|
|||||||
[GameStatusEnum.Installed]: "bg-green-600 text-white hover:bg-green-500 focus-visible:outline-green-600",
|
[GameStatusEnum.Installed]: "bg-green-600 text-white hover:bg-green-500 focus-visible:outline-green-600",
|
||||||
[GameStatusEnum.Updating]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
[GameStatusEnum.Updating]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
||||||
[GameStatusEnum.Uninstalling]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
[GameStatusEnum.Uninstalling]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
||||||
[GameStatusEnum.Running]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700"
|
[GameStatusEnum.Running]: "bg-zinc-800 text-white focus-visible:outline-zinc-700"
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonNames: { [key in GameStatusEnum]: string } = {
|
const buttonNames: { [key in GameStatusEnum]: string } = {
|
||||||
@@ -80,7 +81,7 @@ const buttonNames: { [key in GameStatusEnum]: string } = {
|
|||||||
[GameStatusEnum.Installed]: "Play",
|
[GameStatusEnum.Installed]: "Play",
|
||||||
[GameStatusEnum.Updating]: "Updating",
|
[GameStatusEnum.Updating]: "Updating",
|
||||||
[GameStatusEnum.Uninstalling]: "Uninstalling",
|
[GameStatusEnum.Uninstalling]: "Uninstalling",
|
||||||
[GameStatusEnum.Running]: "Running"
|
[GameStatusEnum.Running]: "Stop"
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonIcons: { [key in GameStatusEnum]: Component } = {
|
const buttonIcons: { [key in GameStatusEnum]: Component } = {
|
||||||
@@ -102,6 +103,6 @@ const buttonActions: { [key in GameStatusEnum]: () => void } = {
|
|||||||
[GameStatusEnum.Installed]: () => emit("launch"),
|
[GameStatusEnum.Installed]: () => emit("launch"),
|
||||||
[GameStatusEnum.Updating]: () => emit("queue"),
|
[GameStatusEnum.Updating]: () => emit("queue"),
|
||||||
[GameStatusEnum.Uninstalling]: () => { },
|
[GameStatusEnum.Uninstalling]: () => { },
|
||||||
[GameStatusEnum.Running]: () => { }
|
[GameStatusEnum.Running]: () => emit("kill")
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
@launch="() => launch()"
|
@launch="() => launch()"
|
||||||
@queue="() => queue()"
|
@queue="() => queue()"
|
||||||
@uninstall="() => uninstall()"
|
@uninstall="() => uninstall()"
|
||||||
|
@kill="() => kill()"
|
||||||
:status="status"
|
:status="status"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
@@ -235,7 +236,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
@click="() => install()"
|
@click="() => install()"
|
||||||
:disabled="
|
:disabled="
|
||||||
!(versionOptions && versionOptions.length > 0 && !installDir)
|
!(versionOptions && versionOptions.length > 0 && !installDir)
|
||||||
"
|
"
|
||||||
@@ -299,7 +300,7 @@ const installDirs = ref<undefined | Array<string>>();
|
|||||||
async function installFlow() {
|
async function installFlow() {
|
||||||
installFlowOpen.value = true;
|
installFlowOpen.value = true;
|
||||||
versionOptions.value = undefined;
|
versionOptions.value = undefined;
|
||||||
installDirs.value = undefined
|
installDirs.value = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
versionOptions.value = await invoke("fetch_game_verion_options", {
|
versionOptions.value = await invoke("fetch_game_verion_options", {
|
||||||
@@ -357,4 +358,21 @@ async function queue() {
|
|||||||
async function uninstall() {
|
async function uninstall() {
|
||||||
await invoke("uninstall_game", { gameId: game.value.id });
|
await invoke("uninstall_game", { gameId: game.value.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function kill() {
|
||||||
|
try {
|
||||||
|
await invoke("kill_game", { gameId: game.value.id });
|
||||||
|
} catch (e) {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: `Couldn't stop "${game.value.mName}"`,
|
||||||
|
description: `Drop failed to stop "${game.value.mName}": ${e}`,
|
||||||
|
buttonText: "Close",
|
||||||
|
},
|
||||||
|
(e, c) => c()
|
||||||
|
);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Generated
+1
@@ -987,6 +987,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde-binary",
|
"serde-binary",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"shared_child",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-deep-link",
|
"tauri-plugin-deep-link",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ chrono = "0.4.38"
|
|||||||
tauri-plugin-os = "2"
|
tauri-plugin-os = "2"
|
||||||
boxcar = "0.2.7"
|
boxcar = "0.2.7"
|
||||||
umu-wrapper-lib = "0.1.0"
|
umu-wrapper-lib = "0.1.0"
|
||||||
|
shared_child = "1.0.1"
|
||||||
|
|
||||||
[dependencies.tauri]
|
[dependencies.tauri]
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ pub fn kill_game(
|
|||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let state_lock = state.lock().unwrap();
|
let state_lock = state.lock().unwrap();
|
||||||
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
let mut process_manager_lock = state_lock.process_manager.lock().unwrap();
|
||||||
process_manager_lock.terminate_child(game_id).map_err(|x| x.to_string())
|
process_manager_lock.kill_game(game_id).map_err(|x| x.to_string())
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap, fs::{File, OpenOptions}, io, path::{Path, PathBuf}, process::{Child, Command, ExitStatus}, sync::{Arc, Mutex}, thread::spawn
|
collections::HashMap,
|
||||||
|
fs::{File, OpenOptions},
|
||||||
|
io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::{Child, Command, ExitStatus},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::spawn,
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use shared_child::SharedChild;
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use umu_wrapper_lib::command_builder::UmuCommandBuilder;
|
use umu_wrapper_lib::command_builder::UmuCommandBuilder;
|
||||||
|
|
||||||
@@ -17,7 +24,7 @@ use crate::{
|
|||||||
pub struct ProcessManager<'a> {
|
pub struct ProcessManager<'a> {
|
||||||
current_platform: Platform,
|
current_platform: Platform,
|
||||||
log_output_dir: PathBuf,
|
log_output_dir: PathBuf,
|
||||||
processes: HashMap<String, Arc<Mutex<Child>>>,
|
processes: HashMap<String, Arc<SharedChild>>,
|
||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
game_launchers: HashMap<(Platform, Platform), &'a (dyn ProcessHandler + Sync + Send + 'static)>,
|
game_launchers: HashMap<(Platform, Platform), &'a (dyn ProcessHandler + Sync + Send + 'static)>,
|
||||||
}
|
}
|
||||||
@@ -75,14 +82,18 @@ impl ProcessManager<'_> {
|
|||||||
*/
|
*/
|
||||||
(absolute_exe, Vec::new())
|
(absolute_exe, Vec::new())
|
||||||
}
|
}
|
||||||
pub fn terminate_child(&mut self, game_id: String) -> Result<(), io::Error> {
|
pub fn kill_game(&mut self, game_id: String) -> Result<(), io::Error> {
|
||||||
return match self.processes.get(&game_id) {
|
return match self.processes.get(&game_id) {
|
||||||
Some(child) => {
|
Some(child) => {
|
||||||
let mut lock = child.lock().unwrap();
|
child.kill()?;
|
||||||
lock.kill()
|
child.wait()?;
|
||||||
|
Ok(())
|
||||||
},
|
},
|
||||||
None => Err(io::Error::new(io::ErrorKind::NotFound, "Game ID not running")),
|
None => Err(io::Error::new(
|
||||||
}
|
io::ErrorKind::NotFound,
|
||||||
|
"Game ID not running",
|
||||||
|
)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_process_finish(&mut self, game_id: String, result: Result<ExitStatus, std::io::Error>) {
|
fn on_process_finish(&mut self, game_id: String, result: Result<ExitStatus, std::io::Error>) {
|
||||||
@@ -123,6 +134,8 @@ impl ProcessManager<'_> {
|
|||||||
let status = GameStatusManager::fetch_state(&game_id);
|
let status = GameStatusManager::fetch_state(&game_id);
|
||||||
|
|
||||||
push_game_update(&self.app_handle, game_id.clone(), status);
|
push_game_update(&self.app_handle, game_id.clone(), status);
|
||||||
|
|
||||||
|
// TODO better management
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn valid_platform(&self, platform: &Platform) -> Result<bool, String> {
|
pub fn valid_platform(&self, platform: &Platform) -> Result<bool, String> {
|
||||||
@@ -235,7 +248,8 @@ impl ProcessManager<'_> {
|
|||||||
error_file,
|
error_file,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let launch_process_handle = Arc::new(Mutex::new(launch_process));
|
let launch_process_handle =
|
||||||
|
Arc::new(SharedChild::new(launch_process).map_err(|e| e.to_string())?);
|
||||||
|
|
||||||
db_lock
|
db_lock
|
||||||
.games
|
.games
|
||||||
@@ -253,8 +267,7 @@ impl ProcessManager<'_> {
|
|||||||
let wait_thread_game_id = game_id.clone();
|
let wait_thread_game_id = game_id.clone();
|
||||||
|
|
||||||
spawn(move || {
|
spawn(move || {
|
||||||
let mut child_handle = wait_thread_handle.lock().unwrap();
|
let result: Result<ExitStatus, std::io::Error> = launch_process_handle.wait();
|
||||||
let result: Result<ExitStatus, std::io::Error> = child_handle.wait();
|
|
||||||
|
|
||||||
let app_state = wait_thread_apphandle.state::<Mutex<AppState>>();
|
let app_state = wait_thread_apphandle.state::<Mutex<AppState>>();
|
||||||
let app_state_handle = app_state.lock().unwrap();
|
let app_state_handle = app_state.lock().unwrap();
|
||||||
@@ -266,10 +279,9 @@ impl ProcessManager<'_> {
|
|||||||
// But just to explicit about it
|
// But just to explicit about it
|
||||||
drop(process_manager_handle);
|
drop(process_manager_handle);
|
||||||
drop(app_state_handle);
|
drop(app_state_handle);
|
||||||
drop(child_handle);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.processes.insert(game_id, launch_process_handle);
|
self.processes.insert(game_id, wait_thread_handle);
|
||||||
|
|
||||||
info!("finished spawning process");
|
info!("finished spawning process");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user