diff --git a/src/main/ipcs/bs-launcher-ipcs.ts b/src/main/ipcs/bs-launcher-ipcs.ts index c453ac49..b85c1d07 100644 --- a/src/main/ipcs/bs-launcher-ipcs.ts +++ b/src/main/ipcs/bs-launcher-ipcs.ts @@ -1,38 +1,16 @@ import { ipcMain } from 'electron'; -import path from "path"; -import { spawn } from 'child_process'; import { UtilsService } from '../services/utils.service' -import { SteamService } from '../services/steam.service'; -import { BSVersion, BSVersionManagerService } from '../services/bs-version-manager.service'; -import { BSInstallerService } from '../services/bs-installer.service' -import { BS_EXECUTABLE, BS_APP_ID } from '../constants'; +import { LauchOption } from "../../shared/models/launch-models.model"; +import { BSLauncherService } from "../services/bs-launcher.service" +import { IpcRequest } from 'shared/models/ipc-models.model'; -export interface LauchOption { - version: BSVersion, - oculus: boolean, - desktop: boolean, - debug: boolean -} +ipcMain.on('bs-launch.launch', (event, request: IpcRequest) => { + const launcherService = BSLauncherService.getInstance(); + const utilsService = UtilsService.getInstance(); -ipcMain.on('bs-launch.launch', async (event, args: LauchOption) => { //Create a service BSLauncherService - const steamService = SteamService.getInstance(); - const installerService = BSInstallerService.getInstance(); - let cwd = ""; - if(!steamService.steamRunning()){ UtilsService.getInstance().ipcSend("bs-launch.launch", 1); return; } - if(args.version.steam){ - cwd = await steamService.getGameFolder(BS_APP_ID, "Beat Saber"); - } - else{ - cwd = path.join(installerService.installationFolder, args.version.BSVersion); - } - const exePath = path.join(cwd, BS_EXECUTABLE); - if(!UtilsService.getInstance().pathExist(exePath)){ UtilsService.getInstance().ipcSend("bs-launch.launch", 2); return; } - - const launchMods = []; - if(args.oculus){ launchMods.push("-vrmode oculus"); } - if(args.debug){ launchMods.push("--verbose"); } - if(args.desktop){ launchMods.push("fpfc"); } - spawn(`\"${exePath}\"`, launchMods, {shell: true, cwd: cwd, env: {...process.env, "SteamAppId": BS_APP_ID}}); - - UtilsService.getInstance().ipcSend("bs-launch.launch", 0); + launcherService.launch(request.args).then(res => { + utilsService.newIpcSenc(request.responceChannel, {success: true, data: res}); + }).catch(err => { + utilsService.newIpcSenc(request.responceChannel, {success: false, error: {title: err, type: 'error'}}); + }) }); diff --git a/src/main/services/bs-launcher.service.ts b/src/main/services/bs-launcher.service.ts new file mode 100644 index 00000000..b4028e00 --- /dev/null +++ b/src/main/services/bs-launcher.service.ts @@ -0,0 +1,58 @@ +import path from "path"; +import { BsLaunchResult, LauchOption } from "shared/models/launch-models.model"; +import { BSInstallerService } from "./bs-installer.service"; +import { BSVersion } from "./bs-version-manager.service"; +import { UtilsService } from "./utils.service"; +import { BS_EXECUTABLE, BS_APP_ID } from "../constants"; +import { ChildProcessWithoutNullStreams, spawn } from "child_process"; +import { SteamService } from "./steam.service"; + +export class BSLauncherService{ + + private static instance: BSLauncherService; + + private readonly utilsService: UtilsService; + private readonly installerService: BSInstallerService; + private readonly steamService: SteamService; + + private bsProcess: ChildProcessWithoutNullStreams; + + public static getInstance(): BSLauncherService{ + if(!BSLauncherService.instance){ BSLauncherService.instance = new BSLauncherService(); } + return BSLauncherService.instance; + } + + private constructor(){ + this.utilsService = UtilsService.getInstance(); + this.installerService = BSInstallerService.getInstance(); + this.steamService = SteamService.getInstance(); + } + + public isBsRunning(): boolean{ + return this.bsProcess?.connected || this.utilsService.taskRunning(BS_EXECUTABLE); + } + + public isExeExist(version: BSVersion): boolean{ + const exePath = path.join(this.installerService.installationFolder, version.BSVersion, BS_EXECUTABLE); + return this.utilsService.pathExist(exePath); + } + + public async launch(launchOptions: LauchOption): Promise{ + if(!this.steamService.steamRunning()){ return "STEAM_NOT_RUNNING" } + if(!this.isExeExist(launchOptions.version)){ return "EXE_NOT_FINDED"; } + if(this.isBsRunning()){ return "BS_ALREADY_RUNNING" } + + const cwd = launchOptions.version.steam ? await this.steamService.getGameFolder(BS_APP_ID, "Beat Saber") : path.join(this.installerService.installationFolder, launchOptions.version.BSVersion); + const exePath = path.join(cwd, BS_EXECUTABLE); + const launchMods = [launchOptions.oculus && "-vrmode oculus", launchOptions.desktop && "fpfc", launchOptions.debug && "--verbose"]; + + this.bsProcess = spawn(`\"${exePath}\"`, launchMods, {shell: true, cwd: cwd, env: {...process.env, "SteamAppId": BS_APP_ID}}); + + this.bsProcess.on('message', msg => { /** EMIT HERE **/ }); + this.bsProcess.on('error', err => { /** EMIT HERE **/ }); + this.bsProcess.on('exit', code => this.utilsService.newIpcSenc("bs-launch.exit", {data: code, success: true})); + + return "LAUNCHED"; + } + +} \ No newline at end of file diff --git a/src/renderer/pages/version-viewer.component.tsx b/src/renderer/pages/version-viewer.component.tsx index a0f247a4..14e08a65 100644 --- a/src/renderer/pages/version-viewer.component.tsx +++ b/src/renderer/pages/version-viewer.component.tsx @@ -2,7 +2,7 @@ import { BSVersion } from '../../main/services/bs-version-manager.service'; import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import BSLogo from '../../../assets/images/apngs/bs-logo.png'; -import { BSLauncherService, LaunchMods, LaunchResult } from '../services/bs-launcher.service'; +import { BSLauncherService, LaunchMods } from '../services/bs-launcher.service'; import { TabNavBar } from 'renderer/components/shared/tab-nav-bar.component'; import wipGif from "../../../assets/images/gifs/wip.gif" import { ConfigurationService } from 'renderer/services/configuration.service'; @@ -14,6 +14,9 @@ import { BSUninstallerService } from '../services/bs-uninstaller.service'; import { BSVersionManagerService } from '../services/bs-version-manager.service'; import { ModalExitCode, ModalService, ModalType } from '../services/modale.service'; import DefautVersionImage from "../../../assets/images/default-version-img.jpg"; +import { NotificationService } from 'renderer/services/notification.service'; +import { BsDownloaderService } from 'renderer/services/bs-downloader.service'; +import { ProgressBarService } from 'renderer/services/progress-bar.service'; export function VersionViewer() { @@ -30,6 +33,9 @@ export function VersionViewer() { const bsUninstallerService = BSUninstallerService.getInstance(); const bsVersionManagerService = BSVersionManagerService.getInstance(); const modalService = ModalService.getInsance(); + const notificationService = NotificationService.getInstance(); + const bsDownloaderService = BsDownloaderService.getInstance(); + const progressService = ProgressBarService.getInstance(); const [launchRes, setLaunchRes] = useState(null); @@ -73,9 +79,28 @@ export function VersionViewer() { } } + const verifyFiles = () => { + progressService.show(bsDownloaderService.downloadProgress$); + bsDownloaderService.download(state).then(res => { + progressService.hide(true); + if(!res.success){ notificationService.notifyError({title: "Unable to verify", desc: "Unable to verify, please try later"}); } + else(notificationService.notifySuccess({title: "Verification complete"})); + }) + } + const launchBs = () => { bsLauncherService.launch(state, oculusMode, desktopMode, debugMode).then(res => { - setLaunchRes(res); + if(!res.success){ notificationService.notifyError({title: 'Unable to launch', desc: res.error.title}); } + else if(res.data === "STEAM_NOT_RUNNING"){ notificationService.notifyWarning({title: "Steam not running", desc: "Steam must be running to launch Beat Saber."}); } + else if(res.data === "BS_ALREADY_RUNNING"){ notificationService.notifyWarning({title: "Beat Saber already running", desc: "Please close Beat Saber to launch."}); } + else if(res.data === "EXE_NOT_FINDED"){ + notificationService.notifyError({title: "Files missing", desc: "Some files are missing, please verify the download", actions: [{id: "0", title:"Verfiy"}]}).then(res => { + if(res !== "0"){ return; } + verifyFiles(); + }); + } + else if(res.data === "LAUNCHED"){ notificationService.notifySuccess({title: "Launching..."}) } + else(notificationService.notifyError({title: res.data || res.error.title})); }); } diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index fb78965d..04cc43e5 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -1,34 +1,29 @@ -import { LauchOption } from "../../main/ipcs/bs-launcher-ipcs"; +import { LauchOption } from "../../shared/models/launch-models.model"; import { BSVersion } from "../../main/services/bs-version-manager.service"; +import { IpcService } from "./ipc.service"; +import { BsLaunchResult } from "../../shared/models/launch-models.model"; +import { IpcResponse } from "shared/models/ipc-models.model"; export class BSLauncherService{ private static instance: BSLauncherService; + private readonly ipcService: IpcService; + public static getInstance(){ if(!BSLauncherService.instance){ BSLauncherService.instance = new BSLauncherService(); } return BSLauncherService.instance; } - private constructor(){} - - public launch(version: BSVersion, oculus: boolean, desktop: boolean, debug: boolean): Promise{ - return new Promise((resolve, reject) => { - - window.electron.ipcRenderer.on('bs-launch.launch', res => { - resolve(res); - }); - const lauchOption: LauchOption = {debug, oculus, desktop, version: version} - window.electron.ipcRenderer.sendMessage('bs-launch.launch', lauchOption); - }) + private constructor(){ + this.ipcService = IpcService.getInstance(); } -} + public launch(version: BSVersion, oculus: boolean, desktop: boolean, debug: boolean): Promise>{ + const lauchOption: LauchOption = {debug, oculus, desktop, version}; + return this.ipcService.send("bs-launch.launch", {args: lauchOption}); + } -export enum LaunchResult { - LAUNCHED = 0, - STEAM_NOT_RUNNING = 1, - EXE_NOT_FIND = 2, } export enum LaunchMods{ diff --git a/src/shared/models/launch-models.model.ts b/src/shared/models/launch-models.model.ts new file mode 100644 index 00000000..c5237fc3 --- /dev/null +++ b/src/shared/models/launch-models.model.ts @@ -0,0 +1,10 @@ +import { BSVersion } from "main/services/bs-version-manager.service"; + +export interface LauchOption { + version: BSVersion, + oculus: boolean, + desktop: boolean, + debug: boolean +} + +export type BsLaunchResult = "LAUNCHED" | "STEAM_NOT_RUNNING" | "EXE_NOT_FINDED" | "BS_ALREADY_RUNNING"; \ No newline at end of file