Skip to content

Commit

Permalink
feat: improve MSFS base path detection (#423)
Browse files Browse the repository at this point in the history
* feat: rewrite MSFS base path detection

* fix: openPath() does not focus Explorer window

* chore: update changelog

* refactor: dismiss promise

(cherry picked from commit 0a3895c)
  • Loading branch information
FoxtrotSierra6829 authored and Benjozork committed Sep 14, 2024
1 parent 9630008 commit 5f340bd
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 111 deletions.
3 changes: 3 additions & 0 deletions .github/CHANGELOG.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ releases:
- title: Shortcut keys no longer override other applications shortcuts
categories: [UI]
authors: [alepouna]
- title: Improve detection of MSFS installation
categories: [Core]
authors: [FoxtrotSierra]
- name: 3.4.0
changes:
- title: Add a button to the error dialog to copy error messages to clipboard
Expand Down
2 changes: 2 additions & 0 deletions src/common/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default {
maximize: 'window/maximize',
close: 'window/close',
isMaximized: 'window/isMaximized',
reload: 'window/reload',
},
update: {
error: 'update/error',
Expand All @@ -21,4 +22,5 @@ export default {
requestSessionID: 'sentry/requestSessionID',
provideSessionID: 'sentry/provideSessionID',
},
openPath: 'openPath',
};
8 changes: 8 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,18 @@ function initializeApp() {
mainWindow.destroy();
});

ipcMain.on(channels.window.reload, () => {
mainWindow.reload();
});

ipcMain.on(channels.window.isMaximized, (event) => {
event.sender.send(channels.window.isMaximized, mainWindow.isMaximized());
});

ipcMain.on(channels.openPath, (_, value: string) => {
void shell.openPath(value);
});

