# Steam compatibility tool interface <!-- This document: Copyright 2021 Collabora Ltd. SPDX-License-Identifier: MIT --> ## Compatibility tool declaration Compatibility tools are declared by a file `compatibilitytool.vdf`, which is a [VDF](https://developer.valvesoftware.com/wiki/KeyValues) text file in this format: ``` "compatibilitytools" { "compat tools" { "my_compat_tool" { "install_path" "." "display_name" "My Compatibility Tool" "from_oslist" "windows" "to_oslist" "linux" } } } ``` `compat tools` can presumably contain one or more compatibility tools, each identified by a unique name, in this case `my_compat_tool`. Each compatibility tool can have these fields: `install_path` : Installation directory containing `toolmanifest.vdf` and other necessary files, for example `/path/to/steamapps/common/Proton - Experimental` for Proton `display_name` : Name to display in the Steam user interface `from_oslist` : Operating system(s?) whose executables can be run by this compatibility tool, for example `windows` for Proton `to_oslist` : Operating system(s?) that can run this compatibility tool, for example `linux` for Proton For the compatibility tools provided by Steam itself, such as Steam Linux Runtime and Proton, the equivalent of this information is downloaded out-of-band and stored in `~/.steam/root/appcache/appinfo.vdf`, so the depot downloaded into the Steam library will not contain this file. Third-party compatibility tools can be installed in various locations such as `~/.steam/root/compatibilitytools.d`. Each subdirectory of `compatibilitytools.d` that contains `compatibilitytool.vdf` is assumed to be a compatibility tool. The installation path should be `"."` in this case. Alternatively, the manifest can be placed directly in `compatibilitytools.d` with the `install_path` set to the relative or absolute path to the tool's installation directory. ## Tool manifest Each compatibility tool has a *tool manifest*, `toolmanifest.vdf`, which is a [VDF](https://developer.valvesoftware.com/wiki/KeyValues) text file with one top-level entry, `manifest`. `manifest` has the following known fields: `version` : The version number of the file format. The current version is `2`. The default is `1`. `commandline` : The command-line for the entry point. `require_tool_appid` : If set, this compatibility tool needs to be wrapped in another compatibility tool, specified by its numeric Steam app ID. For example, Proton 5.13 needs to be wrapped by "Steam Linux Runtime - soldier", so it sets this field to `1391110`. `unlisted` : If `1`, this compatibility tool will not be shown to users as an option for running games. For example, until Steam is able to distinguish between Steam Runtime 1 'scout' and Steam Runtime 2 'soldier' as distinct platforms, it is not useful to run ordinary Steam games under 'soldier', due to it being incompatible with 'scout'. `use_sessions` : If set to `1`, older versions of Steam would use the "session mode" described below. `use_tool_subprocess_reaper` : If set to `1`, Steam will send `SIGINT` to the compatibility tool instead of proceeding directly to `SIGKILL`, to give it a chance to do graceful cleanup. ## Command-line In a version 1 manifest, the `commandline` is a shell expression. The first word should start with `/` and is interpreted as being relative to the directory containing the compat tool, so `/run` really means something like `~/.local/share/Steam/steamapps/common/MyCompatTool/run`. The game's command-line will be appended to the tool's command-line, so if you are using `getopt` or a compatible command-line parser, you might want to use a `commandline` like `/run --option --other-option --` to get the game's command-line to appear after a `--` separator. In a version 2 manifest, the special token `%verb%` can appear in the `commandline`. It is replaced by either `run` or `waitforexitandrun`. If a compatibility tool has `require_tool_appid` set, then the command-line is built up by taking the required tool, then appending the tool that requires it, and finally appending the actual game: so if a compat tool named `Inner` requires a compat tool named `Outer`, and both tools have `/run %verb% --` as their `commandline`, then the final command line will look something like this: /path/to/Outer/run waitforexitandrun -- \ /path/to/Inner/run waitforexitandrun -- \ /path/to/game.exe (In reality it would all be one line, rather than having escaped newlines as shown here.) ## Launch options If a Steam game has user-specified launch options configured in its Properties, and they contain the special token `%command%`, then `%command%` is replaced by the entire command-line built up from the compatibility tools. For example, if a compat tool named `Inner` requires a compat tool named `Outer`, both tools have `/run %verb% --` as their `commandline`, and the user has set the Launch Options in the game's properties to DEBUG=yes taskset --cpu-list 0,2 %command% -console then the final command line will look something like this: DEBUG=yes taskset --cpu-list 0,2 \ /path/to/Outer/run waitforexitandrun -- \ /path/to/Inner/run waitforexitandrun -- \ /path/to/game.exe -console (In reality it would all be one line, rather than having escaped newlines as shown here.) If the user-specified launch options for a Steam game do not contain `%command%`, then they are simply appended to the command-line. For example, setting the launch options to `--debug` is equivalent to `%command% --debug`. Shortcuts for non-Steam games do not currently implement the special handling for `%command%`: the configured launch options are simply appended to the command-line, regardless of whether they contain `%command%` or not. Please note that not all invocations of compatibility tools take the user-specified launch options into account: they are used when running the actual game, but they are not used for install scripts. ## Environment Some environment variables are set by Steam, including: `LD_LIBRARY_PATH` : Used to load Steam Runtime libraries. `LD_PRELOAD` : Used to load the Steam Overlay. `STEAM_COMPAT_APP_ID` : The decimal app-ID of the game, for example 440 for Team Fortress 2. `STEAM_COMPAT_CLIENT_INSTALL_PATH` : The absolute path to the directory where Steam is installed. This is the same as the target of the `~/.steam/root` symbolic link. `STEAM_COMPAT_DATA_PATH` : The absolute path to a directory that compatibility tools can use to store game-specific data. For example, Proton puts its `WINEPREFIX` in `$STEAM_COMPAT_DATA_PATH/pfx`. `STEAM_COMPAT_INSTALL_PATH` : The absolute path to the game's installation directory, for example `/home/you/.local/share/Steam/steamapps/common/Estranged Act I`, even if the current working directory is a subdirectory of this (as is the case for Estranged: Act I (261820) for example). `STEAM_COMPAT_LIBRARY_PATHS` : Colon-delimited list of paths to Steam Library directories containing the game, the compatibility tools if any, and any other resources that the game will need, such as DirectX installers. `STEAM_COMPAT_MOUNTS` : Colon-delimited list of paths to additional directories that are to be made available read/write in a pressure-vessel container. `STEAM_COMPAT_SESSION_ID` : In the historical session mode (see below), this is used to link together multiple container invocations into a logical "session". `STEAM_COMPAT_SHADER_PATH` : The absolute path to the variable data directory used for cached shaders, if any. `STEAM_COMPAT_TOOL_PATHS` : Colon-delimited list of paths to Steam compatibility tools in use. Typically this will be a single path, but when using tools with `require_tool_appid` set, such as Proton, it is a colon-separated list with the "innermost" tool first and the "outermost" tool last. `SteamAppId` : The same as `STEAM_COMPAT_APP_ID`, but only when running the actual game; not set when running [install scripts](https://partner.steamgames.com/doc/sdk/installscripts) or other setup commands. The Steamworks API assumes that every process with this environment variable set is part of the actual game. When Steam invokes a tool with `require_tool_appid` set, such as Proton, the environment variables are set as described for the "outermost" tool, which can change them if required before it invokes the "innermost" tool (although most should not be changed). For example, when using Proton with "Steam Linux Runtime - soldier", the "outer" compatibility tool "Steam Linux Runtime - soldier" is run with `LD_LIBRARY_PATH` set by the Steam Runtime to point to mixed host and scout libraries. After setting up the container, it runs the "inner" compatibility tool (Proton) with an entirely new `LD_LIBRARY_PATH` pointing to mixed host and soldier libraries. ## Native Linux Steam games This is the simplest situation and can be considered to be the baseline. In recent versions of Steam, the game process is wrapped in a `reaper` process which sets itself as a subreaper using `PR_SET_CHILD_SUBREAPER` (see [**prctl**(2)](https://manpages.debian.org/unstable/manpages-dev/prctl.2.en.html) for details). Version 1 compat tools are not invoked specially. Version 2 compat tools are invoked with a `%verb%` in the `commandline` (if any) replaced by `waitforexitandrun`. The game is expected to run in the usual way. Conventionally, it does not double-fork to put itself in the background, but if it does, any background processes will be reparented to have the subreaper as their parent. The current working directory is the working directory configured in the game's Steam metadata (for example `estrangedact1` for Estranged: Act 1, 261820), or the game's directory (or maybe the executable's directory?) if unconfigured. The compatibility tool is expected to preserve this working directory when running the actual game. The environment variable `STEAM_COMPAT_SESSION_ID` is not set. The environment variable `STEAM_COMPAT_APP_ID` is set to the app-ID of the game. The environment variable `SteamAppId` has the same value. The [launch options](#launch-options) are used. ## Windows games with no install script Some Windows games, such as Soldat (638490), do not have an "install script" in their metadata. Running these games behaves much the same as a native Linux game: it is wrapped in a subreaper, version 1 compat tools are not invoked specially, and version 2 compat tools are invoked with a `%verb%` in the `commandline` (if any) replaced by `waitforexitandrun`. The environment variable `STEAM_COMPAT_SESSION_ID` is not set. Historically, it was set to a unique token that was not used in any previous invocation. The environment variable `STEAM_COMPAT_APP_ID` is set to the app-ID of the game. The environment variable `SteamAppId` has the same value. The current working directory is the working directory configured in the game's Steam metadata, as usual. The [launch options](#launch-options) are used. ## Windows games with an install script (since Steam beta 1623823138) Other Windows games, such as Everquest (205710), have an [install script](https://partner.steamgames.com/doc/sdk/installscripts), conventionally named `installscript.vdf`, which must be run by an interpreter. Since Steam beta 1623823138, these games are run like this: * Run the install script. For version 1 compat tools, the compat tool is run as usual, except that the command appended to the command-line is the install script interpreter, not the actual game. For version 2 compat tools, the `%verb%` is set to `run` instead of the usual `waitforexitandrun`. The environment variable `STEAM_COMPAT_SESSION_ID` is not set. The environment variable `STEAM_COMPAT_APP_ID` is set to the app-ID of the game. The environment variable `SteamAppId` is *not* set in this case. The current working directory is *not* the game's directory in this case. As currently implemented, it appears to be the Steam installation (usually `~/.local/share/Steam` or `~/.steam/debian-installation`) but this is probably not guaranteed. The [launch options](#launch-options) are **not** used here. * Run the actual game in the usual way. The environment variable `STEAM_COMPAT_SESSION_ID` is not set. The environment variables `STEAM_COMPAT_APP_ID` and `SteamAppId` are both set to the app-ID of the game as usual. The current working directory is the working directory configured in the game's Steam metadata, as usual. The [launch options](#launch-options) are used, as usual. ## Historical behaviour of Windows games (session mode v2) Before Steam beta 1623823138, each Windows game had several setup commands to run before it could be launched. They were invoked like this: * Run the first setup command. For version 1 compat tools, the compat tool is run as usual, except that the command appended to the command-line is the setup command, not the actual game. For version 2 compat tools, additionally the `%verb%` is set to `run` instead of `waitforexitandrun`. The environment variable `STEAM_COMPAT_SESSION_ID` is set to a unique token (in practice a 64-bit number encoded in hex). The environment variable `STEAM_COMPAT_APP_ID` is set to the app-ID of the game. The environment variable `SteamAppId` is *not* set in this case. The current working directory is *not* the game's directory in this case. As currently implemented, it appears to be the Steam installation (usually `~/.local/share/Steam` or `~/.steam/debian-installation`) but this is probably not guaranteed. The [launch options](#launch-options) are **not** used here. The compat tool is expected to do any setup that it needs to do (for example pressure-vessel must start a container), then run the given command and exit. The compat tool may leave processes running in the background. * Run the second setup command, in the same way as the first, and so on. More than one command can be run in parallel, so tools that need to do setup (such as pressure-vessel) must use a lock to ensure the setup is only done by whichever setup command wins the race to be the first. All environment variables are the same as for the first setup command, and in particular the `STEAM_COMPAT_SESSION_ID` is the same. This is how compatibility tools can know which containers or subprocesses to reuse. * When all setup commands have finished, run the actual game. For version 2 compat tools, the `%verb%` is set to `waitforexitandrun`. The compat tool is expected to terminate any background processes (for example pressure-vessel terminates the container and Proton terminates the `wineserver`) and wait for them to exit, then launch the actual game. The environment variable `STEAM_COMPAT_SESSION_ID` is set to the same unique token as for all the setup commands, so that the compat tool can identify which subprocesses or containers to tear down. The environment variables `STEAM_COMPAT_APP_ID` and `SteamAppId` are both set to the app-ID of the game as usual. The [launch options](#launch-options) are used, as usual. ## Non-Steam games Version 1 compat tools are not invoked specially. Version 2 compat tools are invoked with a `%verb%` in the `commandline` (if any) replaced by `waitforexitandrun`. The environment variable `STEAM_COMPAT_SESSION_ID` is not set. Historically, it was set to a unique token that was not used in any previous invocation. `STEAM_COMPAT_APP_ID` is set to `0`. `SteamAppId` is not set. `STEAM_COMPAT_DATA_PATH` is set to a unique directory per non-Steam game. It is currently in the same format as Steam games' equivalent directories, but with a large arbitrary integer replacing the Steam app ID. `STEAM_COMPAT_INSTALL_PATH` is not set. `LD_LIBRARY_PATH` is usually set to the empty string to "escape" from the Steam Runtime library stack for non-Steam games, although games configured in Steam via development tools can be flagged to use the Steam Runtime library stack (this is done for games that are intended to be released onto Steam as native Linux games). The current working directory defaults to the directory containing the main executable, but can be reconfigured through the shortcut's properties. Steam Linux Runtime (pressure-vessel) containers will not work as expected unless this is set to the game's top-level directory. Launch options are appended to the command-line. The special token `%command%` in Steam games' launch options is not used here.