Trainer Relay is an independent Decky Loader plugin for launching Windows trainer sidecars alongside Epic and GOG shortcuts created by UniFiDeck.
Trainer Relay is complementary to CheatDeck: keep CheatDeck for games launched directly by Steam, and use Trainer Relay for the supported UniFiDeck shortcuts. Trainer Relay does not modify UniFiDeck, Decky Loader, Proton, or Steam Runtime.
The current release candidate is v0.1.0-experimental.19. It remains experimental pending validation on a physical Steam Deck. The v1 contract uses the same Wine prefix plus the explicit container re-entry path implemented by UniFiDeck's bundled UMU 1.4.4. This build also requires UMU's exact re-entry confirmation before reporting the trainer as running.
Supported:
- UniFiDeck shortcuts carrying one literal
epic:<game_id>orgog:<game_id>token. - One absolute
.exetrainer per supported shortcut. - The default UniFiDeck prefix or an explicit absolute prefix override.
Not supported in v1:
- Ubisoft, Amazon, Battle.net, or generic launchers.
.batfiles, trainer arguments, multiple simultaneous sessions, or trainer injection.- Changes to UniFiDeck, Proton, Steam Runtime, or upstream repositories.
- Enable Developer Mode in Steam Deck settings.
- Download
TrainerRelay.zipfrom the experimental release. - In Decky Loader's developer settings, install the downloaded ZIP.
Download the plugin archive, not GitHub's automatically generated Source code.zip. Do not try to install or validate the Decky ZIP on Windows; use the package-layout checks in this repository and install it only on the Steam Deck.
- Open the UniFiDeck shortcut's game details in Game Mode.
- Open the Trainer Relay menu.
- Use the focused folder button to browse the Deck and select an absolute Windows
.exetrainer. The path field is read-only; Trainer Relay rejects relative paths,.batfiles, and arguments. - Confirm Prepare UMU container re-entry. Trainer Relay adds exactly one
UMU_CONTAINER_NSENTER=1assignment, re-reads AppDetails, and enables only after Steam confirms the expected launch options. - Optionally set an absolute prefix override. When empty, Trainer Relay uses
~/.local/share/unifideck/prefixes/<game_id>. - If the game was already open, close and relaunch it after preparation.
Trainer Relay resolves the expected executable from UniFiDeck's mapping,
selects the actual Windows process from /proc, verifies the prefix and launch
identity, and then starts the trainer through UniFiDeck's umu-run. UMU locates
the same-prefix launcher-service bus, re-enters the game's container with
steam-runtime-launch-client, and uses PROTON_VERB=runinprefix. It never
invokes a shell or evaluates user-provided command text.
Earlier CheatDeck setups may have used PROTON_REMOTE_DEBUG_CMD and PRESSURE_VESSEL_FILESYSTEMS_RW in Steam launch options. Trainer Relay can identify these two legacy variables and show the trainer it found before offering migration.
The migration preserves %command%, the epic:<game_id> or gog:<game_id>
token, and unrelated launch options. It removes only the two legacy
assignments and adds UMU_CONTAINER_NSENTER=1. After saving, Trainer Relay
re-reads AppDetails and enables the new configuration only when the expected
text is confirmed.
If CheatDeck reintroduces either legacy variable, Trainer Relay fails closed and reports invalid_config. Remove or migrate the legacy options before trying again. If the proposed trainer is not the one intended for the shortcut, cancel the migration and configure the path manually.
The watcher is deliberately fail-closed:
- zero matching processes means
waiting_for_game; - more than one matching candidate means
ambiguous, and no trainer starts; - PID reuse is rejected using
/proc/<pid>/statstart time; - the executable-like process name is required for initial acquisition, but a
previously accepted
PID + starttimeremains valid if the game renames its main thread while executable, prefix, store, and required environment stay unchanged; - the game, prefix, environment allowlist, and launch identity must agree;
- the accepted game process must contain
UMU_CONTAINER_NSENTER=1; otherwise the plugin reportscontainer_reentry_missingand launches nothing; - before spawn, the plugin resolves the matching UMU runtime launch client and
queries it through the Steam/Deck user session bus, never the game's nested
pressure-vessel bus, and requires the exact same-prefix service to be listed;
otherwise it reports one bounded
container_reentry_*result for that game session and launches nothing until manual Retry or a new session; - the
umu-runsidecar receives the selected UniFiDeck compatdata root, not the child process's Proton-expanded<root>/pfxvalue; - a trainer is considered running only after it remains alive for three seconds;
- one automatic retry is allowed after a two-second delay when the trainer exits
before the watcher has observed it in
running, while the same game session remains active; - later retries require the manual Retry action;
- shutdown signals only the process group created by Trainer Relay, waits five seconds, then force-terminates that same group if necessary.
The game is left untouched if the trainer fails, is ambiguous, or exits prematurely. Trainer Relay does not terminate the game, wineserver, global UMU processes, or another trainer.
Experimental .13 adds a separate Diagnostics page. Diagnostic mode is off by default; once enabled it remains enabled across restarts until you turn it off. The page shows the latest 20 sanitized events, storage use, any bounded storage code, and the last TXT export path. The journal rotates through five 10 MiB files for a hard 50 MiB limit.
Export TXT writes a timestamped report atomically to /home/deck/Downloads. Clear logs removes only Trainer Relay's rotating journal and metadata after confirmation; it does not remove exported TXT files. While diagnostic mode is enabled, the same sanitized events appear in CEF DevTools under the filter [TrainerRelay:diagnostic].
Allowed technical values are limited to identity/session anchors, expected and observed executable/prefix paths, trainer and umu-run paths, GAMEID, STORE, WINEPREFIX, PROTONPATH, bounded counts, exit codes, and timing. The journal rejects complete environments, complete command lines, credentials, cookies, tokens, authorization data, and legacy debug-command content. When a spawned process exits, it may retain at most a 1,024-character sanitized tail from each inherited UMU stdout/stderr pipe; because Proton/Wine children can inherit those pipes, review that small tail before sharing an export. The environment copied to the trainer is a separate explicit allowlist; PROTON_REMOTE_DEBUG_CMD is never copied, the UMU-derived STEAM_COMPAT_CLIENT_INSTALL_PATH is not replayed, and PROTON_VERB=runinprefix is set last.
For the physical-device checklist, see docs/STEAM-DECK-VALIDATION.md. For the architectural decision, see docs/adr/0001-session-watcher.md.
To roll back, disable the per-game Trainer Relay configuration or uninstall the plugin from Decky Loader. Restore the original CheatDeck setup only after removing the Trainer Relay configuration; the game remains independently launchable throughout.
- Unsupported shortcut: confirm the launch options contain exactly one literal
epic:<game_id>orgog:<game_id>token supplied by UniFiDeck. - Legacy migration blocks a plain Epic/GOG token: install
v0.1.0-experimental.5or newer;.4incorrectly required%command%while planning migration. - Controls receive focus but pressing A does nothing: install
v0.1.0-experimental.12. In.11, the Python package was outside Decky's requiredpy_modulesdirectory, so the backend failed to import and the UI remained in a disabled loading state..12repairs the installed layout and reports an unavailable backend after five seconds instead of loading forever. Relay enablement remains fail-closed until legacy launch options are repaired. - The game opens but the trainer remains at
waiting_for_game: install.14, enable the persistent mode on the Diagnostics page, reproduce once, then export the TXT. Look forcandidate_rejectedand its bounded reason such asprefix_mismatch,process_name_mismatch,store_mismatch, orexecutable_mismatch..14accepts UniFiDeck's validGAMEID=umu-0while still rejecting UMU wrappers and Wine helper processes. waiting_for_game: launch the shortcut from UniFiDeck and allow the launcher to reach the game process before pressing Retry.ambiguous: close duplicate launcher/game instances and try again. Trainer Relay will not guess.invalid_config (container_reentry_missing): complete UMU container preparation, close the currently running game, and launch it again.invalid_config (container_reentry_bus_missing): the prepared game did not expose the exact same-prefix service after at most five launch-client invocations across all host-session candidates; restart it and export Diagnostics.container_reentry_unsupportedorcontainer_reentry_probe_failedmeans the runtime/client could not be identified or queried safely..18records a bounded failure class, probe exit code, attempt count, and bus source without retaining stderr or a full environment.- Other
invalid_config: remove the legacy variables, or complete the migration prompt and verify the resulting launch options. failed: confirm the.exeis absolute and readable, the prefix exists, and the trainer supports the game's Wine environment.- Trainer window not visible: switch between open windows with the Steam button; this plugin does not force window focus.
Please include the exported diagnostic TXT, status, and diagnostic code when reporting a bug. Do not attach complete /proc environments, credentials, or private launch options.
Trainer Relay is derived from the CheatDeck project by SheffeyG. It is an independent project and is not officially affiliated with or endorsed by CheatDeck, SheffeyG, UniFiDeck, or Decky Loader.
This project is licensed under the GNU GPL v3 or later.