Skip to content

Instantly share code, notes, and snippets.

@brunoais
Forked from thingsiplay/proton
Last active January 23, 2024 18:48
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save brunoais/575db9912368124d3223784afe20158c to your computer and use it in GitHub Desktop.
Save brunoais/575db9912368124d3223784afe20158c to your computer and use it in GitHub Desktop.
Proton script
#!/bin/bash
# Execute Windows programs with Proton from Steams installation folder, without
# starting Steam client.
#
# 1. Create a directory for Proton environment to run in. As an example make a
# folder "proton" in your home directory. This folder must exist in order
# to make Proton work.
#
# 2. Point the variable "env_dir" in this script to that folder or...
#
# 3. ... alternatively set the environmenal variable "$PROTONPREFIX" to this
# folder before running the script. It works similar to the "$WINEPREFIX"
# from WINE and will have higher priority over "env_dir".
#
# 4. Look in your Steam installation folder at "steamapps/common/" folder for
# available Proton versions. Pick one and point the script variable
# "proton_version" to this that folder name, in example "Proton 3.16".
# Note: You have to download a Proton version from Steam first, if none is
# there yet.
#
# 5. Or alternatively set the environmental variable "$PROTONVERSION" to that
# folder name of Proton version before running the script. It has higher
# priority over script variable "proton_version".
#
# 6. Optionally install/copy this script in a directory that is in your $PATH,
# so you can run it easily from any place. Or set the default interpreter
# for .exe files to this script.
#
# Usage:
# proton program.exe
#
# or:
# export PROTONPREFIX="$HOME/proton_316"
# export PROTONVERSION="Proton 3.16"
# proton program.exe
# Folder name possibilities for the Proton version found under "steamapps/common/".
possible_proton_versions=( "Proton - Experimental" "Proton 6.3" "Proton 5.0" "Proton 3.16" )
# Path to installation directory of Steam.
# Contains the possibilities on where to find steam
possible_client_dirs=( "$HOME/.local/share/Steam" "$HOME/.steam/steam" "/var/.steam" "/var/steam" "/var/local/Steam" )
# Default data folder for Proton/WINE environment. Folder must exist.
# If the environmental variable PROTONPREFIX is set, it will overwrite the value set here.
# If no directory is found, the last one is created and used
alternative_env_dirs=( "$HOME/proton" "$HOME/.proton" )
# Proton logging.
# Uncomment to activate logging for proton
# export PROTON_LOG="+timestamp,+pid,+tid,+seh,+debugstr,+module"
# export PROTON_LOG_DIR="/dev/shm/proton"
# Proton modes to run
# run = start target app
# waitforexitandrun = wait for wineserver to shut down
# getcompatpath = linux -> windows path
# getnativepath = windows -> linux path
mode=run
echoerr() { echo "$@" 1>&2; }
discover_proton_version () {
for possible_proton_version in "${possible_proton_versions[@]}"
do
for possible_client_dir in "${possible_client_dirs[@]}"
do
if [[ -f "$possible_client_dir/steamapps/common/$possible_proton_version/proton" ]]
then
client_dir="${client_dir:-$possible_client_dir}"
proton_version="${proton_version:-$possible_proton_version}"
echoerr "discover_proton_version:" "$possible_client_dir/steamapps/common/${possible_proton_version}/proton"
return
fi
done
done
}
discover_env_dir () {
for alternative_env_dir in "${alternative_env_dirs[@]}"
do
if [[ -d "$alternative_env_dir" ]]
then
echoerr "alternative_env_dir:" "$alternative_env_dir"
env_dir="$alternative_env_dir"
return
fi
done
env_dir=${$alternative_env_dir[-1]}
echoerr "final fallback env dir:" "$env_dir"
}
client_dir=
env_dir=
proton_version=
if [[ "$1" =~ ((.*?)/steamapps/common/([^/]+)/) ]]
then
app_dir="${BASH_REMATCH[1]}"
client_dir="${BASH_REMATCH[2]}"
app_dir_name="${BASH_REMATCH[3]}"
# get the appid
app_id=$(grep '"appid"' "$(grep -l "$app_dir_name" "$client_dir/steamapps/"*.acf)" |
sed -Ee 's/.*?"([0-9]+)".*/\1/g')
env_dir="$client_dir/steamapps/compatdata/$app_id/"
echo "$env_dir"
if [[ ! -d "$env_dir" ]]
then
env_dir="${alternative_env_dirs[0]}"
echoerr "Env dir final fallback"
fi
echoerr "Env dir as:" " $env_dir"
else
discover_proton_version
fi
# ENVIRONMENTAL VARIABLES
if [ -n "${PROTONPREFIX+1}" ]
then
env_dir=$PROTONPREFIX
elif [ -z $env_dir ]
then
discover_env_dir
fi
if [ -n "${PROTONVERSION+1}" ]
then
proton_version=$PROTONVERSION
elif [ -z "$proton_version" ] || [ -z "$client_dir" ]
then
discover_proton_version
fi
# EXECUTE
export STEAM_COMPAT_CLIENT_INSTALL_PATH=$client_dir
export STEAM_COMPAT_DATA_PATH=$env_dir
[[ ! -z "$PROTON_LOG" ]] && echo "$client_dir/steamapps/common/$proton_version/proton" $mode $*
[[ ! -z "$PROTON_LOG_DIR" ]] && mkdir -p "$PROTON_LOG_DIR"
# Make sure the directory exists
[ -d "$env_dir" ] || (mkdir -p "$env_dir" && echoerr "Proton directory created: $env_dir")
"$client_dir/steamapps/common/$proton_version/proton" run "$@"
@thingsiplay
Copy link

