feat: osu! updating and launching does now work
This commit is contained in:
parent
bd5eb97f6e
commit
807485a7f3
@ -2,7 +2,9 @@
|
|||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": ["main"],
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"shell:allow-open",
|
"shell:allow-open",
|
||||||
@ -13,6 +15,8 @@
|
|||||||
"core:window:allow-minimize",
|
"core:window:allow-minimize",
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"cors-fetch:default",
|
"cors-fetch:default",
|
||||||
|
"core:window:allow-hide",
|
||||||
|
"core:window:allow-show",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
{
|
{
|
||||||
"identifier": "fs:allow-write",
|
"identifier": "fs:allow-write",
|
||||||
|
@ -7,9 +7,11 @@ use sysinfo::System;
|
|||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::*;
|
use winreg::enums::*;
|
||||||
|
|
||||||
use crate::utils::get_window_title_by_pid;
|
use crate::utils::{
|
||||||
use crate::utils::set_osu_user_config_value;
|
check_folder_completeness, get_osu_config, get_osu_user_config, get_window_title_by_pid,
|
||||||
use crate::utils::{check_folder_completeness, get_osu_config, get_osu_user_config};
|
set_osu_user_config_val, set_osu_config_val
|
||||||
|
};
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_hwid() -> String {
|
pub fn get_hwid() -> String {
|
||||||
@ -214,25 +216,45 @@ pub fn get_osu_release_stream(folder: String) -> String {
|
|||||||
.unwrap_or_else(|| "Stable40".to_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<bool, String> {
|
||||||
|
set_osu_user_config_val(&osu_folder_path, &key, &value)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_osu_config_value(
|
pub fn set_osu_config_value(
|
||||||
osu_folder_path: String,
|
osu_folder_path: String,
|
||||||
key: String,
|
key: String,
|
||||||
value: String,
|
value: String,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
set_osu_user_config_value(&osu_folder_path, &key, &value)
|
set_osu_config_val(&osu_folder_path, &key, &value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn run_osu_updater(folder: String) -> Result<(), String> {
|
pub fn run_osu_updater(folder: String) -> Result<(), String> {
|
||||||
let osu_exe_path = PathBuf::from(folder).join("osu!.exe");
|
let osu_exe_path = PathBuf::from(folder).join("osu!.exe");
|
||||||
|
#[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)
|
let mut updater_process = Command::new(osu_exe_path)
|
||||||
.arg("-repair")
|
.arg("-repair")
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
|
||||||
let mut sys = System::new_all();
|
let mut sys = System::new_all();
|
||||||
@ -246,15 +268,8 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> {
|
|||||||
let process_id = process.pid();
|
let process_id = process.pid();
|
||||||
let window_title = get_window_title_by_pid(process_id);
|
let window_title = get_window_title_by_pid(process_id);
|
||||||
|
|
||||||
println!("updater_process id: {}", updater_process.id());
|
if !window_title.is_empty() && !window_title.contains("updater") {
|
||||||
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 let Ok(_) = process.kill_and_wait() {
|
if let Ok(_) = process.kill_and_wait() {
|
||||||
println!("osu!.exe process killed");
|
|
||||||
termination_thread_running = false;
|
termination_thread_running = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -265,6 +280,7 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> {
|
|||||||
if !termination_thread_running {
|
if !termination_thread_running {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,3 +288,18 @@ pub fn run_osu_updater(folder: String) -> Result<(), String> {
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
@ -5,7 +5,8 @@ pub mod commands;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
use crate::commands::{
|
use crate::commands::{
|
||||||
find_osu_installation, get_beatmapsets_count, get_hwid, get_osu_release_stream,
|
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)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
@ -32,7 +33,9 @@ pub fn run() {
|
|||||||
get_osu_version,
|
get_osu_version,
|
||||||
get_osu_release_stream,
|
get_osu_release_stream,
|
||||||
set_osu_config_value,
|
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_fs::init())
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
|
@ -2,9 +2,7 @@ use std::ffi::OsString;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::ptr;
|
|
||||||
use sysinfo::Pid;
|
use sysinfo::Pid;
|
||||||
use winapi::um::winuser::{FindWindowW, GetWindowTextW, GetWindowThreadProcessId};
|
|
||||||
|
|
||||||
pub fn check_folder_completeness<P: AsRef<Path>>(folder_path: P, required_files: &[&str]) -> f32 {
|
pub fn check_folder_completeness<P: AsRef<Path>>(folder_path: P, required_files: &[&str]) -> f32 {
|
||||||
let mut found = 0;
|
let mut found = 0;
|
||||||
@ -48,7 +46,7 @@ pub fn get_osu_user_config<P: AsRef<Path>>(
|
|||||||
return Some(config_map);
|
return Some(config_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_osu_user_config_value(
|
pub fn set_osu_user_config_val(
|
||||||
osu_folder_path: &str,
|
osu_folder_path: &str,
|
||||||
key: &str,
|
key: &str,
|
||||||
value: &str,
|
value: &str,
|
||||||
@ -88,6 +86,45 @@ pub fn set_osu_user_config_value(
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_osu_config_val(
|
||||||
|
osu_folder_path: &str,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
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::<Vec<String>>();
|
||||||
|
|
||||||
|
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<P: AsRef<Path>>(
|
pub fn get_osu_config<P: AsRef<Path>>(
|
||||||
osu_folder_path: P,
|
osu_folder_path: P,
|
||||||
) -> Option<std::collections::HashMap<String, String>> {
|
) -> Option<std::collections::HashMap<String, String>> {
|
||||||
@ -113,26 +150,46 @@ pub fn get_osu_config<P: AsRef<Path>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_window_title_by_pid(pid: Pid) -> String {
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "system" fn enum_windows_proc(
|
||||||
|
hwnd: HWND,
|
||||||
|
lparam: winapi::shared::minwindef::LPARAM,
|
||||||
|
) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let hwnd = FindWindowW(ptr::null_mut(), ptr::null_mut());
|
let data = &mut *(lparam as *mut (u32, Arc<Mutex<Option<String>>>));
|
||||||
|
let target_pid = data.0;
|
||||||
|
let result = &data.1;
|
||||||
|
|
||||||
if hwnd.is_null() {
|
let mut window_pid = 0u32;
|
||||||
return String::new();
|
GetWindowThreadProcessId(hwnd, &mut window_pid);
|
||||||
}
|
if window_pid == target_pid && IsWindowVisible(hwnd) != 0 {
|
||||||
|
|
||||||
let mut process_id = 0;
|
|
||||||
GetWindowThreadProcessId(hwnd, &mut process_id);
|
|
||||||
|
|
||||||
if process_id == pid.as_u32() {
|
|
||||||
let mut title = vec![0u16; 512];
|
let mut title = vec![0u16; 512];
|
||||||
let length = GetWindowTextW(hwnd, title.as_mut_ptr(), title.len() as i32);
|
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 = OsString::from_wide(&title[..length as usize]);
|
||||||
window_title = title.to_string_lossy().into_owned();
|
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()
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
export const clientNeedsUpdate = () => {
|
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,
|
||||||
|
});
|
||||||
|
@ -52,7 +52,8 @@ export const formatTimeReadable = (initialSeconds: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const releaseStreamToReadable = (releaseStream: string) => {
|
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';
|
return 'Stable';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@
|
|||||||
} from '@/gamemode';
|
} from '@/gamemode';
|
||||||
import { currentUserInfo } from '@/data';
|
import { currentUserInfo } from '@/data';
|
||||||
import { osuapi } from '@/api/osuapi';
|
import { osuapi } from '@/api/osuapi';
|
||||||
|
import { setConfigValue, setUserConfigValue } from '@/osuUtil';
|
||||||
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
|
|
||||||
let selectedTab = $state('home');
|
let selectedTab = $state('home');
|
||||||
let launching = $state(false);
|
let launching = $state(false);
|
||||||
@ -156,10 +158,30 @@
|
|||||||
launchInfo = 'Update found!';
|
launchInfo = 'Update found!';
|
||||||
await new Promise((res) => setTimeout(res, 1500));
|
await new Promise((res) => setTimeout(res, 1500));
|
||||||
launchInfo = 'Running osu! updater...';
|
launchInfo = 'Running osu! updater...';
|
||||||
|
await setUserConfigValue($osuInstallationPath, 'LastVersion', streamInfo);
|
||||||
await invoke('run_osu_updater', { folder: $osuInstallationPath });
|
await invoke('run_osu_updater', { folder: $osuInstallationPath });
|
||||||
|
launchInfo = 'osu! is now up to date!';
|
||||||
} else {
|
} else {
|
||||||
launchInfo = 'You are up to date!';
|
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) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
toast.error('Hmmm...', {
|
toast.error('Hmmm...', {
|
||||||
@ -690,7 +712,7 @@
|
|||||||
<Input
|
<Input
|
||||||
class="mt-4 w-full bg-theme-950 border-theme-800 border-r-0 rounded-r-none"
|
class="mt-4 w-full bg-theme-950 border-theme-800 border-r-0 rounded-r-none"
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={$osuInstallationPath}
|
value={$osuInstallationPath}
|
||||||
placeholder="Path to osu! installation"
|
placeholder="Path to osu! installation"
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
@ -700,6 +722,30 @@
|
|||||||
onclick={browse_osu_installation}>Browse</Button
|
onclick={browse_osu_installation}>Browse</Button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Label class="text-sm" for="setting-custom-cursor">osu! release stream</Label>
|
||||||
|
<div class="text-muted-foreground text-xs">The release stream of your osu! client</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row w-full">
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
value={$osuStream}
|
||||||
|
onValueChange={async (newStream) => {
|
||||||
|
osuStream.set(newStream);
|
||||||
|
await setConfigValue($osuInstallationPath, '_ReleaseStream', newStream);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Trigger class="border-theme-800 bg-theme-950 text-white font-semibold">
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
{releaseStreamToReadable($osuStream ?? 'Stable40')}
|
||||||
|
</div>
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content class="bg-theme-950 border border-theme-900 rounded-lg">
|
||||||
|
<Select.Item value="Stable40">Stable</Select.Item>
|
||||||
|
<Select.Item value="CuttingEdge">Cutting Edge</Select.Item>
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user