Skip to content

Commit

Permalink
Merge pull request #681 from silentrald/bugfix/nixos
Browse files Browse the repository at this point in the history
[bugfix] fixed issues with NixOS
  • Loading branch information
Zagrios authored Dec 8, 2024
2 parents 8e9eccd + 8771ac2 commit f73b1cd
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 45 deletions.
2 changes: 1 addition & 1 deletion electron-builder.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 8 additions & 2 deletions src/__tests__/unit/os.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
})
});

29 changes: 22 additions & 7 deletions src/main/helpers/os.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/main/services/bs-launcher/abstract-launcher.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
2 changes: 1 addition & 1 deletion src/main/services/bs-launcher/steam-launcher.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand Down
44 changes: 27 additions & 17 deletions src/main/services/bs-version-lib.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@ 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";
private readonly VERSIONS_FILE: string = "bs-versions.json";

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[];

private constructor() {
this.linuxService = LinuxService.getInstance();
this.utilsService = UtilsService.getInstance();
this.requestService = RequestService.getInstance();
this.configService = StaticConfigurationService.getInstance();
}

public static getInstance(): BSVersionLibService {
Expand All @@ -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" });
}

Expand Down
46 changes: 30 additions & 16 deletions src/main/services/linux.service.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -19,15 +18,17 @@ export class LinuxService {
}

private readonly staticConfig: StaticConfigurationService;
private protonCommand = "";
private protonPrefix = "";

private nixOS: boolean | undefined;

private constructor() {
this.staticConfig = StaticConfigurationService.getInstance();
}

// === Launching === //

public setupLaunch(
public async setupLaunch(
launchOptions: LaunchOption,
steamPath: string,
bsFolderPath: string,
Expand Down Expand Up @@ -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, {
Expand Down Expand Up @@ -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;
}
}
2 changes: 2 additions & 0 deletions src/main/services/static-configuration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -90,6 +91,7 @@ export interface StaticConfigKeyValues {

// Linux Specific static configs
"proton-folder": string;
"versions": BSVersion[];
};

export type StaticConfigKeys = keyof StaticConfigKeyValues;
Expand Down

0 comments on commit f73b1cd

Please sign in to comment.