Skip to content

[UI/UX] Improve error handling on BE and FE #1363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3473865
wip: show error message if no games found
flavioislima May 23, 2022
0e9eb68
feat: add Error components
flavioislima May 23, 2022
165c78f
i18n: updated keys
flavioislima May 23, 2022
d5fb22a
chore: typo
flavioislima May 23, 2022
864085d
fix: check
flavioislima May 23, 2022
790c0fc
feat: show error message on gamepage
flavioislima May 23, 2022
8d1903c
chore: added more checks
flavioislima May 23, 2022
21fd5cc
feat: added more messages and error handlers
flavioislima May 24, 2022
9082ec0
chore: change invalid path message
flavioislima May 24, 2022
e843a90
feat: add context menu to Settings
flavioislima May 24, 2022
6983640
fix: steam runtime setting not updating
flavioislima May 24, 2022
e433727
fix: type conversion on logger
flavioislima May 25, 2022
293dae9
feat: add showdialog param to logger
flavioislima May 25, 2022
a4829d3
fix: wrong type coertions
flavioislima May 25, 2022
5119fcb
chore: log level adjustments
flavioislima May 25, 2022
f5ee3e3
fix: epic webview login not working
flavioislima May 25, 2022
67be666
fix: logged in status not changing
flavioislima May 25, 2022
68c8ef0
feat: handle other login errors
flavioislima May 25, 2022
0a9e030
chore: refactor login FE a bit
flavioislima May 25, 2022
2f0f0cb
feat: deal with memory cache failure on install
flavioislima May 25, 2022
e57d098
fix: error handler error
flavioislima May 25, 2022
cc47b81
chore: pr comments
flavioislima May 25, 2022
07e5f7e
chore: pr comments 2
flavioislima May 25, 2022
f06232a
chore: function naming
flavioislima May 25, 2022
035bb5c
Merge branch 'main' of github.com:Heroic-Games-Launcher/HeroicGamesLa…
flavioislima May 25, 2022
afe6f64
feat: refactor and deal with deleted folders
flavioislima May 26, 2022
30f3e6b
feat: gog forceUninstall
flavioislima May 26, 2022
ab7d49e
fix: use legendary uninstall command
flavioislima May 26, 2022
b1a1e54
chore: pr comments
flavioislima May 26, 2022
04600d1
fix: imports and tests
flavioislima May 26, 2022
4e03fe4
feat: check if gog game folder exists
flavioislima May 26, 2022
705bf5d
i18n: updated keys
flavioislima May 26, 2022
feca68f
feat: get runtimes from steam libraries
flavioislima May 26, 2022
cfd8c43
Merge branch 'main' of github.com:Heroic-Games-Launcher/HeroicGamesLa…
flavioislima May 27, 2022
bf5adfe
i18n: updated keys
flavioislima May 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions electron/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ abstract class Game {
abstract moveInstall(newInstallPath: string): Promise<string>
abstract repair(): Promise<ExecResult>
abstract stop(): Promise<void>
abstract forceUninstall(appName: string, runner: string): void
abstract syncSaves(arg: string, path: string): Promise<ExecResult>
abstract uninstall(): Promise<ExecResult>
abstract update(): Promise<{ status: 'done' | 'error' }>
Expand Down
2 changes: 1 addition & 1 deletion electron/gog/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class GOGUser {
} else {
logError('No credentials, auth required', LogPrefix.Gog)
errorHandler({
error: { stderr: 'No credentials', stdout: '' },
error: 'No credentials',
runner: 'GOG'
})
return null
Expand Down
20 changes: 15 additions & 5 deletions electron/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ async function runLegendaryOrGogdlCommand(
}
): Promise<ExecResult> {
const fullRunnerPath = join(runner.dir, runner.bin)
const appName = commandParts[commandParts.findIndex(() => 'launch') + 1]
const safeCommand = getLegendaryOrGogdlCommand(
commandParts,
options?.env,
Expand Down Expand Up @@ -512,12 +513,15 @@ async function runLegendaryOrGogdlCommand(

child.on('close', (code, signal) => {
errorHandler({
error: { stderr: stderr.join(), stdout: stdout.join() },
error: `${stdout.join().concat(stderr.join())}`,
logPath: options?.logFile,
runner: runner.name
})

if (stderr.join().includes('ERROR')) {
if (
stderr.join().includes('ERROR') ||
stderr.join().includes('CRITICAL')
) {
rej(stderr.join())
}

Expand All @@ -543,15 +547,21 @@ async function runLegendaryOrGogdlCommand(
})
.catch((error) => {
errorHandler({
error: { stderr: `${error}`, stdout: `${error}` },
error: `${error}`,
logPath: options?.logFile,
runner: runner.name
runner: runner.name,
appName
})

const dontShowDialog =
`${error}`.includes('signal') &&
`${error}`.includes('MemoryError:') &&
`${error}`.includes('appears to be deleted')

logError(
['Error running', runner.name, 'command', `"${safeCommand}": ${error}`],
runner.logPrefix,
false
dontShowDialog
)
return { stdout: '', stderr: '', fullCommand: safeCommand, error }
})
Expand Down
31 changes: 28 additions & 3 deletions electron/legendary/games.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { existsSync, mkdirSync } from 'graceful-fs'
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'graceful-fs'
import axios from 'axios'

import { BrowserWindow } from 'electron'
import { ExecResult, ExtraInfo, InstallArgs, LaunchResult } from '../types'
import {
ExecResult,
ExtraInfo,
GameInfo,
InstallArgs,
LaunchResult
} from '../types'
import { Game } from '../games'
import { GameConfig } from '../game_config'
import { GlobalConfig } from '../config'
Expand All @@ -15,7 +21,8 @@ import {
userHome,
isLinux,
isMac,
isWindows
isWindows,
installed
} from '../constants'
import { logError, logInfo, LogPrefix, logWarning } from '../logger/logger'
import { spawn } from 'child_process'
Expand All @@ -31,6 +38,7 @@ import { addShortcuts, removeShortcuts } from '../shortcuts'
import { basename, join } from 'path'
import { runLegendaryCommand } from './library'
import { gameInfoStore } from './electronStores'
import { mainWindow } from '../main'

class LegendaryGame extends Game {
public appName: string
Expand Down Expand Up @@ -724,6 +732,23 @@ class LegendaryGame extends Game {
return logInfo(`${pattern} killed`, LogPrefix.Legendary)
})
}

public forceUninstall(appName: string, runner: string) {
// Modify Legendary installed.json file:
try {
const file: Record<string, GameInfo> = JSON.parse(
readFileSync(installed, 'utf8')
)
delete file[appName]
writeFileSync(installed, JSON.stringify(file, null, 2))
mainWindow.webContents.send('refreshLibrary', runner)
} catch (error) {
logError(
`Error reading ${installed}, could not complete operation`,
LogPrefix.Legendary
)
}
}
}

export { LegendaryGame }
9 changes: 7 additions & 2 deletions electron/legendary/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,14 @@ export class LegendaryLibrary {
Object.entries(JSON.parse(readFileSync(installedJSON, 'utf-8')))
)
} catch (error) {
// disabling log here because its giving false positives on import command
logError(
'Corrupted intalled.json file, cannot load installed games',
LogPrefix.Legendary
[
'Corrupted intalled.json file, cannot load installed games',
`${error}`
],
LogPrefix.Legendary,
false
)
this.installedGames = new Map()
}
Expand Down
37 changes: 9 additions & 28 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {
getGOGdlBin,
showErrorBoxModal,
getFileSize,
showErrorBoxModalAuto,
getWineFromProton
} from './utils'
import {
Expand Down Expand Up @@ -98,7 +97,7 @@ import { verifyWinePrefix } from './launcher'
const { showMessageBox, showOpenDialog } = dialog
const isWindows = platform() === 'win32'

let mainWindow: BrowserWindow = null
export let mainWindow: BrowserWindow = null

async function createWindow(): Promise<BrowserWindow> {
configStore.set('userHome', userHome)
Expand Down Expand Up @@ -848,39 +847,21 @@ ipcMain.handle(
let logResult = ''
return Game.get(appName, runner)
.launch(launchArguments)
.then(
async ({
stdout,
stderr,
success,
command,
gameSettings
}: LaunchResult) => {
if (!success) {
showErrorBoxModalAuto(
i18next.t('box.error.title', 'Something Went Wrong'),
i18next.t(
'box.error.launch',
'Error when launching the game, check the logs!'
)
)
}

logResult = `Launch Command: ${command}
.then(async ({ stdout, stderr, command, gameSettings }: LaunchResult) => {
logResult = `Launch Command: ${command}

System Info:
${await getSystemInfo()}

Game Settings: ${JSON.stringify(gameSettings, null, '\t')}
`
if (stderr) {
logResult += `\nError Log:\n${stderr}\n`
}
if (stdout) {
logResult += `\nGame Log:\n${stdout}\n`
}
if (stderr) {
logResult += `\nError Log:\n${stderr}\n`
}
)
if (stdout) {
logResult += `\nGame Log:\n${stdout}\n`
}
})
.catch((exception) => {
logResult = `${exception.name} - ${exception.message}`
logError(logResult, LogPrefix.Backend)
Expand Down
36 changes: 30 additions & 6 deletions electron/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Runner } from './types'
import * as axios from 'axios'
import { app, dialog, net, shell, Notification, BrowserWindow } from 'electron'
import { exec } from 'child_process'
Expand Down Expand Up @@ -31,6 +32,8 @@ import {
import fileSize from 'filesize'
import makeClient from 'discord-rich-presence-typescript'
import { RpcClient, SteamRuntime } from 'types'
import { Game } from './games'
import { mainWindow } from './main'

const execAsync = promisify(exec)
const statAsync = promisify(stat)
Expand Down Expand Up @@ -287,17 +290,19 @@ export const getSystemInfo = async () => {
}

type ErrorHandlerMessage = {
error?: { stderr: string; stdout: string }
error?: string
logPath?: string
appName?: string
runner: string
}

async function errorHandler(
{ error, logPath, runner: r }: ErrorHandlerMessage,
{ error, logPath, runner: r, appName }: ErrorHandlerMessage,
window?: BrowserWindow
): Promise<void> {
const noSpaceMsg = 'Not enough available disk space'
const runner = r === 'Legendary' ? 'Legendary (Epic Games)' : r
const plat = r === 'legendary' ? 'Legendary (Epic Games)' : r
const deletedFolderMsg = 'appears to be deleted'
const otherErrorMessages = [
'No saved credentials',
'in get_user_entitlements',
Expand All @@ -322,11 +327,30 @@ async function errorHandler(
.catch(() => logInfo('operation interrupted', LogPrefix.Backend))
}
if (error) {
if (error.includes(deletedFolderMsg) && appName) {
const runner = r.toLocaleLowerCase() as Runner
const game = Game.get(appName, runner)
const { title } = await game.getGameInfo()
const { response } = await showMessageBox(mainWindow, {
type: 'question',
title,
message: i18next.t(
'box.error.folder-not-found.title',
'Game folder appears to be deleted, do you want to remove the game from the installed list?'
),
buttons: [i18next.t('box.no'), i18next.t('box.yes')]
})

if (response === 1) {
return game.forceUninstall(appName, runner)
}
}

otherErrorMessages.forEach((message) => {
if (error?.stderr?.includes(message)) {
if (error.includes(message)) {
return showErrorBoxModal(
window,
runner,
mainWindow,
plat,
i18next.t(
'box.error.credentials.message',
'Your Crendentials have expired, Logout and Login Again!'
Expand Down
10 changes: 10 additions & 0 deletions src/state/GlobalState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,16 @@ export class GlobalState extends PureComponent<Props> {
const { libraryStatus } = this.state
this.handleGameStatus({ ...libraryStatus, ...args })
})

ipcRenderer.on('refreshLibrary', async (e, runner) => {
this.refreshLibrary({
checkForUpdates: false,
fullRefresh: true,
runInBackground: true,
library: runner
})
})

const legendaryUser = Boolean(configStore.get('userInfo', null))
const gogUser = Boolean(gogConfigStore.get('userData', null))
const platform = await getPlatform()
Expand Down