mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Merge pull request #681 from silentrald/bugfix/nixos
[bugfix] fixed issues with NixOS
This commit is contained in:
@@ -63,7 +63,7 @@ const config = {
|
||||
// Read/write home directory access
|
||||
"--filesystem=~/BSManager:create", // Default BSManager installation folder
|
||||
"--filesystem=~/.steam/steam/steamapps:ro", // for the libraryfolders.vdf
|
||||
"--filesystem=~/.steam/steam/steamapps/common:ro", // Steam game folder
|
||||
"--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
|
||||
// Allow communication with network
|
||||
"--share=network",
|
||||
|
||||
@@ -196,8 +196,14 @@ ifDescribe(IS_LINUX)("Test os.helpers isProcessRunning", () => {
|
||||
const running = await isProcessRunning(`bs-manager-${crypto.randomUUID()}`);
|
||||
expect(running).toBe(false);
|
||||
|
||||
// No errors received
|
||||
expect(logSpy).toHaveBeenCalledTimes(0);
|
||||
// Throws because grep couldn't find any process with that name
|
||||
expect(logSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("Empty process name", async () => {
|
||||
const running = await isProcessRunning("");
|
||||
expect(running).toBe(false);
|
||||
expect(logSpy).toHaveBeenCalledTimes(0);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@ import log from "electron-log";
|
||||
import psList from "ps-list";
|
||||
import { IS_FLATPAK } from "main/constants";
|
||||
|
||||
// There are 2 erroneous lines ps | grep which is both the ps and grep calls themselves
|
||||
const MIN_PROCESS_COUNT_LINUX = 2;
|
||||
|
||||
type LinuxOptions = {
|
||||
// Add the prefix to the command
|
||||
// eg. command - "./Beat Saber.exe" --no-yeet, prefix - "path/to/proton" run
|
||||
@@ -101,14 +98,24 @@ export function bsmExec(command: string, options?: BsmExecOptions): Promise<{
|
||||
});
|
||||
}
|
||||
|
||||
// Transform command from "steam" to "[s]team"
|
||||
// NOTE: Can add an option to isProcessRunning/getProcessId to ignore this transformation
|
||||
// in the future if needed
|
||||
const transformProcessNameForPS = (name: string) => `[${name.at(0)}]${name.substring(1)}`;
|
||||
|
||||
async function isProcessRunningLinux(name: string): Promise<boolean> {
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout: count } = await bsmExec(`ps awwxo args | grep -c "${name}"`, {
|
||||
const processName = transformProcessNameForPS(name);
|
||||
const { stdout: count } = await bsmExec(`ps awwxo args | grep -c "${processName}"`, {
|
||||
log: true,
|
||||
flatpak: { host: IS_FLATPAK },
|
||||
});
|
||||
|
||||
return +count.trim() > MIN_PROCESS_COUNT_LINUX;
|
||||
return +count.trim() > 0;
|
||||
} catch(error) {
|
||||
log.error(error);
|
||||
return false;
|
||||
@@ -143,14 +150,22 @@ async function isProcessRunningWindows(name: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function getProcessIdLinux(name: string): Promise<number | null> {
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await bsmExec(`ps awwxo pid,args | grep "${name}"`, {
|
||||
const processName = transformProcessNameForPS(name);
|
||||
const { stdout } = await bsmExec(`ps awwxo pid,args | grep "${processName}"`, {
|
||||
log: true,
|
||||
flatpak: { host: IS_FLATPAK },
|
||||
});
|
||||
|
||||
if (!stdout) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const line = stdout.split("\n")
|
||||
.slice(0, -MIN_PROCESS_COUNT_LINUX)
|
||||
.map(line => line.trimStart())
|
||||
.find(line => line.includes(name) && !line.includes("grep"));
|
||||
return line ? +line.split(" ").at(0) : null;
|
||||
|
||||
@@ -52,7 +52,7 @@ export abstract class AbstractLauncherService {
|
||||
spawnOptions.shell = true; // For windows to spawn properly
|
||||
return bsmSpawn(`"${bsExePath}"`, {
|
||||
args, options: spawnOptions, log: true,
|
||||
linux: { prefix: this.linux.getProtonCommand() },
|
||||
linux: { prefix: this.linux.getProtonPrefix() },
|
||||
flatpak: {
|
||||
host: IS_FLATPAK,
|
||||
env: [
|
||||
|
||||
@@ -106,7 +106,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
|
||||
|
||||
// Linux setup
|
||||
if (process.platform === "linux") {
|
||||
this.linux.setupLaunch(launchOptions, steamPath, bsFolderPath, env);
|
||||
await this.linux.setupLaunch(launchOptions, steamPath, bsFolderPath, env);
|
||||
}
|
||||
|
||||
obs.next({type: BSLaunchEvent.BS_LAUNCHING});
|
||||
|
||||
@@ -3,10 +3,11 @@ import path from "path";
|
||||
import { writeFileSync } from "fs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { RequestService } from "./request.service";
|
||||
import { pathExistsSync, readJSON } from "fs-extra";
|
||||
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 {
|
||||
private readonly REMOTE_BS_VERSIONS_URL: string = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/jsons/bs-versions.json";
|
||||
@@ -14,9 +15,10 @@ export class BSVersionLibService {
|
||||
|
||||
private static instance: BSVersionLibService;
|
||||
|
||||
private linuxService: LinuxService;
|
||||
private utilsService: UtilsService;
|
||||
private requestService: RequestService;
|
||||
private readonly linuxService: LinuxService;
|
||||
private readonly utilsService: UtilsService;
|
||||
private readonly requestService: RequestService;
|
||||
private readonly configService: StaticConfigurationService;
|
||||
|
||||
private bsVersions: BSVersion[];
|
||||
|
||||
@@ -24,6 +26,7 @@ export class BSVersionLibService {
|
||||
this.linuxService = LinuxService.getInstance();
|
||||
this.utilsService = UtilsService.getInstance();
|
||||
this.requestService = RequestService.getInstance();
|
||||
this.configService = StaticConfigurationService.getInstance();
|
||||
}
|
||||
|
||||
public static getInstance(): BSVersionLibService {
|
||||
@@ -37,25 +40,32 @@ 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 IS_FLATPAK || this.linuxService.isNixOS();
|
||||
}
|
||||
|
||||
private async getLocalVersions(): Promise<BSVersion[]> {
|
||||
if (IS_FLATPAK) {
|
||||
const flatpakVersionsPath = path.join(this.linuxService.getFlatpakLocalVersionFolder(), this.VERSIONS_FILE);
|
||||
if (pathExistsSync(flatpakVersionsPath)) {
|
||||
return readJSON(flatpakVersionsPath);
|
||||
}
|
||||
const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE);
|
||||
|
||||
if (!(await this.shouldLoadFromConfig())) {
|
||||
return readJSON(localVersionsPath);
|
||||
}
|
||||
|
||||
const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE);
|
||||
return readJSON(localVersionsPath);
|
||||
let versions = this.configService.get("versions");
|
||||
if (!versions) {
|
||||
versions = await readJSON(localVersionsPath)
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
private async updateLocalVersions(versions: BSVersion[]): Promise<void> {
|
||||
const localVersionsPath = path.join(
|
||||
IS_FLATPAK
|
||||
? this.linuxService.getFlatpakLocalVersionFolder()
|
||||
: this.utilsService.getAssestsJsonsPath(),
|
||||
this.VERSIONS_FILE
|
||||
);
|
||||
if (await this.shouldLoadFromConfig()) {
|
||||
this.configService.set("versions", versions);
|
||||
return;
|
||||
}
|
||||
|
||||
const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE);
|
||||
writeFileSync(localVersionsPath, JSON.stringify(versions, null, "\t"), { encoding: "utf-8", flag: "w" });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import fs from "fs-extra";
|
||||
import log from "electron-log";
|
||||
import path from "path";
|
||||
import { BS_APP_ID, PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants";
|
||||
import { BS_APP_ID, IS_FLATPAK, PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants";
|
||||
import { StaticConfigurationService } from "./static-configuration.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { BSLaunchError, LaunchOption } from "shared/models/bs-launch";
|
||||
import { app } from "electron";
|
||||
import config from "../../../electron-builder.config";
|
||||
import { bsmExec } from "main/helpers/os.helpers";
|
||||
|
||||
export class LinuxService {
|
||||
private static instance: LinuxService;
|
||||
@@ -19,7 +18,9 @@ export class LinuxService {
|
||||
}
|
||||
|
||||
private readonly staticConfig: StaticConfigurationService;
|
||||
private protonCommand = "";
|
||||
private protonPrefix = "";
|
||||
|
||||
private nixOS: boolean | undefined;
|
||||
|
||||
private constructor() {
|
||||
this.staticConfig = StaticConfigurationService.getInstance();
|
||||
@@ -27,7 +28,7 @@ export class LinuxService {
|
||||
|
||||
// === Launching === //
|
||||
|
||||
public setupLaunch(
|
||||
public async setupLaunch(
|
||||
launchOptions: LaunchOption,
|
||||
steamPath: string,
|
||||
bsFolderPath: string,
|
||||
@@ -64,7 +65,10 @@ export class LinuxService {
|
||||
BSLaunchError.PROTON_NOT_FOUND
|
||||
);
|
||||
}
|
||||
this.protonCommand = `"${protonPath}" run`;
|
||||
|
||||
this.protonPrefix = await this.isNixOS()
|
||||
? `steam-run "${protonPath}" run`
|
||||
: `"${protonPath}" run`;
|
||||
|
||||
// Setup Proton environment variables
|
||||
Object.assign(env, {
|
||||
@@ -111,19 +115,29 @@ export class LinuxService {
|
||||
return winePath;
|
||||
}
|
||||
|
||||
public getProtonCommand(): string {
|
||||
public getProtonPrefix(): string {
|
||||
// Set in setupLaunch
|
||||
return this.protonCommand;
|
||||
return this.protonPrefix;
|
||||
}
|
||||
|
||||
// === Flatpak Specific === //
|
||||
// === NixOS Specific === //
|
||||
|
||||
public getFlatpakLocalVersionFolder(): string {
|
||||
return path.join(
|
||||
app.getPath("home"),
|
||||
".var", "app", config.appId,
|
||||
"resources", "assets", "jsons"
|
||||
);
|
||||
public async isNixOS(): Promise<boolean> {
|
||||
if (this.nixOS !== undefined) {
|
||||
return this.nixOS;
|
||||
}
|
||||
|
||||
try {
|
||||
await bsmExec("nixos-version", {
|
||||
log: true,
|
||||
flatpak: { host: IS_FLATPAK },
|
||||
});
|
||||
this.nixOS = true;
|
||||
} catch (error) {
|
||||
log.info("Not NixOS", error);
|
||||
this.nixOS = false;
|
||||
}
|
||||
|
||||
return this.nixOS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from "path";
|
||||
import { PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
export class StaticConfigurationService {
|
||||
private static instance: StaticConfigurationService;
|
||||
@@ -90,6 +91,7 @@ export interface StaticConfigKeyValues {
|
||||
|
||||
// Linux Specific static configs
|
||||
"proton-folder": string;
|
||||
"versions": BSVersion[];
|
||||
};
|
||||
|
||||
export type StaticConfigKeys = keyof StaticConfigKeyValues;
|
||||
|
||||
Reference in New Issue
Block a user