Skip to content

Commit

Permalink
[UX] UMU used by default if linux and proton, not exprimental anymore…
Browse files Browse the repository at this point in the history
…, allow disabling as needed (#4175)

* UMU: not experimental anymore, allow disabling game-by-game

* fix i18n

* Disable UMU setting in advanced tab
  • Loading branch information
arielj authored Dec 15, 2024
1 parent 8e8e805 commit 2f428e7
Show file tree
Hide file tree
Showing 15 changed files with 81 additions and 77 deletions.
4 changes: 2 additions & 2 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@
"disable_controller": "Disable Heroic navigation using controller",
"disable_logs": "Disable Logs",
"disablePlaytimeSync": "Disable playtime synchronization",
"disableUMU": "Disable UMU",
"discordRPC": "Enable Discord Rich Presence",
"download-no-https": "Download games without HTTPS (useful for CDNs e.g. LanCache)",
"dxvkfpslimit": "Limit FPS (DX9, 10 and 11)",
Expand Down Expand Up @@ -635,8 +636,7 @@
"experimental_features": {
"cometSupport": "Comet support",
"enableHelp": "Help component",
"enableNewDesign": "New design",
"umuSupport": "Use UMU as Proton runtime"
"enableNewDesign": "New design"
},
"frameless-window": {
"confirmation": {
Expand Down
3 changes: 2 additions & 1 deletion src/backend/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ class GlobalConfigV0 extends GlobalConfig {
battlEyeRuntime: isLinux,
framelessWindow: false,
beforeLaunchScriptPath: '',
afterLaunchScriptPath: ''
afterLaunchScriptPath: '',
disableUMU: false
}
// @ts-expect-error TODO: We need to settle on *one* place to define settings defaults
return settings
Expand Down
10 changes: 5 additions & 5 deletions src/backend/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ async function prepareLaunch(
}

if (
(await isUmuSupported(gameSettings.wineVersion.type, false)) &&
(await isUmuSupported(gameSettings, false)) &&
isOnline() &&
!(await isInstalled('umu')) &&
(await getUmuPath()) === defaultUmuPath
Expand All @@ -246,7 +246,7 @@ async function prepareLaunch(
const shouldUseRuntime =
gameSettings.useSteamRuntime &&
(isNative ||
(!(await isUmuSupported(gameSettings.wineVersion.type)) &&
(!(await isUmuSupported(gameSettings)) &&
gameSettings.wineVersion.type === 'proton'))

if (shouldUseRuntime) {
Expand Down Expand Up @@ -819,7 +819,7 @@ export async function verifyWinePrefix(
return { res: { stdout: '', stderr: '' } }
}

if (!existsSync(winePrefix) && !(await isUmuSupported(wineVersion.type))) {
if (!existsSync(winePrefix) && !(await isUmuSupported(settings))) {
mkdirSync(winePrefix, { recursive: true })
}

Expand All @@ -831,7 +831,7 @@ export async function verifyWinePrefix(
const haveToWait = !existsSync(systemRegPath)

const command = runWineCommand({
commandParts: (await isUmuSupported(wineVersion.type))
commandParts: (await isUmuSupported(settings))
? ['createprefix']
: ['wineboot', '--init'],
wait: haveToWait,
Expand Down Expand Up @@ -926,7 +926,7 @@ async function runWineCommand({
}

const wineBin = wineVersion.bin.replaceAll("'", '')
const umuSupported = await isUmuSupported(wineVersion.type)
const umuSupported = await isUmuSupported(settings)
const runnerBin = umuSupported ? await getUmuPath() : wineBin

if (wineVersion.type === 'proton' && !umuSupported) {
Expand Down
12 changes: 2 additions & 10 deletions src/backend/storeManagers/gog/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,22 +576,14 @@ export async function launch(
...wineEnvVars
}

const { bin: wineExec, type: wineType } = gameSettings.wineVersion

if (await isUmuSupported(wineType)) {
if (await isUmuSupported(gameSettings)) {
const umuId = await getUmuId(gameInfo.app_name, gameInfo.runner)
if (umuId) {
commandEnv['GAMEID'] = umuId
}
}

// Fix for people with old config
const wineBin =
wineExec.startsWith("'") && wineExec.endsWith("'")
? wineExec.replaceAll("'", '')
: wineExec

wineFlag = await getWineFlagsArray(wineBin, wineType, shlex.join(wrappers))
wineFlag = await getWineFlagsArray(gameSettings, shlex.join(wrappers))
}

const commandParts = [
Expand Down
11 changes: 2 additions & 9 deletions src/backend/storeManagers/legendary/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,21 +912,14 @@ export async function launch(
...wineEnvVars
}

const { bin: wineExec, type: wineType } = gameSettings.wineVersion

if (await isUmuSupported(wineType)) {
if (await isUmuSupported(gameSettings)) {
const umuId = await getUmuId(gameInfo.app_name, gameInfo.runner)
if (umuId) {
commandEnv['GAMEID'] = umuId
}
}
// Fix for people with old config
const wineBin =
wineExec.startsWith("'") && wineExec.endsWith("'")
? wineExec.replaceAll("'", '')
: wineExec

wineFlags = await getWineFlags(wineBin, wineType, shlex.join(wrappers))
wineFlags = await getWineFlags(gameSettings, shlex.join(wrappers))
}

const appNameToLaunch =
Expand Down
12 changes: 2 additions & 10 deletions src/backend/storeManagers/nile/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,23 +385,15 @@ export async function launch(
...wineEnvVars
}

const { bin: wineExec, type: wineType } = gameSettings.wineVersion

if (await isUmuSupported(wineType)) {
if (await isUmuSupported(gameSettings)) {
const umuId = await getUmuId(gameInfo.app_name, gameInfo.runner)
if (umuId) {
commandEnv['GAMEID'] = umuId
}
}

// Fix for people with old config
const wineBin =
wineExec.startsWith("'") && wineExec.endsWith("'")
? wineExec.replaceAll("'", '')
: wineExec

wineFlag = [
...(await getWineFlagsArray(wineBin, wineType, shlex.join(wrappers))),
...(await getWineFlagsArray(gameSettings, shlex.join(wrappers))),
'--wine-prefix',
gameSettings.winePrefix
]
Expand Down
10 changes: 3 additions & 7 deletions src/backend/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,6 @@ export const Winetricks = {
const gameSettings = await gameManagerMap[runner].getSettings(appName)

const { wineVersion } = gameSettings
const baseWinePrefix = gameSettings.winePrefix

if (!(await validWine(wineVersion))) {
return
Expand All @@ -533,7 +532,7 @@ export const Winetricks = {
await Winetricks.download()
}

if (await isUmuSupported(wineVersion.type)) {
if (await isUmuSupported(gameSettings)) {
winetricks = await getUmuPath()

if (args.includes('-q')) {
Expand All @@ -549,7 +548,7 @@ export const Winetricks = {
}

const { winePrefix, wineVersion: alwaysWine_wineVersion } =
await getWineFromProton(wineVersion, baseWinePrefix)
await getWineFromProton(gameSettings)
return new Promise<string[] | null>((resolve) => {
const wineBin = alwaysWine_wineVersion.bin
// We have to run Winetricks with an actual `wine` binary, meaning we
Expand Down Expand Up @@ -719,10 +718,7 @@ export const Winetricks = {
},
listInstalled: async (runner: Runner, appName: string) => {
const gameSettings = await gameManagerMap[runner].getSettings(appName)
const { winePrefix } = await getWineFromProton(
gameSettings.wineVersion,
gameSettings.winePrefix
)
const { winePrefix } = await getWineFromProton(gameSettings)
const winetricksLogPath = join(winePrefix, 'winetricks.log')
try {
const winetricksLog = await readFile(winetricksLogPath, 'utf8')
Expand Down
11 changes: 5 additions & 6 deletions src/backend/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,12 @@ function semverGt(target: string, base: string) {
const getFileSize = fileSize.partial({ base: 2 }) as (arg: unknown) => string

async function getWineFromProton(
wineVersion: WineInstallation,
winePrefix: string
gameSettings: GameSettings
): Promise<{ winePrefix: string; wineVersion: WineInstallation }> {
if (
wineVersion.type !== 'proton' ||
(await isUmuSupported(wineVersion.type))
) {
const wineVersion = gameSettings.wineVersion
let winePrefix = gameSettings.winePrefix

if (wineVersion.type !== 'proton' || (await isUmuSupported(gameSettings))) {
return { winePrefix, wineVersion }
}

Expand Down
35 changes: 21 additions & 14 deletions src/backend/utils/compatibility_layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import {
configPath,
defaultUmuPath,
getSteamLibraries,
isLinux,
isMac,
toolsPath,
userHome
} from 'backend/constants'
import { logError, LogPrefix, logInfo } from 'backend/logger/logger'
import { execAsync } from 'backend/utils'
import { execSync } from 'child_process'
import { WineInstallation } from 'common/types'
import { GameSettings, WineInstallation } from 'common/types'
import { existsSync, mkdirSync, readFileSync, readdirSync } from 'graceful-fs'
import { homedir } from 'os'
import { dirname, join } from 'path'
Expand Down Expand Up @@ -489,12 +490,18 @@ export type AllowedWineFlags = Pick<
* @param wrapper Any wrappers to be used, may be `''`
*/
export async function getWineFlags(
wineBin: string,
wineType: WineInstallation['type'],
gameSettings: GameSettings,
wrapper: string
): Promise<AllowedWineFlags> {
let partialCommand: AllowedWineFlags = {}
const umuSupported = await isUmuSupported(wineType)
const { type: wineType, bin: wineExec } = gameSettings.wineVersion

// Fix for people with old config
const wineBin =
wineExec.startsWith("'") && wineExec.endsWith("'")
? wineExec.replaceAll("'", '')
: wineExec

switch (wineType) {
case 'wine':
case 'toolkit':
Expand All @@ -508,7 +515,7 @@ export async function getWineFlags(
`${wrapper} "${wineBin}" waitforexitandrun`
)
}
if (umuSupported) {
if (await isUmuSupported(gameSettings)) {
partialCommand['--wrapper'] = NonEmptyString.parse(
(wrapper ? `${wrapper} ` : '') + `"${await getUmuPath()}"`
)
Expand All @@ -530,11 +537,10 @@ export async function getWineFlags(
* Like {@link getWineFlags}, but returns a `string[]` with the flags instead
*/
export async function getWineFlagsArray(
wineBin: string,
wineType: WineInstallation['type'],
gameSettings: GameSettings,
wrapper: string
): Promise<string[]> {
const partialCommand = await getWineFlags(wineBin, wineType, wrapper)
const partialCommand = await getWineFlags(gameSettings, wrapper)

const commandArray: string[] = []
for (const [key, value] of Object.entries(partialCommand)) {
Expand All @@ -548,13 +554,14 @@ export const getUmuPath = async () =>
searchForExecutableOnPath('umu-run').then((path) => path ?? defaultUmuPath)

export async function isUmuSupported(
wineType: WineInstallation['type'],
gameSettings: GameSettings,
checkUmuInstalled = true
): Promise<boolean> {
const umuEnabled =
GlobalConfig.get().getSettings().experimentalFeatures?.umuSupport === true
const wineVersionSupported = wineType === 'proton'
const umuInstalled = checkUmuInstalled ? existsSync(await getUmuPath()) : true
if (!isLinux) return false
if (gameSettings.wineVersion.type !== 'proton') return false
if (gameSettings.disableUMU) return false
if (!checkUmuInstalled) return true
if (!existsSync(await getUmuPath())) return false

return umuEnabled && wineVersionSupported && umuInstalled
return true
}
3 changes: 2 additions & 1 deletion src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export type ExperimentalFeatures = {
enableNewDesign: boolean
enableHelp: boolean
cometSupport: boolean
umuSupport: boolean
}

export interface AppSettings extends GameSettings {
Expand Down Expand Up @@ -102,6 +101,7 @@ export interface AppSettings extends GameSettings {
minimizeOnLaunch: boolean
startInTray: boolean
allowInstallationBrokenAnticheat: boolean
disableUMU: boolean
}

export type LibraryTopSectionOptions =
Expand Down Expand Up @@ -202,6 +202,7 @@ export interface GameSettings {
gogSaves?: GOGCloudSavesLocation[]
beforeLaunchScriptPath: string
afterLaunchScriptPath: string
disableUMU: boolean
}

export type Status =
Expand Down
31 changes: 31 additions & 0 deletions src/frontend/screens/Settings/components/DisableUMU.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { ToggleSwitch } from 'frontend/components/UI'
import useSetting from 'frontend/hooks/useSetting'
import SettingsContext from '../SettingsContext'
import ContextProvider from 'frontend/state/ContextProvider'
import { defaultWineVersion } from '..'

const DisableUMU = () => {
const { t } = useTranslation()
const { isDefault } = useContext(SettingsContext)
const { platform } = useContext(ContextProvider)

const [disableUMU, setDisableUMU] = useSetting('disableUMU', false)
const [wineVersion] = useSetting('wineVersion', defaultWineVersion)

if (isDefault || platform !== 'linux' || wineVersion.type !== 'proton') {
return <></>
}

return (
<ToggleSwitch
htmlId="disableUMU"
value={disableUMU}
handleChange={() => setDisableUMU(!disableUMU)}
title={t('setting.disableUMU', 'Disable UMU')}
/>
)
}

export default DisableUMU
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,15 @@ import { ToggleSwitch } from 'frontend/components/UI'
import ContextProvider from 'frontend/state/ContextProvider'

const ExperimentalFeatures = () => {
const { platform } = useContext(ContextProvider)

const FEATURES = ['enableNewDesign', 'enableHelp', 'cometSupport']

if (platform === 'linux') {
FEATURES.push('umuSupport')
}

const { t } = useTranslation()
const [experimentalFeatures, setExperimentalFeatures] = useSetting(
'experimentalFeatures',
{
enableNewDesign: false,
enableHelp: false,
cometSupport: true,
umuSupport: false
cometSupport: true
}
)
const { handleExperimentalFeatures } = useContext(ContextProvider)
Expand All @@ -39,7 +32,6 @@ const ExperimentalFeatures = () => {
t('setting.experimental_features.enableNewDesign', 'New design')
t('setting.experimental_features.enableHelp', 'Help component')
t('setting.experimental_features.cometSupport', 'Comet support')
t('setting.experimental_features.umuSupport', 'Use UMU as Proton runtime')
*/

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import SyncSaves from '../SyncSaves'
import FooterInfo from '../FooterInfo'
import { Tabs, Tab } from '@mui/material'
import { GameInfo } from 'common/types'
import DisableUMU from '../../components/DisableUMU'

const windowsPlatforms = ['Win32', 'Windows', 'windows']
function getStartingTab(platform: string, gameInfo?: GameInfo | null): string {
Expand Down Expand Up @@ -214,6 +215,7 @@ export default function GamesSettings() {
<OfflineMode />
</>
)}
<DisableUMU />
<AlternativeExe />
<LauncherArgs />
<div className="Field">
Expand Down
Loading

0 comments on commit 2f428e7

Please sign in to comment.