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 25a280e0d..45d33c365 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 f9dfce73a..1d515fc8b 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 15a95a671..29fb62ed4 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 508df1eaa..c1386130a 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 000000000..a12aa1cb5 --- /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 1d515fc8b..ec34d00f3 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 000000000..41aa8fb86 --- /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 000000000..3e65f3cd9 --- /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 ec34d00f3..39b081a9f 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 41aa8fb86..046d0e429 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 ab5d0f713..f0b44b08a 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 45d33c365..6785ad42d 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 046d0e429..31eda4f8c 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 3e65f3cd9..2ed49fd56 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 39b081a9f..8cbba4f44 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 | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/wiki/Linux.md b/docs/wiki/Linux.md index 31eda4f8c..36a04ff37 100644 --- a/docs/wiki/Linux.md +++ b/docs/wiki/Linux.md @@ -4,36 +4,38 @@ 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: +### Ubuntu, Debian (deb) +Download the `.deb` file in the releases and run the following command: ```bash -flatpak install ./bsmanager.flatpak +dpkg -i ./bsmanager.deb ``` -If you are getting errors like packages not existing, run this command so that it finds the correct packages. +### Arch (AUR) -```bash -sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo +Refer to [bs-manager-git](https://aur.archlinux.org/packages/bs-manager-git). -# or +To install AUR packages, you need to install [yay](https://github.com/Jguer/yay). -flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo -``` +### Universal (flatpak) -### Ubuntu, Debian (deb) +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: -Download the `.deb` file in the releases and run the following command: ```bash -dpkg -i ./bsmanager.deb +flatpak install --user ./bsmanager.flatpak ``` -### Arch (AUR) +If you are getting errors like packages not existing, run the command below so that it finds the correct packages. -Refer to [bs-manager-git](https://aur.archlinux.org/packages/bs-manager-git). +```bash +sudo flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo -To install AUR packages, you need to install [yay](https://github.com/Jguer/yay). +# or + +flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo +``` ## Proton Setup 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 2ed49fd56..1869829e4 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 b9b5fdaa0..263b61c43 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 5154d2e25..926f53927 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 2336229a9..6acd3d62c 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 36a04ff37..d3eae7073 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 1869829e4..a8425e672 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 29fb62ed4..1a5b5e559 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 6acd3d62c..9258102d1 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 c0af536ff..553fa20cb 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 9a10dfe68..27f43e73e 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 ac37a98da..6b47a043e 100644
--- a/README.md
+++ b/README.md
@@ -226,6 +226,10 @@
 -->
 

How to install?

+
+ +
+

Windows Installation

+
+

Linux installation

+

Refer to Linux wiki

+