ipcMain.on('request-startup-at-login-changed', (_, value: boolean) => {
app.setLoginItemSettings({
openAtLogin: value,
Expand Down
94 changes: 47 additions & 47 deletions src/renderer/actions/install-path.utils.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,74 @@
import settings from 'renderer/rendererSettings';
import settings, { msStoreBasePath, steamBasePath } from 'renderer/rendererSettings';
import { Directories } from 'renderer/utils/Directories';
import { dialog } from '@electron/remote';
import fs from 'fs';

export const setupMsfsCommunityPath = async (): Promise<string> => {
const currentPath = Directories.installLocation();

const selectPath = async (currentPath: string, dialogTitle: string, setting: string): Promise<string> => {
const path = await dialog.showOpenDialog({
title: 'Select your MSFS community directory',
title: dialogTitle,
defaultPath: typeof currentPath === 'string' ? currentPath : '',
properties: ['openDirectory'],
});

if (path.filePaths[0]) {
settings.set('mainSettings.msfsCommunityPath', path.filePaths[0]);
settings.set(setting, path.filePaths[0]);
return path.filePaths[0];
} else {
return '';
}
};

export const setupInstallPath = async (): Promise<string> => {
const currentPath = Directories.installLocation();
export const setupMsfsBasePath = async (): Promise<string> => {
const currentPath = Directories.msfsBasePath();

const path = await dialog.showOpenDialog({
title: 'Select your install directory',
defaultPath: typeof currentPath === 'string' ? currentPath : '',
properties: ['openDirectory'],
});
const availablePaths: string[] = [];
if (fs.existsSync(msStoreBasePath)) {
availablePaths.push('Microsoft Store Edition');
}
if (fs.existsSync(steamBasePath)) {
availablePaths.push('Steam Edition');
}

if (path.filePaths[0]) {
settings.set('mainSettings.installPath', path.filePaths[0]);
if (!settings.get('mainSettings.separateLiveriesPath')) {
settings.set('mainSettings.liveriesPath', path.filePaths[0]);
if (availablePaths.length > 0) {
availablePaths.push('Custom Directory');

const { response } = await dialog.showMessageBox({
title: 'FlyByWire Installer',
message: 'We found a possible MSFS installation.',
type: 'warning',
buttons: availablePaths,
});

const selection = availablePaths[response];
switch (selection) {
case 'Microsoft Store Edition':
settings.set('mainSettings.msfsBasePath', msStoreBasePath);
return msStoreBasePath;
case 'Steam Edition':
settings.set('mainSettings.msfsBasePath', steamBasePath);
return steamBasePath;
case 'Custom Directory':
break;
}
return path.filePaths[0];
} else {
return '';
}
};

export const setupTempLocation = async (): Promise<string> => {
const currentPath = Directories.tempLocation();
return await selectPath(currentPath, 'Select your MSFS base directory', 'mainSettings.msfsBasePath');
};

const path = await dialog.showOpenDialog({
title: 'Select a location for temporary folders',
defaultPath: typeof currentPath === 'string' ? currentPath : '',
properties: ['openDirectory'],
});
export const setupMsfsCommunityPath = async (): Promise<string> => {
const currentPath = Directories.installLocation();

if (path.filePaths[0]) {
settings.set('mainSettings.tempLocation', path.filePaths[0]);
return path.filePaths[0];
} else {
return '';
}
return await selectPath(currentPath, 'Select your MSFS community directory', 'mainSettings.msfsCommunityPath');
};

export const setupLiveriesPath = async (): Promise<string> => {
const currentPath = Directories.liveries();
export const setupInstallPath = async (): Promise<string> => {
const currentPath = Directories.installLocation();

const path = await dialog.showOpenDialog({
title: 'Select your liveries directory',
defaultPath: typeof currentPath === 'string' ? currentPath : '',
properties: ['openDirectory'],
});
return await selectPath(currentPath, 'Select your install directory', 'mainSettings.installPath');
};

if (path.filePaths[0]) {
settings.set('mainSettings.liveriesPath', path.filePaths[0]);
return path.filePaths[0];
} else {
return '';
}
export const setupTempLocation = async (): Promise<string> => {
const currentPath = Directories.tempLocation();

return await selectPath(currentPath, 'Select a location for temporary folders', 'mainSettings.tempLocation');
};
26 changes: 15 additions & 11 deletions src/renderer/components/AddonSection/MyInstall/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
NamedDirectoryDefinition,
} from 'renderer/utils/InstallerConfiguration';
import { BoxArrowRight, Folder } from 'react-bootstrap-icons';
import { shell } from 'electron';
import { ipcRenderer, shell } from 'electron';
import { Directories } from 'renderer/utils/Directories';
import { useAppSelector } from 'renderer/redux/store';
import { InstallStatusCategories } from 'renderer/components/AddonSection/Enums';
import fs from 'fs';
import channels from 'common/channels';

export interface MyInstallProps {
addon: Addon;
Expand Down Expand Up @@ -39,21 +41,23 @@ export const MyInstall: FC<MyInstallProps> = ({ addon }) => {
}
};

const handleClickDirectory = (def: DirectoryDefinition) => {
let fullPath;
const fulldirectory = (def: DirectoryDefinition) => {
switch (def.location.in) {
case 'community':
fullPath = Directories.inInstallLocation(def.location.path);
break;
return Directories.inInstallLocation(def.location.path);
case 'package':
fullPath = Directories.inInstallPackage(addon, def.location.path);
break;
return Directories.inInstallPackage(addon, def.location.path);
case 'packageCache':
fullPath = Directories.inPackageCache(addon, def.location.path);
break;
return Directories.inPackageCache(addon, def.location.path);
}
};

const handleClickDirectory = (def: DirectoryDefinition) => {
ipcRenderer.send(channels.openPath, fulldirectory(def));
};

shell.openPath(fullPath).then();
const existsDirectory = (def: DirectoryDefinition) => {
return fs.existsSync(fulldirectory(def));
};

const directoriesDisabled = !InstallStatusCategories.installed.includes(installStates[addon.key]?.status);
Expand Down Expand Up @@ -88,7 +92,7 @@ export const MyInstall: FC<MyInstallProps> = ({ addon }) => {
{directories.map((it) => (
<button
key={it.title}
className={`flex items-center gap-x-5 rounded-md border-2 border-navy-light bg-navy-light px-7 py-4 text-3xl transition-colors duration-100 hover:border-cyan hover:bg-transparent ${directoriesDisabled ? 'pointer-events-none opacity-60' : ''}`}
className={`flex items-center gap-x-5 rounded-md border-2 border-navy-light bg-navy-light px-7 py-4 text-3xl transition-colors duration-100 hover:border-cyan hover:bg-transparent ${directoriesDisabled || !existsDirectory(it) ? 'pointer-events-none opacity-60' : ''}`}
onClick={() => handleClickDirectory(it)}
>
<Folder size={24} />
Expand Down
106 changes: 85 additions & 21 deletions src/renderer/components/ErrorModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,112 @@
import React, { useState } from 'react';
import { setupInstallPath } from 'renderer/actions/install-path.utils';
import settings from 'renderer/rendererSettings';
import { setupInstallPath, setupMsfsBasePath } from 'renderer/actions/install-path.utils';
import settings, { defaultCommunityDir } from 'renderer/rendererSettings';
import { Directories } from 'renderer/utils/Directories';
import * as fs from 'fs';
import { Button, ButtonType } from 'renderer/components/Button';
import * as os from 'os';
import { ipcRenderer } from 'electron';
import channels from 'common/channels';

export const ErrorModal = (): JSX.Element => {
const [communityError, setCommunityError] = useState<boolean>(
!fs.existsSync(Directories.installLocation()) || Directories.installLocation() === 'C:\\',
const [msfsBasePathError] = useState<boolean>(
(!fs.existsSync(Directories.msfsBasePath()) && Directories.msfsBasePath() !== 'notInstalled') ||
Directories.msfsBasePath() === 'C:\\',
);
const [installLocationError] = useState<boolean>(
!fs.existsSync(Directories.installLocation()) ||
Directories.installLocation() === 'C:\\' ||
!fs.existsSync(Directories.communityLocation()) ||
!fs.existsSync(Directories.tempLocation()),
);
const [linuxError, setLinuxError] = useState<boolean>(Directories.installLocation() === 'linux');

const handleClose = () => {
setCommunityError(false);
setLinuxError(false);
const handleSelectMsfsBasePath = async () => {
const path = await setupMsfsBasePath();
if (path) {
const communityDir = defaultCommunityDir(path);
settings.set('mainSettings.msfsCommunityPath', communityDir);
settings.set('mainSettings.installPath', communityDir);
settings.set('mainSettings.separateTempLocation', false);
settings.set('mainSettings.tempLocation', communityDir);
ipcRenderer.send(channels.window.reload);
}
};

const handleSelectPath = async () => {
const handleSelectInstallPath = async () => {
const path = await setupInstallPath();
if (path) {
settings.set('mainSettings.liveriesPath', path);
settings.set('mainSettings.separateLiveriesPath', false);
handleClose();
settings.set('mainSettings.msfsCommunityPath', path);
settings.set('mainSettings.separateTempLocation', false);
settings.set('mainSettings.tempLocation', path);
ipcRenderer.send(channels.window.reload);
}
};

const handleNoMSFS = () => {
settings.set('mainSettings.msfsBasePath', 'notInstalled');
ipcRenderer.send(channels.window.reload);
};

const content = (): JSX.Element => {
// Linux's error goes first because it may interfere with the other dir checkers
if (linuxError) {
if (os.platform().toString() === 'linux') {
if (msfsBasePathError) {
return (
<>
<span className="w-3/5 text-center text-2xl">Seems like you&apos;re using Linux</span>
<span className="w-3/5 text-center text-2xl">
We&apos;re unable to autodetect your install currently. Please set the correct location for the MSFS base
path before we can continue. It is usually located somewhere here:
<br />
~/.local/share/Steam/steamapps/compatdata/&lt;APPID&gt;/pfx/drive_c/users/steamuser/AppData/Microsoft
Flight Simulator/
</span>

<Button type={ButtonType.Neutral} onClick={handleSelectMsfsBasePath}>
Select Path
</Button>
<Button type={ButtonType.Neutral} onClick={handleNoMSFS}>
I don&apos;t have Microsoft Flight Simulator installed
</Button>
</>
);
}
if (installLocationError) {
return (
<>
<span className="w-3/5 text-center text-2xl">Seems like you&apos;re using Linux</span>
<span className="w-3/5 text-center text-2xl">
We&apos;re unable to autodetect your install location (community folder) currently. Please set the correct
location before we can continue.
</span>

<Button type={ButtonType.Neutral} onClick={handleSelectInstallPath}>
Select Path
</Button>
</>
);
}
}
if (msfsBasePathError) {
return (
<>
<span className="w-3/5 text-center text-2xl">Seems like you&apos;re using Linux</span>
<span className="w-3/5 text-center text-2xl">
We&apos;re unable to autodetect your install currently. Please set the correct location before we can
continue.
We couldn&apos;t determine the correct MSFS base path. Would you please help us? <br /> <br />
It is usually located somewhere here: <br />
&quot;%LOCALAPPDATA%\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache&quot; <br /> <br /> or
here: <br /> &quot;%APPDATA%\Microsoft Flight Simulator\&quot;
</span>

<Button type={ButtonType.Neutral} onClick={handleSelectPath}>
Select
<Button type={ButtonType.Neutral} onClick={handleSelectMsfsBasePath}>
Select Path
</Button>
<Button type={ButtonType.Neutral} onClick={handleNoMSFS}>
I don&apos;t have Microsoft Flight Simulator installed
</Button>
</>
);
}
if (communityError && Directories.installLocation() !== 'linux') {
if (installLocationError) {
return (
<>
<span className="w-3/5 text-center text-2xl">Your Community folder is set to</span>
Expand All @@ -53,7 +117,7 @@ export const ErrorModal = (): JSX.Element => {
but we couldn&apos;t find it there. Please set the correct location before we can continue.
</span>

<Button type={ButtonType.Neutral} onClick={handleSelectPath}>
<Button type={ButtonType.Neutral} onClick={handleSelectInstallPath}>
Select
</Button>
</>
Expand All @@ -62,7 +126,7 @@ export const ErrorModal = (): JSX.Element => {
return <></>;
};

if (communityError || linuxError) {
if (installLocationError || msfsBasePathError) {
return (
<div className="fixed left-0 top-0 z-50 flex h-screen w-screen flex-col items-center justify-center gap-y-5 bg-navy text-gray-100">
<span className="text-5xl font-semibold">Something went wrong.</span>
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/components/SettingsSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import settings from 'renderer/rendererSettings';
import * as packageInfo from '../../../../package.json';
import { Button, ButtonType } from '../Button';
import { PromptModal, useModals } from 'renderer/components/Modal';
import { ipcRenderer } from 'electron';
import channels from 'common/channels';

interface InstallButtonProps {
type?: ButtonType;
Expand Down Expand Up @@ -40,6 +42,7 @@ export const SettingsSection = (): JSX.Element => {

// Workaround to flush the defaults
settings.set('metaInfo.lastVersion', packageInfo.version);
ipcRenderer.send(channels.window.reload);
}}
/>,
);
Expand Down
Loading

0 comments on commit 5f340bd

Please sign in to comment.