From cb121a9442194acfceadc5cdb41d3ba6e218a4c8 Mon Sep 17 00:00:00 2001 From: silentrald Date: Fri, 25 Oct 2024 13:34:24 +0800 Subject: [PATCH 01/19] [chore] change target builds to deb, rpm, and pacman --- .github/workflows/release-linux.yaml | 3 +++ package.json | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-linux.yaml b/.github/workflows/release-linux.yaml index 25a280e0..45d33c36 100644 --- a/.github/workflows/release-linux.yaml +++ b/.github/workflows/release-linux.yaml @@ -20,6 +20,9 @@ jobs: - name: Check out Git repository uses: actions/checkout@v4 + - name: Install dependencies for rpm and pacman + run: sudo apt-get install -y rpm libarchive-tools + - name: Use Node.js uses: actions/setup-node@v4 with: diff --git a/package.json b/package.json index f9dfce73..1d515fc8 100644 --- a/package.json +++ b/package.json @@ -69,14 +69,22 @@ }, "linux": { "target": [ - "AppImage" + "deb", + "rpm", + "pacman" ], "icon": "./build/icons/png", "category": "Utility;Game;", "extraResources": [ "./build/icons/png", "./assets/scripts/DepotDownloader" - ] + ], + "protocols": { + "name": "BSManager", + "schemes": [ + "bsmanager" + ] + } }, "directories": { "app": "release/app", From a345a3ee7c37df980f0765d1f680e0e04be2cd33 Mon Sep 17 00:00:00 2001 From: silentrald Date: Fri, 25 Oct 2024 13:45:33 +0800 Subject: [PATCH 02/19] [bugfix-627] reverted #577 PR --- src/main/services/bs-version-lib.service.ts | 21 +------------------ .../services/static-configuration.service.ts | 2 -- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/main/services/bs-version-lib.service.ts b/src/main/services/bs-version-lib.service.ts index 15a95a67..29fb62ed 100644 --- a/src/main/services/bs-version-lib.service.ts +++ b/src/main/services/bs-version-lib.service.ts @@ -5,7 +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 { 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"; @@ -15,14 +14,12 @@ export class BSVersionLibService { private utilsService: UtilsService; private requestService: RequestService; - private staticConfigurationService: StaticConfigurationService; private bsVersions: BSVersion[]; private constructor() { this.utilsService = UtilsService.getInstance(); this.requestService = RequestService.getInstance(); - this.staticConfigurationService = StaticConfigurationService.getInstance(); } public static getInstance(): BSVersionLibService { @@ -38,28 +35,12 @@ export class BSVersionLibService { private async getLocalVersions(): Promise { const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE); - - if (process.platform === "linux") { - let versions = this.staticConfigurationService.get("versions"); - if (!versions) { - versions = (await readJSON(localVersionsPath)) as BSVersion[]; - this.staticConfigurationService.set("versions", versions); - } - return versions; - } - return readJSON(localVersionsPath); } private async updateLocalVersions(versions: BSVersion[]): Promise { const localVersionsPath = path.join(this.utilsService.getAssestsJsonsPath(), this.VERSIONS_FILE); - - // Do not write on readonly memory in linux when running on AppImage - if (process.platform === "linux") { - this.staticConfigurationService.set("versions", versions); - } else { - writeFileSync(localVersionsPath, JSON.stringify(versions, null, "\t"), { encoding: "utf-8", flag: "w" }); - } + writeFileSync(localVersionsPath, JSON.stringify(versions, null, "\t"), { encoding: "utf-8", flag: "w" }); } private async loadBsVersions(): Promise { diff --git a/src/main/services/static-configuration.service.ts b/src/main/services/static-configuration.service.ts index 508df1ea..c1386130 100644 --- a/src/main/services/static-configuration.service.ts +++ b/src/main/services/static-configuration.service.ts @@ -3,7 +3,6 @@ import { pathExistsSync } from "fs-extra"; import path from "path"; import { PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants"; import { Observable, Subject } from "rxjs"; -import { BSVersion } from "shared/bs-version.interface"; import { CustomError } from "shared/models/exceptions/custom-error.class"; export class StaticConfigurationService { @@ -90,7 +89,6 @@ export interface StaticConfigKeyValues { "use-symlinks": boolean; // Linux Specific static configs - "versions": BSVersion[]; "proton-folder": string; }; From 38e0cc56d0be4b034598b3b8f207fa64ec1a8aa2 Mon Sep 17 00:00:00 2001 From: silentrald Date: Fri, 25 Oct 2024 19:05:39 +0800 Subject: [PATCH 03/19] [chore] add write permissions to bs-versions.json after install --- build/after-install.sh | 3 +++ package.json | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 build/after-install.sh diff --git a/build/after-install.sh b/build/after-install.sh new file mode 100644 index 00000000..a12aa1cb --- /dev/null +++ b/build/after-install.sh @@ -0,0 +1,3 @@ +# 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 diff --git a/package.json b/package.json index 1d515fc8..ec34d00f 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,15 @@ ] } }, + "deb": { + "fpm": ["--after-install=build/after-install.sh"] + }, + "rpm": { + "fpm": ["--after-install=build/after-install.sh"] + }, + "pacman": { + "fpm": ["--after-install=build/after-install.sh"] + }, "directories": { "app": "release/app", "buildResources": "assets", From 7a85171cd3493ab79eb2afb614a94e77fa6e2cd3 Mon Sep 17 00:00:00 2001 From: silentrald Date: Fri, 25 Oct 2024 19:50:39 +0800 Subject: [PATCH 04/19] [docs] added linux documentation --- docs/wiki/Linux.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/wiki/Linux.md diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md new file mode 100644 index 00000000..41aa8fb8 --- /dev/null +++ b/docs/wiki/Linux.md @@ -0,0 +1,55 @@ +# Linux Guide + +## Installation + +Go to [Releases](https://github.com/Zagrios/bs-manager/releases) page and go to the latest release. Download the necessary build installer for your distro (see below). + +### Ubuntu, Debian + +Install the `.deb` build and run the following command: +```bash +apt install bsmanager.deb +``` + +### CentOS, Fedora + +Install the `.rpm` build and run the following command: +```bash +rpm -i bsmanager.rpm +``` + +### Arch, Manjaro + +Install the `.pacman` build and run the following command: +```bash +pacman -U bsmanager.pacman +``` + +## 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. + +Once Proton is installed, when you open your BSManager application for the first time, it will ask you to link the _Proton Folder_ so that it can access the `proton` binary inside it. This will also check if the folder you've selected is valid or not. You can also change this in the future by going to the **settings page** and search for the _Proton Folder_. + +## Wine Install [Optional] + +[Wine](https://www.winehq.org/) is a tool to run windows application under Linux. BSManager uses to run [BSIPA](https://nike4613.github.io/BeatSaber-IPA-Reloaded/) so that you can play Beat Saber with mods. You can install `wine` depending on your package manager. If you don't install it, this will just fallback to using the `wine` executable found in your _Proton Folder_. + +# Troubleshooting + +## Permission denied on "bs-version.json" + +
+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 +``` + From e6bdf56c6d6dc50d06e5cf53c9782743d835f3bb Mon Sep 17 00:00:00 2001 From: silentrald Date: Wed, 30 Oct 2024 08:13:11 +0800 Subject: [PATCH 05/19] [chore] move build config to electron-builder.ts for better typing --- electron-builder.ts | 83 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 78 +----------------------------------------- 2 files changed, 84 insertions(+), 77 deletions(-) create mode 100644 electron-builder.ts diff --git a/electron-builder.ts b/electron-builder.ts new file mode 100644 index 00000000..3e65f3cd --- /dev/null +++ b/electron-builder.ts @@ -0,0 +1,83 @@ +import { Configuration } from "electron-builder"; + +const config: Configuration = { + extraResources: [ + "./assets/jsons/bs-versions.json", + "./assets/jsons/patreons.json", + "./assets/proto/song_details_cache_v1.proto" + ], + productName: "BSManager", + appId: "org.erb.BSManager", + asarUnpack: "**\\*.{node,dll}", + files: [ + "dist/**/*", + "node_modules", + "package.json" + ], + afterSign: ".erb/scripts/notarize.js", + afterPack: ".erb/scripts/after-pack.js", + win: { + signingHashAlgorithms: ["sha256"], + certificateSha1: "2164d6a7d641ecf6ad57852f665a518ca2bf960f", + target: [ + "nsis", + "nsis-web" + ], + icon: "./build/icons/win/favicon.ico", + extraResources: [ + "./build/icons/win", + "./assets/scripts/*.exe" + ], + }, + linux: { + target: [ + "deb", + "rpm", + "pacman" + ], + icon: "./build/icons/png", + category: "Utility;Game;", + extraResources: [ + "./build/icons/png", + "./assets/scripts/DepotDownloader" + ], + protocols: { + name: "BSManager", + schemes: [ + "bsmanager", + "beatsaver", + "bsplaylist", + "modelsaber", + "web+bsmap", + ], + }, + }, + deb: { + fpm: ["--after-install=build/after-install.sh"], + }, + rpm: { + fpm: ["--after-install=build/after-install.sh"], + }, + pacman: { + fpm: ["--after-install=build/after-install.sh"], + }, + directories: { + app: "release/app", + buildResources: "assets", + output: "release/build", + }, + publish: { + provider: "github", + owner: "Zagrios", + }, + fileAssociations: [ + { + ext: "bplist", + description: "Beat Saber Playlist (BSManager)", + icon: "./assets/bsm_file.ico", + role: "Viewer", + }, + ], +}; + +export default config; diff --git a/package.json b/package.json index ec34d00f..39b081a9 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts", "test": "jest", "test:unit": "jest ./src/__tests__/unit", - "publish": "npm run build && electron-builder -c.win.certificateSha1=2164d6a7d641ecf6ad57852f665a518ca2bf960f --publish always --win --x64", + "publish": "npm run build && electron-builder --publish always --win --x64", "publish:linux": "npm run build && electron-builder --publish always --linux --x64" }, "lint-staged": { @@ -37,82 +37,6 @@ "prettier --ignore-path .eslintignore --single-quote --write" ] }, - "build": { - "extraResources": [ - "./assets/jsons/bs-versions.json", - "./assets/jsons/patreons.json", - "./assets/proto/song_details_cache_v1.proto" - ], - "productName": "BSManager", - "appId": "org.erb.BSManager", - "asarUnpack": "**\\*.{node,dll}", - "files": [ - "dist/**/*", - "node_modules", - "package.json" - ], - "afterSign": ".erb/scripts/notarize.js", - "afterPack": ".erb/scripts/after-pack.js", - "win": { - "signingHashAlgorithms": [ - "sha256" - ], - "target": [ - "nsis", - "nsis-web" - ], - "icon": "./build/icons/win/favicon.ico", - "extraResources": [ - "./build/icons/win", - "./assets/scripts/*.exe" - ] - }, - "linux": { - "target": [ - "deb", - "rpm", - "pacman" - ], - "icon": "./build/icons/png", - "category": "Utility;Game;", - "extraResources": [ - "./build/icons/png", - "./assets/scripts/DepotDownloader" - ], - "protocols": { - "name": "BSManager", - "schemes": [ - "bsmanager" - ] - } - }, - "deb": { - "fpm": ["--after-install=build/after-install.sh"] - }, - "rpm": { - "fpm": ["--after-install=build/after-install.sh"] - }, - "pacman": { - "fpm": ["--after-install=build/after-install.sh"] - }, - "directories": { - "app": "release/app", - "buildResources": "assets", - "output": "release/build" - }, - "publish": { - "provider": "github", - "owner": "Zagrios" - }, - "fileAssociations": [ - { - "ext": "bplist", - "description": "Beat Saber Playlist (BSManager)", - "icon": "./assets/bsm_file.ico", - "role": "Viewer" - } - ] - }, "repository": { "type": "git", "url": "git+https://github.com/Zagrios/bs-manager.git" From 196bdc04f59ade13017785cef8ecbb50c8941a63 Mon Sep 17 00:00:00 2001 From: silentrald Date: Sun, 3 Nov 2024 01:03:16 +0800 Subject: [PATCH 06/19] [docs-627] removed wine installation and updated proton setup --- docs/wiki/Linux.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md index 41aa8fb8..046d0e42 100644 --- a/docs/wiki/Linux.md +++ b/docs/wiki/Linux.md @@ -29,11 +29,7 @@ pacman -U bsmanager.pacman [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. -Once Proton is installed, when you open your BSManager application for the first time, it will ask you to link the _Proton Folder_ so that it can access the `proton` binary inside it. This will also check if the folder you've selected is valid or not. You can also change this in the future by going to the **settings page** and search for the _Proton Folder_. - -## Wine Install [Optional] - -[Wine](https://www.winehq.org/) is a tool to run windows application under Linux. BSManager uses to run [BSIPA](https://nike4613.github.io/BeatSaber-IPA-Reloaded/) so that you can play Beat Saber with mods. You can install `wine` depending on your package manager. If you don't install it, this will just fallback to using the `wine` executable found in your _Proton Folder_. +Once Proton is installed, when you open your BSManager application for the first time, it will ask you to link the _Proton Folder_. The _Proton Folder_ also verifies if the `proton` and `files/bin/wine64` binaries exists. Once set, you should be able to launch the Beat Saber (using `proton`) and install mods (using `files/bin/wine64`). You can still change the _Proton Folder_ in the **settings page** if any new version of Steam Proton is downloaded. # Troubleshooting From 15f8d83c8d6ef2e8c3955e3f20da930419878d57 Mon Sep 17 00:00:00 2001 From: silentrald Date: Sat, 9 Nov 2024 12:02:00 +0800 Subject: [PATCH 07/19] [chore-627] drop rpm and pacman in favor of flatpak --- .github/workflows/build.yaml | 2 +- .github/workflows/release-linux.yaml | 19 +++++++++++++++---- docs/wiki/Linux.md | 28 +++++++++++++++++++--------- electron-builder.ts | 8 -------- package.json | 2 +- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index ab5d0f71..f0b44b08 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,7 +22,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 22.11.0 cache: "npm" - run: npm ci - run: npm run package diff --git a/.github/workflows/release-linux.yaml b/.github/workflows/release-linux.yaml index 45d33c36..6785ad42 100644 --- a/.github/workflows/release-linux.yaml +++ b/.github/workflows/release-linux.yaml @@ -20,16 +20,27 @@ jobs: - name: Check out Git repository uses: actions/checkout@v4 - - name: Install dependencies for rpm and pacman - run: sudo apt-get install -y rpm libarchive-tools + - name: Install flatpak packages + run: sudo apt-get install -y flatpak flatpak-builder + + - name: Setup flatpak repo + run: | + flatpak remote-add --if-not-exists --user \ + flathub https://flathub.org/repo/flathub.flatpakrepo - name: Use Node.js uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 22.11.0 cache: "npm" - run: npm ci - - run: npm run publish:linux + - run: npm run build + + - name: Build deb + run: npx electron-builder --publish always --linux deb --x64 + + - name: Build flatpak + run: env DEBUG="@malept/flatpak-bundler" npx electron-builder --publish always --linux flatpak - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md index 046d0e42..31eda4f8 100644 --- a/docs/wiki/Linux.md +++ b/docs/wiki/Linux.md @@ -4,27 +4,37 @@ Go to [Releases](https://github.com/Zagrios/bs-manager/releases) page and go to the latest release. Download the necessary build installer for your distro (see below). -### Ubuntu, Debian +### Universal (flatpak) + +You are required to have `flatpak` installed your system. After installing that, download the `.flatpak` file in the releases and run the following command: -Install the `.deb` build and run the following command: ```bash -apt install bsmanager.deb +flatpak install ./bsmanager.flatpak ``` -### CentOS, Fedora +If you are getting errors like packages not existing, run this command so that it finds the correct packages. -Install the `.rpm` build and run the following command: ```bash -rpm -i bsmanager.rpm +sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo + +# or + +flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo ``` -### Arch, Manjaro +### Ubuntu, Debian (deb) -Install the `.pacman` build and run the following command: +Download the `.deb` file in the releases and run the following command: ```bash -pacman -U bsmanager.pacman +dpkg -i ./bsmanager.deb ``` +### Arch (AUR) + +Refer to [bs-manager-git](https://aur.archlinux.org/packages/bs-manager-git). + +To install AUR packages, you need to install [yay](https://github.com/Jguer/yay). + ## 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. diff --git a/electron-builder.ts b/electron-builder.ts index 3e65f3cd..2ed49fd5 100644 --- a/electron-builder.ts +++ b/electron-builder.ts @@ -32,8 +32,6 @@ const config: Configuration = { linux: { target: [ "deb", - "rpm", - "pacman" ], icon: "./build/icons/png", category: "Utility;Game;", @@ -55,12 +53,6 @@ const config: Configuration = { deb: { fpm: ["--after-install=build/after-install.sh"], }, - rpm: { - fpm: ["--after-install=build/after-install.sh"], - }, - pacman: { - fpm: ["--after-install=build/after-install.sh"], - }, directories: { app: "release/app", buildResources: "assets", diff --git a/package.json b/package.json index 39b081a9..8cbba4f4 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test": "jest", "test:unit": "jest ./src/__tests__/unit", "publish": "npm run build && electron-builder --publish always --win --x64", - "publish:linux": "npm run build && electron-builder --publish always --linux --x64" + "publish:linux": "npm run build && electron-builder --publish never --linux --x64" }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ From 82a19c0c8b68d29933f62f5ea545a4b85290fd50 Mon Sep 17 00:00:00 2001 From: silentrald Date: Sat, 9 Nov 2024 17:28:37 +0800 Subject: [PATCH 08/19] [docs-627] updated docs on review - updated flatpak installation - moved deb and AUR above flatpak so users with appropriate distros will install those first - idiot-proofing on how to install flatpak on different distros --- docs/wiki/Linux.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md index 31eda4f8..36a04ff3 100644 --- a/docs/wiki/Linux.md +++ b/docs/wiki/Linux.md @@ -4,24 +4,6 @@ Go to [Releases](https://github.com/Zagrios/bs-manager/releases) page and go to the latest release. Download the necessary build installer for your distro (see below). -### Universal (flatpak) - -You are required to have `flatpak` installed your system. After installing that, download the `.flatpak` file in the releases and run the following command: - -```bash -flatpak install ./bsmanager.flatpak -``` - -If you are getting errors like packages not existing, run this command so that it finds the correct packages. - -```bash -sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo - -# or - -flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo -``` - ### Ubuntu, Debian (deb) Download the `.deb` file in the releases and run the following command: @@ -35,6 +17,26 @@ Refer to [bs-manager-git](https://aur.archlinux.org/packages/bs-manager-git). To install AUR packages, you need to install [yay](https://github.com/Jguer/yay). +### Universal (flatpak) + +This should work on any linux distribution. You are only required to have `flatpak` installed in your system. If it is not installed, then go to [flatpak](https://flatpak.org/setup/) to look for a guide on how to install it on your distro. + +After installing, download the `.flatpak` file in the releases and run the following command: + +```bash +flatpak install --user ./bsmanager.flatpak +``` + +If you are getting errors like packages not existing, run the command below so that it finds the correct packages. + +```bash +sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo + +# or + +flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo +``` + ## 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. From 84ea7a26b69a00e839422174ea1956c6b0625b71 Mon Sep 17 00:00:00 2001 From: silentrald Date: Mon, 18 Nov 2024 09:43:57 +0800 Subject: [PATCH 09/19] [bugfix-627] fixed issue with running the BS exe in flatpak moved all launching logic to the LinuxService --- electron-builder.ts | 19 ++++ .../bs-launcher/abstract-launcher.service.ts | 9 +- .../bs-launcher/steam-launcher.service.ts | 67 ++--------- src/main/services/linux.service.ts | 106 +++++++++++++++++- 4 files changed, 137 insertions(+), 64 deletions(-) diff --git a/electron-builder.ts b/electron-builder.ts index 2ed49fd5..1869829e 100644 --- a/electron-builder.ts +++ b/electron-builder.ts @@ -53,6 +53,25 @@ const config: Configuration = { deb: { fpm: ["--after-install=build/after-install.sh"], }, + flatpak: { + finishArgs: [ + // Wayland/X11 Rendering + "--socket=wayland", + "--socket=x11", + "--share=ipc", + // Open GL + "--device=dri", + // Audio output + "--socket=pulseaudio", + // Read/write home directory access + "--filesystem=home", + // Allow communication with network + "--share=network", + // System notifications with libnotify + "--talk-name=org.freedesktop.Notifications", + "--talk-name=org.freedesktop.Flatpak", + ] + }, directories: { app: "release/app", buildResources: "assets", diff --git a/src/main/services/bs-launcher/abstract-launcher.service.ts b/src/main/services/bs-launcher/abstract-launcher.service.ts index b9b5fdaa..263b61c4 100644 --- a/src/main/services/bs-launcher/abstract-launcher.service.ts +++ b/src/main/services/bs-launcher/abstract-launcher.service.ts @@ -4,12 +4,15 @@ import { ChildProcessWithoutNullStreams, SpawnOptionsWithoutStdio, spawn } from import path from "path"; import log from "electron-log"; import { sToMs } from "../../../shared/helpers/time.helpers"; +import { LinuxService } from "../linux.service"; export abstract class AbstractLauncherService { + protected readonly linux = LinuxService.getInstance(); protected readonly localVersions = BSLocalVersionService.getInstance(); constructor(){ + this.linux = LinuxService.getInstance(); this.localVersions = BSLocalVersionService.getInstance(); } @@ -44,10 +47,12 @@ export abstract class AbstractLauncherService { spawnOptions.windowsVerbatimArguments = true; } - log.info(`Launch BS exe at ${bsExePath} with args ${args?.join(" ")}`); + if (process.platform === "linux") { + return this.linux.spawnBsProcess(bsExePath, args, spawnOptions) + } + log.info("Windows launch BS command\n>" ,bsExePath, args?.join(" ")); return spawn(bsExePath, args, spawnOptions); - } protected launchBs(bsExePath: string, args: string[], options?: SpawnBsProcessOptions): {process: ChildProcessWithoutNullStreams, exit: Promise} { diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index 5154d2e2..926f5392 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -1,17 +1,15 @@ import { Observable } from "rxjs"; import { BSLaunchError, BSLaunchEvent, BSLaunchEventData, BSLaunchWarning, LaunchOption } from "../../../shared/models/bs-launch"; import { StoreLauncherInterface } from "./store-launcher.interface"; -import { pathExists, pathExistsSync, rename } from "fs-extra"; +import { pathExists, rename } from "fs-extra"; import { SteamService } from "../steam.service"; import path from "path"; -import { BS_APP_ID, BS_EXECUTABLE, PROTON_BINARY_PREFIX, STEAMVR_APP_ID } from "../../constants"; +import { BS_APP_ID, BS_EXECUTABLE, STEAMVR_APP_ID } from "../../constants"; import log from "electron-log"; import { AbstractLauncherService } from "./abstract-launcher.service"; import { CustomError } from "../../../shared/models/exceptions/custom-error.class"; import { UtilsService } from "../utils.service"; import { exec } from "child_process"; -import fs from 'fs'; -import { StaticConfigurationService } from "../static-configuration.service"; export class SteamLauncherService extends AbstractLauncherService implements StoreLauncherInterface{ @@ -24,13 +22,11 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto return SteamLauncherService.instance; } - private readonly staticConfig: StaticConfigurationService; private readonly steam: SteamService; private readonly util: UtilsService; private constructor(){ super(); - this.staticConfig = StaticConfigurationService.getInstance(); this.steam = SteamService.getInstance(); this.util = UtilsService.getInstance(); } @@ -71,10 +67,10 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto return new Observable(obs => {(async () => { const bsFolderPath = await this.localVersions.getInstalledVersionPath(launchOptions.version); - let exePath = path.join(bsFolderPath, BS_EXECUTABLE); + const bsExePath = path.join(bsFolderPath, BS_EXECUTABLE); - if(!(await pathExists(exePath))){ - throw CustomError.fromError(new Error(`Path not exist : ${exePath}`), BSLaunchError.BS_NOT_FOUND); + if(!(await pathExists(bsExePath))){ + throw CustomError.fromError(new Error(`Path not exist : ${bsExePath}`), BSLaunchError.BS_NOT_FOUND); } // Open Steam if not running @@ -98,7 +94,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto await this.restoreSteamVR().catch(log.error); } - let launchArgs = this.buildBsLaunchArgs(launchOptions); + const launchArgs = this.buildBsLaunchArgs(launchOptions); const steamPath = await this.steam.getSteamPath(); const env = { @@ -110,52 +106,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto // Linux setup if (process.platform === "linux") { - 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 = `${steamPath}/steamapps/compatdata/${BS_APP_ID}`; - if (!fs.existsSync(compatDataPath)) { - log.info(`Proton compat data path not found at '${compatDataPath}', creating directory`); - fs.mkdirSync(compatDataPath); - } - - // proton run BeatSaber.exe - launchArgs = [ - "run", - `${exePath}`, - ...launchArgs, - ]; - - if (!this.staticConfig.has("proton-folder")) { - throw CustomError.fromError(new Error("Proton folder not set"), BSLaunchError.PROTON_NOT_SET); - } - exePath = path.join(this.staticConfig.get("proton-folder"), PROTON_BINARY_PREFIX); - if (!pathExistsSync(exePath)) { - throw CustomError.fromError( - new Error("Could not locate proton binary"), - BSLaunchError.PROTON_NOT_FOUND - ); - } - - // Setup Proton environment variables - Object.assign(env, { - "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", - // Uncomment these to create a proton log file in the Beat Saber install directory. - // "PROTON_LOG": 1, - // "PROTON_LOG_DIR": bsFolderPath, - }); + this.linux.setupLaunch(launchOptions, steamPath, bsFolderPath, env); } obs.next({type: BSLaunchEvent.BS_LAUNCHING}); @@ -163,10 +114,10 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto const spawnOpts = { env, cwd: bsFolderPath }; const launchPromise = !launchOptions.admin ? ( - this.launchBs(exePath, launchArgs, spawnOpts).exit + this.launchBs(bsExePath, launchArgs, spawnOpts).exit ) : ( new Promise(resolve => { - const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${exePath}" ${launchArgs.join(" ")}`, spawnOpts); + const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${bsExePath}" ${launchArgs.join(" ")}`, spawnOpts); adminProcess.on("error", err => { log.error("Error while starting BS as Admin", err); resolve(-1) diff --git a/src/main/services/linux.service.ts b/src/main/services/linux.service.ts index 2336229a..6acd3d62 100644 --- a/src/main/services/linux.service.ts +++ b/src/main/services/linux.service.ts @@ -1,7 +1,11 @@ +import fs from "fs-extra"; +import log from "electron-log"; import path from "path"; -import { pathExistsSync } from "fs-extra"; -import { PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants"; +import { SpawnOptionsWithoutStdio, spawn } from "child_process"; +import { BS_APP_ID, 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"; export class LinuxService { private static instance: LinuxService; @@ -15,10 +19,104 @@ export class LinuxService { private readonly staticConfig: StaticConfigurationService; + public readonly isFlatpak = process.env.container === "flatpak"; + private constructor() { this.staticConfig = StaticConfigurationService.getInstance(); } + // === Launching === // + + public setupLaunch( + launchOptions: LaunchOption, + steamPath: string, + bsFolderPath: 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 = `${steamPath}/steamapps/compatdata/${BS_APP_ID}`; + if (!fs.existsSync(compatDataPath)) { + log.info(`Proton compat data path not found at '${compatDataPath}', creating directory`); + fs.mkdirSync(compatDataPath); + } + + if (!this.staticConfig.has("proton-folder")) { + throw CustomError.fromError( + new Error("Proton folder not set"), + BSLaunchError.PROTON_NOT_SET + ); + } + const protonPath = path.join(this.staticConfig.get("proton-folder"), PROTON_BINARY_PREFIX); + if (!fs.pathExistsSync(protonPath)) { + throw CustomError.fromError( + new Error("Could not locate proton binary"), + BSLaunchError.PROTON_NOT_FOUND + ); + } + + // Setup Proton environment variables + Object.assign(env, { + "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", + // Uncomment these to create a proton log file in the Beat Saber install directory. + // "PROTON_LOG": 1, + // "PROTON_LOG_DIR": bsFolderPath, + }); + } + + public spawnBsProcess(bsExePath: string, args: string[], spawnOptions: SpawnOptionsWithoutStdio) { + // Already checked in setupLaunch + const protonPath = path.join(this.staticConfig.get("proton-folder"), PROTON_BINARY_PREFIX); + + // "/bin/sh" does not see flatpak-spawn + // Most Debian and Arch should also support "/bin/bash" + spawnOptions.shell = "/bin/bash"; + + const command = this.isFlatpak + ? this.createFlatpakCommand(protonPath, bsExePath, args, spawnOptions) + : `"${protonPath}" run "${bsExePath}" ${args.join(" ")}`; + + log.info("Linux launch BS command\n>", command); + 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")) { @@ -30,7 +128,7 @@ export class LinuxService { const protonPath = path.join(protonFolder, PROTON_BINARY_PREFIX); const winePath = path.join(protonFolder, WINE_BINARY_PREFIX); - return pathExistsSync(protonPath) && pathExistsSync(winePath); + return fs.pathExistsSync(protonPath) && fs.pathExistsSync(winePath); } public getWinePath(): string { @@ -42,7 +140,7 @@ export class LinuxService { this.staticConfig.get("proton-folder"), WINE_BINARY_PREFIX ); - if (!pathExistsSync(winePath)) { + if (!fs.pathExistsSync(winePath)) { throw new Error(`"${winePath}" binary file not found`); } From 58656294df909b5575f4b68339737d14ee85d6fc Mon Sep 17 00:00:00 2001 From: silentrald Date: Tue, 19 Nov 2024 00:33:10 +0800 Subject: [PATCH 10/19] [bugfix-627] added finer permissions for flatpak handled issue with write only directory with bs-versions.json --- docs/wiki/Linux.md | 15 ++++- electron-builder.ts | 4 +- src/main/services/bs-version-lib.service.ts | 21 ++++++- src/main/services/linux.service.ts | 60 +++++++++++-------- .../choose-proton-folder-modal.component.tsx | 2 +- .../pages/settings-page.component.tsx | 2 +- 6 files changed, 73 insertions(+), 31 deletions(-) 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 (

From 403995612c5d0a34a4321e0ebc1240835adbe772 Mon Sep 17 00:00:00 2001
From: silentrald 
Date: Tue, 19 Nov 2024 00:34:17 +0800
Subject: [PATCH 11/19] [chore] added silentrald and Insprill in credits and
 linux installation in README file

---
 README.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/README.md b/README.md
index ac37a98d..6b47a043 100644
--- a/README.md
+++ b/README.md
@@ -226,6 +226,10 @@
 -->
 

How to install?

+
+ +
+

Windows Installation

+
+

Linux installation

+

Refer to Linux wiki

+