fix: restore wine configuration

This commit is contained in:
2025-03-14 15:16:49 +01:00
parent e1ff28d4e0
commit 62edcdae08
13 changed files with 97 additions and 72 deletions

View File

@ -18,7 +18,7 @@ bun run tauri build
Once a profile is set up, it is possible to bypass the GUI:
```sh
startliner --start --game ongeki --name profile-name
startliner --start --game ongeki --profile <name>
```
### Package format

View File

@ -13,6 +13,8 @@
"deep-link:default",
"fs:default",
"fs:allow-data-read-recursive",
"fs:allow-data-write-recursive"
"fs:allow-data-write-recursive",
"fs:allow-config-read-recursive",
"fs:allow-config-write-recursive"
]
}

View File

@ -57,7 +57,7 @@ pub async fn run(_args: Vec<String>) {
if let Ok(matches) = app.cli().matches() {
let start_arg = matches.args.get("start").expect("Invalid argument configuration");
let game_arg = matches.args.get("game").expect("Invalid argument configuration");
let name_arg = matches.args.get("name").expect("Invalid argument configuration");
let name_arg = matches.args.get("profile").expect("Invalid argument configuration");
log::debug!("{:?} {:?} {:?}", start_arg, game_arg, name_arg);
if start_arg.occurrences > 0 {
start_immediately = true;

View File

@ -11,6 +11,19 @@ pub struct Segatools {
pub intel: bool,
}
impl Default for Segatools {
fn default() -> Self {
Segatools {
target: PathBuf::default(),
amfs: PathBuf::default(),
option: PathBuf::default(),
appdata: PathBuf::from("appdata"),
enable_aime: false,
intel: false
}
}
}
#[derive(Deserialize, Serialize, Clone, Default, PartialEq)]
pub enum DisplayMode {
Window,
@ -28,6 +41,19 @@ pub struct Display {
pub borderless_fullscreen: bool,
}
impl Default for Display {
fn default() -> Self {
Display {
target: "default".to_owned(),
rez: (1080, 1920),
mode: DisplayMode::Borderless,
rotation: 0,
frequency: 60,
borderless_fullscreen: true,
}
}
}
#[derive(Deserialize, Serialize, Clone, Default, PartialEq)]
pub enum NetworkType {
#[default] Remote,
@ -38,7 +64,7 @@ pub enum NetworkType {
pub struct Network {
pub network_type: NetworkType,
pub local_path: String,
pub local_path: PathBuf,
pub local_console: bool,
pub remote_address: String,
@ -50,5 +76,22 @@ pub struct Network {
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct BepInEx {
pub console: bool
pub console: bool,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Wine {
pub runtime: PathBuf,
pub prefix: PathBuf,
}
impl Default for Wine {
fn default() -> Self {
Wine {
runtime: PathBuf::from("/usr/bin/wine"),
prefix: std::env::var("HOME")
.and_then(|home| Ok(PathBuf::from(home).join(".wine")))
.unwrap_or_default()
}
}
}

View File

@ -4,7 +4,6 @@ use anyhow::Result;
use displayz::{query_displays, DisplaySet};
use tauri::{AppHandle, Listener};
#[cfg(target_os = "windows")]
#[derive(Clone)]
pub struct DisplayInfo {
pub primary: String,
@ -20,20 +19,6 @@ impl Default for DisplayInfo {
}
}
impl Default for Display {
fn default() -> Self {
Display {
target: "default".to_owned(),
rez: (1080, 1920),
mode: DisplayMode::Borderless,
rotation: 0,
frequency: 60,
borderless_fullscreen: true,
}
}
}
#[cfg(target_os = "windows")]
impl Display {
pub fn activate(&self, app: AppHandle) {
let display = self.clone();

View File

@ -1,5 +1,7 @@
pub mod display;
pub mod package;
pub mod segatools;
pub mod network;
pub mod bepinex;
pub mod bepinex;
#[cfg(target_os = "windows")]
pub mod display_windows;

View File

@ -27,7 +27,7 @@ impl Network {
if self.network_type == NetworkType::Artemis {
let network_path = PathBuf::from(&self.local_path);
let artemis_dir = network_path.parent()
.ok_or_else(|| anyhow!("Invalid ARTEMiS path {}", &self.local_path))?;
.ok_or_else(|| anyhow!("Invalid ARTEMiS path {:?}", &self.local_path))?;
let cfg_path = artemis_dir.join("config").join("core.yaml");
let cfg = std::fs::read_to_string(&cfg_path)
@ -41,15 +41,22 @@ impl Network {
if let Some(hostname) = hostname {
ini.with_section(Some("dns")).set("default", hostname);
#[cfg(target_os = "windows")]
let mut cmd = Command::new("cmd.exe");
cmd.arg("/C");
let mut cmd: Command;
if self.local_console == true {
cmd.arg("start");
if cfg!(target_os = "windows") {
cmd = Command::new("cmd.exe");
cmd.arg("/C");
if self.local_console == true {
cmd.arg("start");
}
} else {
cmd = Command::new("sh");
}
cmd.args(["python", &self.local_path]);
cmd.arg("python");
cmd.arg(&self.local_path);
cmd.current_dir(artemis_dir);
cmd.spawn()
.map_err(|e| anyhow!("Unable to spawn artemis: {}", e))?;

View File

@ -1,23 +1,7 @@
use std::path::PathBuf;
use anyhow::{anyhow, Result};
use ini::Ini;
use crate::{model::{config::Segatools, segatools_base::segatools_base}, profiles::ProfilePaths, util::{self, PathStr}};
impl Default for Segatools {
fn default() -> Self {
Segatools {
target: PathBuf::default(),
amfs: PathBuf::default(),
option: PathBuf::default(),
appdata: PathBuf::from("appdata"),
enable_aime: false,
intel: false
}
}
}
impl Segatools {
pub async fn line_up(&self, p: &impl ProfilePaths) -> Result<Ini> {
log::debug!("begin line-up: segatools");

View File

@ -70,7 +70,7 @@ impl AnyProfile {
Self::OngekiProfile(p) => &mut p.mods
}
}
pub async fn line_up(&self, app: AppHandle, pkg_hash: String) -> Result<()> {
pub async fn line_up(&self, _app: AppHandle, pkg_hash: String) -> Result<()> {
match self {
Self::OngekiProfile(p) => {
if !p.data_dir().exists() {
@ -80,7 +80,8 @@ impl AnyProfile {
let hash_path = p.data_dir().join(".sl-state");
let meta = self.meta();
p.display.activate(app.clone());
#[cfg(target_os = "windows")]
p.display.activate(_app.clone());
util::clean_up_opts(p.data_dir().join("option"))?;

View File

@ -4,7 +4,6 @@ use tauri::Emitter;
use std::{collections::BTreeSet, path::PathBuf, process::Stdio};
use crate::model::config::BepInEx;
use crate::profiles::fixed_name;
use crate::util::PathStr;
use crate::{model::{config::{Display, DisplayMode, Network, Segatools}, misc::Game, segatools_base::segatools_base}, pkg::PkgKey, util};
use super::{Profile, ProfileMeta, ProfilePaths};
use anyhow::{anyhow, Result};
@ -23,7 +22,10 @@ pub struct OngekiProfile {
pub sgt: Segatools,
pub display: Display,
pub network: Network,
pub bepinex: BepInEx
pub bepinex: BepInEx,
#[cfg(not(target_os = "windows"))]
pub wine: crate::model::config::Wine,
}
impl Profile for OngekiProfile {
@ -36,7 +38,9 @@ impl Profile for OngekiProfile {
sgt: Segatools::default(),
display: Display::default(),
network: Network::default(),
bepinex: BepInEx::default()
bepinex: BepInEx::default(),
#[cfg(not(target_os = "windows"))]
wine: crate::model::config::Wine::default(),
};
p.save()?;
std::fs::write(p.config_dir().join("segatools-base.ini"), segatools_base())?;
@ -92,11 +96,8 @@ impl Profile for OngekiProfile {
}
#[cfg(target_os = "linux")]
{
let wine = p.data.wine_runtime.clone()
.unwrap_or_else(|| std::path::PathBuf::from("/usr/bin/wine"));
game_builder = Command::new(&wine);
amd_builder = Command::new(&wine);
game_builder = Command::new(&self.wine.runtime);
amd_builder = Command::new(&self.wine.runtime);
game_builder.arg(exe_dir.join("inject.exe"));
amd_builder.arg("cmd.exe");
@ -107,9 +108,10 @@ impl Profile for OngekiProfile {
&ini_path,
)
.current_dir(&exe_dir)
.arg("/C")
.arg(&exe_dir.join("inject.exe"))
.args([
"/C",
&exe_dir.join("inject.exe").stringify()?, "-d", "-k", "mu3hook.dll",
"-d", "-k", "mu3hook.dll",
"amdaemon.exe", "-f", "-c", "config_common.json", "config_server.json", "config_client.json"
]);
game_builder
@ -136,17 +138,10 @@ impl Profile for OngekiProfile {
#[cfg(target_os = "linux")]
{
let wineprefix = p.data.wine_prefix.clone().unwrap_or_else(||
directories::UserDirs::new()
.expect("No home directory")
.home_dir()
.join(".wine")
);
amd_builder.env("WINEPREFIX", &wineprefix);
game_builder.env("WINEPREFIX", &wineprefix);
amd_builder.env("WINEPREFIX", &self.wine.prefix);
game_builder.env("WINEPREFIX", &self.wine.prefix);
}
let amd_log = File::create(self.data_dir().join("amdaemon.log"))?;
let game_log = File::create(self.data_dir().join("mu3.log"))?;

View File

@ -1,6 +1,7 @@
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Manager};
use tokio::process::Command;
use std::{path::{Path, PathBuf}, sync::OnceLock};
use crate::model::misc::Game;
@ -86,7 +87,7 @@ pub fn copy_directory(src: impl AsRef<Path>, dst: impl AsRef<Path>, recursive: b
#[cfg(target_os = "linux")]
pub async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
fs::symlink(src, dst).await
tokio::fs::symlink(src, dst).await
}
#[cfg(target_os = "windows")]
@ -100,8 +101,6 @@ pub static CREATE_NO_WINDOW: u32 = 0x08000000;
#[cfg(target_os = "windows")]
pub async fn pkill(process_name: &str) {
use tokio::process::Command;
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg(process_name)
.creation_flags(CREATE_NO_WINDOW).output().await;
}
@ -120,7 +119,10 @@ pub fn clean_up_opts(dir: impl AsRef<Path>) -> Result<()> {
let path = entry.path();
log::debug!("{:?}", path);
if path.is_symlink() {
#[cfg(target_os = "windows")]
std::fs::remove_dir(path)?;
#[cfg(not(target_os = "windows"))]
std::fs::remove_file(path)?;
} else {
log::error!("Not a symlink: {:?}", path);
}

View File

@ -34,8 +34,8 @@
"possibleValues": ["chunithm", "ongeki"]
},
{
"short": "n",
"name": "name",
"short": "p",
"name": "profile",
"takesValue": true,
"description": "Profile name"
}

View File

@ -181,7 +181,11 @@ const extraDisplayOptionsDisabled = computed(() => {
:disabled="extraDisplayOptionsDisabled"
/>
</OptionRow>
<OptionRow class="number-input" title="Refresh Rate">
<OptionRow
v-if="capabilities.includes('display')"
class="number-input"
title="Refresh Rate"
>
<InputNumber
class="shrink"
size="small"