diff --git a/build/after-install.sh b/build/after-install.sh
index a9e1aedd..f2ec23df 100644
--- a/build/after-install.sh
+++ b/build/after-install.sh
@@ -1,6 +1,2 @@
-# Add write permissions to anybody so that this can be sync with the
-# github's bs-versions.json when starting bsmanager
-/usr/bin/chmod +002 /opt/BSManager/resources/assets/jsons/bs-versions.json
-
# https://github.com/electron/electron/issues/42510
/usr/bin/chmod 4755 /opt/BSManager/chrome-sandbox
diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md
index a0fbf9a7..737d8fce 100644
--- a/docs/wiki/Home.md
+++ b/docs/wiki/Home.md
@@ -49,7 +49,6 @@ Guidance on fixing errors during setup or version updates
- __🐧Linux__:
- [Missing Icons in Game]([Linux]-Missing-Icons-in-Game)
- - [Permission Denied on "bs-versions.json"]([Linux]-Permission-Denied-on-bs-version.json)
- [[Deb] The SUID Sandbox Helper Binary Was Found]([Linux]-[deb]-The-SUID-Sandbox-Helper-Binary-Was-Found)
- [[Flatpak] Steam Beat Saber Version Not Showing / Proton Not Detected]([Linux]-[Flatpak]-Steam-Beat-Saber-Version-Not-Showing-Proton-Not-Detected)
- [[Flatpak] Changing Installation Folder]([Linux]-[Flatpak]-Changing-Installation-Folder)
diff --git a/docs/wiki/Troubleshoots/Installation-Problems/Linux/[Linux]-Permission-Denied-on-bs-version.json/[Linux]-Permission-Denied-on-bs-version.json.md b/docs/wiki/Troubleshoots/Installation-Problems/Linux/[Linux]-Permission-Denied-on-bs-version.json/[Linux]-Permission-Denied-on-bs-version.json.md
deleted file mode 100644
index 81e9af5d..00000000
--- a/docs/wiki/Troubleshoots/Installation-Problems/Linux/[Linux]-Permission-Denied-on-bs-version.json/[Linux]-Permission-Denied-on-bs-version.json.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-Unhandled Exception UnhandledRejection Error: EACCES: permission denied, open '/opt/BSManager/resources/assets/jsons/bs-versions.json'
-
-
-To fix this issue, the current user must have write permissions to the "bs-versions.json". To correct the permissions do command below:
-
-```bash
-chmod +002 /opt/BSManager/resources/assets/jsons/bs-versions.json
-
-# or
-
-chown $(whoami) /opt/BSManager/resources/assets/jsons/bs-versions.json
-```
diff --git a/docs/wiki/_Sidebar.md b/docs/wiki/_Sidebar.md
index d114fbc6..1c5eddab 100644
--- a/docs/wiki/_Sidebar.md
+++ b/docs/wiki/_Sidebar.md
@@ -71,7 +71,6 @@
🐧 Linux
Missing Icons in Game
- Permission Denied on "bs-versions.json"
[Deb] The SUID Sandbox Helper Binary Was Found
[Flatpak] Steam Beat Saber Version Not Showing / Proton Not Detected
[Flatpak] Changing Installation Folder
diff --git a/electron-builder.config.js b/electron-builder.config.js
index c692c4dc..8c76afb9 100644
--- a/electron-builder.config.js
+++ b/electron-builder.config.js
@@ -69,6 +69,8 @@ const config = {
"--filesystem=~/.steam/steam/steamapps:ro", // for the libraryfolders.vdf
"--filesystem=~/.steam/steam/steamapps/common:create", // Steam game folder
"--filesystem=~/.steam/steam/steamapps/common/Beat Saber:create", // For installing mods/maps to original Beat Saber version
+ "--filesystem=~/Desktop", // allow writing shortcuts to desktop
+ "--filesystem=~/.steam/steam/userdata", // allow writing steam shortcuts
// Allow BSManager to create the compat folder if it does not exist
"--filesystem=~/.steam/steam/steamapps/compatdata/620980:create",
// Allow communication with network
diff --git a/src/main/services/bs-launcher/abstract-launcher.service.ts b/src/main/services/bs-launcher/abstract-launcher.service.ts
index cf4ee365..b61d23b9 100644
--- a/src/main/services/bs-launcher/abstract-launcher.service.ts
+++ b/src/main/services/bs-launcher/abstract-launcher.service.ts
@@ -9,6 +9,33 @@ import { BsmShellLog, bsmSpawn } from "main/helpers/os.helpers";
import { IS_FLATPAK } from "main/constants";
import { LaunchMods } from "shared/models/bs-launch/launch-option.interface";
+export function buildBsLaunchArgs(launchOptions: LaunchOption): string[] {
+ const launchArgs = [];
+
+ if(!launchOptions.version.steam && !launchOptions.version.oculus){
+ launchArgs.push("--no-yeet")
+ }
+ if(launchOptions.launchMods?.includes(LaunchMods.OCULUS)) {
+ launchArgs.push("-vrmode");
+ launchArgs.push("oculus");
+ }
+ if(launchOptions.launchMods?.includes(LaunchMods.FPFC)) {
+ launchArgs.push("fpfc");
+ }
+ if(launchOptions.launchMods?.includes(LaunchMods.DEBUG)) {
+ launchArgs.push("--verbose");
+ }
+ if(launchOptions.launchMods?.includes(LaunchMods.EDITOR)) {
+ launchArgs.push("editor");
+ }
+
+ if (launchOptions.additionalArgs) {
+ launchArgs.push(...launchOptions.additionalArgs);
+ }
+
+ return Array.from(new Set(launchArgs).values());
+}
+
export abstract class AbstractLauncherService {
protected readonly linux = LinuxService.getInstance();
@@ -19,34 +46,7 @@ export abstract class AbstractLauncherService {
this.localVersions = BSLocalVersionService.getInstance();
}
- protected buildBsLaunchArgs(launchOptions: LaunchOption): string[]{
- const launchArgs = [];
-
- if(!launchOptions.version.steam && !launchOptions.version.oculus){
- launchArgs.push("--no-yeet")
- }
- if(launchOptions.launchMods?.includes(LaunchMods.OCULUS)) {
- launchArgs.push("-vrmode");
- launchArgs.push("oculus");
- }
- if(launchOptions.launchMods?.includes(LaunchMods.FPFC)) {
- launchArgs.push("fpfc");
- }
- if(launchOptions.launchMods?.includes(LaunchMods.DEBUG)) {
- launchArgs.push("--verbose");
- }
- if(launchOptions.launchMods?.includes(LaunchMods.EDITOR)) {
- launchArgs.push("editor");
- }
-
- if (launchOptions.additionalArgs) {
- launchArgs.push(...launchOptions.additionalArgs);
- }
-
- return Array.from(new Set(launchArgs).values());
- }
-
- protected launchBSProcess(bsExePath: string, args: string[], options?: SpawnOptionsWithoutStdio): ChildProcessWithoutNullStreams {
+ protected launchBSProcess(bsExePath: string, args: string[], options?: SpawnBsProcessOptions): ChildProcessWithoutNullStreams {
const spawnOptions: SpawnOptionsWithoutStdio = { detached: true, cwd: path.dirname(bsExePath), ...(options || {}) };
@@ -57,7 +57,7 @@ export abstract class AbstractLauncherService {
spawnOptions.shell = true; // For windows to spawn properly
return bsmSpawn(`"${bsExePath}"`, {
args, options: spawnOptions, log: BsmShellLog.Command,
- linux: { prefix: this.linux.getProtonPrefix() },
+ linux: { prefix: options?.protonPrefix || "" },
flatpak: {
host: IS_FLATPAK,
env: [
@@ -70,6 +70,8 @@ export abstract class AbstractLauncherService {
"STEAM_COMPAT_CLIENT_INSTALL_PATH",
"STEAM_COMPAT_APP_ID",
"SteamEnv",
+ "PROTON_LOG",
+ "PROTON_LOG_DIR",
],
},
});
@@ -78,7 +80,7 @@ export abstract class AbstractLauncherService {
protected launchBs(bsExePath: string, args: string[], options?: SpawnBsProcessOptions): {process: ChildProcessWithoutNullStreams, exit: Promise} {
const process = this.launchBSProcess(bsExePath, args, options);
- let timoutId: NodeJS.Timeout;
+ let timeoutId: NodeJS.Timeout;
const exit = new Promise((resolve, reject) => {
// Don't remove, useful for debugging!
@@ -101,7 +103,7 @@ export abstract class AbstractLauncherService {
const unrefAfter = options?.unrefAfter ?? sToMs(10);
- timoutId = setTimeout(() => {
+ timeoutId = setTimeout(() => {
log.error("BS process unref after timeout", unrefAfter);
process.unref();
process.removeAllListeners();
@@ -109,7 +111,7 @@ export abstract class AbstractLauncherService {
}, unrefAfter);
}).finally(() => {
- clearTimeout(timoutId);
+ clearTimeout(timeoutId);
});
return { process, exit };
@@ -117,5 +119,6 @@ export abstract class AbstractLauncherService {
}
export type SpawnBsProcessOptions = {
+ protonPrefix?: string;
unrefAfter?: number;
} & SpawnOptionsWithoutStdio;
diff --git a/src/main/services/bs-launcher/bs-launcher.service.ts b/src/main/services/bs-launcher/bs-launcher.service.ts
index 6ebe7ac7..b03dd0ef 100644
--- a/src/main/services/bs-launcher/bs-launcher.service.ts
+++ b/src/main/services/bs-launcher/bs-launcher.service.ts
@@ -24,6 +24,7 @@ import { LaunchMod, LaunchMods } from "shared/models/bs-launch/launch-option.int
import { StaticConfigurationService } from "../static-configuration.service";
import { SteamService } from "../steam.service";
import { tryit } from "shared/helpers/error.helpers";
+import { LinuxService } from "../linux.service";
export class BSLauncherService {
private static instance: BSLauncherService;
@@ -37,6 +38,7 @@ export class BSLauncherService {
private readonly steam: SteamService;
private readonly oculusLauncher: OculusLauncherService;
private readonly staticConfig: StaticConfigurationService;
+ private readonly linux: LinuxService;
public static getInstance(): BSLauncherService {
if (!BSLauncherService.instance) {
@@ -55,6 +57,7 @@ export class BSLauncherService {
this.oculusLauncher = OculusLauncherService.getInstance();
this.staticConfig = StaticConfigurationService.getInstance();
this.steam = SteamService.getInstance();
+ this.linux = LinuxService.getInstance();
this.bsmProtocolService.on("launch", link => {
log.info("Launch from bsm protocol", link.toString());
@@ -182,7 +185,7 @@ export class BSLauncherService {
* @returns {Promise} Path of the icon
*/
private async createShortcutIco(color: Color): Promise{
- const pngBuffer = await this.createShortcutPngBuffer(color);
+ const pngBuffer = this.createShortcutPngBuffer(color);
const icoBuffer = await toIco([pngBuffer]);
await ensureDir(IMAGE_CACHE_PATH);
@@ -208,17 +211,28 @@ export class BSLauncherService {
if(steamShortcut){
const userId = await tryit(() => this.steam.getActiveUser());
const exePath = app.getPath("exe");
- return this.steam.createShortcut({
- AppName: shortcutName,
- Exe: exePath,
- StartDir: path.dirname(exePath),
- LaunchOptions: this.createLaunchLink(launchOptions),
- icon: await this.createShortcutPng(shortcutIconColor),
- OpenVR: "\u0001"
- }, userId.result).then(() => true).catch(e => {
- log.error(e);
- return false;
- });
+ const icon = await this.createShortcutPng(shortcutIconColor)
+ return this.steam.createShortcut(
+ process.platform === "win32"
+ ? {
+ AppName: shortcutName,
+ Exe: exePath,
+ StartDir: path.dirname(exePath),
+ LaunchOptions: this.createLaunchLink(launchOptions),
+ OpenVR: "\u0001", icon,
+ }
+ : await this.linux.getSteamShortcutData(
+ shortcutName, icon, launchOptions,
+ await this.steam.getSteamPath(),
+ await this.localVersionService.getVersionPath(launchOptions.version)
+ ),
+ userId.result
+ )
+ .then(() => true)
+ .catch(e => {
+ log.error(e);
+ return false;
+ });
}
return execOnOs({
@@ -230,12 +244,13 @@ export class BSLauncherService {
description: [shortcutName, launchOptions.version.color].join(" "), // <= Need color in description to help windows know that the shortcut is different
})
),
- linux: async () => (
- createDesktopUrlShortcut(path.join(app.getPath("desktop"), `${shortcutName}.desktop`), {
- name: shortcutName,
- url: shortcutUrl,
- icon: await this.createShortcutPng(shortcutIconColor),
- })
+ linux: async () => this.linux.createDesktopShortcut(
+ path.join(app.getPath("desktop"), `${shortcutName}.desktop`),
+ shortcutName,
+ await this.createShortcutPng(shortcutIconColor),
+ launchOptions,
+ await this.steam.getSteamPath(),
+ await this.localVersionService.getVersionPath(launchOptions.version)
)
})
@@ -282,26 +297,3 @@ type ShortcutParams = {
versionOculus?: string;
}
-/**
- * Create .desktop file for url shortcut (only for linux)
- * @param {string} shortcutPath
- * @param options
- * @returns
- */
-function createDesktopUrlShortcut(shortcutPath: string, options?: {
- url: string
- name: string,
- icon: string
-}): Promise {
- const { url, name, icon } = options || {};
-
- const data = [
- "[Desktop Entry]",
- "Type=Link",
- `Name=${name}`,
- `Icon=${icon}`,
- `URL=${url}`
- ].join("\n");
-
- return writeFile(shortcutPath, data).then(() => true);
-}
diff --git a/src/main/services/bs-launcher/oculus-launcher.service.ts b/src/main/services/bs-launcher/oculus-launcher.service.ts
index d60ffe3a..d92ccef9 100644
--- a/src/main/services/bs-launcher/oculus-launcher.service.ts
+++ b/src/main/services/bs-launcher/oculus-launcher.service.ts
@@ -6,7 +6,7 @@ import { BS_EXECUTABLE } from "../../constants";
import path from "path";
import log from "electron-log";
import { pathExists } from "fs-extra";
-import { AbstractLauncherService } from "./abstract-launcher.service";
+import { AbstractLauncherService, buildBsLaunchArgs } from "./abstract-launcher.service";
import { isProcessRunning } from "../../helpers/os.helpers";
import { CustomError } from "../../../shared/models/exceptions/custom-error.class";
import { UtilsService } from "../utils.service";
@@ -57,7 +57,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St
obs.next({type: BSLaunchEvent.BS_LAUNCHING});
// Launch Beat Saber
- const process = this.launchBs(exePath, this.buildBsLaunchArgs(launchOptions));
+ const process = this.launchBs(exePath, buildBsLaunchArgs(launchOptions));
return process.exit.catch(err => {
throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR);
diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts
index d3d25b31..ab8277d6 100644
--- a/src/main/services/bs-launcher/steam-launcher.service.ts
+++ b/src/main/services/bs-launcher/steam-launcher.service.ts
@@ -6,7 +6,7 @@ import { SteamService } from "../steam.service";
import path from "path";
import { BS_APP_ID, BS_EXECUTABLE, STEAMVR_APP_ID } from "../../constants";
import log from "electron-log";
-import { AbstractLauncherService } from "./abstract-launcher.service";
+import { AbstractLauncherService, buildBsLaunchArgs } from "./abstract-launcher.service";
import { CustomError } from "../../../shared/models/exceptions/custom-error.class";
import { UtilsService } from "../utils.service";
import { exec } from "child_process";
@@ -102,7 +102,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
await this.restoreSteamVR().catch(log.error);
}
- const launchArgs = this.buildBsLaunchArgs(launchOptions);
+ const launchArgs = buildBsLaunchArgs(launchOptions);
const steamPath = await this.steam.getSteamPath();
const env = {
@@ -112,9 +112,14 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
"SteamGameId": BS_APP_ID,
};
+ let protonPrefix = "";
// Linux setup
if (process.platform === "linux") {
- await this.linux.setupLaunch(launchOptions, steamPath, bsFolderPath, env);
+ const linuxSetup = await this.linux.setupLaunch(
+ launchOptions, steamPath, bsFolderPath
+ );
+ protonPrefix = linuxSetup.protonPrefix;
+ Object.assign(env, linuxSetup.env);
}
obs.next({type: BSLaunchEvent.BS_LAUNCHING});
@@ -122,7 +127,10 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
const spawnOpts = { env, cwd: bsFolderPath };
const launchPromise = !launchOptions.admin ? (
- this.launchBs(bsExePath, launchArgs, spawnOpts).exit
+ this.launchBs(bsExePath, launchArgs, {
+ ...spawnOpts,
+ protonPrefix
+ }).exit
) : (
new Promise(resolve => {
const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${bsExePath}" ${launchArgs.join(" ")} --log-path "${path.join(app.getPath("logs"), "bs-admin-start.log")}"`, spawnOpts);
diff --git a/src/main/services/bs-version-lib.service.ts b/src/main/services/bs-version-lib.service.ts
index 70c16e9c..b90582e1 100644
--- a/src/main/services/bs-version-lib.service.ts
+++ b/src/main/services/bs-version-lib.service.ts
@@ -5,8 +5,6 @@ import { BSVersion } from "shared/bs-version.interface";
import { RequestService } from "./request.service";
import { readJSON } from "fs-extra";
import { allSettled } from "../../shared/helpers/promise.helpers";
-import { LinuxService } from "./linux.service";
-import { IS_FLATPAK } from "main/constants";
import { StaticConfigurationService } from "./static-configuration.service";
export class BSVersionLibService {
@@ -15,7 +13,6 @@ export class BSVersionLibService {
private static instance: BSVersionLibService;
- private readonly linuxService: LinuxService;
private readonly utilsService: UtilsService;
private readonly requestService: RequestService;
private readonly configService: StaticConfigurationService;
@@ -23,7 +20,6 @@ export class BSVersionLibService {
private bsVersions: BSVersion[];
private constructor() {
- this.linuxService = LinuxService.getInstance();
this.utilsService = UtilsService.getInstance();
this.requestService = RequestService.getInstance();
this.configService = StaticConfigurationService.getInstance();
@@ -40,15 +36,10 @@ export class BSVersionLibService {
return this.requestService.getJSON(this.REMOTE_BS_VERSIONS_URL).then(res => res.data);
}
- private async shouldLoadFromConfig(): Promise {
- // Some special cases of readonly memory installations
- return process.platform === "linux" && (IS_FLATPAK || this.linuxService.isNixOS());
- }
-
private async getLocalVersions(): Promise {
const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE);
- if (!(await this.shouldLoadFromConfig())) {
+ if (process.platform !== "linux") {
return readJSON(localVersionsPath);
}
@@ -60,7 +51,7 @@ export class BSVersionLibService {
}
private async updateLocalVersions(versions: BSVersion[]): Promise {
- if (await this.shouldLoadFromConfig()) {
+ if (process.platform === "linux") {
this.configService.set("versions", versions);
return;
}
diff --git a/src/main/services/linux.service.ts b/src/main/services/linux.service.ts
index 8db15e8d..5f12896a 100644
--- a/src/main/services/linux.service.ts
+++ b/src/main/services/linux.service.ts
@@ -1,13 +1,15 @@
import fs from "fs-extra";
import log from "electron-log";
import path from "path";
-import { BS_APP_ID, IS_FLATPAK, PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants";
+import { BS_APP_ID, BS_EXECUTABLE, IS_FLATPAK, PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants";
import { InstallationLocationService } from "./installation-location.service";
import { StaticConfigurationService } from "./static-configuration.service";
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { BSLaunchError, LaunchOption } from "shared/models/bs-launch";
import { BsmShellLog, bsmExec } from "main/helpers/os.helpers";
import { LaunchMods } from "shared/models/bs-launch/launch-option.interface";
+import { SteamShortcutData } from "shared/models/steam/shortcut.model";
+import { buildBsLaunchArgs } from "./bs-launcher/abstract-launcher.service";
export class LinuxService {
private static instance: LinuxService;
@@ -21,7 +23,6 @@ export class LinuxService {
private readonly installLocationService: InstallationLocationService;
private readonly staticConfig: StaticConfigurationService;
- private protonPrefix = "";
private nixOS: boolean | undefined;
@@ -40,24 +41,26 @@ export class LinuxService {
public async setupLaunch(
launchOptions: LaunchOption,
steamPath: string,
- bsFolderPath: string,
- env: Record
- ) {
+ bsFolderPath: string
+ ): Promise<{
+ protonPrefix: string;
+ env: Record;
+ }> {
if (launchOptions.admin) {
log.warn("Launching as admin is not supported on Linux! Starting the game as a normal user.");
launchOptions.admin = false;
}
- // Create the compat data path if it doesn't exist.
- // If the user never ran Beat Saber through steam before
- // using bsmanager, it won't exist, and proton will fail
- // to launch the game.
- const compatDataPath = this.getCompatDataPath();
- if (!fs.existsSync(compatDataPath)) {
- log.info(`Proton compat data path not found at '${compatDataPath}', creating directory`);
- fs.mkdirSync(compatDataPath);
- }
+ const protonPath = await this.getProtonPath();
+ return {
+ protonPrefix: await this.isNixOS()
+ ? `steam-run "${protonPath}" run`
+ : `"${protonPath}" run`,
+ env: await this.buildEnvVariables(launchOptions, steamPath, bsFolderPath)
+ };
+ }
+ private async getProtonPath(): Promise {
if (!this.staticConfig.has("proton-folder")) {
throw CustomError.fromError(
new Error("Proton folder not set"),
@@ -75,27 +78,41 @@ export class LinuxService {
);
}
- this.protonPrefix = await this.isNixOS()
- ? `steam-run "${protonPath}" run`
- : `"${protonPath}" run`;
+ return protonPath;
+ }
+
+ private async buildEnvVariables(
+ launchOptions: LaunchOption,
+ steamPath: string,
+ bsFolderPath: string
+ ): Promise> {
+ // Create the compat data path if it doesn't exist.
+ // If the user never ran Beat Saber through steam before
+ // using bsmanager, it won't exist, and proton will fail
+ // to launch the game.
+ const compatDataPath = this.getCompatDataPath();
+ if (!fs.existsSync(compatDataPath)) {
+ log.info(`Proton compat data path not found at '${compatDataPath}', creating directory`);
+ await fs.ensureDir(compatDataPath);
+ }
// Setup Proton environment variables
- Object.assign(env, {
+ const envVars: Record = {
"WINEDLLOVERRIDES": "winhttp=n,b", // Required for mods to work
"STEAM_COMPAT_DATA_PATH": compatDataPath,
"STEAM_COMPAT_INSTALL_PATH": bsFolderPath,
"STEAM_COMPAT_CLIENT_INSTALL_PATH": steamPath,
"STEAM_COMPAT_APP_ID": BS_APP_ID,
// Run game in steam environment; fixes #585 for unicode song titles
- "SteamEnv": 1,
- });
+ "SteamEnv": "1",
+ };
if (launchOptions.launchMods?.includes(LaunchMods.PROTON_LOGS)) {
- Object.assign(env, {
- "PROTON_LOG": 1,
- "PROTON_LOG_DIR": path.join(bsFolderPath, "Logs"),
- });
+ envVars.PROTON_LOG = "1";
+ envVars.PROTON_LOG_DIR = path.join(bsFolderPath, "Logs");
}
+
+ return envVars;
}
public verifyProtonPath(protonFolder: string = ""): boolean {
@@ -134,11 +151,6 @@ export class LinuxService {
? path.join(compatDataPath, "pfx") : "";
}
- public getProtonPrefix(): string {
- // Set in setupLaunch
- return this.protonPrefix;
- }
-
// === NixOS Specific === //
public async isNixOS(): Promise {
@@ -159,4 +171,96 @@ export class LinuxService {
return this.nixOS;
}
+
+ // === Shortcuts === //
+
+ private getCommand(
+ protonPrefix: string,
+ bsFolderPath: string,
+ env: Record,
+ launchOptions: LaunchOption
+ ): string {
+ const envString = Object.entries(env)
+ .map(([ key, value ]) => `${key}="${value}"`)
+ .join(" ");
+ const bsExe = path.join(bsFolderPath, BS_EXECUTABLE);
+ const args = buildBsLaunchArgs(launchOptions).join(" ");
+ return `${envString} ${protonPrefix} "${bsExe}" ${args}`;
+ }
+
+ public async createDesktopShortcut(
+ shortcutPath: string,
+ name: string,
+ icon: string,
+ launchOptions: LaunchOption,
+ steamPath: string,
+ bsFolderPath: string
+ ): Promise {
+ try {
+ const {
+ protonPrefix, env
+ } = await this.setupLaunch(launchOptions, steamPath, bsFolderPath);
+
+ Object.assign(env, {
+ "SteamAppId": BS_APP_ID,
+ "SteamOverlayGameId": BS_APP_ID,
+ "SteamGameId": BS_APP_ID,
+ });
+
+ const command = this.getCommand(
+ protonPrefix, bsFolderPath,
+ env, launchOptions
+ );
+
+ const desktopEntry = [
+ "[Desktop Entry]",
+ "Type=Application",
+ `Name=${name}`,
+ `Icon=${icon}`,
+ `Path=${bsFolderPath}`,
+ `Exec=${command}`
+ ].join("\n");
+
+ await fs.writeFile(shortcutPath, desktopEntry);
+ log.info("Created shorcut at ", `"${shortcutPath}/${name}"`);
+ return true;
+ } catch (error) {
+ log.error("Could not create shortcut", error);
+ return false;
+ }
+ }
+
+ public async getSteamShortcutData(
+ shortcutName: string,
+ icon: string,
+ launchOptions: LaunchOption,
+ steamPath: string,
+ bsFolderPath: string
+ ): Promise {
+ const env = await this.buildEnvVariables(
+ launchOptions, steamPath, bsFolderPath
+ );
+ Object.assign(env, {
+ "SteamAppId": BS_APP_ID,
+ "SteamOverlayGameId": BS_APP_ID,
+ "SteamGameId": BS_APP_ID,
+ });
+
+ const protonPrefix = await this.isNixOS()
+ ? "steam-run %command% run"
+ : "%command% run";
+
+ return {
+ AppName: shortcutName,
+ Exe: await this.getProtonPath(),
+ StartDir: bsFolderPath,
+ icon,
+ OpenVR: "\x01",
+ LaunchOptions: this.getCommand(
+ protonPrefix, bsFolderPath,
+ env, launchOptions
+ )
+ };
+ }
+
}
diff --git a/src/main/services/steam.service.ts b/src/main/services/steam.service.ts
index 9298f8c8..54414ff5 100644
--- a/src/main/services/steam.service.ts
+++ b/src/main/services/steam.service.ts
@@ -211,7 +211,7 @@ export class SteamService {
for (const userId of userIds) {
const shortcuts = await this.getShortcuts(userId).catch(e => {
log.warn("Error while reading shortcuts", e);
- return [];
+ return [] as SteamShortcut[];
});
shortcuts.push(new SteamShortcut(shortcutData));
diff --git a/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx
index bfea54ca..7ef2b418 100644
--- a/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx
+++ b/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx
@@ -60,7 +60,9 @@ export function LaunchModItem({ id, icon: Icon, label, description, active, visi
{onPinChange && (
{ e.preventDefault(); e.stopPropagation(); onPinChange?.(!pinned) }}>
- {pinned ? : }
+ {pinned
+ ?
+ : }
)}