From 807485a7f3a4916ac1d6e82e709f381df894c991 Mon Sep 17 00:00:00 2001 From: HorizonCode Date: Thu, 3 Jul 2025 22:10:38 +0200 Subject: [PATCH] feat: osu! updating and launching does now work --- src-tauri/capabilities/default.json | 8 ++- src-tauri/src/commands.rs | 65 ++++++++++++++----- src-tauri/src/lib.rs | 7 ++- src-tauri/src/utils.rs | 97 +++++++++++++++++++++++------ src/lib/osuUtil.ts | 18 +++++- src/lib/utils.ts | 3 +- src/pages/Launch.svelte | 48 +++++++++++++- 7 files changed, 200 insertions(+), 46 deletions(-) diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 166655b..ef9c2c1 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,7 +2,9 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main"], + "windows": [ + "main" + ], "permissions": [ "core:default", "shell:allow-open", @@ -13,6 +15,8 @@ "core:window:allow-minimize", "core:window:allow-close", "cors-fetch:default", + "core:window:allow-hide", + "core:window:allow-show", "fs:default", { "identifier": "fs:allow-write", @@ -71,4 +75,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index d1ed917..830523c 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -7,9 +7,11 @@ use sysinfo::System; use winreg::RegKey; use winreg::enums::*; -use crate::utils::get_window_title_by_pid; -use crate::utils::set_osu_user_config_value; -use crate::utils::{check_folder_completeness, get_osu_config, get_osu_user_config}; +use crate::utils::{ + check_folder_completeness, get_osu_config, get_osu_user_config, get_window_title_by_pid, + set_osu_user_config_val, set_osu_config_val +}; +use std::os::windows::process::CommandExt; #[tauri::command] pub fn get_hwid() -> String { @@ -214,24 +216,44 @@ pub fn get_osu_release_stream(folder: String) -> String { .unwrap_or_else(|| "Stable40".to_string()); } +#[tauri::command] +pub fn set_osu_user_config_value( + osu_folder_path: String, + key: String, + value: String, +) -> Result { + set_osu_user_config_val(&osu_folder_path, &key, &value) +} + #[tauri::command] pub fn set_osu_config_value( osu_folder_path: String, key: String, value: String, ) -> Result { - set_osu_user_config_value(&osu_folder_path, &key, &value) + set_osu_config_val(&osu_folder_path, &key, &value) } #[tauri::command] pub fn run_osu_updater(folder: String) -> Result<(), String> { let osu_exe_path = PathBuf::from(folder).join("osu!.exe"); - let mut updater_process = Command::new(osu_exe_path) - .arg("-repair") - .spawn() - .map_err(|e| e.to_string())?; + #[cfg(windows)] + const DETACHED_PROCESS: u32 = 0x00000008; + #[cfg(windows)] + const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; - + #[cfg(windows)] + let mut updater_process = Command::new(osu_exe_path) + .arg("-repair") + .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP) + .spawn() + .map_err(|e| e.to_string())?; + + #[cfg(not(windows))] + let mut updater_process = Command::new(osu_exe_path) + .arg("-repair") + .spawn() + .map_err(|e| e.to_string())?; thread::sleep(Duration::from_millis(500)); @@ -246,15 +268,8 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> { let process_id = process.pid(); let window_title = get_window_title_by_pid(process_id); - println!("updater_process id: {}", updater_process.id()); - println!("process_id: {}", process_id.as_u32()); - - println!("found osu!.exe process with window title: {}", window_title); - - if !window_title.is_empty() { - println!("Killing osu!.exe process"); + if !window_title.is_empty() && !window_title.contains("updater") { if let Ok(_) = process.kill_and_wait() { - println!("osu!.exe process killed"); termination_thread_running = false; break; } @@ -265,6 +280,7 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> { if !termination_thread_running { break; } + sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true); thread::sleep(Duration::from_millis(500)); } @@ -272,3 +288,18 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> { Ok(()) } + +#[tauri::command] +pub fn run_osu(folder: String) -> Result<(), String> { + let osu_exe_path = PathBuf::from(folder).join("osu!.exe"); + + let mut game_process = Command::new(osu_exe_path) + .arg("-devserver") + .arg("ez-pp.farm") + .spawn() + .map_err(|e| e.to_string())?; + + game_process.wait().map_err(|e| e.to_string())?; + + Ok(()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 23e2b76..17ee5d6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,7 +5,8 @@ pub mod commands; pub mod utils; use crate::commands::{ find_osu_installation, get_beatmapsets_count, get_hwid, get_osu_release_stream, - get_osu_version, get_skins_count, run_osu_updater, set_osu_config_value, valid_osu_folder, + get_osu_version, get_skins_count, run_osu, run_osu_updater, set_osu_config_value, + set_osu_user_config_value, valid_osu_folder, }; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -32,7 +33,9 @@ pub fn run() { get_osu_version, get_osu_release_stream, set_osu_config_value, - run_osu_updater + set_osu_user_config_value, + run_osu_updater, + run_osu ]) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index af7efc4..928a0a1 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -2,9 +2,7 @@ use std::ffi::OsString; use std::fs; use std::os::windows::ffi::OsStringExt; use std::path::Path; -use std::ptr; use sysinfo::Pid; -use winapi::um::winuser::{FindWindowW, GetWindowTextW, GetWindowThreadProcessId}; pub fn check_folder_completeness>(folder_path: P, required_files: &[&str]) -> f32 { let mut found = 0; @@ -48,7 +46,7 @@ pub fn get_osu_user_config>( return Some(config_map); } -pub fn set_osu_user_config_value( +pub fn set_osu_user_config_val( osu_folder_path: &str, key: &str, value: &str, @@ -88,6 +86,45 @@ pub fn set_osu_user_config_value( Ok(true) } +pub fn set_osu_config_val( + osu_folder_path: &str, + key: &str, + value: &str, +) -> Result { + let osu_config_path = Path::new(osu_folder_path).join("osu!.cfg"); + + if !osu_config_path.exists() { + return Ok(false); + } + + let mut lines = fs::read_to_string(&osu_config_path) + .map_err(|e| e.to_string())? + .lines() + .map(|line| line.to_string()) + .collect::>(); + + let mut found_key = false; + + for line in lines.iter_mut() { + if let Some((existing_key, _)) = line.split_once(" = ") { + if existing_key.trim() == key { + *line = format!("{} = {}", key, value); + found_key = true; + break; + } + } + } + + if !found_key { + lines.push(format!("{} = {}", key, value)); + } + + let new_content = lines.join("\n") + "\n"; + fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?; + + Ok(true) +} + pub fn get_osu_config>( osu_folder_path: P, ) -> Option> { @@ -113,26 +150,46 @@ pub fn get_osu_config>( } pub fn get_window_title_by_pid(pid: Pid) -> String { - let mut window_title = String::new(); + use std::sync::{Arc, Mutex}; + use winapi::shared::windef::HWND; + use winapi::um::winuser::{ + EnumWindows, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible, + }; - unsafe { - let hwnd = FindWindowW(ptr::null_mut(), ptr::null_mut()); + extern "system" fn enum_windows_proc( + hwnd: HWND, + lparam: winapi::shared::minwindef::LPARAM, + ) -> i32 { + unsafe { + let data = &mut *(lparam as *mut (u32, Arc>>)); + let target_pid = data.0; + let result = &data.1; - if hwnd.is_null() { - return String::new(); - } - - let mut process_id = 0; - GetWindowThreadProcessId(hwnd, &mut process_id); - - if process_id == pid.as_u32() { - let mut title = vec![0u16; 512]; - let length = GetWindowTextW(hwnd, title.as_mut_ptr(), title.len() as i32); - - let title = OsString::from_wide(&title[..length as usize]); - window_title = title.to_string_lossy().into_owned(); + let mut window_pid = 0u32; + GetWindowThreadProcessId(hwnd, &mut window_pid); + if window_pid == target_pid && IsWindowVisible(hwnd) != 0 { + let mut title = vec![0u16; 512]; + let length = GetWindowTextW(hwnd, title.as_mut_ptr(), title.len() as i32); + if length > 0 { + let title = OsString::from_wide(&title[..length as usize]); + let title_str = title.to_string_lossy().into_owned(); + if !title_str.is_empty() { + *result.lock().unwrap() = Some(title_str); + return 0; // Stop enumeration + } + } + } + 1 // Continue enumeration } } - window_title + let result = Arc::new(Mutex::new(None)); + let mut data = (pid.as_u32(), Arc::clone(&result)); + unsafe { + EnumWindows( + Some(enum_windows_proc), + &mut data as *mut _ as winapi::shared::minwindef::LPARAM, + ); + } + result.lock().unwrap().clone().unwrap_or_default() } diff --git a/src/lib/osuUtil.ts b/src/lib/osuUtil.ts index fdb59d2..281fff9 100644 --- a/src/lib/osuUtil.ts +++ b/src/lib/osuUtil.ts @@ -1,3 +1,15 @@ -export const clientNeedsUpdate = () => { - -} \ No newline at end of file +import { invoke } from '@tauri-apps/api/core'; + +export const setUserConfigValue = async (osuFolderPath: string, key: string, value: string) => + await invoke('set_osu_user_config_value', { + osuFolderPath, + key, + value, + }); + +export const setConfigValue = async (osuFolderPath: string, key: string, value: string) => + await invoke('set_osu_config_value', { + osuFolderPath, + key, + value, + }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f61edb2..4d32c7b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -52,7 +52,8 @@ export const formatTimeReadable = (initialSeconds: number) => { }; export const releaseStreamToReadable = (releaseStream: string) => { - if (releaseStream.toLowerCase() === 'cuttingedge') return 'Cutting Edge'; + console.log(releaseStream.replaceAll(' ', '').toLowerCase()); + if (releaseStream.replaceAll(' ', '').toLowerCase() === 'cuttingedge') return 'Cutting Edge'; return 'Stable'; }; diff --git a/src/pages/Launch.svelte b/src/pages/Launch.svelte index 93fc093..57051a2 100644 --- a/src/pages/Launch.svelte +++ b/src/pages/Launch.svelte @@ -67,6 +67,8 @@ } from '@/gamemode'; import { currentUserInfo } from '@/data'; import { osuapi } from '@/api/osuapi'; + import { setConfigValue, setUserConfigValue } from '@/osuUtil'; + import { getCurrentWindow } from '@tauri-apps/api/window'; let selectedTab = $state('home'); let launching = $state(false); @@ -156,10 +158,30 @@ launchInfo = 'Update found!'; await new Promise((res) => setTimeout(res, 1500)); launchInfo = 'Running osu! updater...'; + await setUserConfigValue($osuInstallationPath, 'LastVersion', streamInfo); await invoke('run_osu_updater', { folder: $osuInstallationPath }); + launchInfo = 'osu! is now up to date!'; } else { launchInfo = 'You are up to date!'; } + if ($currentUser) { + const username = $userAuth.value('username').get(''); + const password = $userAuth.value('password').get(''); + if (username.length > 0 && password.length > 0) { + await setUserConfigValue($osuInstallationPath, 'Username', username); + await setUserConfigValue($osuInstallationPath, 'Password', password); + await setUserConfigValue($osuInstallationPath, 'SaveUsername', '1'); + await setUserConfigValue($osuInstallationPath, 'SavePassword', '1'); + } + await setUserConfigValue($osuInstallationPath, 'CredentialEndpoint', 'ez-pp.farm'); + } + await new Promise((res) => setTimeout(res, 1500)); + launchInfo = 'Launching osu!...'; + await new Promise((res) => setTimeout(res, 1000)); + await getCurrentWindow().hide(); + await invoke('run_osu', { folder: $osuInstallationPath }); + await getCurrentWindow().show(); + launching = false; } catch (err) { console.log(err); toast.error('Hmmm...', { @@ -690,7 +712,7 @@ @@ -700,6 +722,30 @@ onclick={browse_osu_installation}>Browse +
+ +
The release stream of your osu! client
+
+
+ { + osuStream.set(newStream); + await setConfigValue($osuInstallationPath, '_ReleaseStream', newStream); + }} + > + +
+ {releaseStreamToReadable($osuStream ?? 'Stable40')} +
+
+ + Stable + Cutting Edge + +
+
{/if}