diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index e117f972..1182e7f2 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -561,6 +561,16 @@ "actions": { "STEAM_NOT_RUNNING": "Launch Steam" } + }, + "warnings": { + "titles": { + "FPFC_NEED_ADMIN": "FPFC mode setup incomplete", + "UNABLE_TO_LAUNCH_STEAM": "Could not launch Steam" + }, + "msg": { + "FPFC_NEED_ADMIN": "BSManager needs administrator privileges to properly set up FPFC mode. Run BSManager as administrator for the best experience.", + "UNABLE_TO_LAUNCH_STEAM": "Steam could not be started automatically." + } } }, "steam": { diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index cf9476fd..17478d40 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -38,14 +38,25 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto return this.steam.getGameFolder(STEAMVR_APP_ID, "SteamVR"); } - private async backupSteamVR(): Promise { + private timedRename(src: string, dest: string): Promise { + return Promise.race([ + rename(src, dest), + new Promise((_, reject) => setTimeout(() => reject(new Error("Rename timed out")), 5000)), + ]); + } + + private async backupSteamVR(): Promise { const steamVrFolder = await this.getSteamVRPath(); - if (!(await pathExists(steamVrFolder))) { - return; + if (!steamVrFolder || !(await pathExists(steamVrFolder))) { + return false; + } + try { + await this.timedRename(steamVrFolder, `${steamVrFolder}.bak`); + return false; + } catch (err) { + log.warn("Could not backup SteamVR folder, skipping", err); + return err?.code === "EPERM" || err?.message?.includes("timed out"); } - return rename(steamVrFolder, `${steamVrFolder}.bak`).catch(err => { - log.error("Error while create backup of SteamVR", err); - }); } private getStartBsAsAdminExePath(): string { @@ -54,14 +65,11 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto public async restoreSteamVR(): Promise { const steamVrFolder = await this.getSteamVRPath(); + if (!steamVrFolder) { return; } const steamVrBackup = `${steamVrFolder}.bak`; - - if (!(await pathExists(steamVrBackup))) { - return; - } - - return rename(steamVrBackup, steamVrFolder).catch(err => { - log.error("Error while restoring SteamVR", err); + if (!(await pathExists(steamVrBackup))) { return; } + return this.timedRename(steamVrBackup, steamVrFolder).catch(err => { + log.warn("Could not restore SteamVR folder", err); }); } @@ -136,12 +144,16 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto obs.next({ type: BSLaunchEvent.SKIPPING_STEAM_LAUNCH}); } - // Backup SteamVR when desktop mode is enabled - if(launchOptions.launchMods?.includes(LaunchMods.FPFC)){ - await this.backupSteamVR().catch(() => { - return this.restoreSteamVR(); + const isFpfc = launchOptions.launchMods?.includes(LaunchMods.FPFC); + const isOculus = launchOptions.launchMods?.includes(LaunchMods.OCULUS); + if(isFpfc && !isOculus){ + const backupPermError = await this.backupSteamVR().catch(() => { + return this.restoreSteamVR().then(() => false); }); - } else { + if(backupPermError){ + obs.next({type: BSLaunchWarning.FPFC_NEED_ADMIN}); + } + } else if(!isFpfc) { await this.restoreSteamVR().catch(log.error); } diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index 8c630c95..e18742db 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -45,10 +45,15 @@ export class BSLauncherService { } private handleLaunchEvents(events$: Observable): Observable{ - const eventToFilter = [...Object.values(BSLaunchWarning), BSLaunchEvent.STEAM_LAUNCHED] + const warningTypes: string[] = Object.values(BSLaunchWarning); + const eventToFilter = [...warningTypes, BSLaunchEvent.STEAM_LAUNCHED] return events$.pipe(tap({ next: event => { + if(warningTypes.includes(event.type)){ + this.notificationService.notifyWarning({title: `notifications.bs-launch.warnings.titles.${event.type}`, desc: `notifications.bs-launch.warnings.msg.${event.type}`, duration: sToMs(9)}); + return; + } if(eventToFilter.includes(event.type)){ return; } this.notificationService.notifySuccess({title: `notifications.bs-launch.success.titles.${event.type}`, desc: `notifications.bs-launch.success.msg.${event.type}`}); }, diff --git a/src/shared/models/bs-launch/launch-event.model.ts b/src/shared/models/bs-launch/launch-event.model.ts index b744d66d..3b61c902 100644 --- a/src/shared/models/bs-launch/launch-event.model.ts +++ b/src/shared/models/bs-launch/launch-event.model.ts @@ -29,6 +29,7 @@ export enum BSLaunchEvent{ export enum BSLaunchWarning{ UNABLE_TO_LAUNCH_STEAM = "UNABLE_TO_LAUNCH_STEAM", + FPFC_NEED_ADMIN = "FPFC_NEED_ADMIN", } export type BSLaunchEventType = BSLaunchEvent | BSLaunchWarning;