From 947b384511ccc2a4453d7b6c87927bb7712b6fbe Mon Sep 17 00:00:00 2001 From: akanyan Date: Thu, 27 Feb 2025 02:11:37 +0100 Subject: [PATCH] feat: etc --- .prettierignore | 1 + README.md | 3 +- bun.lockb | Bin 146807 -> 147178 bytes package.json | 1 + rust/Cargo.lock | 1 + rust/Cargo.toml | 1 + rust/capabilities/default.json | 36 +++++----- rust/src/appdata.rs | 20 ++++-- rust/src/cmd.rs | 16 +++-- rust/src/lib.rs | 3 +- rust/src/liner.rs | 54 ++++++++------- rust/src/pkg.rs | 2 +- rust/src/profile.rs | 19 ++++- rust/src/start.rs | 123 +++++++++++++++++++-------------- rust/src/util.rs | 9 --- src/components/App.vue | 24 +------ src/components/Options.vue | 37 ++++++++-- src/components/StartButton.vue | 59 ++++++++++++++++ 18 files changed, 263 insertions(+), 146 deletions(-) create mode 100644 .prettierignore create mode 100644 src/components/StartButton.vue diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..d74745f --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +rust/**/* \ No newline at end of file diff --git a/README.md b/README.md index 9750166..ba0935c 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,8 @@ Arbitrary scripts are not supported by design and that will probably never chang - CHUNITHM support - segatools as a special package - Progress bars and other GUI sugar -- Rebuilding the profile only when necessary +- Search bar +- Start check ### Endgame diff --git a/bun.lockb b/bun.lockb index 5341a6b3ef60d52205981bd47767efd7cc18fca0..fedd93f3735fc8598163b365b02346f7c9b841f8 100755 GIT binary patch delta 23909 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 K;0$Y+@c#fOH`f;c delta 23787 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, - pub pkgs: PackageStore + pub pkgs: PackageStore, } impl AppData { @@ -37,4 +39,14 @@ impl AppData { Ok(()) } -} \ No newline at end of file + + pub fn sum_packages(&self, p: &Profile) -> String { + let mut hasher = DefaultHasher::new(); + for pkg in &p.mods { + let x = self.pkgs.get(pkg).unwrap().loc.as_ref().unwrap(); + pkg.hash(&mut hasher); + x.version.hash(&mut hasher); + } + hasher.finish().to_string() + } +} diff --git a/rust/src/cmd.rs b/rust/src/cmd.rs index a24abcf..9816309 100644 --- a/rust/src/cmd.rs +++ b/rust/src/cmd.rs @@ -20,16 +20,24 @@ pub async fn startline(app: AppHandle) -> Result<(), String> { let appd = state.lock().await; if let Some(p) = &appd.profile { - // TODO if p.needsUpdate - liner::line_up(p).await.expect("Line-up failed"); + let hash = appd.sum_packages(p); + liner::line_up(p, hash).await + .map_err(|e| e.to_string())?; start::start(p, app_copy) - .map_err(|e| { log::error!("Error launching: {}", e.to_string()); e.to_string() }) - //Ok(()) + .map_err(|e| e.to_string()) } else { Err("No profile".to_owned()) } } +#[tauri::command] +pub async fn kill() -> Result<(), String> { + start::pkill("amdaemon.exe").await; + // The start routine will kill the other process + + Ok(()) +} + #[tauri::command] pub async fn install_package(state: State<'_, tokio::sync::Mutex>, key: PkgKey) -> Result { log::debug!("invoke: install_package({})", key); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 783b3a5..9965a77 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -107,7 +107,8 @@ pub async fn run(_args: Vec) { cmd::init_profile, cmd::save_profile, cmd::startline, - cmd::set_cfg + cmd::kill, + cmd::set_cfg, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/rust/src/liner.rs b/rust/src/liner.rs index e23d5fd..991add5 100644 --- a/rust/src/liner.rs +++ b/rust/src/liner.rs @@ -17,9 +17,23 @@ async fn symlink(src: impl AsRef, dst: impl AsRef) -> std::io::Resul junction::create(src, dst) } -pub async fn line_up(p: &Profile) -> Result<()> { - let dir_out = util::profile_dir(&p); - log::info!("Preparing {}", dir_out.to_string_lossy()); +pub async fn line_up(p: &Profile, pkg_hash: String) -> Result<()> { + let hash_path = p.dir().join(".sl-state"); + let prev_hash = fs::read_to_string(&hash_path).await.unwrap_or_default(); + if prev_hash != pkg_hash { + log::debug!("state {} -> {}", prev_hash, pkg_hash); + fs::write(hash_path, pkg_hash).await + .map_err(|e| anyhow!("Unable to write the state file: {}", e))?; + prepare_packages(p).await?; + } + + prepare_config(p).await?; + + Ok(()) +} + +async fn prepare_packages(p: &Profile) -> Result<()> { + let dir_out = p.dir(); let mut futures = JoinSet::new(); if dir_out.join("BepInEx").exists() { @@ -34,25 +48,29 @@ pub async fn line_up(p: &Profile) -> Result<()> { for m in &p.mods { log::debug!("Preparing {}", m); - let dir_out = util::profile_dir(&p); let (namespace, name) = m.0.split_at(m.0.find("-").expect("Invalid mod definition")); - let bpx = util::pkg_dir_of(namespace, &name[1..]) + let bpx_dir = util::pkg_dir_of(namespace, &name[1..]) .join("app") .join("BepInEx"); - if bpx.exists() { - util::copy_recursive(&bpx, &dir_out.join("BepInEx"))?; + if bpx_dir.exists() { + util::copy_recursive(&bpx_dir, &dir_out.join("BepInEx"))?; } - let opt = util::pkg_dir_of(namespace, &name[1..]).join("option"); - if opt.exists() { - let x = opt.read_dir().unwrap().next().unwrap()?; + let opt_dir = util::pkg_dir_of(namespace, &name[1..]).join("option"); + if opt_dir.exists() { + let x = opt_dir.read_dir().unwrap().next().unwrap()?; if x.metadata()?.is_dir() { symlink(&x.path(), &dir_out.join("option").join(x.file_name())).await?; } } } - // Todo temporary + Ok(()) +} + +pub async fn prepare_config(p: &Profile) -> Result<()> { + let dir_out = p.dir(); + let ini_in_raw = fs::read_to_string(p.exe_dir.join("segatools.ini")).await?; let ini_in = Ini::load_from_str(&ini_in_raw)?; let mut opt_dir_in = PathBuf::from( @@ -78,7 +96,7 @@ pub async fn line_up(p: &Profile) -> Result<()> { util::path_to_str(dir_out.join("BepInEx").join("core").join("BepInEx.Preloader.dll"))? ); - if prepare_aime(p).await.unwrap_or(false) { + if p.get_bool("aime", false) { ini_out.with_section(Some("aime")) .set("enable", "1") .set("aimePath", util::path_to_str(dir_out.join("aime.txt"))?); @@ -93,16 +111,4 @@ pub async fn line_up(p: &Profile) -> Result<()> { } Ok(()) -} - -// Todo multiple codes -async fn prepare_aime(p: &Profile) -> Result { - if p.get_bool("aime", true) { - if let Some(code) = p.cfg.get("aime-code") { - let code = code.as_str().expect("Invalid config"); - fs::write(util::profile_dir(&p).join("aime.txt"), code).await?; - return Ok(true); - } - } - Ok(false) } \ No newline at end of file diff --git a/rust/src/pkg.rs b/rust/src/pkg.rs index 7d72cde..0c78af5 100644 --- a/rust/src/pkg.rs +++ b/rust/src/pkg.rs @@ -6,7 +6,7 @@ use tokio::fs; use crate::{model::{local, rainy}, util}; // {namespace}-{name} -#[derive(Eq, Hash, PartialEq, Clone, Serialize, Deserialize, Display)] +#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Display)] pub struct PkgKey(pub String); // {namespace}-{name}-{version} diff --git a/rust/src/profile.rs b/rust/src/profile.rs index a55d3c2..8318220 100644 --- a/rust/src/profile.rs +++ b/rust/src/profile.rs @@ -1,4 +1,5 @@ -use std::{collections::{HashMap, HashSet}, path::PathBuf}; +use anyhow::Result; +use std::{collections::{BTreeSet, HashMap}, path::PathBuf}; use crate::{model::misc, pkg::PkgKey, util}; use serde::{Deserialize, Serialize}; use tokio::fs; @@ -11,7 +12,7 @@ pub struct Profile { pub game: misc::Game, pub exe_dir: PathBuf, pub name: String, - pub mods: HashSet, + pub mods: BTreeSet, pub wine_runtime: Option, pub wine_prefix: Option, // cfg is temporarily just a map to make iteration easier @@ -25,7 +26,7 @@ impl Profile { game: misc::Game::Ongeki, exe_dir: exe_path.parent().unwrap().to_owned(), name: "ongeki-default".to_owned(), - mods: HashSet::new(), + mods: BTreeSet::new(), #[cfg(target_os = "linux")] wine_runtime: Some(std::path::Path::new("/usr/bin/wine").to_path_buf()), @@ -45,6 +46,13 @@ impl Profile { } } + pub fn dir(&self) -> PathBuf { + util::get_dirs() + .data_dir() + .join("profile-".to_owned() + &self.name) + .to_owned() + } + pub fn load() -> Option { let path = util::get_dirs() .config_dir() @@ -65,6 +73,11 @@ impl Profile { log::info!("Written to {}", path.to_string_lossy()); } + pub fn get_cfg(&self, key: &str) -> Result<&serde_json::Value> { + self.cfg.get(key) + .ok_or_else(|| anyhow::anyhow!("Invalid config entry {}", key)) + } + pub fn get_bool(&self, key: &str, default: bool) -> bool { self.cfg.get(key) .and_then(|c| c.as_bool()) diff --git a/rust/src/start.rs b/rust/src/start.rs index f9f1665..65be205 100644 --- a/rust/src/start.rs +++ b/rust/src/start.rs @@ -1,50 +1,40 @@ use anyhow::Result; +use std::fs::File; use tokio::process::Command; use tauri::{AppHandle, Emitter}; +use std::process::Stdio; use crate::profile::Profile; use crate::util; -#[cfg(target_os = "linux")] -pub fn start(p: &Profile, app: AppHandle) -> Result<()> { - let p = p.clone(); - tauri::async_runtime::spawn(async move { - let rv = Command::new(p.wine_runtime.as_ref().unwrap()) - .env( - "SEGATOOLS_CONFIG_PATH", - util::profile_dir(&p).join("segatools.ini"), - ) - .env("WINEPREFIX", p.wine_prefix.as_ref().unwrap()) - .arg(p.exe_dir.join("start.bat")) - .spawn(); - match rv { - Ok(mut child) => { - _ = child.wait().await; - log::debug!("Fin"); - }, - Err(e) => { - log::error!("Fail: {}", e); - } - } - _ = app.emit("launch-end", ""); - }); - - Ok(()) -} - #[cfg(target_os = "windows")] +static CREATE_NO_WINDOW: i32 = 0x08000000; + pub fn start(p: &Profile, app: AppHandle) -> Result<()> { - use std::process::Stdio; use tokio::task::JoinSet; - let create_no_window = 0x08000000; - - let ini_path = util::profile_dir(&p).join("segatools.ini"); + let ini_path = p.dir().join("segatools.ini"); log::debug!("With path {}", ini_path.to_string_lossy()); - log::info!("Launching amdaemon"); - let mut amd_builder = Command::new("cmd.exe"); - let mut game_builder = Command::new(p.exe_dir.join("inject.exe")); + let mut game_builder; + let mut amd_builder; + + #[cfg(target_os = "windows")] + { + game_builder = Command::new(p.exe_dir.join("inject.exe")); + amd_builder = Command::new("cmd.exe"); + } + #[cfg(target_os = "linux")] + { + let wine = p.wine_runtime.as_ref() + .expect("No wine path specified"); + + game_builder = Command::new(wine); + amd_builder = Command::new(wine); + + game_builder.arg(p.exe_dir.join("inject.exe")); + amd_builder.arg("cmd.exe"); + } let display_mode = p.get_str("display-mode", "borderless"); @@ -76,24 +66,38 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> { game_builder.arg("-popupwindow"); } - if !cfg!(debug_assertions) { - amd_builder - .creation_flags(create_no_window) - // Obviously, this is a meme - // Output will be handled properly at a later time - .stdout(Stdio::null()) - .stderr(Stdio::null()); + #[cfg(target_os = "linux")] + { + let wineprefix = p.wine_prefix.as_ref() + .expect("No wineprefix specified"); + amd_builder.env("WINEPREFIX", wineprefix); + game_builder.env("WINEPREFIX", wineprefix); + } - game_builder - .creation_flags(create_no_window) - .stdout(Stdio::null()) - .stderr(Stdio::null()); + + let amd_log = File::create(p.dir().join("amdaemon.log"))?; + let game_log = File::create(p.dir().join("mu3.log"))?; + + amd_builder + .stdout(Stdio::from(amd_log)); + // do they use stderr? + + game_builder + .stdout(Stdio::from(game_log)); + + #[cfg(target_os = "windows")] + { + amd_builder.creation_flags(CREATE_NO_WINDOW); + game_builder.creation_flags(CREATE_NO_WINDOW); } if p.get_bool("intel", false) == true { amd_builder.env("OPENSSL_ia32cap", ":~0x20000000"); } + log::info!("Launching amdaemon: {:?}", amd_builder); + log::info!("Launching mu3: {:?}", game_builder); + let mut amd = amd_builder.spawn()?; let mut game = game_builder.spawn()?; @@ -101,19 +105,24 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> { let mut set = JoinSet::new(); set.spawn(async move { - amd.wait().await.expect("amdaemon failed to run") + (amd.wait().await.expect("amdaemon failed to run"), "amdaemon.exe") }); set.spawn(async move { - game.wait().await.expect("mu3 failed to run") + (game.wait().await.expect("mu3 failed to run"), "mu3.exe") }); - let res = set.join_next().await.expect("No spawn").expect("No result"); + _ = app.emit("launch-start", ""); - log::info!("One of the processes died with return code {}", res); + let (rc, process_name) = set.join_next().await.expect("No spawn").expect("No result"); - _ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("amdaemon.exe").creation_flags(create_no_window).output().await; - _ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("mu3.exe").creation_flags(create_no_window).output().await; + log::info!("{} died with return code {}", process_name, rc); + + if process_name == "amdaemon.exe" { + pkill("mu3.exe").await; + } else { + pkill("amdaemon.exe").await; + } set.join_next().await.expect("No spawn").expect("No result"); @@ -123,4 +132,16 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> { }); Ok(()) +} + +#[cfg(target_os = "windows")] +pub async fn pkill(process_name: &str) { + _ = Command::new("taskkill.exe").arg("/f").arg("/im").arg(process_name) + .creation_flags(CREATE_NO_WINDOW).output().await; +} + +#[cfg(target_os = "linux")] +pub async fn pkill(process_name: &str) { + _ = Command::new("pkill").arg(process_name) + .output().await; } \ No newline at end of file diff --git a/rust/src/util.rs b/rust/src/util.rs index 9a5b341..1dae268 100644 --- a/rust/src/util.rs +++ b/rust/src/util.rs @@ -2,8 +2,6 @@ use anyhow::{anyhow, Result}; use directories::ProjectDirs; use std::path::{Path, PathBuf}; -use crate::profile::Profile; - pub fn get_dirs() -> ProjectDirs { ProjectDirs::from("org", "7EVENDAYSHOLIDAYS", "STARTLINER") .expect("Unable to set up config directories") @@ -21,13 +19,6 @@ pub fn pkg_dir_of(namespace: &str, name: &str) -> PathBuf { pkg_dir().join(format!("{}-{}", namespace, name)).to_owned() } -pub fn profile_dir(p: &Profile) -> PathBuf { - get_dirs() - .data_dir() - .join("profile-".to_owned() + &p.name) - .to_owned() -} - pub fn cache_dir() -> PathBuf { get_dirs().cache_dir().to_owned() } diff --git a/src/components/App.vue b/src/components/App.vue index 13486b7..f1e0143 100644 --- a/src/components/App.vue +++ b/src/components/App.vue @@ -6,13 +6,12 @@ import TabList from 'primevue/tablist'; import TabPanel from 'primevue/tabpanel'; import TabPanels from 'primevue/tabpanels'; import Tabs from 'primevue/tabs'; -import { invoke } from '@tauri-apps/api/core'; -import { listen } from '@tauri-apps/api/event'; import { onOpenUrl } from '@tauri-apps/plugin-deep-link'; import { open } from '@tauri-apps/plugin-dialog'; import ModList from './ModList.vue'; import ModStore from './ModStore.vue'; import Options from './Options.vue'; +import StartButton from './StartButton.vue'; import { usePkgStore } from '../stores'; import { changePrimaryColor } from '../util'; @@ -20,7 +19,6 @@ const store = usePkgStore(); store.setupListeners(); const currentTab = ref('3'); -const startEnabled = ref(false); const loadProfile = async (openWindow: boolean) => { await store.reloadProfile(); @@ -42,7 +40,6 @@ const loadProfile = async (openWindow: boolean) => { } if (store.profile !== null) { changePrimaryColor(store.profile.game); - startEnabled.value = true; currentTab.value = '0'; } @@ -51,11 +48,6 @@ const loadProfile = async (openWindow: boolean) => { const isProfileDisabled = computed(() => store.profile === null); -const startline = async () => { - startEnabled.value = false; - await invoke('startline'); -}; - onOpenUrl((urls) => { console.log('deep link:', urls); }); @@ -63,10 +55,6 @@ onOpenUrl((urls) => { onMounted(async () => { await loadProfile(false); }); - -listen('launch-end', () => { - startEnabled.value = true; -});