[feat-578] store protonFolder in static config

* changed protonPath to protonFolder
* log stdout for running bsipa executable
This commit is contained in:
silentrald
2024-09-22 13:07:02 +08:00
parent 4a91fa04e7
commit 0398a957ad
17 changed files with 189 additions and 52 deletions
@@ -1,7 +1,7 @@
import { Observable } from "rxjs";
import { BSLaunchError, BSLaunchEvent, BSLaunchEventData, BSLaunchWarning, LaunchOption } from "../../../shared/models/bs-launch";
import { StoreLauncherInterface } from "./store-launcher.interface";
import { pathExists, rename } from "fs-extra";
import { pathExists, pathExistsSync, rename } from "fs-extra";
import { SteamService } from "../steam.service";
import path from "path";
import { BS_APP_ID, BS_EXECUTABLE, STEAMVR_APP_ID } from "../../constants";
@@ -11,6 +11,7 @@ import { CustomError } from "../../../shared/models/exceptions/custom-error.clas
import { UtilsService } from "../utils.service";
import { exec } from "child_process";
import fs from 'fs';
import { StaticConfigurationService } from "../static-configuration.service";
export class SteamLauncherService extends AbstractLauncherService implements StoreLauncherInterface{
@@ -23,11 +24,13 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
return SteamLauncherService.instance;
}
private readonly staticConfig: StaticConfigurationService;
private readonly steam: SteamService;
private readonly util: UtilsService;
private constructor(){
super();
this.staticConfig = StaticConfigurationService.getInstance();
this.steam = SteamService.getInstance();
this.util = UtilsService.getInstance();
}
@@ -128,9 +131,16 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
`${exePath}`,
...launchArgs,
];
exePath = launchOptions.protonPath;
if (!exePath) {
throw CustomError.fromError(new Error("Proton path not set"), BSLaunchError.PROTON_NOT_SET);
if (!this.staticConfig.has("proton-folder")) {
throw CustomError.fromError(new Error("Proton folder not set"), BSLaunchError.PROTON_NOT_SET);
}
exePath = path.join(this.staticConfig.get("proton-folder"), "proton");
if (!pathExistsSync(exePath)) {
throw CustomError.fromError(
new Error("Could not locate proton binary"),
BSLaunchError.PROTON_NOT_FOUND
);
}
// Setup Proton environment variables
@@ -17,6 +17,7 @@ import { sToMs } from "../../../shared/helpers/time.helpers";
import { ensureDir, pathExistsSync } from "fs-extra";
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { popElement } from "shared/helpers/array.helpers";
import { StaticConfigurationService } from "../static-configuration.service";
export class BsModsManagerService {
private static instance: BsModsManagerService;
@@ -24,6 +25,7 @@ export class BsModsManagerService {
private readonly beatModsApi: BeatModsApiService;
private readonly bsLocalService: BSLocalVersionService;
private readonly requestService: RequestService;
private readonly staticConfig: StaticConfigurationService;
private manifestMatches: Mod[];
@@ -38,6 +40,7 @@ export class BsModsManagerService {
this.beatModsApi = BeatModsApiService.getInstance();
this.bsLocalService = BSLocalVersionService.getInstance();
this.requestService = RequestService.getInstance();
this.staticConfig = StaticConfigurationService.getInstance();
}
private async getModFromHash(hash: string): Promise<Mod> {
@@ -140,9 +143,28 @@ export class BsModsManagerService {
return false;
}
// Just use the wine binary within the proton folder so no additional wine installation is needed
let winePath: string = "";
if (process.platform === "linux") {
if (!this.staticConfig.has("proton-folder")) {
log.error("Proton folder not setup");
return false;
}
winePath = path.join(
this.staticConfig.get("proton-folder"),
"files", "bin", "wine"
);
if (!pathExistsSync(winePath)) {
log.error("Wine binary not found");
return false;
}
}
return new Promise<boolean>(resolve => {
const cmd = process.platform === 'linux'
? `screen -dmS "BSIPA" dotnet "${ipaPath}" ${args.join(" ")}` // Must run through screen, otherwise BSIPA tries to move console cursor and crashes.
const cmd = process.platform === "linux"
? `"${winePath}" "${ipaPath}" ${args.join(" ")}`
: `"${ipaPath}" ${args.join(" ")}`;
log.info("START IPA PROCESS", cmd);
@@ -153,6 +175,9 @@ export class BsModsManagerService {
resolve(false)
}, sToMs(30));
processIPA.stdout.on("data", data => {
log.info("IPA process stdout", data.toString());
});
processIPA.stderr.on("data", data => {
log.error("IPA process stderr", data.toString());
})
@@ -195,7 +220,7 @@ export class BsModsManagerService {
}
const crypto = require("crypto");
const files = await zip.files;
const { files } = zip;
const checkedEntries = (
await Promise.all(
@@ -1,6 +1,9 @@
import ElectronStore from "electron-store";
import { pathExistsSync } from "fs-extra";
import path from "path";
import { Observable, Subject } from "rxjs";
import { BSVersion } from "shared/bs-version.interface";
import { CustomError } from "shared/models/exceptions/custom-error.class";
export class StaticConfigurationService {
private static instance: StaticConfigurationService;
@@ -34,7 +37,17 @@ export class StaticConfigurationService {
cb(this.get(key));
}
public set<K extends StaticConfigKeys>(key: K, value: StaticConfigKeyValues[K]): void {
public async set<K extends StaticConfigKeys>(key: K, value: StaticConfigKeyValues[K]): Promise<void> {
// Validate the setters
switch (key) {
case "proton-folder":
this.validateProtonFolder(value as string);
break;
default:
break;
}
this.store.set(key, value);
if (this.watchers[key]) {
@@ -42,6 +55,16 @@ export class StaticConfigurationService {
}
}
// Setters with validation
private validateProtonFolder(protonFolder: string): void {
const protonPath = path.join(protonFolder, "proton");
const winePath = path.join(protonFolder, "files", "bin", "wine");
if (!pathExistsSync(protonPath) || !pathExistsSync(winePath)) {
throw new CustomError("Invalid proton folder path", "invalid-folder");
}
}
public delete<K extends StaticConfigKeys>(key: K): void {
this.store.delete(key);
}
@@ -60,12 +83,15 @@ export class StaticConfigurationService {
}
export interface StaticConfigKeyValues {
"versions": BSVersion[];
"installation-folder": string;
"song-details-cache-etag": string;
"disable-hadware-acceleration": boolean;
"use-symlinks": boolean;
}
// Linux Specific static configs
"versions": BSVersion[];
"proton-folder": string;
};
export type StaticConfigKeys = keyof StaticConfigKeyValues;
@@ -77,5 +103,5 @@ export type StaticConfigGetIpcRequestResponse<K extends StaticConfigKeys> = {
export type StaticConfigSetIpcRequest<K extends StaticConfigKeys> = {
request: { key: K, value: StaticConfigKeyValues[K] };
response: void;
}
};