Nice iteration and modifications. I have quickly tested it and the added functionalities are nice to have quality of life improvements, such as the logging. This versions may actually be better suited for new users who don't know how to edit shell scripts, as these things are all done here. Thanks for the sharing.

@brunoais
Copy link
Author

Ur welcome

@Matheus-Garbelini
Copy link

Hi @thingsiplay and @brunoais
I get the following error:

discover_proton_version: /home/matheus/.steam/steam/steamapps/common/Proton - Experimental/proton
final fallback env dir: /home/matheus/.steam/steam
wine: could not load ntdll.so: /home/matheus/.steam/steam/steamapps/common/Proton - Experimental/files/lib//wine/ntdll.so: wrong ELF class: ELFCLASS32

@thingsiplay
Copy link

What are you trying to run? A game? Running Proton through such a script could still fail in many complex cases, such as playing a game and is not a full replacement for WINE or running Proton in Steam itself. Just a disclaimer here.

As for the error, it might be that the program is not compatible with WINE/Proton (Proton is based on WINE). To me it looks like the program would complain about a file in WINE. Now all I am saying here is speculation. Searching for this problem in the web, I found someone having wrong ELF class on ntdll.so running WINE on ARM and I don't know how relevant this is: https://unix.stackexchange.com/questions/670748/wine-wine-could-not-load-ntdll-so-null-on-archlinux-arm

Does the program or game work in Steam itself? If it doesn't work in Steam anyway, then there is less of a chance to get it working without Steam. The problem could be WINE related and isn't a simple script solution like here. I hope I am not totally wrong there.

@brunoais
Copy link
Author

Thanks for answering him, @thingsiplay

@Matheus-Garbelini
Copy link

Matheus-Garbelini commented Dec 13, 2021

@thingsiplay @brunoais thanks all. I was just trying to execute notepad or cmd using this script to test my proton installation via SteamCMD.
Since i searched around and it seems there is a lot of environmental variables that are passed through Steam Runtime Solder.
I ended up using this script with such runtime and got it to work with simple programs like notepad by doing this:

./run-in-soldier "/home/matheus/.steam/steamapps/common/Proton - Experimental/run.sh" /home/matheus/.steam/steamapps/compatdata/90/pfx/drive_c/windows/notepad.exe

Above uses env_dir="/home/matheus/.steam/steamapps/compatdata/90/"

The run-in-soldier here does the trick and supposedly runs the script in an environment that proton can run. Even then I still get crashes when trying to run half life server (hlds). Since I don't know the specifics of proton, I was hoping to just get the command line steam uses to launch such tool (hlds) via soldier runtime.

I ended up just using Lutris custom wine version 6.21-3, but it would be nice to get that one-line command that run your EXE binary after downloading it via SteamCMD.

If you guys know how to get the exact command line that steam uses when launching some game via steam play compatibility it would be great

@brunoais
Copy link
Author

You can try launching the game and then run
ps -efly | grep 'half|hl'
If nothing appears, try other alternatives in replacement of:
half|hl
The objective is to find the executable.
In the worst case, you have to look at all results without grep
ps -efly
As see if you can find proton call.

@heeen
Copy link

heeen commented Mar 25, 2022

How do I add commandline arguments?

@brunoais
Copy link
Author

@heeen You just type them after the command

@heeen
Copy link

heeen commented Mar 25, 2022

That doesn't seem to work, it does not launch the app at all

@brunoais
Copy link
Author

brunoais commented Mar 25, 2022

What's the output? What's the returning status code? Does it work if launching from steam?
Did you try forcing the same proton version as the one steam uses?
Does the program have DRM?

@heeen
Copy link

heeen commented Mar 25, 2022

its a plain unity3d app build.

⌘ [windows-build] $ PROTONPREFIX=~/proton-env proton MyApp.exe -logFile -
discover_proton_version: /home/florian/.steam/steam/steamapps/common/Proton - Experimental/proton
esync: up and running.
wine: RLIMIT_NICE is <= 20, unable to use setpriority safely
⌘ [windows-build] $ echo $?
0

it works if you run it with plain wine. it runs with proton if you run it without arguments

@heeen
Copy link

heeen commented Mar 25, 2022

you can reproduce it easily

PROTONPREFIX=~/proton-env proton notepad.exe test.txt
wine notepad.exe test.txt

@heeen
Copy link

heeen commented Mar 25, 2022

using "$@" instead of "$*" in the proton run call makes it work

@thingsiplay
Copy link

@heeen Good catch and thank you for reporting the solution (I was reading too, but didn't found anything). Need to update the base script as well soon.

@brunoais
Copy link
Author

brunoais commented May 27, 2023

using "$@" instead of "$*" in the proton run call makes it work

Wow 1 year without reply. I went on to test again and in my setup it works.

My guess is a need to expand the arguments themselves instead of specifying each argument individually. I ended up not doing anything for your case but my script keeps working for me, even since you mentioned and I haven't tweaked it since... I did make a wrapper for some of the end vars so it finds my directories but other than that, it keeps on working...

With your change, my tests fail because there's spaces in the directory paths which ruin the process.

@thingsiplay
Copy link

That's interesting. I would think the other way round. BTW I just linked to this fork, because someone in Reddit pointed out that the old version from me did not work, but your script worked with modern Proton. So nice work!

@brunoais
Copy link
Author

Thanks mate!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment