diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 830523c..bb0dbfa 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -9,7 +9,7 @@ use winreg::enums::*; 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 + set_osu_config_vals, set_osu_user_config_vals, }; use std::os::windows::process::CommandExt; @@ -217,21 +217,46 @@ pub fn get_osu_release_stream(folder: String) -> 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) +pub fn get_osu_previous_release_stream(folder: String) -> Option { + let path = PathBuf::from(folder); + let osu_config = get_osu_config(path.clone()); + osu_config.and_then(|config| config.get("_PreviousReleaseStream").cloned()) +} + +#[derive(serde::Deserialize)] +pub struct ConfigEntry { + pub key: String, + pub value: String, } #[tauri::command] -pub fn set_osu_config_value( +pub fn set_osu_user_config_values( osu_folder_path: String, - key: String, - value: String, + entries: Vec, ) -> Result { - set_osu_config_val(&osu_folder_path, &key, &value) + let converted: Vec<(&str, Option<&str>)> = entries + .iter() + .map(|entry| (entry.key.as_str(), Some(entry.value.as_str()))) + .collect(); + match set_osu_user_config_vals(&osu_folder_path, &converted) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } +} + +#[tauri::command] +pub fn set_osu_config_values( + osu_folder_path: String, + entries: Vec, +) -> Result { + let converted: Vec<(&str, Option<&str>)> = entries + .iter() + .map(|entry| (entry.key.as_str(), Some(entry.value.as_str()))) + .collect(); + match set_osu_config_vals(&osu_folder_path, &converted) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } } #[tauri::command] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 17ee5d6..e94c833 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4,9 +4,9 @@ use tauri::Manager; 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, run_osu_updater, set_osu_config_value, - set_osu_user_config_value, valid_osu_folder, + find_osu_installation, get_beatmapsets_count, get_hwid, get_osu_previous_release_stream, + get_osu_release_stream, get_osu_version, get_skins_count, run_osu, run_osu_updater, + set_osu_config_values, set_osu_user_config_values, valid_osu_folder, }; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -32,10 +32,11 @@ pub fn run() { get_skins_count, get_osu_version, get_osu_release_stream, - set_osu_config_value, - set_osu_user_config_value, + get_osu_previous_release_stream, + set_osu_config_values, + set_osu_user_config_values, run_osu_updater, - run_osu + 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 928a0a1..cd5b463 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -46,16 +46,15 @@ pub fn get_osu_user_config>( return Some(config_map); } -pub fn set_osu_user_config_val( +pub fn set_osu_user_config_vals( osu_folder_path: &str, - key: &str, - value: &str, + key_values: &[(&str, Option<&str>)], ) -> Result { let current_user = std::env::var("USERNAME").unwrap_or_else(|_| "Admin".to_string()); let osu_config_path = Path::new(osu_folder_path).join(format!("osu!.{}.cfg", current_user)); if !osu_config_path.exists() { - return Ok(false); + return Err("osu! user config file does not exist".to_string()); } let mut lines = fs::read_to_string(&osu_config_path) @@ -64,20 +63,36 @@ pub fn set_osu_user_config_val( .map(|line| line.to_string()) .collect::>(); - let mut found_key = false; + let mut keys_to_set: std::collections::HashMap<&str, &str> = std::collections::HashMap::new(); + let mut keys_to_add: std::collections::HashSet<&str> = std::collections::HashSet::new(); - 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; - } + for (key, value_opt) in key_values.iter() { + if let Some(value) = value_opt { + keys_to_set.insert(*key, *value); + keys_to_add.insert(*key); } } - if !found_key { - lines.push(format!("{} = {}", key, value)); + // Collect indices and keys to update to avoid borrow checker issues + let mut updates = Vec::new(); + for (i, line) in lines.iter().enumerate() { + if let Some((existing_key, _)) = line.split_once(" = ") { + let trimmed_key = existing_key.trim(); + if let Some(new_value) = keys_to_set.get(trimmed_key) { + updates.push((i, trimmed_key.to_string(), new_value.to_string())); + } + } + } + for (i, trimmed_key, new_value) in updates { + lines[i] = format!("{} = {}", trimmed_key, new_value); + keys_to_add.remove(trimmed_key.as_str()); + } + + // Add new keys that were not found + for key in keys_to_add { + if let Some(value) = keys_to_set.get(key) { + lines.push(format!("{} = {}", key, value)); + } } let new_content = lines.join("\n") + "\n"; @@ -86,15 +101,14 @@ pub fn set_osu_user_config_val( Ok(true) } -pub fn set_osu_config_val( +pub fn set_osu_config_vals( osu_folder_path: &str, - key: &str, - value: &str, + key_values: &[(&str, Option<&str>)], ) -> Result { let osu_config_path = Path::new(osu_folder_path).join("osu!.cfg"); if !osu_config_path.exists() { - return Ok(false); + return Err("osu!.cfg file does not exist".to_string()); } let mut lines = fs::read_to_string(&osu_config_path) @@ -103,20 +117,35 @@ pub fn set_osu_config_val( .map(|line| line.to_string()) .collect::>(); - let mut found_key = false; + let mut keys_to_set: std::collections::HashMap<&str, &str> = std::collections::HashMap::new(); + let mut keys_to_add: std::collections::HashSet<&str> = std::collections::HashSet::new(); - 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; - } + for (key, value_opt) in key_values.iter() { + if let Some(value) = value_opt { + keys_to_set.insert(*key, *value); + keys_to_add.insert(*key); } } - if !found_key { - lines.push(format!("{} = {}", key, value)); + let mut updates = Vec::new(); + for (i, line) in lines.iter().enumerate() { + if let Some((existing_key, _)) = line.split_once(" = ") { + let trimmed_key = existing_key.trim(); + if let Some(new_value) = keys_to_set.get(trimmed_key) { + updates.push((i, trimmed_key.to_string(), new_value.to_string())); + } + } + } + for (i, trimmed_key, new_value) in updates { + lines[i] = format!("{} = {}", trimmed_key, new_value); + keys_to_add.remove(trimmed_key.as_str()); + } + + // Add new keys that were not found + for key in keys_to_add { + if let Some(value) = keys_to_set.get(key) { + lines.push(format!("{} = {}", key, value)); + } } let new_content = lines.join("\n") + "\n"; diff --git a/src/lib/displayUtils.ts b/src/lib/displayUtils.ts index 4e84ed7..8810852 100644 --- a/src/lib/displayUtils.ts +++ b/src/lib/displayUtils.ts @@ -8,8 +8,8 @@ export function estimateRefreshRate(): Promise { frames++; if (now - last >= 1000) { - console.log(`Estimated Refresh Rate: ${frames} FPS`); - resolve(frames); // estimated Hz + console.log(`Estimated Refresh Rate: ${frames - 2} FPS`); + resolve(frames - 2); // estimated Hz } else { requestAnimationFrame(loop); } diff --git a/src/lib/osuUtil.ts b/src/lib/osuUtil.ts index 281fff9..aec5fc3 100644 --- a/src/lib/osuUtil.ts +++ b/src/lib/osuUtil.ts @@ -1,15 +1,24 @@ import { invoke } from '@tauri-apps/api/core'; -export const setUserConfigValue = async (osuFolderPath: string, key: string, value: string) => - await invoke('set_osu_user_config_value', { +export const setUserConfigValues = async ( + osuFolderPath: string, + entries: { key: string; value: string }[] +) => + await invoke('set_osu_user_config_values', { osuFolderPath, - key, - value, + entries, }); -export const setConfigValue = async (osuFolderPath: string, key: string, value: string) => - await invoke('set_osu_config_value', { +export const setConfigValues = async ( + osuFolderPath: string, + entries: { key: string; value: string }[] +) => + await invoke('set_osu_config_values', { osuFolderPath, - key, - value, + entries, }); + +export const getPreviousReleaseStream = async (folder: string) => { + const result = await invoke('get_osu_previous_release_stream', { folder }); + return typeof result === 'string' ? result : undefined; +}; diff --git a/src/pages/Launch.svelte b/src/pages/Launch.svelte index 2b59794..a590d2f 100644 --- a/src/pages/Launch.svelte +++ b/src/pages/Launch.svelte @@ -67,7 +67,7 @@ } from '@/gamemode'; import { currentUserInfo } from '@/data'; import { osuapi } from '@/api/osuapi'; - import { setConfigValue, setUserConfigValue } from '@/osuUtil'; + import { getPreviousReleaseStream, setConfigValues, setUserConfigValues } from '@/osuUtil'; import { getCurrentWindow } from '@tauri-apps/api/window'; let selectedTab = $state('home'); @@ -154,12 +154,15 @@ return; } + const previousReleaseStream = await getPreviousReleaseStream(osuPath); + let forceUpdate = previousReleaseStream && previousReleaseStream !== $osuStream; + const versions = compareBuildNumbers($osuBuild, streamInfo); - if (versions > 0) { + if (versions > 0 || forceUpdate) { launchInfo = 'Update found!'; await new Promise((res) => setTimeout(res, 1500)); launchInfo = 'Running osu! updater...'; - await setUserConfigValue(osuPath, 'LastVersion', streamInfo); + await setUserConfigValues(osuPath, [{ key: 'LastVersion', value: streamInfo }]); await invoke('run_osu_updater', { folder: osuPath }); launchInfo = 'osu! is now up to date!'; } else { @@ -169,13 +172,29 @@ const username = $userAuth.value('username').get(''); const password = $userAuth.value('password').get(''); if (username.length > 0 && password.length > 0) { - //TODO: make this one function to prevent multiple file writes - await setUserConfigValue(osuPath, 'Username', username); - await setUserConfigValue(osuPath, 'Password', password); - await setUserConfigValue(osuPath, 'SaveUsername', '1'); - await setUserConfigValue(osuPath, 'SavePassword', '1'); + await setUserConfigValues(osuPath, [ + { + key: 'Username', + value: username, + }, + { + key: 'Password', + value: password, + }, + { + key: 'SaveUsername', + value: '1', + }, + { + key: 'SavePassword', + value: '1', + }, + { + key: 'CredentialEndpoint', + value: 'ez-pp.farm', + }, + ]); } - await setUserConfigValue(osuPath, 'CredentialEndpoint', 'ez-pp.farm'); } await new Promise((res) => setTimeout(res, 1500)); launchInfo = 'Launching osu!...'; @@ -754,8 +773,12 @@ type="single" value={$osuStream} onValueChange={async (newStream) => { + const oldStream = $osuStream; osuStream.set(newStream); - await setConfigValue($osuInstallationPath, '_ReleaseStream', newStream); + await setConfigValues($osuInstallationPath, [ + { key: '_ReleaseStream', value: newStream }, + { key: '_PreviousReleaseStream', value: oldStream ?? newStream }, + ]); }} >