diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md index 36a04ff3..d3eae707 100644 --- a/docs/wiki/Linux.md +++ b/docs/wiki/Linux.md @@ -37,6 +37,8 @@ sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/rep flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo ``` +Flatpak also supports sandboxing which gives the minimal access to your machine. To configure this, you can download [Flatseal](https://flathub.org/apps/com.github.tchx84.Flatseal) which has a GUI to edit your permissions. You can also this with the `flatpak` executable but it will not be discussed here. + ## Proton Setup [Proton](https://github.com/ValveSoftware/Proton) is needed to run the Beat Saber executable under Linux. You need to download this from either from Steam or building it from their GitHub repo. @@ -45,7 +47,7 @@ Once Proton is installed, when you open your BSManager application for the first # Troubleshooting -## Permission denied on "bs-version.json" +## Permission denied on "bs-versions.json"
 Unhandled Exception UnhandledRejection Error: EACCES: permission denied, open '/opt/BSManager/resources/assets/jsons/bs-versions.json'
@@ -61,3 +63,14 @@ chmod +002 /opt/BSManager/resources/assets/jsons/bs-versions.json
 chown $(whoami) /opt/BSManager/resources/assets/jsons/bs-versions.json
 ```
 
+## [Flatpak] Steam Beat Saber version not showing / Proton not detected
+
+Flatpak should have permissions with the steam games folder. By default, the minimum permissions are `~/.steam/steam/steamapps/common:ro` and `~/.steam/steam/steamapps/common:ro`. If you changed the steam installation path, add that path instead into the permissions.
+
+## [Flatpak] Changing installation folder
+
+To change the installation path of the **BSManager** folder, you have to edit the flatpak permissions to destination folder.
+- In flatpak or Flatseal, add the destination folder with `:create` permissions.
+- In BSM, move the folder to the destination folder.
+- [Optional] In flatpak or Flatseal, remove the original folder permissions.
+
diff --git a/electron-builder.ts b/electron-builder.ts
index 1869829e..a8425e67 100644
--- a/electron-builder.ts
+++ b/electron-builder.ts
@@ -64,7 +64,9 @@ const config: Configuration = {
             // Audio output
             "--socket=pulseaudio",
             // Read/write home directory access
-            "--filesystem=home",
+            "--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
             // Allow communication with network
             "--share=network",
             // System notifications with libnotify
diff --git a/src/main/services/bs-version-lib.service.ts b/src/main/services/bs-version-lib.service.ts
index 29fb62ed..1a5b5e55 100644
--- a/src/main/services/bs-version-lib.service.ts
+++ b/src/main/services/bs-version-lib.service.ts
@@ -3,8 +3,9 @@ import path from "path";
 import { writeFileSync } from "fs";
 import { BSVersion } from "shared/bs-version.interface";
 import { RequestService } from "./request.service";
-import { readJSON } from "fs-extra";
+import { pathExistsSync, readJSON } from "fs-extra";
 import { allSettled } from "../../shared/helpers/promise.helpers";
+import { LinuxService } from "./linux.service";
 
 export class BSVersionLibService {
     private readonly REMOTE_BS_VERSIONS_URL: string = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/jsons/bs-versions.json";
@@ -12,12 +13,14 @@ export class BSVersionLibService {
 
     private static instance: BSVersionLibService;
 
+    private linuxService: LinuxService;
     private utilsService: UtilsService;
     private requestService: RequestService;
 
     private bsVersions: BSVersion[];
 
     private constructor() {
+        this.linuxService = LinuxService.getInstance();
         this.utilsService = UtilsService.getInstance();
         this.requestService = RequestService.getInstance();
     }
@@ -29,17 +32,29 @@ export class BSVersionLibService {
         return BSVersionLibService.instance;
     }
 
-    private getRemoteVersions(): Promise {
+    private async getRemoteVersions(): Promise {
         return this.requestService.getJSON(this.REMOTE_BS_VERSIONS_URL).then(res => res.data);
     }
 
     private async getLocalVersions(): Promise {
+        if (this.linuxService.isFlatpak) {
+            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);
         return readJSON(localVersionsPath);
     }
 
     private async updateLocalVersions(versions: BSVersion[]): Promise {
-        const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE);
+        const localVersionsPath = path.join(
+            this.linuxService.isFlatpak
+                ? this.linuxService.getFlatpakLocalVersionFolder()
+                : this.utilsService.getAssestsJsonsPath(),
+            this.VERSIONS_FILE
+        );
         writeFileSync(localVersionsPath, JSON.stringify(versions, null, "\t"), { encoding: "utf-8", flag: "w" });
     }
 
diff --git a/src/main/services/linux.service.ts b/src/main/services/linux.service.ts
index 6acd3d62..9258102d 100644
--- a/src/main/services/linux.service.ts
+++ b/src/main/services/linux.service.ts
@@ -6,6 +6,7 @@ import { BS_APP_ID, PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/consta
 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";
 
 export class LinuxService {
     private static instance: LinuxService;
@@ -93,30 +94,6 @@ export class LinuxService {
         return spawn(command, spawnOptions);
     }
 
-    private createFlatpakCommand(protonPath: string, bsExePath: string, args: string[], spawnOptions: SpawnOptionsWithoutStdio): string {
-
-        // DON'T REMOVE: Good for injecting commands while debugging with flatpak
-        // return args.slice(1).join(" ");
-
-        // The env vars are hidden to flatpak-spawn, need to set them manually in --env arg
-        // Minimal copy of the env, don't need to copy them all
-        const envArgs = [
-            "SteamAppId",
-            "SteamOverlayGameId",
-            "SteamGameId",
-            "WINEDLLOVERRIDES",
-            "STEAM_COMPAT_DATA_PATH",
-            "STEAM_COMPAT_INSTALL_PATH",
-            "STEAM_COMPAT_CLIENT_INSTALL_PATH",
-            "STEAM_COMPAT_APP_ID",
-            "SteamEnv",
-        ].map(envName => {
-            return `--env=${envName}="${spawnOptions.env[envName]}"`;
-        }).join(" ");
-
-        return `flatpak-spawn --host ${envArgs} "${protonPath}" run "${bsExePath}" ${args.join(" ")}`;
-    }
-
     public verifyProtonPath(protonFolder: string = ""): boolean {
         if (protonFolder === "") {
             if (!this.staticConfig.has("proton-folder")) {
@@ -146,4 +123,39 @@ export class LinuxService {
 
         return winePath;
     }
+
+    // === Flatpak Specific === //
+
+    private createFlatpakCommand(protonPath: string, bsExePath: string, args: string[], spawnOptions: SpawnOptionsWithoutStdio): string {
+
+        // DON'T REMOVE: Good for injecting commands while debugging with flatpak
+        // return args.slice(1).join(" ");
+
+        // The env vars are hidden to flatpak-spawn, need to set them manually in --env arg
+        // Minimal copy of the env, don't need to copy them all
+        const envArgs = [
+            "SteamAppId",
+            "SteamOverlayGameId",
+            "SteamGameId",
+            "WINEDLLOVERRIDES",
+            "STEAM_COMPAT_DATA_PATH",
+            "STEAM_COMPAT_INSTALL_PATH",
+            "STEAM_COMPAT_CLIENT_INSTALL_PATH",
+            "STEAM_COMPAT_APP_ID",
+            "SteamEnv",
+        ].map(envName => {
+            return `--env=${envName}="${spawnOptions.env[envName]}"`;
+        }).join(" ");
+
+        return `flatpak-spawn --host ${envArgs} "${protonPath}" run "${bsExePath}" ${args.join(" ")}`;
+    }
+
+    public getFlatpakLocalVersionFolder(): string {
+        return path.join(
+            app.getPath("home"),
+            ".var", "app", "org.erb.BSManager",
+            "resources", "assets", "jsons"
+        );
+    }
+
 }
diff --git a/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx b/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx
index c0af536f..553fa20c 100644
--- a/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx
+++ b/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx
@@ -22,7 +22,7 @@ export const ChooseProtonFolderModal: ModalComponent<{}, {}> = ({ resolver }) =>
     const selectProtonPath = async () => {
         const response = await lastValueFrom(ipcService.sendV2("choose-folder", {
             parent: "home",
-            defaultPath: ".local/share/Steam/steamapps/common",
+            defaultPath: ".steam/steam/steamapps/common",
             showHidden: true,
         }));
 
diff --git a/src/renderer/pages/settings-page.component.tsx b/src/renderer/pages/settings-page.component.tsx
index 9a10dfe6..27f43e73 100644
--- a/src/renderer/pages/settings-page.component.tsx
+++ b/src/renderer/pages/settings-page.component.tsx
@@ -164,7 +164,7 @@ export function SettingsPage() {
         try {
             const pathResponse = await lastValueFrom(ipcService.sendV2("choose-folder", {
                 parent: "home",
-                defaultPath: ".local/share/Steam/steamapps/common",
+                defaultPath: ".steam/steam/steamapps/common",
                 showHidden: true,
         }));
             if (