Skip to content
Open
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
18 changes: 11 additions & 7 deletions src/backend/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,23 @@ import shlex from 'shlex'
import { isOnline } from './online_monitor'
import { showDialogBoxModalAuto } from './dialog/dialog'
import { legendarySetup } from './storeManagers/legendary/setup'
import { gameManagerMap } from 'backend/storeManagers'
import { gameManagerMap, libraryManagerMap } from 'backend/storeManagers'
import * as VDF from '@node-steam/vdf'
import { readFileSync, writeFileSync } from 'fs'
import { LegendaryCommand } from './storeManagers/legendary/commands'
import { commandToArgsArray } from './storeManagers/legendary/library'
import { searchForExecutableOnPath } from './utils/os/path'
import {
createAbortController,
deleteAbortController
} from './utils/aborthandler/aborthandler'
import { download, isInstalled } from './wine/runtimes/runtimes'
import { storeMap } from 'common/utils'
import { runWineCommandOnGame } from './storeManagers/legendary/games'
import { getMainWindow } from './main_window'
import { sendFrontendMessage } from './ipc'
import { getUmuPath, isUmuSupported } from './utils/compatibility_layers'
import { copyFile } from 'fs/promises'
import { app, powerSaveBlocker } from 'electron'
import gogPresence from './storeManagers/gog/presence'
import { updateGOGPlaytime } from './storeManagers/gog/games'
import { addRecentGame } from './recent_games/recent_games'
import { tsStore } from './constants/key_value_stores'
import {
Expand Down Expand Up @@ -265,7 +262,11 @@ const launchEventCallback: (args: LaunchParams) => StatusPromise = async ({
const { disablePlaytimeSync } = GlobalConfig.get().getSettings()
if (runner === 'gog') {
if (!disablePlaytimeSync) {
await updateGOGPlaytime(appName, startPlayingDate, finishedPlayingDate)
await gameManagerMap['gog'].updateGOGPlaytime(
appName,
startPlayingDate,
finishedPlayingDate
)
} else {
logWarning(
'Posting playtime session to server skipped - playtime sync disabled',
Expand Down Expand Up @@ -1002,7 +1003,9 @@ async function installFixes(appName: string, runner: Runner) {

for (const filePath of knownFixes.runInPrefix) {
const fullPath = join(gameInfo.install.install_path!, filePath)
await runWineCommandOnGame(appName, {
// FIXME: This doesn't seem right, shouldn't we use a generic function instead
// of the Legendary-specific one?
await gameManagerMap['legendary'].runWineCommandOnGame(appName, {
commandParts: [fullPath],
wait: true,
protonVerb: 'run'
Expand Down Expand Up @@ -1844,7 +1847,8 @@ function getRunnerCallWithoutCredentials(
env: Record<string, string> | NodeJS.ProcessEnv = {},
runnerPath: string
): string {
if (!Array.isArray(command)) command = commandToArgsArray(command)
if (!Array.isArray(command))
command = libraryManagerMap['legendary'].commandToArgsArray(command)

const modifiedCommand = [...command]
// Redact sensitive arguments (Authorization Code for Legendary, token for GOGDL)
Expand Down
61 changes: 32 additions & 29 deletions src/backend/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,14 @@ import { getDefaultSavePath } from './save_sync'
import { initTrayIcon } from './tray_icon/tray_icon'
import { createMainWindow, getMainWindow, isFrameless } from './main_window'

import * as GOGLibraryManager from 'backend/storeManagers/gog/library'
import {
getCyberpunkMods,
getBranchPassword,
setBranchPassword,
getGOGPlaytime,
syncQueuedPlaytimeGOG
} from 'backend/storeManagers/gog/games'
import { playtimeSyncQueue } from './storeManagers/gog/electronStores'
import * as LegendaryLibraryManager from 'backend/storeManagers/legendary/library'
import {
autoUpdate,
gameManagerMap,
initStoreManagers,
libraryManagerMap
} from './storeManagers'
import { updateWineVersionInfos } from './wine/manager/utils'
import { addNewApp } from './storeManagers/sideload/library'
import {
getGameOverride,
getGameSdl
} from 'backend/storeManagers/legendary/library'
import { backendEvents } from './backend_events'
import { configStore } from './constants/key_value_stores'
import {
Expand Down Expand Up @@ -382,7 +368,7 @@ if (!gotTheLock) {
// Make sure lock is not present when starting up
playtimeSyncQueue.delete('lock')
if (!settings.disablePlaytimeSync) {
runOnceWhenOnline(syncQueuedPlaytimeGOG)
runOnceWhenOnline(gameManagerMap['gog'].syncQueuedPlaytimeGOG)
} else {
logDebug('Skipping playtime sync queue upload - playtime sync disabled', {
prefix: LogPrefix.Backend
Expand Down Expand Up @@ -665,8 +651,12 @@ addHandler('getMaxCpus', () => cpus().length)

addHandler('getHeroicVersion', app.getVersion)
addHandler('isFullscreen', () => isSteamDeckGameMode || isCLIFullscreen)
addHandler('getGameOverride', async () => getGameOverride())
addHandler('getGameSdl', async (event, appName) => getGameSdl(appName))
addHandler('getGameOverride', async () =>
libraryManagerMap['legendary'].getGameOverride()
)
addHandler('getGameSdl', async (event, appName) =>
libraryManagerMap['legendary'].getGameSdl(appName)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if we can export each manager too, not just the hash, then we can import legendaryLibraryManager to not have to import the hash and access with a hardcoded 'legendary' string, it would make the code more declarative and easier to understand in my opinion (I know it's a small difference, but I think it will help since it's used so much throughout the app, there's many places we just have hardcoded runners)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree that this would be a good idea for now. I do have a concern: I'd like to eventually get rid of every one of these hardcoded runner-specific library manager invocations (my end goal is to dynamically enable/disable library integrations at runtime; to load them as "plugins"). Obviously exporting individual library managers would go against that goal
So, I can definitely do this for now (and if you'd like me to, I would). I just don't see it as that much of a problem right now, and given these areas will change relatively soon-ish anyways (I have a draft of this plugins idea around already) I hadn't bothered to change it yet

)

addHandler('showUpdateSetting', () => !isFlatpak)

Expand Down Expand Up @@ -714,7 +704,10 @@ addHandler('isGameAvailable', async (e, args) => {

addHandler('getGameInfo', async (event, appName, runner) => {
// Fastpath since we sometimes have to request info for a GOG game as Legendary because we don't know it's a GOG game yet
if (runner === 'legendary' && !LegendaryLibraryManager.hasGame(appName)) {
if (
runner === 'legendary' &&
!libraryManagerMap['legendary'].hasGame(appName)
) {
return null
}
const tempGameInfo = gameManagerMap[runner].getGameInfo(appName)
Expand All @@ -729,7 +722,10 @@ addHandler('getGameInfo', async (event, appName, runner) => {

addHandler('getExtraInfo', async (event, appName, runner) => {
// Fastpath since we sometimes have to request info for a GOG game as Legendary because we don't know it's a GOG game yet
if (runner === 'legendary' && !LegendaryLibraryManager.hasGame(appName)) {
if (
runner === 'legendary' &&
!libraryManagerMap['legendary'].hasGame(appName)
) {
return null
}
return gameManagerMap[runner].getExtraInfo(appName)
Expand All @@ -745,7 +741,7 @@ addHandler('getGameSettings', async (event, appName, runner) => {
})

addHandler('getGOGLinuxInstallersLangs', async (event, appName) =>
GOGLibraryManager.getLinuxInstallersLanguages(appName)
libraryManagerMap['gog'].getLinuxInstallersLanguages(appName)
)

addHandler(
Expand Down Expand Up @@ -807,7 +803,7 @@ addHandler('getAlternativeWine', async () =>
addHandler('readConfig', async (event, configClass) => {
if (configClass === 'library') {
await libraryManagerMap['legendary'].refresh()
return LegendaryLibraryManager.getListOfGames()
return libraryManagerMap['legendary'].getListOfGames()
}
const userInfo = LegendaryUser.getUserInfo()
return userInfo?.displayName ?? ''
Expand Down Expand Up @@ -864,7 +860,10 @@ if (existsSync(legendaryInstalled)) {
// decode the JSON data. So instead of immediately calling LegendaryLibrary.get().refreshInstalled(), call it only after no writes happen
// in a 500ms timespan
if (watchTimeout) clearTimeout(watchTimeout)
watchTimeout = setTimeout(LegendaryLibraryManager.refreshInstalled, 500)
watchTimeout = setTimeout(
libraryManagerMap['legendary'].refreshInstalled,
500
)
})
}

Expand Down Expand Up @@ -1066,7 +1065,7 @@ addHandler('changeInstallPath', async (event, { appName, path, runner }) => {
})

addHandler('egsSync', async (event, args) => {
return LegendaryLibraryManager.toggleGamesSync(args)
return libraryManagerMap['legendary'].toggleGamesSync(args)
})

addHandler('syncGOGSaves', async (event, gogSaves, appName, arg) =>
Expand Down Expand Up @@ -1277,7 +1276,9 @@ addListener('setTitleBarOverlay', (e, args) => {
}
})

addListener('addNewApp', (e, args) => addNewApp(args))
addListener('addNewApp', (e, args) =>
libraryManagerMap['sideload'].addNewApp(args)
)

addHandler('isNative', (e, { appName, runner }) => {
return gameManagerMap[runner].isNative(appName)
Expand Down Expand Up @@ -1325,23 +1326,25 @@ addHandler(
return
}
if (runner === 'gog') {
return getGOGPlaytime(appName)
return gameManagerMap['gog'].getGOGPlaytime(appName)
}

return
}
)

addHandler('getPrivateBranchPassword', (e, appName) =>
getBranchPassword(appName)
gameManagerMap['gog'].getBranchPassword(appName)
)
addHandler('setPrivateBranchPassword', (e, appName, password) =>
setBranchPassword(appName, password)
gameManagerMap['gog'].setBranchPassword(appName, password)
)

addHandler('getAvailableCyberpunkMods', async () => getCyberpunkMods())
addHandler('getAvailableCyberpunkMods', async () =>
gameManagerMap['gog'].getCyberpunkMods()
)
addHandler('setCyberpunkModConfig', async (e, props) =>
GOGLibraryManager.setCyberpunkModConfig(props)
libraryManagerMap['gog'].setCyberpunkModConfig(props)
)

addListener('changeGameVersionPinnedStatus', (e, appName, runner, status) => {
Expand Down
11 changes: 6 additions & 5 deletions src/backend/save_sync.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { InstalledInfo, Runner } from 'common/types'
import { GOGCloudSavesLocation, SaveFolderVariable } from 'common/types/gog'
import { getWinePath, setupWineEnvVars, verifyWinePrefix } from './launcher'
import { runRunnerCommand as runLegendaryCommand } from 'backend/storeManagers/legendary/library'
import { getSaveSyncLocation, readInfoFile } from './storeManagers/gog/library'
import { logDebug, LogPrefix, logInfo, logError, logWarning } from './logger'
import { getShellPath } from './utils'
import {
Expand Down Expand Up @@ -71,7 +69,7 @@ async function getDefaultLegendarySavePath(appName: string): Promise<string> {
}

logInfo(['Computing default save path for', appName], LogPrefix.Legendary)
await runLegendaryCommand(
await libraryManagerMap['legendary'].runRunnerCommand(
{
subcommand: 'sync-saves',
appName: LegendaryAppName.parse(appName),
Expand Down Expand Up @@ -113,7 +111,10 @@ async function getDefaultGogSavePaths(
const gameSettings = await gameManagerMap['gog'].getSettings(appName)
const installInfo = gameManagerMap['gog'].getGameInfo(appName)
.install as InstalledInfo
const gog_save_location = await getSaveSyncLocation(appName, installInfo)
const gog_save_location = await libraryManagerMap['gog'].getSaveSyncLocation(
appName,
installInfo
)

const { platform: installed_platform, install_path } = installInfo
if (!gog_save_location || !install_path) {
Expand All @@ -128,7 +129,7 @@ async function getDefaultGogSavePaths(

// If no save locations are defined, assume the default
if (!gog_save_location.length) {
const clientId = readInfoFile(appName)?.clientId
const clientId = libraryManagerMap['gog'].readInfoFile(appName)?.clientId
gog_save_location.push({
name: '__default',
location:
Expand Down
4 changes: 2 additions & 2 deletions src/backend/shortcuts/shortcuts/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { GameInfo } from 'common/types'
import { getIcon } from '../utils'
import { addNonSteamGame } from '../nonesteamgame/nonesteamgame'
import sanitize from 'sanitize-filename'
import * as GogLibraryManager from '../../storeManagers/gog/library'
import { libraryManagerMap } from 'backend/storeManagers'
import { isMac } from 'backend/constants/environment'
import { userHome } from 'backend/constants/paths'

Expand Down Expand Up @@ -79,7 +79,7 @@ Categories=Game;
}
let executable = gameInfo.install.executable
if (gameInfo.runner === 'gog') {
executable = GogLibraryManager.getExecutable(gameInfo.app_name)
executable = libraryManagerMap['gog'].getExecutable(gameInfo.app_name)
}
if (executable) {
let icon: string
Expand Down
4 changes: 2 additions & 2 deletions src/backend/shortcuts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'graceful-fs'
import { GameInfo } from 'common/types'
import { basename, dirname, extname, join } from 'path'
import { getProductApi } from 'backend/storeManagers/gog/library'
import { libraryManagerMap } from '../storeManagers'
import { downloadFile } from 'backend/utils'
import { createAbortController } from 'backend/utils/aborthandler/aborthandler'
import { heroicIconFolder as iconsFolder } from 'backend/constants/paths'
Expand Down Expand Up @@ -82,7 +82,7 @@ async function getIcon(appName: string, gameInfo: GameInfo) {
} else if (existsSync(linuxNativePath)) {
return linuxNativePath
}
const productApiData = await getProductApi(appName)
const productApiData = await libraryManagerMap['gog'].getProductApi(appName)
if (productApiData && productApiData.data.images?.icon) {
image = 'https:' + productApiData.data.images?.icon
icon = `${iconsFolder}/${appName}.png` // Allow transparency
Expand Down
Loading
Loading