diff --git a/src/components/settings-components/SettingsView.vue b/src/components/settings-components/SettingsView.vue index 5ade507ba..ac1606123 100644 --- a/src/components/settings-components/SettingsView.vue +++ b/src/components/settings-components/SettingsView.vue @@ -347,6 +347,23 @@ import CdnProvider from '../../providers/generic/connection/CdnProvider'; 'fa-file-alt', () => this.emitInvoke('ShowDependencyStrings') ), + new SettingsRow( + 'Other', + 'Launch as Flatpak (Linux)', + 'Launch Steam via flatpak', + async () => { + switch (this.settings.getContext().global.linuxUseFlatpak) { + case null: + return 'Automatic'; + case true: + return 'Flatpak'; + case false: + return 'Native'; + } + }, + 'fa-exchange-alt', + () => this.emitInvoke('ToggleLinuxUseFlatpak') + ), ]; @Watch('search') diff --git a/src/pages/Manager.vue b/src/pages/Manager.vue index 652f4e3e0..6c414522d 100644 --- a/src/pages/Manager.vue +++ b/src/pages/Manager.vue @@ -370,6 +370,10 @@ import ModalCard from '../components/ModalCard.vue'; this.settings.setFunkyMode(value); } + setLinuxUseFlatpak(value: boolean) { + this.settings.setLinuxUseFlatpak(value); + } + async exportProfile() { if (!this.localModList.length) { const err = new R2Error( @@ -583,6 +587,9 @@ import ModalCard from '../components/ModalCard.vue'; case "ToggleFunkyMode": this.setFunkyMode(!this.settings.getContext().global.funkyModeEnabled); break; + case "ToggleLinuxUseFlatpak": + this.setLinuxUseFlatpak(!this.settings.getContext().global.linuxUseFlatpak); + break; case "SwitchTheme": this.toggleDarkTheme(); document.documentElement.classList.toggle('html--dark', this.settings.getContext().global.darkTheme); diff --git a/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts b/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts index 81cba2839..f9a28ce23 100644 --- a/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts +++ b/src/r2mm/launching/runners/linux/SteamGameRunner_Linux.ts @@ -53,26 +53,50 @@ export default class SteamGameRunner_Linux extends GameRunnerProvider { if (args instanceof R2Error) { return args } - return this.start(game, args); + return this.start(game, args, profile.getProfilePath()); } public async startVanilla(game: Game, profile: Profile): Promise { const instructions = await GameInstructions.getInstructionsForGame(game, profile); - return this.start(game, instructions.vanillaParameters); + return this.start(game, instructions.vanillaParameters, null); } - async start(game: Game, args: string): Promise { + async start(game: Game, args: string, profiledir: string | null): Promise { const settings = await ManagerSettings.getSingleton(game); const steamDir = await GameDirectoryResolverProvider.instance.getSteamDirectory(); if(steamDir instanceof R2Error) { return steamDir; } + let as_flatpak = settings.getContext().global.linuxUseFlatpak; + if (as_flatpak === null) { + // Assume that if the path contains the flatpak directory we must want to use flatpak + as_flatpak = steamDir.includes("/com.valvesoftware.Steam/"); + } + + if (as_flatpak && profiledir) { + // Ensure we have permission to read our config dir + const visible = await new Promise((resolve) => { + const check_cmd = `flatpak run --command='sh' com.valvesoftware.Steam -c 'ls "${profiledir}"'`; + exec(check_cmd).on('close', (error) => { + resolve(error == 0); + }); + }); + if (!visible) { + throw new R2Error('Flatpak Permissions Error', + `The directory "${profiledir}" is not visible from within the Flatpak container`, + '`flatpak override com.valvesoftware.Steam --user --filesystem="${XDG_CONFIG_HOME:-$HOME/.config}/r2modmanPlus-local"` to grant access, then restart Steam.'); + } + } LoggerProvider.instance.Log(LogSeverity.INFO, `Steam folder is: ${steamDir}`); try { - const cmd = `"${steamDir}/steam.sh" -applaunch ${game.activePlatform.storeIdentifier} ${args} ${settings.getContext().gameSpecific.launchParameters}`; + let cmd = `"${steamDir}/steam.sh" -applaunch ${game.activePlatform.storeIdentifier} ${args} ${settings.getContext().gameSpecific.launchParameters}`; + if (as_flatpak) { + cmd = `flatpak run com.valvesoftware.Steam ${cmd}`; + } + LoggerProvider.instance.Log(LogSeverity.INFO, `Running command: ${cmd}`); await exec(cmd); } catch(err) { diff --git a/src/r2mm/manager/ManagerSettings.ts b/src/r2mm/manager/ManagerSettings.ts index 187f8cf38..80ffc7bc5 100644 --- a/src/r2mm/manager/ManagerSettings.ts +++ b/src/r2mm/manager/ManagerSettings.ts @@ -97,6 +97,11 @@ export default class ManagerSettings { await this.save(); } + public async setLinuxUseFlatpak(enabled: boolean) { + ManagerSettings.CONTEXT.global.linuxUseFlatpak = enabled; + await this.save(); + } + public async expandCards() { ManagerSettings.CONTEXT.global.expandedCards = true; await this.save(); diff --git a/src/r2mm/manager/SettingsDexieStore.ts b/src/r2mm/manager/SettingsDexieStore.ts index 3685c1582..4da4f89e6 100644 --- a/src/r2mm/manager/SettingsDexieStore.ts +++ b/src/r2mm/manager/SettingsDexieStore.ts @@ -119,7 +119,8 @@ export default class SettingsDexieStore extends Dexie { favouriteGames: [], defaultGame: undefined, defaultStore: undefined, - gameSelectionViewMode: GameSelectionViewMode.CARD + gameSelectionViewMode: GameSelectionViewMode.CARD, + linuxUseFlatpak: null, }, gameSpecific: { version: 2, @@ -197,6 +198,7 @@ export interface ManagerSettingsInterfaceGlobal_V2 { defaultGame: string | undefined; defaultStore: StorePlatform | undefined; gameSelectionViewMode: GameSelectionViewMode; + linuxUseFlatpak: boolean | null; } /**