From d2730ab1d19105ebd1fa606e25db91c09a504e87 Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Wed, 16 Jun 2021 18:12:17 +0100 Subject: [PATCH] Document the Steam compatibility tool interface This is based on testing with Steam beta 1623823138 and older versions, and is not necessarily authoritative. Signed-off-by: Simon McVittie <smcv@collabora.com> --- docs/steam-compat-tool-interface.md | 361 ++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 docs/steam-compat-tool-interface.md diff --git a/docs/steam-compat-tool-interface.md b/docs/steam-compat-tool-interface.md new file mode 100644 index 000000000..51b3f8bc1 --- /dev/null +++ b/docs/steam-compat-tool-interface.md @@ -0,0 +1,361 @@ +# Steam compatibility tool interface + +## 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: + + /path/to/Outer/run waitforexitandrun -- /path/to/Inner/run waitforexitandrun -- /path/to/game.exe + +## 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. + +## 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. + +## 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. + +* 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. + +## 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 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. + +## 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. -- GitLab