work on back-end to get bs version from remote, detect installed depot and steam bs, add store in front, get all bs version available in front, add steam launch test in front

This commit is contained in:
MathieuG-P
2022-05-15 22:34:00 +02:00
parent f98c1a2b49
commit ccb37b4358
23 changed files with 393 additions and 18229 deletions
+1 -3
View File
@@ -179,9 +179,7 @@ const configuration: webpack.Configuration = {
static: {
publicPath: '/',
},
historyApiFallback: {
verbose: true,
},
historyApiFallback: true,
setupMiddlewares(middlewares) {
console.log('Starting preload.js builder...');
const preloadProcess = spawn('npm', ['run', 'start:preload'], {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+136 -18130
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -13,8 +13,7 @@
"start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only ./src/main/main.ts",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
"test": "jest",
"prepare": "husky install"
"test": "jest"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
@@ -234,6 +233,7 @@
},
"dependencies": {
"@node-steam/vdf": "^2.2.0",
"@reduxjs/toolkit": "^1.8.1",
"electron-debug": "^3.2.0",
"electron-log": "^4.4.6",
"electron-updater": "^4.6.5",
@@ -241,7 +241,9 @@
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-icons": "^4.3.1",
"react-redux": "^8.0.1",
"react-router-dom": "^6.3.0",
"redux": "^4.2.0",
"regedit": "^5.1.1",
"tailwind-scrollbar-hide": "^1.1.7"
},
@@ -270,7 +272,8 @@
"electronmon": {
"patterns": [
"!src/__tests__/**",
"!release/**"
"!release/**",
"!assets/**"
],
"logLevel": "quiet"
}
+1 -10
View File
@@ -1,14 +1,5 @@
{
"name": "electron-react-boilerplate",
"version": "4.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "electron-react-boilerplate",
"version": "4.5.0",
"hasInstallScript": true,
"license": "MIT"
}
}
"lockfileVersion": 1
}
+7 -5
View File
@@ -9,9 +9,11 @@ import { BS_EXECUTABLE, BS_APP_ID } from '../constants';
ipcMain.on('test-launch', async () => {
console.log("steam "+ UtilsService.getInstance().taskRunning('steam.exe'));
console.log(await SteamService.getInstance().getSteamGameFolder());
console.log(await SteamService.getInstance().getSteamGamesFolder());
console.log(BSInstallerService.getInstance().getBSInstallationFolder());
console.log((await BSVersionManagerService.getInstance().loadBsVersions()));
console.log((await BSVersionManagerService.getInstance().getAvailableVersions()));
console.log(await BSVersionManagerService.getInstance().getVersionOfBSFolder(path.join(await SteamService.getInstance().getSteamGamesFolder(), "Beat Saber")));
console.log(await BSInstallerService.getInstance().getInstalledBsVersion());
return;
const BS_EXE = "Beat Saber.exe"
@@ -28,9 +30,9 @@ ipcMain.on('test-launch', async () => {
ipcMain.on('bs-launch.steam', async (event, args) => {
const steamService = SteamService.getInstance();
if(!steamService.isGameInstalled(BS_APP_ID)){ event.reply('bs-launch.not-installed'); }
const beatSaberSteamFolder = path.join(await steamService.getSteamPath(), "Beat Saber");
const beatSaberSteamFolder = path.join(await steamService.getSteamGamesFolder(), "Beat Saber");
const beatSaberSteamExe = path.join(beatSaberSteamFolder, BS_EXECUTABLE);
const spawnProcess = spawn(`\"${beatSaberSteamExe}\"`, [], {shell: true, cwd: beatSaberSteamFolder, env: {...process.env, "SteamAppId": "620980"}});
const spawnProcess = spawn(`\"${beatSaberSteamExe}\"`, [], {shell: true, cwd: beatSaberSteamFolder, env: {...process.env, "SteamAppId": BS_APP_ID}});
})
+11 -4
View File
@@ -1,8 +1,15 @@
import { ipcMain } from 'electron';
import { readdirSync } from 'fs';
import { UtilsService } from '../services/utils.service';
import { BSInstallerService } from '../services/bs-installer.service';
import { BSVersionManagerService } from '../services/bs-version-manager.service'
ipcMain.on('bs-version.request', async (event, args) => {
ipcMain.on('bs-version.request-versions', async (event, args) => {
const bsVersionService = BSVersionManagerService.getInstance();
const versions = (await bsVersionService.getAvailableVersions());
event.reply('bs-version.request-versions', versions);
});
ipcMain.on('bs-version.installed-versions', async (event, args) => {
event.reply('bs-version.installed-versions', await BSInstallerService.getInstance().getInstalledBsVersion())
})
+1
View File
@@ -1,3 +1,4 @@
import './bs-download-ipcs';
import './window-controls-ipcs';
import './bs-launcher-ipcs';
import './bs-version-ipcs';
+25 -2
View File
@@ -1,14 +1,18 @@
import { BS_APP_ID } from "../constants";
import path from "path";
import { BSVersionManagerService } from "./bs-version-manager.service";
import { BSVersion, BSVersionManagerService } from "./bs-version-manager.service";
import { SteamService } from "./steam.service";
import { UtilsService } from "./utils.service";
export class BSInstallerService{
private static readonly INSTALLATION_FOLDER = "BSManager";
private static readonly INSTALLATION_FOLDER = path.join("BSManager", "BSInstances");
private static instance: BSInstallerService;
private readonly utils: UtilsService = UtilsService.getInstance();
private readonly steamService: SteamService = SteamService.getInstance();
private readonly bsVersionService: BSVersionManagerService = BSVersionManagerService.getInstance();
private installationFolder: string;
@@ -22,8 +26,27 @@ export class BSInstallerService{
}
public getBSInstallationFolder(): string{ return this.installationFolder; }
public setBSInstallationFolder(path: string){ this.installationFolder = path; }
public async getInstalledBsVersion(): Promise<BSVersion[]>{
const versions: BSVersion[] = [];
if(this.steamService.isGameInstalled(BS_APP_ID)){
const steamBsFolder = path.join(await this.steamService.getSteamGamesFolder(), "Beat Saber");
const steamBsVersion = await this.bsVersionService.getVersionOfBSFolder(steamBsFolder);
const steamVersionDetails = this.bsVersionService.getVersionDetailFromVersionNumber(steamBsVersion);
versions.push(steamVersionDetails ? {...steamVersionDetails, steam: true} : {BSVersion: steamBsVersion, steam: true});
}
if(!this.utils.folderExist(this.getBSInstallationFolder())){ return versions }
const folderInInstallation = this.utils.listDirsInDir(this.getBSInstallationFolder());
folderInInstallation.forEach(f => {
const version = this.bsVersionService.getVersionDetailFromVersionNumber(f);
versions.push(version);
})
return versions;
}
}
@@ -1,21 +1,31 @@
import { get } from 'https'
import { UtilsService } from './utils.service';
import path, { resolve } from 'path';
import { createReadStream, writeFileSync } from 'fs';
import { createInterface } from 'readline';
export class BSVersionManagerService{
private static readonly REMOTE_BS_VERSIONS_URL: string = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/dynamic-resources/bs-versions.json"
private static readonly REMOTE_BS_VERSIONS_URL: string = "https://raw.githubusercontent.com/Zagrios/bs-manager/master/assets/bs-versions.json"
private static readonly VERSIONS_FILE: string = "bs-versions.json";
private static instance: BSVersionManagerService;
private utilsService: UtilsService;
private bsVersions: BSVersion[] = [];
private constructor(){}
private constructor(){
this.utilsService = UtilsService.getInstance();
}
public static getInstance(): BSVersionManagerService{
if(!BSVersionManagerService.instance){ BSVersionManagerService.instance = new BSVersionManagerService() }
return BSVersionManagerService.instance;
}
public loadBsVersions(): Promise<BSVersion[]>{
private getRemoteVersions(): Promise<BSVersion[]>{
return new Promise<BSVersion[]>((resolve, reject) => {
let body = ''
get(BSVersionManagerService.REMOTE_BS_VERSIONS_URL, (res) => {
@@ -24,15 +34,65 @@ export class BSVersionManagerService{
this.bsVersions = JSON.parse(body);
resolve(this.bsVersions);
});
res.on('error', (err) => reject(err))
res.on('error', (err) => reject(null))
})
})
}
private async getLocalVersions(): Promise<BSVersion[]>{
const localVersionsPath = path.join(this.utilsService.getAssetsPath(), BSVersionManagerService.VERSIONS_FILE);
const rawVersion = (await this.utilsService.readFileAsync(localVersionsPath)).toString();
return JSON.parse(rawVersion);
}
private async updateLocalVersions(versions: BSVersion[]): Promise<void>{
const localVersionsPath = path.join(this.utilsService.getAssetsPath(), BSVersionManagerService.VERSIONS_FILE);
writeFileSync(localVersionsPath, JSON.stringify(versions, null, "\t"), {encoding: 'utf-8', flag: 'w'});
};
public async loadBsVersions(): Promise<BSVersion[]>{
if(this.bsVersions && this.bsVersions.length){ return this.bsVersions; }
const [localVersions, remoteVersions] = await Promise.all([
this.getLocalVersions(), this.getRemoteVersions()
]);
let resVersions = localVersions;
if(remoteVersions && remoteVersions.length){ resVersions = remoteVersions; this.updateLocalVersions(resVersions); }
this.bsVersions = resVersions;
return this.bsVersions;
}
public async getAvailableVersions(): Promise<BSVersion[]>{
const bsVersions = await this.loadBsVersions();
if(!bsVersions || !bsVersions.length){ return []; }
return bsVersions;
}
public async getVersionOfBSFolder(bsPath: string): Promise<string>{
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
if(!this.utilsService.pathExist(versionFilePath)){ return null; }
const versionsAvailable = await this.getAvailableVersions();
return new Promise<string>(async (resolve, reject) => {
const readLine = createInterface({ input: createReadStream(versionFilePath) });
let findVersion: string = null;
readLine.on('line', (line) => {
for(const version of versionsAvailable){
if(line.includes(version.BSVersion)){ findVersion = version.BSVersion; readLine.close(); }
}
});
readLine.on('close', () => {
if(findVersion){ resolve(findVersion) }
reject(findVersion);
});
})
}
public getVersionDetailFromVersionNumber(version: string): BSVersion{
return this.bsVersions.find(v => v.BSVersion === version);
}
}
export interface BSVersion { BSVersion: string, BSManifest: string, ReleaseURL: string, year: string }
export interface BSVersion { BSVersion: string, BSManifest?: string, ReleaseURL?: string, year?: string, steam?: boolean }
+5 -3
View File
@@ -37,9 +37,10 @@ export class SteamService{
return this.steamPath;
}
public async getSteamGameFolder(): Promise<string>{
public async getSteamGamesFolder(): Promise<string>{
this.isGameInstalled("");
const steamFolder = await this.getSteamPath();
return path.join(steamFolder, 'common');
return path.join(steamFolder, 'steamapps', 'common');
}
public async isGameInstalled(gameId: string): Promise<boolean>{
@@ -49,11 +50,12 @@ export class SteamService{
if(!this.utils.pathExist(libraryFolders)){ return false; }
libraryFolders = parse(await readFile(libraryFolders, {encoding: 'utf-8'}));
if(!libraryFolders.libraryfolders){ return false; }
libraryFolders = libraryFolders.libraryfolders
for(const libKey in Object.keys(libraryFolders)){
if(!libraryFolders[libKey]["apps"]){ continue; }
if(!libraryFolders[libKey] || !libraryFolders[libKey]["apps"]){ continue; }
if(libraryFolders[libKey]["apps"][gameId]){ return true };
}
return false;
+14 -3
View File
@@ -1,4 +1,4 @@
import { existsSync, mkdirSync } from "fs";
import { existsSync, mkdirSync, readdirSync, readFile } from "fs";
import { spawnSync } from "child_process";
import { homedir } from "os";
import path from "path";
@@ -39,8 +39,19 @@ export class UtilsService{
return path.join(this.getUserFolder(), 'Documents');
}
public readFileAsync(path: string){
return new Promise<Buffer>((resolve, reject) => {
readFile(path, (err, data) => {
if(err){ reject(err); }
else{ resolve(data); }
});
});
}
public listDirsInDir(dirPath: string): string[]{
let files = readdirSync(dirPath, { withFileTypes:true});
files = files.filter(f => f.isDirectory())
return files.map(f => f.name);
}
}
+22 -1
View File
@@ -1,15 +1,36 @@
import { NavBar } from "./components/nav-bar/nav-bar.component";
import TitleBar from "./components/title-bar/title-bar.component";
import { Routes, Route } from "react-router-dom";
import VersionsList from "./pages/versions-list.components";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import VersionViewer from "./pages/version-viewer.component";
export default function App() {
const dispatch = useDispatch()
useEffect(() => {
initIpcsListener();
window.electron.ipcRenderer.sendMessage('bs-version.installed-versions');
}, [])
const initIpcsListener= () => {
window.electron.ipcRenderer.on('bs-version.installed-versions', versions => dispatch({type: 'INSTALLED_BS_ADD_ALL', payload: versions}));
}
return (
<div className="w-screen h-screen overflow-hidden flex dark:bg-[#202225]">
<NavBar/>
<div className="flex flex-col grow">
<TitleBar/>
<div className="bg-[#2C2F33] rounded-tl-lg grow">
a
<Routes>
<Route path={"/bs-version/:versionNumber"} element={<VersionViewer/>}/>
<Route path="*" element={<VersionsList/>}/>
</Routes>
</div>
</div>
</div>
@@ -0,0 +1,10 @@
import { BSVersion } from "main/services/bs-version-manager.service";
import { Link } from "react-router-dom";
export default function BsVersionItem(props: {version: BSVersion}) {
return (
<div className='w-full h-fit pl-1 pr-1 cursor-pointer'>
<Link to={`/bs-version/${props.version.BSVersion}`} state={props.version} className="block w-full flex justify-center items-center content-center text-lg text-gray-200 font-bold">{props.version.BSVersion}</Link>
</div>
)
}
@@ -1,9 +1,16 @@
import React from 'react'
import { FaPlus, } from 'react-icons/fa';
import { AiFillSetting } from 'react-icons/ai'
import './nav-bar.component.css'
import { useSelector } from 'react-redux';
import BsVersionItem from './bs-version-item.component';
import { BSVersion } from 'main/services/bs-version-manager.service';
export function NavBar() {
const { installedVersions }: {installedVersions: BSVersion[]} = useSelector((state: any) => ({...state.installedBSReducer}));
console.log(installedVersions);
return (
<div id='nav-bar' className='flex flex-col items-center w-fit h-full max-h-full p-2 bg-gray-200 dark:bg-[#202225]'>
<div className='w-full flex items-start content-start justify-center relative mb-3'>
@@ -12,62 +19,8 @@ export function NavBar() {
<span id='logo-top' className='bg-red-500 aspect-square w-16'> </span>
</div>
</div>
<div id='versions' className='relative left-1 w-full grow overflow-y-hidden scrollbar-th scrollbar-track-transparent scrollbar-thin scrollbar-thumb-neutral-900 hover:overflow-y-scroll'>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div className='cursor-pointer grow pr-4 pl-4 w-full h-fit flex justify-center items-center content-center pt-2 pb-2 text-center rounded-full overflow-hidden hover:bg-[#2C2F33]'>
<span className='font-bold text-gray-200'>1.19.0</span>
</div>
<div id='versions' className='relative w-full left-[2px] grow overflow-y-hidden scrollbar-th scrollbar-track-transparent scrollbar-thin scrollbar-thumb-neutral-900 hover:overflow-y-scroll'>
{installedVersions.map((version, index) => <BsVersionItem key={index} version={version}/>)}
</div>
<div className='w-full p-2 flex flex-col items-center content-center justify-start'>
<span className='cursor-pointer mb-3'>
+14 -3
View File
@@ -1,12 +1,22 @@
import { createRoot } from 'react-dom/client';
import { InitDownloadInfoInterface } from '../main/ipcs/bs-download-ipcs';
import { HashRouter } from 'react-router-dom';
import App from './App';
import 'tailwindcss/tailwind.css';
import './index.css'
import { Provider } from 'react-redux';
import store from './store/store'
const container = document.getElementById('root')!;
const root = createRoot(container);
root.render(<App />);
root.render(
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
);
// setTimeout(() => {
// window.electron.ipcRenderer.sendMessage('bs-download.start', {app: '620980', depot: '620981', manifest: '6222769774084748916', stay: true, username: 'lekilleur618', folder: 'test2', cwd: 'C:\\test'} as InitDownloadInfoInterface);
@@ -15,7 +25,8 @@ root.render(<App />);
// window.electron.ipcRenderer.on('bs-download.ask-password', () => console.log('omg popup password'));
setTimeout(() => {
window.electron.ipcRenderer.sendMessage('test-launch', null);
//window.electron.ipcRenderer.sendMessage('test-launch', null);
//window.electron.ipcRenderer.sendMessage('bs-version.request-versions', null);
}, 5000);
window.electron.ipcRenderer.on('bs-download.ask-password', () => console.log('omg popup password'));
//window.electron.ipcRenderer.on('bs-version.request-versions', (v) => console.log(v));
@@ -0,0 +1,18 @@
import React from 'react'
import { useLocation } from 'react-router-dom'
export default function VersionViewer() {
const {state} = useLocation();
console.log(state);
const launchBs = () => {
window.electron.ipcRenderer.sendMessage('bs-launch.steam'); //Temporary
}
return (
<div>
<button onClick={launchBs}>Launch</button>
</div>
)
}
@@ -0,0 +1,7 @@
import React from 'react'
export default function VersionsList() {
return (
<div>versions-list.components</div>
)
}
@@ -0,0 +1,25 @@
import { BSVersion } from "../../../main/services/bs-version-manager.service"
import { ReducerAction } from "../store"
const INITIAL_STATE = {
installedVersions: [] as BSVersion[]
}
function installedBSReducer(state = INITIAL_STATE, action: ReducerAction<BSVersion|BSVersion[]>){
switch(action.type){
case 'INSTALLED_BS_ADD_ALL': {
if(!action.payload){ return state; }
const payload = action.payload as BSVersion[];
return { ...state, installedVersions: [...state.installedVersions, ...payload] };
}
case 'INSTALLED_BS_ADD':{
if(!action.payload){ return state; }
const payload = action.payload as BSVersion;
return { ...state, installedVersions: [...state.installedVersions, payload]}
}
}
return state;
}
export default installedBSReducer;
+15
View File
@@ -0,0 +1,15 @@
import { configureStore } from "@reduxjs/toolkit";
import installedBSReducer from './reducers/bs-versions-installed.reducer'
const store = configureStore({
reducer:{
installedBSReducer,
}
});
export default store;
export interface ReducerAction<T> {
type: string,
payload?: T
}