Skip to content

[Logs] Add more info in game logs #3394

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 2 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 79 additions & 24 deletions src/backend/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
* Note that with console.log and console.warn everything will be saved too.
* error equals console.error
*/
import { AppSettings, GameSettings } from 'common/types'
import { AppSettings, GameInfo, GameSettings } from 'common/types'
import { showDialogBoxModalAuto } from '../dialog/dialog'
import { appendMessageToLogFile, getLongestPrefix } from './logfile'
import { backendEvents } from 'backend/backend_events'
import { GlobalConfig } from 'backend/config'
import { getGOGdlBin, getLegendaryBin } from 'backend/utils'
import { join } from 'path'
import { formatSystemInfo, getSystemInfo } from '../utils/systeminfo'
import { appendFileSync } from 'graceful-fs'
import { appendFileSync, writeFileSync } from 'graceful-fs'
import { gamesConfigPath } from 'backend/constants'
import { writeFile } from 'fs/promises'
import { gameManagerMap } from 'backend/storeManagers'
import { isEnabled } from 'backend/storeManagers/legendary/eos_overlay/eos_overlay'
import { Winetricks } from 'backend/tools'
import { platform } from 'os'

export enum LogPrefix {
General = '',
Expand Down Expand Up @@ -359,16 +362,16 @@ export function logFileLocation(appName: string) {
const logsWriters: Record<string, LogWriter> = {}

class LogWriter {
appName: string
gameInfo: GameInfo
queue: string[] | undefined
initialized: boolean
timeoutId: NodeJS.Timeout | undefined
filePath: string

constructor(appName: string) {
this.appName = appName
constructor(gameInfo: GameInfo) {
this.gameInfo = gameInfo
this.initialized = false
this.filePath = lastPlayLogFileLocation(appName)
this.filePath = lastPlayLogFileLocation(gameInfo.app_name)
}

logMessage(message: string) {
Expand All @@ -386,27 +389,76 @@ class LogWriter {
}

async initLog() {
const { app_name, runner } = this.gameInfo

const notNative =
['windows', 'Windows', 'Win32'].includes(
this.gameInfo.install.platform || ''
) && platform() !== 'win32'

// init log file and then append message if any
try {
const info = await getSystemInfo()
const systemInfo = await formatSystemInfo(info)
// log game title and install directory
writeFileSync(
this.filePath,
`Launching "${this.gameInfo.title}" (${runner})\n` +
`Native? ${notNative ? 'No' : 'Yes'}\n` +
`Installed in: ${this.gameInfo.install.install_path}\n\n`
)

// init log file and then append message if any
try {
await writeFile(
this.filePath,
'System Info:\n' + `${systemInfo}\n` + '\n'
)
// log system information
const info = await getSystemInfo()
const systemInfo = await formatSystemInfo(info)

this.initialized = true
this.appendMessages()
appendFileSync(this.filePath, `System Info:\n${systemInfo}\n\n`)
} catch (error) {
logError(
[`Failed to initialize log ${this.filePath}:`, error],
['Failed to fetch system information', error],
LogPrefix.Backend
)
}

// log game settings
const gameSettings = await gameManagerMap[runner].getSettings(app_name)
const gameSettingsString = JSON.stringify(gameSettings, null, '\t')
const startPlayingDate = new Date()

appendFileSync(this.filePath, `Game Settings: ${gameSettingsString}\n\n`)

// log if EOS overlay is enabled for Epic games
if (runner === 'legendary') {
const enabled = await isEnabled(app_name)

appendFileSync(
this.filePath,
`EOS Overlay enabled? ${enabled ? 'Yes' : 'No'}\n`
)
}

// log winetricks packages if not native
if (notNative) {
const winetricksPackages = await Winetricks.listInstalled(
runner,
app_name
)

appendFileSync(
this.filePath,
`Winetricks packages installed: ${
winetricksPackages?.join(', ') || 'None'
}\n\n`
)
}

appendFileSync(this.filePath, `Game launched at: ${startPlayingDate}\n\n`)

this.initialized = true
} catch (error) {
logError(['Failed to fetch system information', error], LogPrefix.Backend)
logError(
[`Failed to initialize log ${this.filePath}:`, error],
LogPrefix.Backend
)
}
}

Expand Down Expand Up @@ -434,16 +486,19 @@ class LogWriter {
}
}

export function appendGameLog(appName: string, message: string) {
logsWriters[appName] ??= new LogWriter(appName)
logsWriters[appName].logMessage(message)
export function appendGameLog(gameInfo: GameInfo, message: string) {
logsWriters[gameInfo.app_name] ??= new LogWriter(gameInfo)
logsWriters[gameInfo.app_name].logMessage(message)
}

export function initGameLog(appName: string) {
logsWriters[appName] ??= new LogWriter(appName)
logsWriters[appName].initLog()
export function initGameLog(gameInfo: GameInfo) {
logsWriters[gameInfo.app_name] ??= new LogWriter(gameInfo)
logsWriters[gameInfo.app_name].initLog()
}

export function stopLogger(appName: string) {
logsWriters[appName].logMessage(
'============= End of game logs ============='
)
delete logsWriters[appName]
}
20 changes: 4 additions & 16 deletions src/backend/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1029,20 +1029,11 @@ ipcMain.handle(
powerDisplayId = powerSaveBlocker.start('prevent-display-sleep')
}

initGameLog(appName)

const gameSettingsString = JSON.stringify(gameSettings, null, '\t')
appendGameLog(
appName,
`Game Settings: ${gameSettingsString}\n` +
'\n' +
`Game launched at: ${startPlayingDate}\n` +
'\n'
)
initGameLog(game)

if (logsDisabled) {
appendGameLog(
appName,
game,
'IMPORTANT: Logs are disabled. Enable logs before reporting an issue.'
)
}
Expand All @@ -1051,10 +1042,7 @@ ipcMain.handle(

// check if isNative, if not, check if wine is valid
if (!isNative) {
const isWineOkToLaunch = await checkWineBeforeLaunch(
appName,
gameSettings
)
const isWineOkToLaunch = await checkWineBeforeLaunch(game, gameSettings)

if (!isWineOkToLaunch) {
logError(
Expand Down Expand Up @@ -1090,7 +1078,7 @@ ipcMain.handle(
.catch((exception) => {
logError(exception, LogPrefix.Backend)
appendGameLog(
appName,
game,
`An exception occurred when launching the game:\n${exception.stack}`
)

Expand Down
8 changes: 4 additions & 4 deletions src/backend/storeManagers/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ export async function launch(
steamRuntime
} = await prepareLaunch(gameSettings, gameInfo, isNative(appName))
if (!launchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${launchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${launchPrepFailReason}`)
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
message: launchPrepFailReason!,
Expand Down Expand Up @@ -477,7 +477,7 @@ export async function launch(
envVars: wineEnvVars
} = await prepareWineLaunch('gog', appName)
if (!wineLaunchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${wineLaunchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${wineLaunchPrepFailReason}`)
if (wineLaunchPrepFailReason) {
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
Expand Down Expand Up @@ -523,7 +523,7 @@ export async function launch(
commandEnv,
join(...Object.values(getGOGdlBin()))
)
appendGameLog(appName, `Launch Command: ${fullCommand}\n\nGame Log:\n`)
appendGameLog(gameInfo, `Launch Command: ${fullCommand}\n\nGame Log:\n`)

sendGameStatusUpdate({ appName, runner: 'gog', status: 'playing' })

Expand All @@ -539,7 +539,7 @@ export async function launch(
wrappers,
logMessagePrefix: `Launching ${gameInfo.title}`,
onOutput: (output: string) => {
if (!logsDisabled) appendGameLog(appName, output)
if (!logsDisabled) appendGameLog(gameInfo, output)
}
})

Expand Down
2 changes: 1 addition & 1 deletion src/backend/storeManagers/gog/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async function setup(

const gameSettings = GameConfig.get(appName).config
if (!isWindows) {
const isWineOkToLaunch = await checkWineBeforeLaunch(appName, gameSettings)
const isWineOkToLaunch = await checkWineBeforeLaunch(gameInfo, gameSettings)

if (!isWineOkToLaunch) {
logError(
Expand Down
18 changes: 10 additions & 8 deletions src/backend/storeManagers/legendary/eos_overlay/eos_overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import { Path } from '../commands/base'
import { LegendaryCommand } from '../commands'
import { sendGameStatusUpdate } from 'backend/utils'

const currentVersionPath = join(legendaryConfigPath, 'overlay_version.json')
const installedVersionPath = join(legendaryConfigPath, 'overlay_install.json')
const defaultInstallPath = join(toolsPath, 'eos_overlay')
const currentVersionPath = () =>
join(legendaryConfigPath, 'overlay_version.json')
const installedVersionPath = () =>
join(legendaryConfigPath, 'overlay_install.json')
const defaultInstallPath = () => join(toolsPath, 'eos_overlay')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved these consts into functions to avoid an import loop

const eosOverlayAppName = '98bc04bc842e4906993fd6d6644ffb8d'

function getStatus(): {
Expand All @@ -29,7 +31,7 @@ function getStatus(): {
}

const { version, install_path } = JSON.parse(
readFileSync(installedVersionPath, 'utf-8')
readFileSync(installedVersionPath(), 'utf-8')
)

if (install_path !== defaultInstallPath) {
Expand All @@ -43,13 +45,13 @@ function getStatus(): {
}

async function getLatestVersion(): Promise<string> {
if (!existsSync(currentVersionPath)) {
if (!existsSync(currentVersionPath())) {
// HACK: `overlay_version.json` isn't created when the overlay isn't installed
if (!isInstalled()) {
return ''
}
await updateInfo()
if (!existsSync(currentVersionPath)) {
if (!existsSync(currentVersionPath())) {
logError(
'EOS Overlay information not found after manual update. User is probably not logged in anymore',
LogPrefix.Legendary
Expand All @@ -58,7 +60,7 @@ async function getLatestVersion(): Promise<string> {
}
}
const { buildVersion }: { buildVersion: string } = JSON.parse(
readFileSync(currentVersionPath, 'utf-8')
readFileSync(currentVersionPath(), 'utf-8')
).data
return buildVersion
}
Expand Down Expand Up @@ -242,7 +244,7 @@ async function disable(appName: string) {
}

function isInstalled() {
return existsSync(installedVersionPath)
return existsSync(installedVersionPath())
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/backend/storeManagers/legendary/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ export async function launch(
offlineMode
} = await prepareLaunch(gameSettings, gameInfo, isNative(appName))
if (!launchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${launchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${launchPrepFailReason}`)
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
message: launchPrepFailReason!,
Expand Down Expand Up @@ -811,7 +811,7 @@ export async function launch(
envVars: wineEnvVars
} = await prepareWineLaunch('legendary', appName)
if (!wineLaunchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${wineLaunchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${wineLaunchPrepFailReason}`)
if (wineLaunchPrepFailReason) {
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
Expand Down Expand Up @@ -864,7 +864,7 @@ export async function launch(
commandEnv,
join(...Object.values(getLegendaryBin()))
)
appendGameLog(appName, `Launch Command: ${fullCommand}\n\nGame Log:\n`)
appendGameLog(gameInfo, `Launch Command: ${fullCommand}\n\nGame Log:\n`)

sendGameStatusUpdate({ appName, runner: 'legendary', status: 'playing' })

Expand All @@ -880,7 +880,7 @@ export async function launch(
wrappers: wrappers,
logMessagePrefix: `Launching ${gameInfo.title}`,
onOutput: (output) => {
if (!logsDisabled) appendGameLog(appName, output)
if (!logsDisabled) appendGameLog(gameInfo, output)
}
})

Expand Down
8 changes: 4 additions & 4 deletions src/backend/storeManagers/nile/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ export async function launch(
} = await prepareLaunch(gameSettings, gameInfo, isNative())

if (!launchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${launchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${launchPrepFailReason}`)
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
message: launchPrepFailReason!,
Expand Down Expand Up @@ -358,7 +358,7 @@ export async function launch(
envVars: wineEnvVars
} = await prepareWineLaunch('nile', appName)
if (!wineLaunchPrepSuccess) {
appendGameLog(appName, `Launch aborted: ${wineLaunchPrepFailReason}`)
appendGameLog(gameInfo, `Launch aborted: ${wineLaunchPrepFailReason}`)
if (wineLaunchPrepFailReason) {
showDialogBoxModalAuto({
title: t('box.error.launchAborted', 'Launch aborted'),
Expand Down Expand Up @@ -404,7 +404,7 @@ export async function launch(
commandEnv,
join(...Object.values(getNileBin()))
)
appendGameLog(appName, `Launch Command: ${fullCommand}\n\nGame Log:\n`)
appendGameLog(gameInfo, `Launch Command: ${fullCommand}\n\nGame Log:\n`)

sendGameStatusUpdate({ appName, runner: 'nile', status: 'playing' })

Expand All @@ -420,7 +420,7 @@ export async function launch(
wrappers,
logMessagePrefix: `Launching ${gameInfo.title}`,
onOutput(output) {
if (!logsDisabled) appendGameLog(appName, output)
if (!logsDisabled) appendGameLog(gameInfo, output)
}
})

Expand Down
7 changes: 6 additions & 1 deletion src/backend/storeManagers/nile/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export default async function setup(
installedPath?: string
): Promise<void> {
const gameInfo = getGameInfo(appName)
if (!gameInfo) {
logError([`Could not find game info for ${appName}. Skipping setup`])
return
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't run setup steps if we can't find game info


const basePath = installedPath ?? gameInfo?.install.install_path
if (!basePath) {
logError([
Expand Down Expand Up @@ -62,7 +67,7 @@ export default async function setup(

const gameSettings = GameConfig.get(appName).config
if (!isWindows) {
const isWineOkToLaunch = await checkWineBeforeLaunch(appName, gameSettings)
const isWineOkToLaunch = await checkWineBeforeLaunch(gameInfo, gameSettings)

if (!isWineOkToLaunch) {
logError(
Expand Down
Loading