chore: make updater thread non blocking

This commit is contained in:
HorizonCode 2025-07-07 08:46:30 +02:00
parent 67352bfe37
commit 8f7269ba1b
3 changed files with 87 additions and 70 deletions

1
src-tauri/Cargo.lock generated
View File

@ -1101,6 +1101,7 @@ dependencies = [
"tauri-plugin-sql", "tauri-plugin-sql",
"tokio", "tokio",
"winapi", "winapi",
"windows-sys 0.60.2",
"winreg 0.55.0", "winreg 0.55.0",
] ]

View File

@ -35,6 +35,7 @@ reqwest = { version = "0.12.22", features = ["json", "stream"] }
md5 = "0.8.0" md5 = "0.8.0"
tokio = { version = "1.46.0", features = ["full"] } tokio = { version = "1.46.0", features = ["full"] }
open = "5.3.2" open = "5.3.2"
windows-sys = "0.60.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = "2.3.0" tauri-plugin-single-instance = "2.3.0"

View File

@ -3,14 +3,13 @@ use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command;
use std::thread;
use std::time::Duration;
use sysinfo::System; use sysinfo::System;
use tauri::AppHandle; use tauri::AppHandle;
use tauri::Emitter; use tauri::Emitter;
use tokio::fs; use tokio::fs;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use tokio::time::{Duration, sleep};
use winreg::RegKey; use winreg::RegKey;
use winreg::enums::*; use winreg::enums::*;
@ -18,7 +17,6 @@ use crate::utils::{
check_folder_completeness, get_osu_config, get_osu_user_config, get_window_title_by_pid, check_folder_completeness, get_osu_config, get_osu_user_config, get_window_title_by_pid,
set_osu_config_vals, set_osu_user_config_vals, set_osu_config_vals, set_osu_user_config_vals,
}; };
use std::os::windows::process::CommandExt;
#[tauri::command] #[tauri::command]
pub fn get_hwid() -> String { pub fn get_hwid() -> String {
@ -269,110 +267,128 @@ pub fn set_osu_config_values(
} }
#[tauri::command] #[tauri::command]
pub fn run_osu_updater(folder: String) -> Result<(), String> { pub async fn run_osu_updater(folder: String) -> Result<(), String> {
let osu_exe_path = PathBuf::from(folder.clone()).join("osu!.exe"); let osu_exe_path = PathBuf::from(&folder).join("osu!.exe");
#[cfg(windows)] #[cfg(windows)]
const DETACHED_PROCESS: u32 = 0x00000008; const DETACHED_PROCESS: u32 = 0x00000008;
#[cfg(windows)] #[cfg(windows)]
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
let handle = thread::spawn(move || { let mut updater_process = {
#[cfg(windows)] #[cfg(windows)]
let mut updater_process = Command::new(&osu_exe_path) {
.arg("-repair") Command::new(&osu_exe_path)
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP) .arg("-repair")
.spawn() .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.ok()?; // Ignore error, just exit thread .spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))?
}
#[cfg(not(windows))] #[cfg(not(windows))]
let mut updater_process = Command::new(&osu_exe_path).arg("-repair").spawn().ok()?; // Ignore error, just exit thread {
Command::new(&osu_exe_path)
.arg("-repair")
.spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))?
}
};
thread::sleep(Duration::from_millis(500)); sleep(Duration::from_millis(500)).await;
let mut sys = System::new_all(); let mut sys = System::new_all();
loop {
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true); sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
let mut termination_thread_running = true; let mut found = false;
while termination_thread_running { for (_pid, process) in sys.processes() {
for (_, process) in sys.processes() { if process.name() == "osu!.exe" {
if process.name() == "osu!.exe" { let pid = process.pid();
let process_id = process.pid(); let title = get_window_title_by_pid(pid);
let window_title = get_window_title_by_pid(process_id);
if !window_title.is_empty() && !window_title.contains("updater") { if !title.is_empty() && !title.contains("updater") {
if let Ok(_) = process.kill_and_wait() { let _ = process.kill_and_wait();
termination_thread_running = false; found = true;
break; break;
}
}
} }
} }
if !termination_thread_running {
break;
}
thread::sleep(Duration::from_millis(500));
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
} }
let _ = updater_process.wait(); if found {
break;
}
let force_update_files = [".require_update", "help.txt", "_pending"]; sleep(Duration::from_millis(500)).await;
}
for update_file_name in &force_update_files { // Wait for updater process to fully exit
let update_file = PathBuf::from(&folder).join(update_file_name); let _ = updater_process.wait().await;
if update_file.exists() {
let metadata = std::fs::symlink_metadata(&update_file); // Clean up update-related files
if let Ok(meta) = metadata { let force_update_files = [".require_update", "help.txt", "_pending"];
let result = if meta.is_dir() { for update_file_name in &force_update_files {
std::fs::remove_dir_all(&update_file) let path = PathBuf::from(&folder).join(update_file_name);
if path.exists() {
match std::fs::symlink_metadata(&path) {
Ok(meta) => {
let res = if meta.is_dir() {
std::fs::remove_dir_all(&path)
} else { } else {
std::fs::remove_file(&update_file) std::fs::remove_file(&path)
}; };
if let Err(e) = result {
eprintln!( if let Err(e) = res {
"Failed to remove force update file {:?}: {}", eprintln!("Failed to remove {:?}: {}", path, e);
update_file, e
);
} }
} }
Err(e) => {
eprintln!("Could not stat {:?}: {}", path, e);
}
} }
} }
}
Some(())
});
handle.join().map_err(|_| "Thread panicked".to_string())?;
Ok(()) Ok(())
} }
#[tauri::command] #[tauri::command]
pub fn run_osu(folder: String, patch: bool) -> Result<(), String> { pub async fn run_osu(folder: String, patch: bool) -> Result<(), String> {
/* #[cfg(windows)]
use std::os::windows::process::CommandExt; */
let osu_exe_path = PathBuf::from(&folder).join("osu!.exe"); let osu_exe_path = PathBuf::from(&folder).join("osu!.exe");
#[cfg(windows)] #[cfg(windows)]
const DETACHED_PROCESS: u32 = 0x00000008; const DETACHED_PROCESS: u32 = 0x00000008;
#[cfg(windows)] #[cfg(windows)]
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
#[cfg(windows)] let mut game_process = {
let mut game_process = Command::new(osu_exe_path) #[cfg(windows)]
.creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP) {
.arg("-devserver") Command::new(&osu_exe_path)
.arg("ez-pp.farm") .arg("-devserver")
.spawn() .arg("ez-pp.farm")
.map_err(|e| e.to_string())?; .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
.spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))?
}
#[cfg(not(windows))] #[cfg(not(windows))]
let mut game_process = Command::new(osu_exe_path) {
.arg("-devserver") Command::new(&osu_exe_path)
.arg("ez-pp.farm") .arg("-devserver")
.spawn() .arg("ez-pp.farm")
.map_err(|e| e.to_string())?; .spawn()
.map_err(|e| format!("Failed to spawn updater: {}", e))?
}
};
if patch { if patch {
thread::sleep(Duration::from_secs(3)); sleep(Duration::from_secs(3)).await;
let patcher_exe_path = PathBuf::from(&folder) let patcher_exe_path = PathBuf::from(&folder)
.join("EZPPLauncher") .join("EZPPLauncher")
.join("patcher") .join("patcher")
@ -396,8 +412,7 @@ pub fn run_osu(folder: String, patch: bool) -> Result<(), String> {
} }
} }
game_process.wait().map_err(|e| e.to_string())?; game_process.wait().await.map_err(|e| e.to_string())?;
Ok(()) Ok(())
} }