mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #745 from silentrald/bugfix/flatpak-stuff
[bugfix] fixed flatpack issues
This commit is contained in:
@@ -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<number>} {
|
||||
const process = this.launchBSProcess(bsExePath, args, options);
|
||||
|
||||
let timoutId: NodeJS.Timeout;
|
||||
let timeoutId: NodeJS.Timeout;
|
||||
|
||||
const exit = new Promise<number>((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;
|
||||
|
||||
@@ -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<string>} Path of the icon
|
||||
*/
|
||||
private async createShortcutIco(color: Color): Promise<string>{
|
||||
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<boolean> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<number>(resolve => {
|
||||
const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${bsExePath}" ${launchArgs.join(" ")} --log-path "${path.join(app.getPath("logs"), "bs-admin-start.log")}"`, spawnOpts);
|
||||
|
||||
@@ -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<BSVersion[]>(this.REMOTE_BS_VERSIONS_URL).then(res => res.data);
|
||||
}
|
||||
|
||||
private async shouldLoadFromConfig(): Promise<boolean> {
|
||||
// Some special cases of readonly memory installations
|
||||
return process.platform === "linux" && (IS_FLATPAK || this.linuxService.isNixOS());
|
||||
}
|
||||
|
||||
private async getLocalVersions(): Promise<BSVersion[]> {
|
||||
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<void> {
|
||||
if (await this.shouldLoadFromConfig()) {
|
||||
if (process.platform === "linux") {
|
||||
this.configService.set("versions", versions);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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<string, string>
|
||||
) {
|
||||
bsFolderPath: string
|
||||
): Promise<{
|
||||
protonPrefix: string;
|
||||
env: Record<string, string>;
|
||||
}> {
|
||||
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<string> {
|
||||
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<Record<string, string>> {
|
||||
// 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<string, string> = {
|
||||
"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<boolean> {
|
||||
@@ -159,4 +171,96 @@ export class LinuxService {
|
||||
|
||||
return this.nixOS;
|
||||
}
|
||||
|
||||
// === Shortcuts === //
|
||||
|
||||
private getCommand(
|
||||
protonPrefix: string,
|
||||
bsFolderPath: string,
|
||||
env: Record<string, string>,
|
||||
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<boolean> {
|
||||
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<SteamShortcutData> {
|
||||
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
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
+3
-1
@@ -60,7 +60,9 @@ export function LaunchModItem({ id, icon: Icon, label, description, active, visi
|
||||
{onPinChange && (
|
||||
<Tippy theme="default" placement="right" content={pinned ? t("misc.unpin") : t("misc.pin")} hideOnClick>
|
||||
<button className="h-full py-2 px-1.5" onClick={e => { e.preventDefault(); e.stopPropagation(); onPinChange?.(!pinned) }}>
|
||||
{pinned ? <UnpinIcon className="size-full"/> : <PinIcon className="size-full"/>}
|
||||
{pinned
|
||||
? <UnpinIcon className="size-full text-gray-800 dark:text-gray-200"/>
|
||||
: <PinIcon className="size-full text-gray-800 dark:text-gray-200"/>}
|
||||
</button>
|
||||
</Tippy>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user