feat: enhance osu! configuration management with batch updates and previous release stream retrieval
This commit is contained in:
parent
239009decb
commit
40795ad6d9
@ -9,7 +9,7 @@ use winreg::enums::*;
|
|||||||
|
|
||||||
use crate::utils::{
|
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_user_config_val, set_osu_config_val
|
set_osu_config_vals, set_osu_user_config_vals,
|
||||||
};
|
};
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
|
|
||||||
@ -217,21 +217,46 @@ pub fn get_osu_release_stream(folder: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_osu_user_config_value(
|
pub fn get_osu_previous_release_stream(folder: String) -> Option<String> {
|
||||||
osu_folder_path: String,
|
let path = PathBuf::from(folder);
|
||||||
key: String,
|
let osu_config = get_osu_config(path.clone());
|
||||||
value: String,
|
osu_config.and_then(|config| config.get("_PreviousReleaseStream").cloned())
|
||||||
) -> Result<bool, String> {
|
}
|
||||||
set_osu_user_config_val(&osu_folder_path, &key, &value)
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct ConfigEntry {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_osu_config_value(
|
pub fn set_osu_user_config_values(
|
||||||
osu_folder_path: String,
|
osu_folder_path: String,
|
||||||
key: String,
|
entries: Vec<ConfigEntry>,
|
||||||
value: String,
|
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
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<ConfigEntry>,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
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]
|
#[tauri::command]
|
||||||
|
@ -4,9 +4,9 @@ use tauri::Manager;
|
|||||||
pub mod commands;
|
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_previous_release_stream,
|
||||||
get_osu_version, get_skins_count, run_osu, run_osu_updater, set_osu_config_value,
|
get_osu_release_stream, get_osu_version, get_skins_count, run_osu, run_osu_updater,
|
||||||
set_osu_user_config_value, valid_osu_folder,
|
set_osu_config_values, set_osu_user_config_values, valid_osu_folder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
@ -32,10 +32,11 @@ pub fn run() {
|
|||||||
get_skins_count,
|
get_skins_count,
|
||||||
get_osu_version,
|
get_osu_version,
|
||||||
get_osu_release_stream,
|
get_osu_release_stream,
|
||||||
set_osu_config_value,
|
get_osu_previous_release_stream,
|
||||||
set_osu_user_config_value,
|
set_osu_config_values,
|
||||||
|
set_osu_user_config_values,
|
||||||
run_osu_updater,
|
run_osu_updater,
|
||||||
run_osu
|
run_osu,
|
||||||
])
|
])
|
||||||
.plugin(tauri_plugin_fs::init())
|
.plugin(tauri_plugin_fs::init())
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
|
@ -46,16 +46,15 @@ pub fn get_osu_user_config<P: AsRef<Path>>(
|
|||||||
return Some(config_map);
|
return Some(config_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_osu_user_config_val(
|
pub fn set_osu_user_config_vals(
|
||||||
osu_folder_path: &str,
|
osu_folder_path: &str,
|
||||||
key: &str,
|
key_values: &[(&str, Option<&str>)],
|
||||||
value: &str,
|
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let current_user = std::env::var("USERNAME").unwrap_or_else(|_| "Admin".to_string());
|
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));
|
let osu_config_path = Path::new(osu_folder_path).join(format!("osu!.{}.cfg", current_user));
|
||||||
|
|
||||||
if !osu_config_path.exists() {
|
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)
|
let mut lines = fs::read_to_string(&osu_config_path)
|
||||||
@ -64,21 +63,37 @@ pub fn set_osu_user_config_val(
|
|||||||
.map(|line| line.to_string())
|
.map(|line| line.to_string())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
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() {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(" = ") {
|
if let Some((existing_key, _)) = line.split_once(" = ") {
|
||||||
if existing_key.trim() == key {
|
let trimmed_key = existing_key.trim();
|
||||||
*line = format!("{} = {}", key, value);
|
if let Some(new_value) = keys_to_set.get(trimmed_key) {
|
||||||
found_key = true;
|
updates.push((i, trimmed_key.to_string(), new_value.to_string()));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (i, trimmed_key, new_value) in updates {
|
||||||
|
lines[i] = format!("{} = {}", trimmed_key, new_value);
|
||||||
|
keys_to_add.remove(trimmed_key.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
if !found_key {
|
// 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));
|
lines.push(format!("{} = {}", key, value));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let new_content = lines.join("\n") + "\n";
|
let new_content = lines.join("\n") + "\n";
|
||||||
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
||||||
@ -86,15 +101,14 @@ pub fn set_osu_user_config_val(
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_osu_config_val(
|
pub fn set_osu_config_vals(
|
||||||
osu_folder_path: &str,
|
osu_folder_path: &str,
|
||||||
key: &str,
|
key_values: &[(&str, Option<&str>)],
|
||||||
value: &str,
|
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let osu_config_path = Path::new(osu_folder_path).join("osu!.cfg");
|
let osu_config_path = Path::new(osu_folder_path).join("osu!.cfg");
|
||||||
|
|
||||||
if !osu_config_path.exists() {
|
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)
|
let mut lines = fs::read_to_string(&osu_config_path)
|
||||||
@ -103,21 +117,36 @@ pub fn set_osu_config_val(
|
|||||||
.map(|line| line.to_string())
|
.map(|line| line.to_string())
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
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() {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut updates = Vec::new();
|
||||||
|
for (i, line) in lines.iter().enumerate() {
|
||||||
if let Some((existing_key, _)) = line.split_once(" = ") {
|
if let Some((existing_key, _)) = line.split_once(" = ") {
|
||||||
if existing_key.trim() == key {
|
let trimmed_key = existing_key.trim();
|
||||||
*line = format!("{} = {}", key, value);
|
if let Some(new_value) = keys_to_set.get(trimmed_key) {
|
||||||
found_key = true;
|
updates.push((i, trimmed_key.to_string(), new_value.to_string()));
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (i, trimmed_key, new_value) in updates {
|
||||||
|
lines[i] = format!("{} = {}", trimmed_key, new_value);
|
||||||
|
keys_to_add.remove(trimmed_key.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
if !found_key {
|
// 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));
|
lines.push(format!("{} = {}", key, value));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let new_content = lines.join("\n") + "\n";
|
let new_content = lines.join("\n") + "\n";
|
||||||
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
fs::write(&osu_config_path, new_content).map_err(|e| e.to_string())?;
|
||||||
|
@ -8,8 +8,8 @@ export function estimateRefreshRate(): Promise<number> {
|
|||||||
frames++;
|
frames++;
|
||||||
|
|
||||||
if (now - last >= 1000) {
|
if (now - last >= 1000) {
|
||||||
console.log(`Estimated Refresh Rate: ${frames} FPS`);
|
console.log(`Estimated Refresh Rate: ${frames - 2} FPS`);
|
||||||
resolve(frames); // estimated Hz
|
resolve(frames - 2); // estimated Hz
|
||||||
} else {
|
} else {
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
export const setUserConfigValue = async (osuFolderPath: string, key: string, value: string) =>
|
export const setUserConfigValues = async (
|
||||||
await invoke('set_osu_user_config_value', {
|
osuFolderPath: string,
|
||||||
|
entries: { key: string; value: string }[]
|
||||||
|
) =>
|
||||||
|
await invoke('set_osu_user_config_values', {
|
||||||
osuFolderPath,
|
osuFolderPath,
|
||||||
key,
|
entries,
|
||||||
value,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setConfigValue = async (osuFolderPath: string, key: string, value: string) =>
|
export const setConfigValues = async (
|
||||||
await invoke('set_osu_config_value', {
|
osuFolderPath: string,
|
||||||
|
entries: { key: string; value: string }[]
|
||||||
|
) =>
|
||||||
|
await invoke('set_osu_config_values', {
|
||||||
osuFolderPath,
|
osuFolderPath,
|
||||||
key,
|
entries,
|
||||||
value,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const getPreviousReleaseStream = async (folder: string) => {
|
||||||
|
const result = await invoke('get_osu_previous_release_stream', { folder });
|
||||||
|
return typeof result === 'string' ? result : undefined;
|
||||||
|
};
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
} 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 { getPreviousReleaseStream, setConfigValues, setUserConfigValues } from '@/osuUtil';
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
|
|
||||||
let selectedTab = $state('home');
|
let selectedTab = $state('home');
|
||||||
@ -154,12 +154,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previousReleaseStream = await getPreviousReleaseStream(osuPath);
|
||||||
|
let forceUpdate = previousReleaseStream && previousReleaseStream !== $osuStream;
|
||||||
|
|
||||||
const versions = compareBuildNumbers($osuBuild, streamInfo);
|
const versions = compareBuildNumbers($osuBuild, streamInfo);
|
||||||
if (versions > 0) {
|
if (versions > 0 || forceUpdate) {
|
||||||
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(osuPath, 'LastVersion', streamInfo);
|
await setUserConfigValues(osuPath, [{ key: 'LastVersion', value: streamInfo }]);
|
||||||
await invoke('run_osu_updater', { folder: osuPath });
|
await invoke('run_osu_updater', { folder: osuPath });
|
||||||
launchInfo = 'osu! is now up to date!';
|
launchInfo = 'osu! is now up to date!';
|
||||||
} else {
|
} else {
|
||||||
@ -169,13 +172,29 @@
|
|||||||
const username = $userAuth.value('username').get('');
|
const username = $userAuth.value('username').get('');
|
||||||
const password = $userAuth.value('password').get('');
|
const password = $userAuth.value('password').get('');
|
||||||
if (username.length > 0 && password.length > 0) {
|
if (username.length > 0 && password.length > 0) {
|
||||||
//TODO: make this one function to prevent multiple file writes
|
await setUserConfigValues(osuPath, [
|
||||||
await setUserConfigValue(osuPath, 'Username', username);
|
{
|
||||||
await setUserConfigValue(osuPath, 'Password', password);
|
key: 'Username',
|
||||||
await setUserConfigValue(osuPath, 'SaveUsername', '1');
|
value: username,
|
||||||
await setUserConfigValue(osuPath, 'SavePassword', '1');
|
},
|
||||||
|
{
|
||||||
|
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));
|
await new Promise((res) => setTimeout(res, 1500));
|
||||||
launchInfo = 'Launching osu!...';
|
launchInfo = 'Launching osu!...';
|
||||||
@ -754,8 +773,12 @@
|
|||||||
type="single"
|
type="single"
|
||||||
value={$osuStream}
|
value={$osuStream}
|
||||||
onValueChange={async (newStream) => {
|
onValueChange={async (newStream) => {
|
||||||
|
const oldStream = $osuStream;
|
||||||
osuStream.set(newStream);
|
osuStream.set(newStream);
|
||||||
await setConfigValue($osuInstallationPath, '_ReleaseStream', newStream);
|
await setConfigValues($osuInstallationPath, [
|
||||||
|
{ key: '_ReleaseStream', value: newStream },
|
||||||
|
{ key: '_PreviousReleaseStream', value: oldStream ?? newStream },
|
||||||
|
]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select.Trigger class="border-theme-800 bg-theme-950 text-white font-semibold">
|
<Select.Trigger class="border-theme-800 bg-theme-950 text-white font-semibold">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user