Skip to content
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

[General] Beta-Feature: GOG implementation #872

Merged
merged 64 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
d9177fa
Fix wine prefixes not being created
imLinguin Dec 31, 2021
3586733
Merge branch 'main' into main
Jan 2, 2022
a690bcc
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 2, 2022
dc1fbb0
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 5, 2022
ee0bdd6
Add sidebar GOG button temporarly. Handle login and library sync
imLinguin Jan 5, 2022
1c225d3
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 6, 2022
df1bf00
Update with latest changes
imLinguin Jan 6, 2022
8701177
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 7, 2022
24f6803
Update to latest fixes
imLinguin Jan 7, 2022
cdfcc75
Getting images using gamesdb, displaying library
imLinguin Jan 7, 2022
e19b473
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 8, 2022
45cf279
Pull from upstream
imLinguin Jan 8, 2022
eda57b1
Add gog API endpoint, display requirements
imLinguin Jan 8, 2022
da125a8
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 10, 2022
e1d66ec
Pull latest changes
imLinguin Jan 10, 2022
c9c8e9c
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 14, 2022
16c6c3f
Fetch gamepad update
imLinguin Jan 14, 2022
69253f6
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 17, 2022
c69b558
Pull latest main
imLinguin Jan 17, 2022
9639af7
Basic heroic-gogdl integration (installing, canceling)
imLinguin Jan 22, 2022
ce22792
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 25, 2022
c3b9dc5
Pull latest changes
imLinguin Jan 25, 2022
5479aae
Update logging to new naming
imLinguin Jan 25, 2022
527b03f
Launching GOG games, move launching to one unified function
imLinguin Jan 29, 2022
e1b7c2a
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Jan 29, 2022
67a36e0
Merge from upstream
imLinguin Jan 29, 2022
6d40928
Fix not enough space issue and library state after authenticating
imLinguin Jan 29, 2022
acf593a
Uninstalling, Repairing
imLinguin Feb 1, 2022
a54bb9c
Fix gogdl not found in bundled app
imLinguin Feb 1, 2022
98d9ea2
Make login look like in Galaxy client
imLinguin Feb 2, 2022
ae304cd
Update gogdl, Add moving games
imLinguin Feb 4, 2022
a40d27d
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Feb 4, 2022
5a92f80
Pull latest changes from upstream
imLinguin Feb 4, 2022
0f0bd05
Add language select in InstallModal, allow importing games
imLinguin Feb 6, 2022
50a3bfb
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Feb 6, 2022
5d72a38
Pull latest upstream
imLinguin Feb 6, 2022
cc5840e
Fix linting
imLinguin Feb 6, 2022
e72ec0e
Load images and game descriptions from gamesdb
imLinguin Feb 7, 2022
f8e49fe
Update gogdl, fix removing multiple games from installed config
imLinguin Feb 8, 2022
c214065
Updating, changing install path, prevent convertToString from failing
imLinguin Feb 9, 2022
c84bb04
Add mac and windows gogdl builds, support for most known setup instru…
imLinguin Feb 13, 2022
cdf416f
Login manager gog warnings get gog shortcut icons
imLinguin Feb 15, 2022
977097d
Support libraries with more than 100 games :fearful:
imLinguin Feb 16, 2022
5d70d75
Linux native titles support (without updating yet)
imLinguin Feb 16, 2022
3a76ece
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Feb 17, 2022
0dc649b
Update gogdl, add external login for epic, add runner indicator, merge
imLinguin Feb 17, 2022
108472d
Display language options for linux installers, update linux natives
imLinguin Feb 18, 2022
8db0cff
Merge branch 'main' into gog_support
imLinguin Feb 18, 2022
d11a6c8
Resolve review suggestions
imLinguin Feb 18, 2022
c2d44f6
MacOS fixes
imLinguin Feb 19, 2022
22edb9a
[Fix] Windows titles not launching on MacOS
imLinguin Feb 20, 2022
ec7ccea
Review changes, generate i18n keys
imLinguin Feb 20, 2022
04cf669
Merge branch 'Heroic-Games-Launcher:main' into main
imLinguin Feb 20, 2022
6789888
Merge with upstream main
imLinguin Feb 20, 2022
1db4088
[Fix] Linux native games not installing correctly
imLinguin Feb 20, 2022
209490d
Add use Steam runtime setting, fix installed state not refreshing (pr…
imLinguin Feb 21, 2022
65af599
Generate i18n keys
imLinguin Feb 21, 2022
4e5f347
Better importing,Linux DLC support,minor bugs fix
imLinguin Feb 22, 2022
38daf84
Fix merge conflicts
imLinguin Feb 22, 2022
a5a5420
Hide Linux platform filter for non-GOG games
imLinguin Feb 22, 2022
e88b8e0
Pull upstream
imLinguin Feb 22, 2022
4361f59
Fix errors on Windows, make shortcuts on windows display Primary task…
imLinguin Feb 22, 2022
1cd11b1
Apply review suggestions
imLinguin Feb 23, 2022
f1cbab0
Resolve Branch out-of date
imLinguin Feb 23, 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
29 changes: 21 additions & 8 deletions electron/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,29 @@ class GOGGame extends Game {
}
public async getExtraInfo(namespace: string): Promise<ExtraInfo> {
const gameInfo = GOGLibrary.get().getGameInfo(this.appName)
let targetPlatform: 'windows' | 'osx' | 'linux' = 'windows'
switch (process.platform) {
Copy link
Collaborator

@arielj arielj Feb 23, 2022

Choose a reason for hiding this comment

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

maybe we can use the isWindows isMac and isLinux from constants?

like:

let targetPlatform: 'windows' | 'osx' | 'linux' = 'windows'

if (isMac && gameInfo.is_mac_native) {
  targetPlatform = 'osx'
}
if (isLinux && gameInfo.is_linux_native) {
  targetPlatform = 'linux'
}

case 'darwin': {
if (gameInfo.is_mac_native) {
targetPlatform = 'osx'
}
break
}
case 'linux': {
if (gameInfo.is_linux_native) {
targetPlatform = 'linux'
}
break
}
default: {
targetPlatform = 'windows'
break
}
}

const extra: ExtraInfo = {
about: gameInfo.extra.about,
reqs: await GOGLibrary.get().createReqsArray(
this.appName,
gameInfo.is_mac_native && process.platform == 'darwin'
? 'osx'
: gameInfo.is_linux_native && process.platform == 'linux'
? 'linux'
: 'windows'
)
reqs: await GOGLibrary.get().createReqsArray(this.appName, targetPlatform)
}
return extra
}
Expand Down
18 changes: 2 additions & 16 deletions electron/gog/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,6 @@ export class GOGLibrary {
}
}
}
const movies = await axios
.get(
'https://embed.gog.com/account/getFilteredProducts?mediaType=2&totalPages=1&sortBy=title',
{ headers: headers }
)
.catch((e: AxiosError) => {
logError(
['There was an error getting movies library data', e.message],
LogPrefix.Gog
)
return null
})

if (games) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe the else part of this can be moved right after the const games = await axios call in line 65? I personally feel a quick exist/guard check is clearer than having if/elses

something like

    const games = await axios
      .get(
        'https://embed.gog.com/account/getFilteredProducts?mediaType=1&sortBy=title',
        { headers }
      )
      .catch((e: AxiosError) => {
        logError(
          ['There was an error getting games library data', e.message],
          LogPrefix.Gog
        )
        return null
This conversation was marked as resolved by flavioislima
 Show conversation
      })

    if (!games) {
      logError('There was an error Loading games library', LogPrefix.Gog)
      return
    }

    if (games?.data?....) {
      ...
    }

    const gamesObjects: gameInfo[] = []
    ....

just a suggestion though, I think it's easier to reason when we can remove nesting

const gamesObjects: GameInfo[] = []
Expand Down Expand Up @@ -125,10 +113,8 @@ export class GOGLibrary {
libraryStore.set('totalGames', games.data.totalProducts)
libraryStore.set('totalMovies', games.data.moviesCount)
logInfo('Saved games data', LogPrefix.Gog)
}
if (movies) {
libraryStore.set('movies', movies.data.products)
logInfo('Saved movies data', LogPrefix.Gog)
} else {
logError('There was an error Loading games library', LogPrefix.Gog)
}
}

Expand Down
2 changes: 1 addition & 1 deletion electron/gog/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function setup(appName: string): Promise<void> {
const isCrossover = gameSettings.wineVersion.name.includes('CrossOver')
const crossoverBottle = gameSettings.wineCrossoverBottle
const crossoverEnv =
isCrossover && crossoverBottle != '' ? `CX_BOTTLE=${crossoverBottle}` : ''
isCrossover && crossoverBottle ? `CX_BOTTLE=${crossoverBottle}` : ''
const isProton =
gameSettings.wineVersion.name.includes('Proton') ||
gameSettings.wineVersion.name.includes('Steam')
Expand Down
41 changes: 7 additions & 34 deletions electron/gog/user.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,24 @@
import axios from 'axios'
import Store from 'electron-store'
import { BrowserWindow } from 'electron'
import { logError, logInfo, LogPrefix } from '../logger/logger'
import { gogLoginUrl } from '../constants'
import { GOGLoginData } from '../types'

const configStore = new Store({
cwd: 'gog_store'
})

const gogEmbedRegExp = new RegExp('https://embed.gog.com/on_login_success?')
const gogAuthenticateUrl =
'https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&code='
Copy link
Collaborator

Choose a reason for hiding this comment

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

is it ok that there's client_id and secret hardcoded like this? maybe it's needed but it looks suspicious to me, just checking (maybe this is the only way)

Copy link
Member Author

Choose a reason for hiding this comment

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

This is GOG Galaxy's client_id nothing to worry about

const gogRefreshTokenUrl =
'https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=refresh_token'

export class GOGUser {
// This is executed below in edge error cases most likely won't run ever
public static async handleGOGLogin() {
const popupWindow = new BrowserWindow({
width: 450,
height: 700,
title: 'GOG'
})
popupWindow.loadURL(gogLoginUrl)
popupWindow.once('ready-to-show', popupWindow.show)

popupWindow.webContents.on('did-finish-load', () => {
const pageUrl = popupWindow.webContents.getURL()
if (pageUrl.match(gogEmbedRegExp)) {
const parsedURL = new URL(pageUrl)
const code = parsedURL.searchParams.get('code')
this.login(code)
popupWindow.close()
}
})
}

static async login(code: string) {
logInfo('Logging using GOG credentials', LogPrefix.Gog)

// Gets token from GOG basaed on authorization code
const response = await axios
.get(
`https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&code=${code}`
)
.get(gogAuthenticateUrl + code)
.catch((error) => {
// Handle fetching error
logError(['Failed to get access_token', `${error}`], LogPrefix.Gog)
Expand All @@ -57,7 +33,7 @@ export class GOGUser {
data.loginTime = Date.now()
configStore.set('credentials', data)
logInfo('Login Successful', LogPrefix.Gog)
this.getUserDetails()
await this.getUserDetails()
return { status: 'done' }
}

Expand Down Expand Up @@ -96,7 +72,6 @@ export class GOGUser {
'Error with refreshing token, reauth required',
LogPrefix.Gog
)
this.handleGOGLogin()
return null
})

Expand All @@ -110,7 +85,6 @@ export class GOGUser {
logInfo('Token refreshed successfully', LogPrefix.Gog)
} else {
logError('No credentials, auth required', LogPrefix.Gog)
await this.handleGOGLogin()
}
}

Expand All @@ -124,10 +98,9 @@ export class GOGUser {
}
public static logout() {
const libraryStore = new Store({ cwd: 'gog_store', name: 'library' })
configStore.delete('credentials')
configStore.delete('userData')
libraryStore.delete('games')
libraryStore.delete('movies')
configStore.clear()
libraryStore.clear()
logInfo('Logging user out', LogPrefix.Gog)
}

public static isLoggedIn() {
Expand Down
19 changes: 13 additions & 6 deletions electron/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,19 @@ async function launch(
} ${launcherArgs}`
logInfo(['Launch Command:', command], LogPrefix.Legendary)
} else if (runner == 'gog') {
// MangoHud,Gamemode, nvidia prime can be used in native titles
command = `${mangohud} ${runWithGameMode} ${
nvidiaPrime
? 'DRI_PRIME=1 __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia'
: ''
} ${audioFix ? `PULSE_LATENCY_MSEC=60` : ''} ${gogdlBin} launch "${
// MangoHud,Gamemode, nvidia prime, audio fix can be used in Linux native titles
if (isLinux) {
const options = [
mangohud,
runWithGameMode,
nvidiaPrime
? 'DRI_PRIME=1 __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia'
: '',
audioFix ? `PULSE_LATENCY_MSEC=60` : ''
]
envVars = options.join(' ')
}
command = `${envVars} ${gogdlBin} launch "${
gameInfo.install.install_path
}" ${gameInfo.app_name} --platform=${gameInfo.install.platform} ${
launchArguments ?? ''
Expand Down
2 changes: 1 addition & 1 deletion electron/legendary/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export class LegendaryUser {
})
child.on('close', () => {
logInfo('finished login', LogPrefix.Legendary)
res('finished')
this.getUserInfo().then(() => res('finished'))
})
})
}
Expand Down
25 changes: 18 additions & 7 deletions electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,11 @@ ipcMain.on('resetHeroic', async () => {
}
})

ipcMain.handle('authGOG', (event, code) => GOGUser.login(code))
ipcMain.handle('authGOG', (event, code) =>
GOGUser.login(code).then(() =>
mainWindow.webContents.send('updateLoginState')
)
)

ipcMain.on('createNewWindow', (e, url) =>
new BrowserWindow({ height: 700, width: 1200 }).loadURL(url)
Expand Down Expand Up @@ -613,7 +617,12 @@ ipcMain.handle('getUserInfo', async () => await LegendaryUser.getUserInfo())
// Checks if the user have logged in with Legendary already
ipcMain.handle('isLoggedIn', async () => await LegendaryUser.isLoggedIn())

ipcMain.handle('login', async (event, sid) => await LegendaryUser.login(sid))
ipcMain.handle('login', async (event, sid) =>
LegendaryUser.login(sid).then((value) => {
mainWindow.webContents.send('updateLoginState')
return value
})
)

ipcMain.handle('logoutLegendary', async () => await LegendaryUser.logout())
ipcMain.handle('logoutGOG', async () => GOGUser.logout())
Expand Down Expand Up @@ -902,13 +911,15 @@ ipcMain.handle('install', async (event, params) => {
})

ipcMain.handle('uninstall', async (event, args) => {
const title = (await Game.get(args[0], args[2]).getGameInfo()).title
const winePrefix = (await Game.get(args[0], args[2]).getSettings()).winePrefix
const [appName, shouldRemovePrefix, runner] = args

return Game.get(args[0], args[2])
const title = (await Game.get(appName, runner).getGameInfo()).title
const winePrefix = (await Game.get(appName, runner).getSettings()).winePrefix

return Game.get(appName, runner)
.uninstall()
.then(() => {
if (args[1]) {
if (shouldRemovePrefix) {
logInfo(`Removing prefix ${winePrefix}`)
if (existsSync(winePrefix)) {
// remove prefix if exists
Expand Down Expand Up @@ -1006,7 +1017,7 @@ ipcMain.handle('importGame', async (event, args) => {
})

ipcMain.handle('updateGame', async (e, game, runner) => {
if (!(await isOnline()) && runner == 'legendary') {
if (!(await isOnline())) {
logWarning(
`App offline, skipping install for game '${game}'.`,
LogPrefix.Backend
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@
"@fortawesome/react-fontawesome": "^0.1.16",
"@mui/icons-material": "^5.3.1",
"@mui/material": "^5.4.0",
"@types/ini": "^1.3.31",
"axios": "^0.21.1",
"classnames": "^2.2.6",
"discord-rich-presence-typescript": "^0.0.8",
Expand Down Expand Up @@ -175,6 +174,7 @@
"@testing-library/user-event": "^13.1.9",
"@types/classnames": "^2.2.11",
"@types/i18next-fs-backend": "^1.0.0",
"@types/ini": "^1.3.31",
"@types/jest": "^26.0.23",
"@types/node": "^17.0.10",
"@types/plist": "^3.0.2",
Expand Down Expand Up @@ -207,4 +207,4 @@
"last 1 chrome version"
]
}
}
}
1 change: 1 addition & 0 deletions public/locales/bg/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"downloadSize": "Размер за сваляне",
"firstPlayed": "Пусната за пръв път",
"installSize": "Размер на инсталацията",
"language": "Language",
"lastPlayed": "Последно пускане",
"neverPlayed": "Никога",
"totalPlayed": "Изиграно време"
Expand Down
6 changes: 1 addition & 5 deletions public/locales/bg/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
"button": {
"login": "Вход"
},
"input": {
"placeholder": "Въведете своя SID тук"
},
"message": {
"part1": "За да можете да влезете и инсталирате игрите си, трябва първо да изпълните следните стъпки:",
"part2": "",
Expand All @@ -17,8 +14,7 @@
},
"status": {
"error": "Грешка",
"loading": "Зареждане на списъка с игри. Моля, изчакайте",
"logging": "Влизане…"
"loading": "Зареждане на списъка с игри. Моля, изчакайте"
},
"welcome": "Добре дошли!"
}
7 changes: 4 additions & 3 deletions public/locales/bg/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"yes": "ДА"
},
"button": {
"continue": "Continue",
"login": "Вход",
"sync": "Синхронизиране",
"syncing": "Синхронизиране",
Expand All @@ -100,6 +101,7 @@
},
"Filter": "Филтриране",
"globalSettings": "Глобални настройки",
"GOG": "GOG",
"help": {
"general": "Синхронизирайте с EGS, в случай че вече имате работеща инсталация на Epic Games Store и искате да внесете игрите си от там, за да избегнете повторното им сваляне.",
"other": {
Expand Down Expand Up @@ -137,8 +139,7 @@
"website": "Зареждане на уеб сайта"
},
"login": {
"loginWithEpic": "Вход чрез Epic",
"loginWithSid": "Login with SID"
"externalLogin": "External Login"
},
"message": {
"sync": "Синхронизацията е завършена",
Expand Down Expand Up @@ -270,7 +271,6 @@
},
"Settings": "Настройки",
"status": {
"loading": "Зареждане на списъка с игри. Моля, изчакайте",
"logging": "Влизане…"
},
"store": "Магазин",
Expand Down Expand Up @@ -308,6 +308,7 @@
"discord": "Дискорд",
"logout": "Излизане от акаунта",
"logout_confirmation": "Наистина ли искате да излезете от акаунта си?",
"manageaccounts": "Manage Accounts",
"quit": "Изход"
},
"wiki": "Уики",
Expand Down
1 change: 1 addition & 0 deletions public/locales/ca/gamepage.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"downloadSize": "Mida de la descàrrega",
"firstPlayed": "Jugat primer",
"installSize": "Mida d'instal·lació",
"language": "Language",
"lastPlayed": "Jugat abans",
"neverPlayed": "Mai jugat",
"totalPlayed": "Temps jugat"
Expand Down
6 changes: 1 addition & 5 deletions public/locales/ca/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
"button": {
"login": "Iniciar sessió"
},
"input": {
"placeholder": "Enganxa l'SID aquí"
},
"message": {
"part1": "Per iniciar sessió i començar a instal·lar els teus jocs, primer has de seguir les passes d'aquí avall:",
"part2": "Obrir",
Expand All @@ -17,8 +14,7 @@
},
"status": {
"error": "Error",
"loading": "Cargant la llista de jocs, si us plau esperi",
"logging": "Iniciant sessió…"
"loading": "Cargant la llista de jocs, si us plau esperi"
},
"welcome": "Benvingut/da!"
}
Loading