Skip to content

Instantly share code, notes, and snippets.

@Damglador
Last active July 29, 2025 20:04
Show Gist options
  • Save Damglador/8063983f424a800531699cdc7e894462 to your computer and use it in GitHub Desktop.
Save Damglador/8063983f424a800531699cdc7e894462 to your computer and use it in GitHub Desktop.
Doorstop for Linux

Overview

Script to use BepInEx on Linux native games, mainly in Steam.

Quirks:

  • If it's a Steam game, you can't use this script on it outside of Steam
  • Any launch parameters or wrappers between ./doorstop.sh and %command% will be discarded
  • Needs doorstop_libs/libdoorstop_x64.so and doorstop_libs/libdoorstop_x86.so near itself.

Instructions

  1. Go to Steam, right click on your game > Properties > Local Files > View local files.
  2. Create a file called doorstop.sh in the opened folder
  3. Paste in the file text from doorstop.sh below.
  4. Make the file executable (chmod +x /path/to/doorstop.sh or using your file manager)
  5. Copy doorstop_libs from /home/damglador/.config/r2modmanPlus-local/$NATIVEGAME/profiles/$ANYPROFILE/ to the directory where doorstop.sh is
  6. Go back to Steam, right click on your game > Properties > General
  7. Paste ./doorstop.sh %command% in Launch Options
  8. Launch the game through Steam or r2modman/Gale

If the game is launched from Steam it'll try to load BepInEx from the game directory, if it's launched through r2modman/Gale it'll load the profile you used.

If it can't find executable, edit executable= in the script to match the name of your game's executable, for example executable=valheim.x86_64. To see the logs of the script you can do ./doorstop.sh %command% > ~/doorstop.log in Launch Options in Steam

Related issues

#!/bin/sh
# Written by Damglador for r2modman and Gale. Based on script made by Naomi Calabretta for r2modman
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(cd "$a" || exit; pwd -P)
[ -z $GAMEDIR ] && GAMEDIR=$BASEDIR
[ -z $PROFILEDIR ] && PROFILEDIR=$BASEDIR
log() {
printf "[doorstop.sh] $@\n"
}
# If it can't find executable, set this to executable name, for example valheim.x86_64 or EtG.x86_64
executable=
i=0
for arg in "$@"; do
i=$((i + 1))
log "Arg $i = $arg"
case "$arg" in
SteamLaunch) SteamLaunch=1; GAMEDIR=$(pwd -P);;
"$GAMEDIR"/*."$(uname -m)"|"$GAMEDIR"/*.x86_64|"$GAMEDIR"/*.x86) gamearg=${i};;
esac
done
if [ -n $gamearg ] && [ "$SteamLaunch" = "1" ]; then
shift $gamearg # Skip over Steam wrappers
fi
find_game(){
for f in "$GAMEDIR/"*.$(uname -m); do
[ -f "$f" ] && echo "${f}" && return
done
for f in "$GAMEDIR/"*.x86_64; do
[ -f "$f" ] && echo "${f}" && return
done
for f in "$GAMEDIR/"*.x86; do
[ -f "$f" ] && echo "${f}" && return
done
return 1
}
# If executable is empty, try to find executable in game directory
if [ -z "$executable" ]; then
log "Executable name is not specified, trying to find executable"
exec=$(find_game)
[ -z "$exec" ] && log "No executable found, exiting" && exit 1
else
exec="$GAMEDIR/$executable" # If script is run from Steam, take pwd -P as the directory with game executable
fi
export DOORSTOP_ENABLE=TRUE
export DOORSTOP_INVOKE_DLL_PATH="$PROFILEDIR/BepInEx/core/BepInEx.Preloader.dll"
export DOORSTOP_CORLIB_OVERRIDE_PATH=""
# Helper to convert common boolean strings into just 0 and 1
doorstop_bool() {
case "$(echo "$1" | tr a-z A-Z)" in
TRUE|T|1|Y|YES)
echo "TRUE"
;;
FALSE|F|0|N|NO)
echo "FALSE"
;;
esac
}
# r2modman gives --doorstop-enable true
# --doorstop-target ".../profiles/Default/BepInEx/core/BepInEx.Preloader.dll"
# --r2profile "Default"
# Gale gives "-applaunch" "311690"
# "--doorstop-enable" "true"
# "--doorstop-target" ".../profiles/Default/BepInEx/core/BepInEx.Preloader.dll"
while :; do
case $1 in
--doorstop-enable)
if [ -n "$2" ]; then
export DOORSTOP_ENABLE=$(doorstop_bool "$2")
shift
else
echo "No --doorstop-enable value specified, using default!"
fi
;;
--doorstop-target)
if [ -n "$2" ]; then
export DOORSTOP_INVOKE_DLL_PATH="$2"
shift
else
echo "No --doorstop-target value specified, using default!"
fi
;;
--doorstop-dll-search-override)
if [ -n "$2" ]; then
export DOORSTOP_CORLIB_OVERRIDE_PATH="$2"
shift
else
echo "No --doorstop-dll-search-override value specified, using default!"
fi
;;
--doorstop-target-assembly)
if [ -n "$2" ]; then
export DOORSTOP_TARGET_ASSEMBLY="$2"
shift
fi
;;
--doorstop-boot-config-override)
if [ -n "$2" ]; then
export DOORSTOP_BOOT_CONFIG_OVERRIDE="$2"
shift
fi
;;
--doorstop-mono-dll-search-path-override)
if [ -n "$2" ]; then
export DOORSTOP_MONO_DLL_SEARCH_PATH_OVERRIDE="$2"
shift
fi
;;
--doorstop-mono-debug-enabled)
if [ -n "$2" ]; then
export DOORSTOP_MONO_DEBUG_ENABLED=$(doorstop_bool "$2")
shift
fi
;;
--doorstop-mono-debug-suspend)
if [ -n "$2" ]; then
export DOORSTOP_MONO_DEBUG_SUSPEND=$(doorstop_bool "$2")
shift
fi
;;
--doorstop-mono-debug-address)
if [ -n "$2" ]; then
export DOORSTOP_MONO_DEBUG_ADDRESS="$2"
shift
fi
;;
--doorstop-clr-runtime-coreclr-path)
if [ -n "$2" ]; then
export DOORSTOP_CLR_RUNTIME_CORECLR_PATH="$2"
shift
fi
;;
--doorstop-clr-corlib-dir)
if [ -n "$2" ]; then
export DOORSTOP_CLR_CORLIB_DIR="$2"
shift
fi
;;
*)
if [ -z "$1" ]; then
break
fi
rest="$rest $1"
;;
esac
shift
done
export LD_LIBRARY_PATH="$PROFILEDIR/doorstop_libs:$LD_LIBRARY_PATH"
export LD_PRELOAD="libdoorstop_x64.so:libdoorstop_x86.so:$LD_PRELOAD"
log "DOORSTOP_ENABLE = $DOORSTOP_ENABLE"
log "DOORSTOP_INVOKE_DLL_PATH = $DOORSTOP_INVOKE_DLL_PATH"
log "DOORSTOP_CORLIB_OVERRIDE_PATH = $DOORSTOP_CORLIB_OVERRIDE_PATH"
log "LD_LIBRARY_PATH = $LD_LIBRARY_PATH"
log "LD_PRELOAD = $LD_PRELOAD"
log "rest = $rest"
# Run the main executable
exec "$exec" $rest
@Damglador
Copy link
Author

Hmm, I should implement a fallback for cases when a game is using Proton

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