Skip to content

Commit

Permalink
Better DXVK/VKD3D Handler (#719)
Browse files Browse the repository at this point in the history
* feat: better dxvk and vkd3d handling

* i18n: updated labels

* fix: error on prefix creation

* feat: add bkp Dlls function

* feat: handle backup/restore dxvk/vkd3d

* feat: refactor webview screens

* fix: do not bring heroic to front after playing

* feat: add show fps setting on mac

* chore: ReadMe + version

* chore: remove appName from general settings

* feat: add wiki and discord link to Login Page

* feat: show navbar when logged out as well

* fix: check game updates on login

* feat: add support links on navbar

* chore: styling and crypto address

* chore: update legendary to 0.20.18

* i18n: updated keys
  • Loading branch information
flavioislima authored Nov 5, 2021
1 parent a13405b commit ec854fa
Show file tree
Hide file tree
Showing 87 changed files with 641 additions and 330 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Discord](https://img.shields.io/discord/812703221789097985.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=7389D8)](https://discord.gg/rHJ2uqdquK) [![GitHub release](https://img.shields.io/github/release/Heroic-Games-Launcher/HeroicGamesLauncher.svg?label=Release)](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/releases/) ![GitHub all releases](https://img.shields.io/github/downloads/Heroic-Games-Launcher/HeroicGamesLauncher/total?label=Downloads) [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/blob/main/COPYING) [![Patreon](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dheroicgameslauncher%26type%3Dpatrons&style=flat&label=Patreon)](https://patreon.com/heroicgameslauncher) [![Donate](https://img.shields.io/badge/PayPal-Donate-blue?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyBzdHlsZT0iLW1zLXRyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKTstd2Via2l0LXRyYW5zZm9ybTpyb3RhdGUoMzYwZGVnKTt0cmFuc2Zvcm06cm90YXRlKDM2MGRlZykiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDI0IDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj48bWV0YWRhdGE+PHJkZjpSREY+PGNjOldvcmsgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PHBhdGggZD0iTTguMzIgMjEuOTdhLjU0Ni41NDYgMCAwIDEtLjI2LS4zMmMtLjAzLS4xNS0uMDYuMTEuNi00LjA5Yy42LTMuOC41OS0zLjc0LjY3LTMuODVjLjEzLS4xNy4xMS0uMTcgMS42MS0uMThjMS4zMi0uMDMgMS42LS4wMyAyLjE5LS4xMmMzLjI1LS40NSA1LjI2LTIuMzYgNS45Ni01LjY2Yy4wNC0uMjIuMDgtLjQxLjA5LS40MWMwLS4wMS4wNy4wNC4xNS4xYzEuMDMuNzggMS4zOCAyLjIyLjk5IDQuMTRjLS40NiAyLjI5LTEuNjggMy44MS0zLjU4IDQuNDZjLS44MS4yOC0xLjQ5LjM5LTIuNjkuNDJjLS44LjA0LS44Mi4wNC0xLjA1LjE5Yy0uMTcuMTctLjE2LjE0LS41NSAyLjU1Yy0uMjcgMS43LS4zNyAyLjI1LS40MSAyLjM1Yy0uMDcuMTYtLjIxLjMtLjM3LjM4bC0uMTEuMDdIMTBjLTEuMjkgMC0xLjYyIDAtMS42OC0uMDNtLTQuNS0yLjIzYy0uMTktLjEtLjMyLS4yNy0uMzItLjQ3QzMuNSAxOSA2LjExIDIuNjggNi4xOCAyLjVjLjA5LS4xOC4zMi0uMzcuNS0uNDRMNi44MyAyaDMuNTNjMy45MSAwIDMuNzYgMCA0LjY0LjJjMi42Mi41NSAzLjgyIDIuMyAzLjM3IDQuOTNjLS41IDIuOTMtMS45OCA0LjY3LTQuNSA1LjNjLS44Ny4yMS0xLjQ4LjI3LTMuMTQuMjdjLTEuMzEgMC0xLjQxLjAxLTEuNjcuMTVjLS4yNi4xNS0uNDcuNDItLjU2Ljc1Yy0uMDQuMDctLjI3IDEuNDctLjUzIDMuMWEyNDEuMyAyNDEuMyAwIDAgMC0uNDcgMy4wMmwtLjAzLjA2SDUuNjljLTEuNTggMC0xLjggMC0xLjg3LS4wNHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=)](https://www.paypal.me/heroicgl) [![kofi](https://img.shields.io/badge/Ko--Fi-Donate-orange?style=flat&logo=ko-fi)](https://ko-fi.com/heroicgames)


Heroic is an Open Source Game Launcher for Linux, Windows and MacOS (Limited to Windows games using Wine/Crossover - tested only on Intel and on 10.14).
Heroic is an Open Source Game Launcher for Linux, Windows and MacOS (Limited to Windows games using Wine/Crossover).
Right now it supports launching games from the Epic Games Store using [Legendary](https://github.com/derrod/legendary), a CLI alternative to the Epic Games Launcher.
Heroic is built with Web Technologies like: TypeScript, React, NodeJS and Electron.

Expand All @@ -17,9 +17,12 @@ Heroic is built with Web Technologies like: TypeScript, React, NodeJS and Electr
- [Language Support](#language-support)
- [Help with Translations Here](#help-with-translations-here)
- [Installation](#installation)
- [Any OS (development environment)](#any-os-development-environment)
- [Building with VS Code](#building-with-vs-code)
- [Development Using a Container](#development-using-a-container)
- [Linux](#linux)
- [Debian](#debian)
- [Debian (third party `apt` repository)](#-third-party-apt-repository)
- [Debian, Ubuntu and Derivatives](#debian-ubuntu-and-derivatives)
- [Debian (third party `apt` repository)](#debian-third-party-apt-repository)
- [Arch (AUR)](#arch-aur)
- [Fedora](#fedora)
- [Other Distributions](#other-distributions)
Expand Down
5 changes: 5 additions & 0 deletions Support.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
### Support Monthly: [Patreon Page](https://patreon.com/heroicgameslauncher)

### Buy a Cofee: [Ko-fi](https://ko-fi.com/flavioislima)

### Support with Crypto

- ETH (Main net/Polygon) and BNB (Binance Smart Chain): 0x639877c61Fa30E34AF99B435a3bAf070DF908c4d
- BTC: 1ApnwVnupgrJHuwSXVVp1tC9YoeAzDxFq1
11 changes: 10 additions & 1 deletion electron/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {

function getLegendaryBin(){
const bin = GlobalConfig?.get()?.config?.altLegendaryBin ?? `${fixAsarPath(join(__dirname, '/bin/', process.platform, isWindows ? '/legendary.exe' : '/legendary'))}`
if (bin.includes(' ')){
return `"${bin}"`
}
return bin
}

Expand Down Expand Up @@ -43,7 +46,10 @@ const heroicGithubURL =
const supportURL =
'https://github.com/flavioislima/HeroicGamesLauncher/blob/main/Support.md'
const discordLink = 'https://discord.gg/rHJ2uqdquK'
const wikiLink = 'https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/wiki'
const weblateUrl = 'https://hosted.weblate.org/projects/heroic-games-launcher'
const kofiPage = 'https://ko-fi.com/heroicgames'
const patreonPage = 'https://www.patreon.com/heroicgameslauncher'

/**
* Get shell for different os
Expand Down Expand Up @@ -99,6 +105,7 @@ export {
heroicInstallPath,
heroicToolsPath,
home,
kofiPage,
icon,
iconDark,
iconLight,
Expand All @@ -109,8 +116,10 @@ export {
legendaryConfigPath,
libraryPath,
loginUrl,
patreonPage,
sidInfoUrl,
supportURL,
userInfo,
weblateUrl
weblateUrl,
wikiLink
}
154 changes: 103 additions & 51 deletions electron/dxvk.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as axios from 'axios'
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { exec } from 'child_process'
import { exec, spawn } from 'child_process'
import {
existsSync,
readFileSync
readFileSync,
readdirSync
} from 'graceful-fs'

import { execAsync, isOnline } from './utils'
Expand All @@ -20,63 +21,74 @@ export const DXVK = {
logWarning('App offline, skipping possible DXVK update.')
return
}
const {
data: { assets }
} = await axios.default.get(
'https://api.github.com/repos/lutris/dxvk/releases/latest'
)
const current = assets[0]
const pkg = current.name
const name = pkg.replace('.tar.gz', '')
const downloadUrl = current.browser_download_url

const dxvkLatest = `${heroicToolsPath}/DXVK/${pkg}`
const pastVersionCheck = `${heroicToolsPath}/DXVK/latest_dxvk`
let pastVersion = ''

if (existsSync(pastVersionCheck)) {
pastVersion = readFileSync(pastVersionCheck).toString().split('\n')[0]
}

if (pastVersion === name) {
return
}
const tools = [
{ name: 'vkd3d',
url: 'https://api.github.com/repos/HansKristian-Work/vkd3d-proton/releases/latest',
extractCommand: 'tar -I zstd -xvf'
},
{ name: 'dxvk',
url: 'https://api.github.com/repos/doitsujin/dxvk/releases/latest',
extractCommand: 'tar -zxf'
}
]

tools.forEach(async (tool) => {
const {
data: { assets }
} = await axios.default.get(tool.url)

const {name, browser_download_url: downloadUrl} = assets[0]
const pkg = name.replace('.tar.gz', '').replace('.tar.zst', '')

const latestVersion = `${heroicToolsPath}/${tool.name}/${name}`
const pastVersionCheck = `${heroicToolsPath}/${tool.name}/latest_${tool.name}`
let pastVersion = ''
if (existsSync(pastVersionCheck)) {
pastVersion = readFileSync(pastVersionCheck).toString().split('\n')[0]
}

if (pastVersion === pkg) {
return
}
const downloadCommand = `curl -L ${downloadUrl} -o ${latestVersion} --create-dirs`
const extractCommand = `${tool.extractCommand} ${latestVersion} -C ${heroicToolsPath}/${tool.name}`
const echoCommand = `echo ${pkg} > ${heroicToolsPath}/${tool.name}/latest_${tool.name}`
const cleanCommand = `rm ${latestVersion}`

logInfo(`Updating ${tool.name} to:`, pkg)

return execAsync(downloadCommand)
.then(async () => {
logInfo(`downloaded ${tool.name}`)
logInfo(`extracting ${tool.name}`)
exec(echoCommand)
await execAsync(extractCommand)
logInfo(`extracting ${tool.name} updated!`)
exec(cleanCommand)
})
.catch((error) => logError(`Error when downloading ${tool.name}`, error))
})


const downloadCommand = `curl -L ${downloadUrl} -o ${dxvkLatest} --create-dirs`
const extractCommand = `tar -zxf ${dxvkLatest} -C ${heroicToolsPath}/DXVK/`
const echoCommand = `echo ${name} > ${heroicToolsPath}/DXVK/latest_dxvk`
const cleanCommand = `rm ${dxvkLatest}`

logInfo('Updating DXVK to:', name)

return execAsync(downloadCommand)
.then(async () => {
logInfo('downloaded DXVK')
logInfo('extracting DXVK')
exec(echoCommand)
await execAsync(extractCommand)
logInfo('DXVK updated!')
exec(cleanCommand)
})
.catch(() => logError('Error when downloading DXVK'))
},

install: async (prefix: string) => {
installRemove: async (prefix: string, tool: 'dxvk' | 'vkd3d', action: 'backup' | 'restore') => {
if (!prefix) {
return
}
const winePrefix = prefix.replace('~', home)

if (!existsSync(`${heroicToolsPath}/DXVK/latest_dxvk`)) {
if (!existsSync(`${heroicToolsPath}/${tool}/latest_${tool}`)) {
logError('dxvk not found!')
await DXVK.getLatest()
}

const globalVersion = readFileSync(`${heroicToolsPath}/DXVK/latest_dxvk`)
const globalVersion = readFileSync(`${heroicToolsPath}/${tool}/latest_${tool}`)
.toString()
.split('\n')[0]
const dxvkPath = `${heroicToolsPath}/DXVK/${globalVersion}/`
const currentVersionCheck = `${winePrefix}/current_dxvk`
const toolPath = `${heroicToolsPath}/${tool}/${globalVersion}`
const currentVersionCheck = `${winePrefix}/current_${tool}`
let currentVersion = ''

if (existsSync(currentVersionCheck)) {
Expand All @@ -85,21 +97,61 @@ export const DXVK = {
.split('\n')[0]
}

const x32Path = `${winePrefix}/drive_c/windows/system32/`
const x64Path = `${winePrefix}/drive_c/windows/syswow64/`
const x86Fix = tool === 'vkd3d' ? 'x86' : 'x32'

const installCommand = `ln -sf ${toolPath}/${x86Fix}/* ${x32Path} && ln -sf ${toolPath}/x64/* ${x64Path}`
const updatedVersionfile = `echo '${globalVersion}' > ${currentVersionCheck}`

const filesToBkpx32= readdirSync(`${toolPath}/${x86Fix}`)
const filesToBkpx64= readdirSync(`${toolPath}/x64/`)

logInfo(`${action === 'backup' ? 'Backuping' : 'Restoring'} original DLLs`)
backupRestoreDLLs(filesToBkpx32, x32Path, action)
backupRestoreDLLs(filesToBkpx64, x64Path, action)

if(action === 'restore'){
logInfo(`Removing ${tool} version information`)
const updatedVersionfile = `rm -rf ${currentVersionCheck}`
exec(updatedVersionfile)
return logInfo(`Removed ${tool} from`, prefix)
}

if (currentVersion === globalVersion) {
return
}

const installCommand = `WINEPREFIX='${winePrefix}' bash ${dxvkPath}setup_dxvk.sh install`
const echoCommand = `echo '${globalVersion}' > ${currentVersionCheck}`

logInfo(`installing DXVK on...`, installCommand)
await execAsync(`WINEPREFIX='${winePrefix}' wineboot`)
logInfo(`installing ${tool} on...`, prefix)
await execAsync(installCommand, { shell: '/bin/bash' })
.then(() => exec(echoCommand))
.catch(() =>
.then(() => exec(updatedVersionfile))
.catch((error) => {
logError(error)
logError(
'error when installing DXVK, please try launching the game again'
)
}
)
}
}

export function backupRestoreDLLs(filesToHandle: Array<string>, path: string, action: 'backup' | 'restore'){
if (action === 'backup'){
return filesToHandle.forEach(file => {
const filePath = `${path}/${file}`
if (existsSync(filePath)){
spawn('mv', [filePath, `${filePath}.bkp`])
}
})
}

return filesToHandle.forEach(file => {
const filePath = `${path}/${file}`
if (existsSync(filePath)){
spawn('rm', ['-rf', `${filePath}`])
spawn('mv', [`${filePath}.bkp`, `${filePath}`])
}
})


}
32 changes: 22 additions & 10 deletions electron/legendary/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from 'graceful-fs'
import axios from 'axios';

import { BrowserWindow, app, shell } from 'electron';
import { app, shell } from 'electron';
import { DXVK } from '../dxvk'
import { ExtraInfo, GameStatus, InstallArgs } from '../types';
import { Game } from '../games';
Expand Down Expand Up @@ -447,7 +447,6 @@ Categories=Game;
}

public async launch(launchArguments?: string) {
const mainWindow = BrowserWindow.getAllWindows()[0]
const isOffline = !(await isOnline())
let envVars = ''
let gameMode: string
Expand Down Expand Up @@ -534,7 +533,9 @@ Categories=Game;
const isCrossover =
wineVersion.name.includes('CrossOver')
prefix = (isProton || isCrossover) ? '' : prefix

const x = wineVersion.bin.split('/')
x.pop()
const winePath = x.join('/').replaceAll("'", '')
const options = {
audio: audioFix ? `PULSE_LATENCY_MSEC=60` : '',
crossoverBottle: (isCrossover && wineCrossoverBottle != '') ? `CX_BOTTLE=${wineCrossoverBottle}` : '' ,
Expand Down Expand Up @@ -564,15 +565,12 @@ Categories=Game;
)
}

// Proton doesn't create a prefix folder so this is a workaround
if (isProton && !existsSync(fixedWinePrefix)) {
const command = `mkdir '${fixedWinePrefix}' -p`
await execAsync(command, execOptions)
}
await this.createNewPrefix(isProton, fixedWinePrefix, winePath);

// Install DXVK for non Proton/CrossOver Prefixes
if (!isProton && !isCrossover && autoInstallDxvk) {
await DXVK.install(winePrefix)
await DXVK.installRemove(winePrefix, 'dxvk', 'backup')
await DXVK.installRemove(winePrefix, 'vkd3d', 'backup')
}

if (wineVersion.name !== 'Wine Default') {
Expand All @@ -593,7 +591,6 @@ Categories=Game;
logInfo('\n Launch Command:', command)
const v = await execAsync(command, execOptions).then((v) => {
this.state.status = 'playing'
mainWindow.show()
return v
})

Expand All @@ -604,6 +601,21 @@ Categories=Game;
return v
}

private async createNewPrefix(isProton: boolean, fixedWinePrefix: string, winePath: string) {
if (isProton && !existsSync(fixedWinePrefix)) {
const command = `mkdir '${fixedWinePrefix}' -p`;
await execAsync(command, execOptions);
}

if (!existsSync(fixedWinePrefix)) {
const initPrefixCommand = `WINEPREFIX='${fixedWinePrefix}' '${winePath}/wineboot' -i && '${winePath}/wineserver' --wait`;
logInfo('creating new prefix', fixedWinePrefix)
return execAsync(initPrefixCommand)
.then(() => logInfo('Prefix created succesfuly!'))
.catch((error) => logError(error))
}
}

public async stop() {
// until the legendary bug gets fixed, kill legendary on mac
// not a perfect solution but it's the only choice for now
Expand Down
Loading

0 comments on commit ec854fa

Please sign in to comment.