feat: less bad launches

This commit is contained in:
2025-02-23 23:48:00 +01:00
parent fcd3855c49
commit 70b8b3ae55
27 changed files with 145 additions and 34 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

BIN
rust/icons/slow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -9,18 +9,21 @@ use crate::profile::Profile;
use crate::appdata::AppData; use crate::appdata::AppData;
use crate::{liner, start}; use crate::{liner, start};
use tauri::State; use tauri::{AppHandle, Manager, State};
#[tauri::command] #[tauri::command]
pub async fn startline(state: State<'_, Mutex<AppData>>) -> Result<(), String> { pub async fn startline(app: AppHandle) -> Result<(), String> {
log::debug!("invoke: startline"); log::debug!("invoke: startline");
let app_copy = app.clone();
let state = app.state::<Mutex<AppData>>();
let appd = state.lock().await; let appd = state.lock().await;
if let Some(p) = &appd.profile { if let Some(p) = &appd.profile {
// TODO if p.needsUpdate // TODO if p.needsUpdate
liner::line_up(p).await.expect("Line-up failed"); liner::line_up(p).await.expect("Line-up failed");
start::start(p).map_err(|e| { log::error!("Error launching: {}", e.to_string()); e.to_string() }).map(|_| ()) start::start(p, app_copy)
.map_err(|e| { log::error!("Error launching: {}", e.to_string()); e.to_string() })
//Ok(()) //Ok(())
} else { } else {
Err("No profile".to_owned()) Err("No profile".to_owned())

View File

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::path::Path; use std::path::Path;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -6,7 +6,7 @@ use tauri::{AppHandle, Emitter};
use tokio::fs; use tokio::fs;
use tokio::task::JoinSet; use tokio::task::JoinSet;
use crate::model::rainy; use crate::model::rainy;
use crate::pkg::{Package, PkgKey}; use crate::pkg::{Package, PkgKey, Remote};
use crate::util; use crate::util;
use crate::download_handler::DownloadHandler; use crate::download_handler::DownloadHandler;
@ -134,12 +134,14 @@ impl PackageStore {
pkg: key.to_owned() pkg: key.to_owned()
})?; })?;
let rmt = pkg.rmt.as_ref() //clone() let rmt = pkg.rmt.as_ref()
.ok_or_else(|| anyhow!("Attempted to install a pkg without remote data"))?; .ok_or_else(|| anyhow!("Attempted to install a pkg without remote data"))?;
if install_deps { if install_deps {
for dep in &rmt.dependencies { let mut set = HashSet::new();
Box::pin(self.install_package(&dep, false, true)).await?; self.resolve_deps(rmt.clone(), &mut set)?;
for dep in set {
Box::pin(self.install_package(&dep, false, false)).await?;
} }
} }
@ -244,4 +246,18 @@ impl PackageStore {
Ok(()) Ok(())
} }
fn resolve_deps(&self, rmt: Remote, set: &mut HashSet<PkgKey>) -> Result<()> {
for d in rmt.dependencies {
set.insert(d.clone());
let subrmt = self.store.get(&d)
.ok_or_else(|| anyhow!("Attempted to delete a nonexistent pkg"))?
.rmt
.clone()
.ok_or_else(|| anyhow!("Attempted to resolve deps without fetching"))?;
self.resolve_deps(subrmt, set)?;
}
Ok(())
}
} }

View File

@ -1,18 +1,32 @@
use anyhow::Result; use anyhow::Result;
use tokio::process::Command; use tokio::process::Command;
use tauri::{AppHandle, Emitter};
use crate::profile::Profile; use crate::profile::Profile;
use crate::util; use crate::util;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn start(p: &Profile) -> Result<()> { pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
Command::new(p.wine_runtime.as_ref().unwrap()) let p = p.clone();
.env( tauri::async_runtime::spawn(async move {
"SEGATOOLS_CONFIG_PATH", let rv = Command::new(p.wine_runtime.as_ref().unwrap())
util::profile_dir(&p).join("segatools.ini"), .env(
) "SEGATOOLS_CONFIG_PATH",
.env("WINEPREFIX", p.wine_prefix.as_ref().unwrap()) util::profile_dir(&p).join("segatools.ini"),
.arg(p.exe_dir.join("start.bat")) )
.spawn()?; .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(()) Ok(())
} }
@ -72,6 +86,8 @@ pub fn start(p: &Profile) -> Result<()> {
set.join_next().await.expect("No spawn").expect("No result"); set.join_next().await.expect("No spawn").expect("No result");
log::debug!("Fin"); log::debug!("Fin");
app.emit("launch-end", "");
}); });
Ok(()) Ok(())

View File

@ -42,12 +42,6 @@
"bundle": { "bundle": {
"active": true, "active": true,
"targets": "all", "targets": "all",
"icon": [ "icon": ["icons/slow.png"]
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
} }
} }

View File

@ -7,6 +7,7 @@ import TabPanel from 'primevue/tabpanel';
import TabPanels from 'primevue/tabpanels'; import TabPanels from 'primevue/tabpanels';
import Tabs from 'primevue/tabs'; import Tabs from 'primevue/tabs';
import { invoke } from '@tauri-apps/api/core'; import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { onOpenUrl } from '@tauri-apps/plugin-deep-link'; import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
import { open } from '@tauri-apps/plugin-dialog'; import { open } from '@tauri-apps/plugin-dialog';
import ModList from './ModList.vue'; import ModList from './ModList.vue';
@ -19,6 +20,7 @@ const store = usePkgStore();
store.setupListeners(); store.setupListeners();
const currentTab = ref('3'); const currentTab = ref('3');
const startEnabled = ref(true);
const loadProfile = async () => { const loadProfile = async () => {
await store.reloadProfile(); await store.reloadProfile();
@ -49,9 +51,8 @@ const loadProfile = async () => {
const isProfileDisabled = computed(() => store.profile === null); const isProfileDisabled = computed(() => store.profile === null);
const startline = async () => { const startline = async () => {
startEnabled.value = false;
await invoke('startline'); await invoke('startline');
//startDisabled.value = true;
}; };
onOpenUrl((urls) => { onOpenUrl((urls) => {
@ -61,6 +62,10 @@ onOpenUrl((urls) => {
onMounted(async () => { onMounted(async () => {
await loadProfile(); await loadProfile();
}); });
listen('launch-end', () => {
startEnabled.value = true;
});
</script> </script>
<template> <template>
@ -82,7 +87,7 @@ onMounted(async () => {
></Tab> ></Tab>
<div class="grow"></div> <div class="grow"></div>
<Button <Button
:disabled="false" :disabled="!startEnabled"
icon="pi pi-play" icon="pi pi-play"
label="START" label="START"
aria-label="start" aria-label="start"

View File

@ -4,6 +4,7 @@ import ToggleSwitch from 'primevue/toggleswitch';
import { open } from '@tauri-apps/plugin-shell'; import { open } from '@tauri-apps/plugin-shell';
import InstallButton from './InstallButton.vue'; import InstallButton from './InstallButton.vue';
import ModTitlecard from './ModTitlecard.vue'; import ModTitlecard from './ModTitlecard.vue';
import UpdateButton from './UpdateButton.vue';
import { usePkgStore } from '../stores'; import { usePkgStore } from '../stores';
import { Package } from '../types'; import { Package } from '../types';
@ -21,6 +22,7 @@ const toggle = async (value: boolean) => {
<template> <template>
<div class="flex items-center"> <div class="flex items-center">
<ModTitlecard showVersion :pkg="pkg" /> <ModTitlecard showVersion :pkg="pkg" />
<UpdateButton :pkg="pkg" />
<ToggleSwitch <ToggleSwitch
class="scale-[1.33] shrink-0" class="scale-[1.33] shrink-0"
inputId="switch" inputId="switch"
@ -28,7 +30,7 @@ const toggle = async (value: boolean) => {
:modelValue="store.isEnabled(pkg)" :modelValue="store.isEnabled(pkg)"
v-on:value-change="toggle" v-on:value-change="toggle"
/> />
<InstallButton /> <InstallButton :pkg="pkg" />
<Button <Button
rounded rounded
icon="pi pi-folder" icon="pi pi-folder"

View File

@ -11,7 +11,12 @@ onMounted(() => {
</script> </script>
<template> <template>
<div v-for="p in pkgs.allRemote" class="flex flex-row"> <div
v-for="p in pkgs.allRemote.sort((p1, p2) =>
p1.name.localeCompare(p2.name)
)"
class="flex flex-row"
>
<ModStoreEntry :pkg="p" /> <ModStoreEntry :pkg="p" />
</div> </div>
</template> </template>

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { convertFileSrc } from '@tauri-apps/api/core'; import { convertFileSrc } from '@tauri-apps/api/core';
import { Package } from '../types'; import { Package } from '../types';
import { needsUpdate } from '../util';
const props = defineProps({ const props = defineProps({
pkg: Object as () => Package, pkg: Object as () => Package,
@ -39,11 +40,19 @@ const iconSrc = () => {
> >
by&nbsp;{{ pkg.namespace }} by&nbsp;{{ pkg.namespace }}
</span> </span>
<span <span class="m-2">
v-if="showVersion && pkg?.loc?.version" <span
class="text-sm opacity-75 m-2" v-if="showVersion && pkg?.loc?.version"
> class="text-sm opacity-75"
{{ pkg?.loc?.version ?? '?.?.?' }} >
{{ pkg?.loc?.version ?? '?.?.?' }}
</span>
<span
v-if="showVersion && needsUpdate(pkg)"
class="text-sm opacity-75"
>
-> {{ pkg?.rmt?.version ?? '?.?.?' }}</span
>
</span> </span>
</div> </div>
<div class="text-sm opacity-75"> <div class="text-sm opacity-75">

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import Button from 'primevue/button';
import { invoke } from '@tauri-apps/api/core';
import { Package } from '../types';
import { needsUpdate, pkgKey } from '../util';
const props = defineProps({
pkg: Object as () => Package,
});
const install = async () => {
if (props.pkg === undefined) {
return;
}
try {
await invoke('install_package', { key: pkgKey(props.pkg) });
} catch (err) {
console.error(err);
if (props.pkg !== undefined) {
props.pkg.js.busy = false;
}
}
//if (rv === 'Deferred') { /* download progress */ }
};
</script>
<template>
<Button
v-if="needsUpdate(pkg) && !pkg?.js.busy"
rounded
icon="pi pi-sync"
severity="success"
aria-label="remove"
size="small"
class="self-center mr-4"
style="width: 2rem; height: 2rem"
v-on:click="install()"
/>
</template>

View File

@ -20,3 +20,23 @@ export const changePrimaryColor = (game: 'Ongeki' | 'Chunithm') => {
}; };
export const pkgKey = (pkg: Package) => `${pkg.namespace}-${pkg.name}`; export const pkgKey = (pkg: Package) => `${pkg.namespace}-${pkg.name}`;
export const needsUpdate = (pkg: Package | undefined) => {
const loc = pkg?.loc?.version;
const rmt = pkg?.rmt?.version;
if (loc === undefined || rmt === undefined) {
return false;
}
const [l1, l2, l3] = loc.split('.');
const [r1, r2, r3] = rmt.split('.');
if (l1 === r1) {
if (l2 === r2) {
return l3 < r3;
}
return l2 < r2;
}
return l1 < r1;
};